Vue3 全家桶 + Element Plus + Vite + TypeScript + Eslint 项目配置最佳实践
尤大的 Vue3.0 已经发布有一阵子了,已经很成熟了。
而且 Element Plus + Vite 也出了一段时间了,是时候该上手体验分享一波了。
主要是要熟练一下 Vue3,好准备用 Vue3 重构一下自己的网站项目: blog-vue-typescript ,计划是过年期间会着手重构这个项目,年后会上线。
1. 初化化项目
全局安装 vite-app
npm i -g vite-app
创建项目
yarn create vite-app
# 或者
npm init vite-app
进入项目,安装依赖
cd
yarn # 或 npm i
运行项目
yarn dev
打开浏览器 http://localhost:3000 查看
2. 引入TypeScript
加入 ts 依赖
yarn add --dev typescript
在 项目根目录下创建 TypeScript 的配置文件 tsconfig.json
{
"compilerOptions": {
// 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
"allowSyntheticDefaultImports": true,
// 解析非相对模块名的基准目录
"baseUrl": ".",
"esModuleInterop": true,
// 从 tslib 导入辅助工具函数(比如 __extends, __rest等)
"importHelpers": true,
// 指定生成哪个模块系统代码
"module": "esnext",
// 决定如何处理模块。
"moduleResolution": "node",
// 启用所有严格类型检查选项。
// 启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict,
// --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。
"strict": true,
// 生成相应的 .map文件。
"sourceMap": true,
// 忽略所有的声明文件( *.d.ts)的类型检查。
"skipLibCheck": true,
// 指定ECMAScript目标版本
"target": "esnext",
// 要包含的类型声明文件名列表
"types": [
],
"isolatedModules": true,
// 模块名到基于 baseUrl的路径映射的列表。
"paths": {
"@/*": [
"src/*"
]
},
// 编译过程中需要引入的库文件的列表。
"lib": [
"ESNext",
"DOM",
"DOM.Iterable",
"ScriptHost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
在 src 目录下新加 shim.d.ts 文件
/* eslint-disable */
import type { DefineComponent } from 'vue'
declare module '*.vue' {
const component: DefineComponent<{}, {}, any>
export default component
}
把 main.js 修改成 main.ts
在根目录,打开 Index.html
3. 引入 eslint
安装 eslint prettier 依赖
@typescript-eslint/parser @typescr ipt-eslint/eslint-plugin
为 eslint 对 typescript 支持。
yarn add --dev eslint prettier eslint-config-prettier eslint-plugin-prettier eslint-plugin-vue @typescript-eslint/parser @typescr ipt-eslint/eslint-plugin
在根目录下建立 eslint 配置文件:.eslintrc.js
module.exports = {
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
// 'no-use-before-define': [
// 'error',
// {
// functions: false,
// classes: true,
// },
// ],
'@typescript-eslint/no-use-before-define': 'off',
// '@typescript-eslint/no-use-before-define': [
// 'error',
// {
// functions: false,
// classes: true,
// },
// ],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$'
}
],
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^h$',
varsIgnorePattern: '^h$'
}
],
'space-before-function-paren': 'off',
quotes: ['error', 'single'],
'comma-dangle': ['error', 'never']
}
};
建立 prettier.config.js
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: false, // 未尾逗号
vueIndentScriptAndStyle: true,
singleQuote: true, // 单引号
quoteProps: 'as-needed',
bracketSpacing: true,
trailingComma: 'none', // 未尾分号
jsxBracketSameLine: false,
jsxSingleQuote: false,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'lf'
}
4. vue-router、vuex
yarn add vue-router@next vuex@next
4.1 vuex
在根目录下创建 store/index.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'
export interface State {
count: number
}
export const key: InjectionKey> = Symbol()
export const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
increment(state) {
state.count++
}
}
})
main.ts 修改
import { createApp } from 'vue'
import { store, key } from './store'
import App from './App'
import './index.css'
const app = createApp(App)
app.use(store, key)
app.mount('#app')
components/HelloWord.vue 修改
{{ msg }}
{{ count }}
4.2 vue-router
在 src 目录下建立 router/index.ts,内容如下:
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import HelloWorld from "../components/HelloWorld.vue";
const routes: Array = [
{
path: "/",
name: "HelloWorld",
component: HelloWorld,
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "About" */ "../components/About.vue")
}
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
再新建一个 components/About.vue 文件,内容如下:
<img
alt="Vue logo"
src="../assets/logo.png"
/>
<h1>{{ msg }}h1>
</template>