Record the use of Vite to create Vue3+TS projects and integrate the Element Plus framework, as well as plugins or components such as Less, Pinia, Vue-router, and monaco-editor.
1. Use Vite to create a Vue3+TS project
Step 1: Create a new temp folder on the desktop, then open it in VS Code, and open a terminal;
Step 2: Create a Vite project
npm create vite@latest
(1) 输入项目名,如: vue3_vite_ts_less_element_plus ,然后回车
? Project name: » vue3_vite_ts_less_element_plus
(2) 选择 Vue 框架,回车
? Select a framework: » - Use arrow-keys. Return to submit.
Vanilla
> Vue
React
Preact
Lit
Svelte
Others
(3) 选择数据类型,回车
? Select a variant: » - Use arrow-keys. Return to submit.
JavaScript
> TypeScript
Customize with create-vue ↗
Nuxt ↗
(4) 创建完成,运行项目
Done. Now run:
cd vue3_vite_ts_less_element_plus
npm install
npm run dev
PS C:\Users\Administrator\Desktop\temp>
2. Configure @ alias
Step 1: Modify the vite.config.ts configuration file
before fixing:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
})
After modification:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
/**
* 详情见 vitejs 文档:https://vitejs.dev/config/
*/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': resolve(__dirname, './src')
}
}
})
Step 2: Modify the tsconfig.json configuration file
before fixing:
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
After modification:
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"paths": {
"@": ["src"],
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
Step 3: The configuration is successful!
3. Integrate the Element Plus framework
Step 1: Import dependent packages
npm i element-plus
Step 2: Create a plugins folder in the src directory of the project, create a new element-plus.ts file inside, and write the following code
import ElementPlus from 'element-plus'
import 'element-plus/theme-chalk/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn' // 汉化 element-plus 组件
export default (app: any) => {
app.use(ElementPlus, {
locale: zhCn,
})
}
Step 3: Import and use the plug-in and registration icon in the main.ts folder of the project, that is, the integration is complete, and the main.ts file is as follows
import { createApp } from 'vue'
import App from './App.vue'
import './global.less' // 全局样式
const app = createApp(App)
// 引入 ElementPlus 插件(npm i element-plus)
import ElementPlusPlugin from '@/plugins/element-plus'
// 全局注册 ElementPlus 图标组件(npm i @element-plus/icons-vue)
import * as ElementPlusIcons from '@element-plus/icons-vue'
for(const [key, component] of Object.entries(ElementPlusIcons)) {
app.component(key, component)
}
app
.use(ElementPlusPlugin)
.mount('#app')
Step 4: Verify that the integration is successful. In the App.vue folder of the project, for example, write a button label and save it to see the effect. The App.vue file is as follows
<template>
<el-button
size="small"
type="primary"
icon="UploadFilled"
@click="void(0)"
>
点击事件
</el-button>
<el-button size="small" type="primary" plain @click="void(0)">
<el-icon :size="18">
<UploadFilled />
</el-icon>
<span>点击事件</span>
</el-button>
<el-button
size="small"
type="primary"
circle
>
<el-icon :size="18">
<UploadFilled />
</el-icon>
</el-button>
<el-icon
:size="20"
style="color: #5e7ce0; cursor: pointer"
@click="void(0)"
>
<UploadFilled />
</el-icon>
</template>
<style lang="less">
* {
margin: 0;
padding: 0;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: none;
}
#app {
width: 100%;
height: 100%;
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
}
</style>
4. Integrate plugins such as vue-router, pinia, and less
Step 1: View all versions of vue-router, and specify a newer version to install, the usage is the same as before
npm view vue-router versions --json
npm i [email protected]
Step 2: Install pinia. I heard that Vuex embraces ts not as good as Pinia. For details, see the official website ( Pinia Chinese document )
npm i pinia
Create a new store folder in the src directory, and then create a new index.ts file. The content of the file is as follows
import { createPinia } from 'pinia'
const store = createPinia()
export default store
Then create a new ILoveYouStore file, the file content is as follows
import { defineStore } from 'pinia'
/**
* 爱老虎油状态管理仓库
*/
export const ILoveYouStore = defineStore({
id: 'ILoveYouStore', // ID必填且唯一
state: () => {
return {
xxx: 'Hello,World!',
yyy: 520,
}
},
getters: {
},
actions: {
setXxx(xxx: string) {
this.xxx = xxx
},
}
})
The following is how to use Pinia in a vue page
<script>
// 引入爱老虎油状态管理仓库
import { ILoveYouStore } from '@/store/ILoveYouStore'
const useILoveYouStore = ILoveYouStore()
</script>
然后随便用Vue2、Vue3、Vue3+语法糖来定义数据
<!-- ^ Vue2 -->
<template>
<div v-if="useILoveYouStore.$state.xxx != null">{
{ useILoveYouStore.$state.xxx }}</div>
<div v-else>{
{ useILoveYouStore.$state }}</div>
</template>
<script>
export default {
data: () => ({
useILoveYouStore: useILoveYouStore,
}),
}
</script>
<!-- / Vue2 -->
<!-- ^ Vue3 -->
<script>
import { ref } from 'vue';
export default {
setup() {
const useILoveYouStore = useILoveYouStore
return { useILoveYouStore }
},
}
</script>
<!-- / Vue3 -->
<!-- ^ Vue3+语法糖 -->
<script setup>
const useILoveYouStore = useILoveYouStore
</script>
<!-- / Vue3+语法糖 -->
Step 3: Install less/scss, depending on personal habits to install
npm i less
npm i scss
The fourth step, the integration of other plug-ins is similar, and another package uses the Axios plug-in
5. Integrate the monaco-editor plug-in of Microsoft's online code editor. This plug-in can be easily implemented. Online browsing of only one page can exceed 300,000 lines of code or logs, and the function of quickly rendering highlighted text without getting stuck
Step 1: View monaco-editor version
npm view monaco-editor versions --json
Step 2: Install the specified version of monaco-editor
npm i [email protected]
Step 3: Change App.vue to the following code
<template>
<div class="m-e" id="m-e-id">
<div class="m-e-main">
<div class="m-e-main_toolbar" :style="isThemeLightOrBlack ? 'background-color: #fff; box-shadow: 0px 2px 5px #ddd;' : 'background-color: #1e1e1e; box-shadow: 0px 2px 5px #111;'">
<div class="m-e-main_toolbar_left">
<span>日志 - {
{ title }}</span>
</div>
<div class="m-e-main_toolbar_right" :style="isThemeLightOrBlack ? 'color: #000' : 'color: #fff'">
<a title="查找" @click="findByKeyword"><i class="fa fa-search"/></a>
<a title="回到顶部" @click="scrollToTop"><i class="fa fa-chevron-circle-up"/></a>
<a title="回到底部" @click="scrollToBottom"><i class="fa fa-chevron-circle-down"/></a>
<a title="是否截断换行" @click="setEditorWordWrap"><i class="fa fa-bars"/></a>
<a title="切换白天或暗夜模式" @click="setEditorTheme"><i class="fa fa-adjust"/></a>
<a :title="fullScreen ? '退出全屏' : '全屏显示'" @click="handleFullScreenClick"><i :class="fullScreen ? 'fa fa-compress' : 'fa fa-expand'"/></a>
<a title="下载日志" @click="handleDownloadLogClick"><i class="fa fa-cloud-download" style="position: relative; top: 0px"/></a>
</div>
</div>
<div :id="id" class="m-e-main_container" :style="'height: calc(100% - 40px)'"></div>
</div>
</div>
</template>
<script>
// 引入获取原始数据组件
import { toRaw } from 'vue'
// 引用 font-awesome 资源(npm i font-awesome)
import 'font-awesome/css/font-awesome.min.css';
// 引入 monaco-editor 组件
import * as me from 'monaco-editor'
export default {
data: () => ({
editor: null,// 编辑器对象
id: null,// 编辑器DOM节点ID
title: '',// 编辑器标题
content: '',// 编辑器内容
height: 'auto',// 编辑器高度
readOnly: false,// 编辑器是否禁用
isScrollToBottom: false, // 是否滚动到底部
// 其他配置项...
fullScreen: false,// 是否全屏状态
wordWrap: false,// 当单行文本太长时截断换行,true 为换行,false 为不换行
isThemeLightOrBlack: false,// 明亮或暗夜模式,true 为白天模式,false 为暗夜模式
}),
mounted() {
this.newME()
/**
* 监听全屏显示状态
*/
let that = this;
window.onresize = function() {
if (!document.fullscreenElement) {
that.fullScreen = false;
} else {
that.fullScreen = true;
}
}
},
watch: {
},
methods: {
/** */
newME() {
this.id = 'me_czq' // 随便起一个DOM节点ID名称
this.title = '帅龍之龍' // 随便起一个编辑器标题
this.content = 'Hello,World!' // 随便写入一点编辑器内容
this.initMonacoEditor(this.id, this.content, this.readOnly)
},
/**
* 实例化在线代码编辑器
*
* 文档地址:https://microsoft.github.io/monaco-editor/api/index.html
*/
async initMonacoEditor(id, content, readOnly) {
try {
// 异步获取节点,确保 Dom 节点已经渲染完成,不可删
await document.getElementById(this.id);
this.editor = me.editor.create(document.getElementById(id), {
value: content,// 编辑器内容
language: 'python',// 选择支持语言
automaticLayout: true,// 是否自动布局
theme: 'vs-dark',// 官方自带三种主题:vs、hc-black、vs-dark
readOnly: readOnly,// 设置是否只读
wordWrap: this.wordWrap ? 'on' : 'off',// 设置启用截断功能
scrollBeyondLastLine: false,// 滚动完最后一行后再滚动一屏幕
// 滚动条
// scrollbar: {
// verticalScrollbarSize: 15,
// horizontalScrollbarSize: 15
// },
// 是否开启小地图
minimap: {
enabled: true
},
});
} catch (e) {
console.error(e)
}
// 设置编辑器滚动到最底部
// this.scrollToBottom();
},
/**
* 设置编辑器的内容且滚动到最底部
*/
setEditorContent(val) {
toRaw(this.editor).setValue(val);
this.scrollToBottom();
},
/**
* 获取编辑器的内容
*/
getEditorContent() {
toRaw(this.editor).getValue();
},
/**
* 打开编辑器查找功能
*/
findByKeyword() {
try {
// 先聚焦编辑器
toRaw(this.editor).focus();
// 从模型中获取要查找的字符串范围 new Range(startLineNumber, startColumn, endLineNumber, endColumn)
toRaw(this.editor).setSelection(new me.Range(1, 9999, 1, 10000));
// 触发查找操作
// toRaw(this.editor).getAction('actions.find').run();// 查找方式一
toRaw(this.editor).trigger('', 'actions.find');// 查找方式二
} catch(error) {
console.log(error);
}
},
/**
* 设置编辑器从只读变成可写
*/
setEditorRW() {
toRaw(this.editor).updateOptions({readOnly: false});
},
/**
* 设置编辑器开关截断功能
*/
setEditorWordWrap() {
this.wordWrap = this.wordWrap ? false : true;
if (this.wordWrap) {
toRaw(this.editor).updateOptions({wordWrap: 'on'});
} else {
toRaw(this.editor).updateOptions({wordWrap: 'off'});
}
},
/**
* 设置编辑器明亮或暗夜模式
*/
setEditorTheme() {
this.isThemeLightOrBlack = this.isThemeLightOrBlack ? false : true;
if (this.isThemeLightOrBlack) {
toRaw(this.editor).updateOptions({theme: 'vs'});
} else {
toRaw(this.editor).updateOptions({theme: 'vs-dark'});
}
},
/**
* 设置编辑器滚动到最顶部
*/
scrollToTop() {
toRaw(this.editor).setScrollPosition({scrollTop: 0});
},
/**
* 设置编辑器滚动到最底部
*/
scrollToBottom() {
toRaw(this.editor).revealLine(toRaw(this.editor).getModel().getLineCount());
},
/**
* 全屏显示句柄
*/
handleFullScreenClick () {
const element = document.getElementById('m-e-id');
if (!document.fullscreenElement) {
element.requestFullscreen();
} else {
document.exitFullscreen();
}
},
/**
* 下载日志句柄
*/
handleDownloadLogClick() {
this.exportFile(this.title, this.content);
},
/**
* 下载日志
*/
exportFile(name, data) {
let url = window.URL || window.webkitURL || window;
let blob = new Blob([data]);
let event = document.createEvent("MouseEvents");
event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
let link = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
link.href = url.createObjectURL(blob);
link.download = name;
link.dispatchEvent(event);
}
},
/**
* 销毁在线代码编辑器
*/
beforeDestroy() {
if (toRaw(this.editor)) {
toRaw(this.editor).dispose()
}
},
}
</script>
<style lang="less" scoped>
.m-e {
width: 100%;
height: 100%;
.m-e-main {
width: 100%;
height: 100%;
.m-e-main_toolbar {
width: 100%;
height: 40px;
box-shadow: 0px 2px 5px #000;
display: flex;
position: relative;
z-index: 99;
.m-e-main_toolbar_left {
flex: 1;
overflow: hidden;
span {
color: #fff;
display: block;
font-size: 15px;
padding-left: 10px;
line-height: 26px;
line-height: 40px;
white-space:nowrap;/* 不换行 */
overflow:hidden;/* 内容超出宽度时隐藏超出部分的内容 */
text-overflow:ellipsis;/* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
// user-select: none;
}
}
.m-e-main_toolbar_right {
margin-right: 15px;
display: flex;
a {
width: 16px;
height: 16px;
line-height: 16px;
transition: ease all 0.3s ;
text-align: center;
display: inline-block;
padding: 5px;
cursor: pointer;
border-radius: 2px;
margin: 7px 0 7px 5px;
i {
font-size: 15px;
}
&:hover {
background-color: rgba(255, 255, 255, 0.1);
}
}
}
}
}
}
</style>
6. Configure the server interface address
Step 1: Add server configuration to the vite.config.ts file, the complete content is as follows.
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
/**
* 详情见 vitejs 文档:https://vitejs.dev/config/
*/
export default defineConfig({
plugins: [vue()],
base: '/xxx/', // 配置相对地址或绝对地址,此处应为绝对地址,若将 Web 部署到 Nginx 所在的目录为 nginx-1.17.8/html/xxx ,则这个 base 的值就为 /xxx/
resolve: {
alias: {
'@': resolve(__dirname, './src'),
assets: resolve(__dirname, './src/assets'),
}
},
server: {
host: '', // 主机
port: 5173, // 端口
proxy: {
// 项目 v1 的服务端接口地址
'/v1/api': {
target: 'http://127.0.0.1:8091/',
changeOrigin: true,
secure: false,
ws: true
},
// 项目 v2 的服务端接口地址
'/v2/api': {
target: 'http://127.0.0.1:8092/',
changeOrigin: true,
secure: false,
ws: true
},
// 项目 v3 的服务端接口地址
'/v3/api': {
target: 'http://127.0.0.1:8093/',
changeOrigin: true,
secure: false,
ws: true
},
// // 默认服务端接口地址
// '/': {
// target: 'http://127.0.0.1:8090/',
// changeOrigin: true,
// secure: false,
// ws: false
// }
}
}
})
Tip: How to reduce the vite version?
Step 1: Delete the node_modules directory in the project
Step 2: Delete the package-lock.json file in the project
Step 3: Install the specified version of vite
npm i [email protected] -D
Step 4: Install the specified version of @vitejs/plugin-vue
npm i @vitejs/[email protected] -D
Step 5: Re-execute the import dependency command. If there are two vite version errors, repeat step 1 and step 2, and then re-import the dependency
npm i
Step 6: Start the project and run normally
npm run dev
Summary of error reporting problems
Problem One: Could not find name '__dirname'.
Reason: The path module is a built-in module of node.js, and node.js does not support ts files by default
Solution: Install @type/node dependency package npm install @types/node --save-dev
Problem 2: Non-relative paths are not allowed when "baseUrl" is not set. Did you forget the leading "./"?.
Solution: Add baseUrl configuration in tsconfig.json file
{
"compilerOptions": {
...
"baseUrl": ".", // 未设置 "baseUrl" 时,不允许使用非相对路径。
"paths": {
"@": ["src"],
"@/*": ["src/*"]
}
},
...
}
Problem Three: Could not find module './App.vue' or its corresponding type declaration.
Solution: Create a new shims-vue.d.ts file in the src directory, and the content of the file is the following code
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
Question 4: How to configure relative address or absolute address for Vite project
Solution: Add the base attribute in the vite.config.ts file, the value can be a relative address './' or an absolute address '/xxx/'
export default defineConfig({
plugins: [vue()],
base: '/xxx/', // 配置相对地址或绝对地址,此处应为绝对地址,若将 Web 部署到 Nginx 所在的目录为 nginx-1.17.8/html/xxx ,则这个 base 的值就为 /xxx/
resolve: {
alias: {
'@': resolve(__dirname, './src'),
assets: resolve(__dirname, './src/assets'),
}
},
})
Question 5: When npm run build is packaged, an error is reported, and the prompt message is Did you mean to enable the 'allowJs' option?
Solution: Add allowJs configuration in tsconfig.json file
{
"compilerOptions": {
"allowJs": true, // xxx.vue is a JavaScript file. Did you mean to enable the 'allowJs' option?
// ...
}
}
Question 6: Because the project does not specify an ESlint parser, some syntax parsing errors are caused
Solution: Create a new .eslintrc.js file, pay attention to the dot at the beginning of the file name, and then it will be solved perfectly
module.exports = {
env: {
browser: true,
es2022: true,
node: true,
},
extends: ["plugin:vue/vue3-recommended", "plugin:prettier/recommended"],
parserOptions: {
ecmaVersion: "latest",
parser: "@typescript-eslint/parser", // 指定ESlint的解析器
sourceType: "module",
},
plugins: ["vue", "@typescript-eslint", "prettier"],
rules: {
"prettier/prettier": "error",
},
}
问题七:the >>> and /deep/ combinators have been deprecated. Use :deep() instead.
Solution: replace /deep/ with: deep()
修改前:
/deep/ .el-table {
// ...
}
修改后:
:deep(.el-table) {
// ...
}
project address