vue3.2 + vite + ts + vue-router4 + pinia + elementPlus + echarts5 项目初始化
一.项目搭建
全局安装pnpm
,安装命令为
注意: 安装 pnpm
, node
的版本号要大于 V14.19.0
npm i pnpm -g
复制
在VScode里面下载插件Vue Language Features(Volar)
,禁用掉 Vetur
,不然代码会报一堆错误
接下来,我们开始构建项目
1.在需要创建项目文件目录下打开 cmd
运行下列命令
pnpm create vite
复制
2.输入项目名称
? Project name: » vue3-ts-elementPlus-pinia-echarts
复制
3.选择一个框架
? Select a framework: » - Use arrow-keys. Return to submit. Vanilla > Vue React Preact Lit Svelte Others
复制
4.选择使用 TypeScript
? Select a variant: » - Use arrow-keys. Return to submit. JavaScript > TypeScript Customize with create-vue ↗ Nuxt ↗
复制
5.找到文件夹,用 vscode 打开,首先使用 pnpm i
安装依赖, 在使用命令 pnpm dev
就可以启动项目了
这里注意: 如果安装依赖时出错,可以切换淘宝远
pnpm config set registry https://registry.npmmirror.com/
复制
项目启动成功,打开之后,可以看到如下所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nkwBWSwu-1672799309466)(C:\Users\260572.INTERNET\Desktop\文档\企业微信截图_16692713148370.png)]
二.设置并约束代码风格
往往我们写代码的时候,最讨厌的是地下大部分的红色报错信息,并且格式错乱,所以可以配置一些相关信息,完善我们的代码风格
配置 eslint
和 prettier
1.执行下列命令安装依赖
pnpm i eslint -D pnpm i prettier -D pnpm i eslint-plugin-vue -D pnpm i eslint-plugin-prettier -D pnpm i eslint-config-prettier -D pnpm i @typescript-eslint/eslint-plugin -D pnpm i @typescript-eslint/parser -D pnpm i @vue/eslint-config-prettier -D pnpm i @vue/eslint-config-typescript -D
复制
2.项目下新建 .eslintrc.js
文件,并配置 eslint
的检验规则
module.exports = { // ESLint 一旦发现配置文件中有 "root": true,它就会停止在父级目录中寻找。 root: true, // 指定脚本的运行环境。每种环境都有一组特定的预定义全局变量。 env: { browser: true, es6: true, node: true, 'vue/setup-compiler-macros': true }, // 指定如何解析语法 parser: 'vue-eslint-parser', // 优先级低于parse的语法解析配置 parserOptions: { // js的版本 ecmaVersion: 2020, // 解析器 parser: '@typescript-eslint/parser', // 模块化方案 sourceType: 'module', ecmaFeatures: { jsx: true } }, // plugins: ['@typescript-eslint'], //定义了该eslint文件所依赖的插件 // 代表你启动哪些 lint 选项,如果多个规则直接有冲突的话,extends后面的选项会覆盖前面的。 extends: [ // 'eslint:recommended', // 防止错误或意外行为的规则 'plugin:vue/vue3-essential', // 大大提高代码可读性和/或开发体验的规则 'plugin:vue/vue3-strongly-recommended', // 加上强制执行主观社区默认值的规则,以确保一致性 'plugin:vue/vue3-recommended', // typescript-eslint推荐规则 'plugin:@typescript-eslint/recommended', 'prettier', // 使用prettier中的样式规范,且如果使得ESLint会检测prettier的格式问题,同样将格式问题以error的形式抛出 'plugin:prettier/recommended' ], rules: { // override/add rules settings here, such as: // 禁止不必要的分号 // '@typescript-eslint/no-extra-semi': 'off', // 如不禁用这条规则会导致命名为index或其他单字母组件名的文件报错,(不要求组件名称始终为多字) 'vue/multi-word-component-names': 0, // 允许属性之间使用驼峰命名,不强制要求使用 - 连字符 'vue/attribute-hyphenation': 0, // 不需要 props 的默认值 'vue/require-default-prop': 'off', // 禁止 v-for 指令或范围属性的未使用变量定义 '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' } ], 'no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' } ], '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-explicit-any': 'off' } }
复制
自定义规则,具体配置可参考:https://eslint.vuejs.org/rules/
3.在项目下新建 .eslintignore
文件,配置 eslint
忽略检查的文件或目录
dist/ es/ lib/ node_modules/ .gitignore *.sh *.md *.woff *.ttf .vscode .idea /public /docs .husky .local /bin Dockerfile
复制
4.项目下新建 .prettierrc.js
文件,配置 prettier
代码风格
module.exports = { // 使用 2 个空格缩进 tabWidth: 2, // 不使用 tab 缩进,而使用空格 useTabs: false, // 行尾不需要分号 semi: false, // // jsx 标签的反尖括号需要换行 // jsxBracketSameLine: false, // // jsx 不使用单引号,而使用双引号 // jsxSingleQuote: false, // 箭头函数,只有一个参数的时候,不需要括号 arrowParens: 'avoid', // 使用单引号代替双引号 singleQuote: true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }" bracketSpacing: true, // 换行符使用 lf endOfLine: 'lf', // 末尾不使用逗号 trailingComma: 'none', // 使用默认的折行标准 proseWrap: 'preserve' // 根据显示样式决定 html 要不要折行 // htmlWhitespaceSensitivity: 'css' }
复制
更多配置信息,可参考官网:https://www.prettier.cn/docs/index.html
这里有个细节,如果vite的版本大于3的话,文件的后缀名改为cjs
并且重新打开编辑器
5.项目下新建 .prettierignore
,配置 prettier
忽略文件
dist/ es/ lib/ node_modules/ pnpm-lock.yaml pnpm-workspace.yaml CHANGELOG.md .local .output.js **/*.svg **/*.sh /public/*
复制
6.package.json
文件下添加以下配置
"scripts": { "eslint:comment": "使用 ESLint 检查并自动修复 src 目录下所有扩展名为 .js 和 .vue 的文件", "eslint": "eslint --ext .tsx,.ts,.js,.vue --ignore-path .gitignore --fix src", "prettier:comment": "自动格式化当前目录下的所有文件", "prettier": "prettier --write" },
复制
配置编辑器风格
1.项目下 .vscode
文件夹下新增两个文件 extensions.json
和 settings.json
, 并配置以下信息
extensions,json
{ "recommendations": [ "Vue.volar", "EditorConfig.EditorConfig", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "streetsidesoftware.code-spell-checker", "jaskang.vsc-commitizen" ] }
复制
settings.json
{ "editor.tabSize": 2, "editor.wordWrap": "on", // #每次保存的时候自动格式化 "editor.formatOnSave": true, // #每次保存的时候将代码按eslint格式进行修复 "eslint.codeActionsOnSave": { "source.fixAll.eslint": true }, // #去掉代码结尾的分号 "prettier.semi": false, // #使用单引号替代双引号 "prettier.singleQuote": true, // #让函数(名)和后面的括号之间加个空格 "javascript.format.insertSpaceBeforeFunctionParenthesis": false, // 设置默认序列换行符为lf "files.eol": "\n", "vue3snippets.enable-compile-vue-file-on-did-save-code": false }
复制
2.在项目下新建 .editorconfig
文件,配置以下信息
root = true # 匹配全部文件 [*] # 设置字符集 charset = utf-8 # 末尾行去掉尾随的空格 trim_trailing_whitespace = true # 末尾行后加多一行空行 insert_final_newline = true # <"tab" | "space"> 制表符类型 indent_style = space indent_size = 2 # <"lf" | "cr" | "crlf"> 换行符类型 end_of_line = lf
复制
更多配置信息,可参考官网:http://editorconfig.org
配置文件引用别名
在 vite.config.ts
下配置以下信息:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, 'src') } } })
复制
修改配置 tsconfig.json
文件
{ "compilerOptions": { // 使用的es版本 esnext表示最高 "target": "esnext", "useDefineForClassFields": true, // tsc编译后的规范文件 "module": "esnext", // 是否允许引入js文件 "allowJs": true, // 编译的模块化的方案 "moduleResolution": "node", // 是否采用严格模式 "strict": true, // 限制对空值的检查 "strictNullChecks": false, // jsx代码编译后的规范 "jsx": "preserve", // 生成编译前代码的映射文件 "sourceMap": true, "resolveJsonModule": true, // 将每个文件作为单独的模块 "isolatedModules": true, // 允许ts文件中,同时引入 es和commonJS规范的文件 "esModuleInterop": true, // 编译过程中需要引入的库文件的列表。 "lib": ["esnext", "dom"], // 跳过声明文件的类型检查 "skipLibCheck": true, // 开始查找ts文件的基础目录 "baseUrl": "./", // 路径别名,通过某个标识符指定特定的目录 "paths": { "@": ["src"], "@/*": ["src/*"] } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], "references": [{ "path": "./tsconfig.node.json" }], // ts编译和检查排除的文件或者文件夹 "exclude": ["node_modules", "dist", "**/*.js"] }
复制
至此,我们的代码风格及约束文件已经配置完毕,在之后的代码编辑中,能及时纠正格式错误,使代码书写更加规范,整洁,如有缺失遗漏的部分,望大家积极评论.
三.配置husky +lint-staged
使用 husky
和 lint-staged
来帮助团队进行编码规范
husky
是 Git Hook
工具,可以设置 git 在各个阶段触发的命令.
1.执行以下命令安装 husky
注意: 执行以下命令之前,必须使用 git init
初始化 git 仓库
pnpm dlx husky-init pnpm install
复制
可以看到,在 package.json
中添加了一个脚本
"prepare": "husky install"
复制
并且在项目下创建了 .husky
目录
husky
包含了很多 hook
, 这里,我么使用 pre-commit
来触发 eslint
2.安装 lint-staged
执行下列命令进行安装
pnpm i lint-staged -D
复制
接下来结合 prettier
和 eslint
的配置信息在 package.json
中加入以下配置
"lint-staged": { "*.{md,js,ts,tsx,vue,scss}": "prettier --write", "*.{vue,js,jsx,cjs,mjs,ts,tsx,cts,mts}": "eslint --fix" }, "husky":{ "hooks":{ "pre-commit": "lint-staged" } }
复制
使用 husky
生成 pre-commit
文件,触发 lint-staged
npx husky add .husky/pre-commit "npx --no-install lint-staged"
复制
可以看到: .husky
目录下生成了 pre-commit
文件,内容为:
复制
3.配置代码提交规范
安装提交工具,执行以下命令
pnpm i commitizen cz-conventional-changelog -D pnpm i cz-customizable commitlint-config-cz -D pnpm i @commitlint/cli @commitlint/config-conventional -D
复制
在项目下新建 .cz-config.js
文件,用于自定义提示信息,配置代码如下:
module.exports = { types: [ { value: 'feature', name: 'feature: 增加新功能' }, { value: 'fix', name: 'fix: 修复bug' }, { value: 'bug', name: 'bug: 测试反馈bug列表中的bug号' }, { value: 'ui', name: 'ui: 更新UI' }, { value: 'docs', name: 'docs: 文档变更' }, { value: 'style', name: 'style: 代码格式(不影响代码运行的变动)' }, { value: 'perf', name: 'perf: 性能优化' }, { value: 'refactor', name: 'refactor: 重构(既不是增加feature,也不是修复bug)' }, { value: 'release', name: 'release: 发布' }, { value: 'deploy', name: 'deploy: 部署' }, { value: 'test', name: 'test: 增加测试' }, { value: 'chore', name: 'chore: 构建过程或辅助工具的变动(更改配置文件)' }, { value: 'revert', name: 'revert: 回退' }, { value: 'build', name: 'build: 打包' } ], // override the messages, defaults are as follows messages: { type: '请选择提交类型:', customScope: '请输入您修改的范围(可选):', subject: '请简要描述提交 message (必填):', body: '请输入详细描述(可选):', breaking: '是破坏性修改吗?(可选,默认N)', footer: '关联关闭的issue(可选):', confirmCommit: '确认使用以上信息提交吗?(yes/no)' }, allowCustomScopes: true, // allowBreakingChanges: ['feat', 'fix'], // 跳过哪些步骤 // skipQuestions: ['body', 'breaking'], subjectLimit: 100 }
复制
接下来在 package.json
中添加如下配置
"scripts": { "commit": "git-cz", }, "config": { "commitizen": { "path": "node_modules/cz-customizable" } },
复制
4.集成 commitlint
验证提交规范
以上配置完成之后,我们还是可以使用 git commit
命令提交不规范的代码格式,所以需要 commitlint
验证提交规范
在项目下新建 commitlint.config.js
来配置 commitlint
和提交代码的规则,配置如下:
module.exports = { extends: ['@commitlint/config-conventional', 'cz'], rules: { 'type-enum': [ 2, 'always', [ 'feature', // 新功能(feature) 'bug', // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况 'fix', // 修补bug 'ui', // 更新 ui 'docs', // 文档(documentation) 'style', // 格式(不影响代码运行的变动) 'perf', // 性能优化 'release', // 发布 'deploy', // 部署 'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动) 'test', // 增加测试 'chore', // 构建过程或辅助工具的变动 'revert', // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit) 'merge', // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址 'build' // 打包 ] ], // <type> 格式 小写 'type-case': [2, 'always', 'lower-case'], // <type> 不能为空 'type-empty': [2, 'never'], // <scope> 范围不能为空 'scope-empty': [2, 'never'], // <scope> 范围格式 'scope-case': [0], // <subject> 主要 message 不能为空 'subject-empty': [2, 'never'], // <subject> 以什么为结束标志,禁用 'subject-full-stop': [0, 'never'], // <subject> 格式,禁用 'subject-case': [0, 'never'], // <body> 以空行开头 'body-leading-blank': [1, 'always'], 'header-max-length': [0, 'always', 72] } }
复制
使用 husky
生成 commit-msg
文件,验证提交信息
npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
复制
最后,在 package.json
中添加下列代码
"husky":{ "hooks":{ "pre-commit": "lint-staged", "commint-msg": "commitlint -E HUSKY_GIT_PARAMS" } }
复制
至此,我们的代码提交规范以配置完毕,下面进行验证
5.验证提交规范
先试用 git add .
把代码存放到暂存区, 在使用 git cz
命令提交
第一步,选择本次更新的类型
第二步,选择本次修改的范围 (可选)
第三步,针对本次提交改动写一个简短的描述
第四步,针对本次提交改动写一个详细的描述 (可选),不填写可按回车跳过
第五步,确认是否与某个未关闭的 issue 有关联,如果有输入 y ,按回车后填写具体的 issue, 没有的话回车跳过
第六步,确认提交信息,确认无误,回车确认
温馨提示: 提交代码之前,可以使用 pnpm eslint
命令检查一下代码格式
四.element-plus
引入 element-plus
执行安装命令
pnpm i element-plus pnpm i @element-plus/icons-vue pnpm i sass -D
复制
在 main.ts
中引入并挂载,全局注册 element-plus 的图标
import { createApp } from 'vue' import ElementPlus from 'element-plus' import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } const app = createApp(App) app.use(ElementPlus).mount('#app')
复制
这里,我们使用插件,按需自动导入 element-plus 组件
首先,执行下列的命令安装插件
pnpm i unplugin-vue-components unplugin-auto-import -D
复制
其次,在 vite.config.ts
文件夹先导入插件,然后在 plugins
中添加
// 按需导入element-plus组件 import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }),
复制
使用 button
组件测试以下,就可以看到如图所示的结果
配置全局scss样式文件
在 路径src/styles
新建三个个文件夹
element.scss: 修改elementPlus的主题色
/* 只需要重写需要的即可 */ $--colors: ( 'primary': ( 'base': #0c3ca8, ), 'success': ( 'base': #67c23a, ), 'warning': ( 'base': #e6a23c, ), 'danger': ( 'base': #f56c6c, ), 'error': ( 'base': #f56c6c, ), 'info': ( 'base': #909399, ), ); @forward 'element-plus/theme-chalk/src/common/var.scss' with ( $colors: $--colors ); // 全量引入 @use 'element-plus/theme-chalk/src/index.scss' as *;
复制
variables.module.scss: 定义自己的样式变量
$test-color: blue
复制
index.scss: 引入上面连个样式文件
@import './element.scss'; @import './variables.module.scss';
复制
然后在 vite
中加入以下配置
plugins: [ vue(), AutoImport({ resolvers: [ ElementPlusResolver({ importStyle: 'sass' }) ] }), Components({ dts: true, resolvers: [ ElementPlusResolver({ importStyle: 'sass', directives: true, version: '1.56.1' }) ] }), ElementPlus({ useSource: true }) ], css: { preprocessorOptions: { scss: { additionalData: `@use "@/styles/index.scss" as *;` } } },
复制
五.路由
执行下列命令安装依赖
pnpm i vue-router@4
复制
在 src
新新建目录 router
,并新建 index.ts
文件, 内容如下:
import { RouteRecordRaw, createRouter, createWebHashHistory } from 'vue-router' const modules = import.meta.glob('../views/**/**.vue') const routes: RouteRecordRaw[] = [ { path: '/', name: 'Login', component: modules['../views/login/index.vue'] } ] const router = createRouter({ history: createWebHashHistory(), routes }) export default router // 原来的 import 路由懒加载在开发环境可行,在生产环境不可行,故改为上面 // import.meta.glob 形式
复制
修改入口文件main.ts
import { createApp } from 'vue' import App from './App.vue' import ElementPlus from 'element-plus' import router from './router' import * as ElementPlusIconsVue from '@element-plus/icons-vue' const app = createApp(App) for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(router).use(ElementPlus).mount('#app')
复制
注意: 在App.vue
中添加路由出口
至此,路由的基本配置已经完成,更多配置信息,详见官网:https://router.vuejs.org/zh/guide/
六.统一封装请求 (axios)
执行下列命令安装依赖
pnpm i axios pnpm i qs -D pnpm i @types/qs -D
复制
在 src
下新建目录 utils
,并新建文件 http.ts
,内容如下:
import axios, { AxiosResponse, AxiosRequestConfig } from 'axios' import qs from 'qs' // axios声明合并,在axios的配置项AxiosRequestConfig接口定义中自定义新值 declare module 'axios' { export interface AxiosRequestConfig { /** * @description 设置为false,表示请求错误是用户自定义处理(true)还是全局处理(false) */ customError?: boolean } } // 创建axios实例,此时的content-type已指定为json,如需使用form-data,请自行修改 const http = axios.create({ timeout: 10000, headers: { 'Content-Type': 'application/json;charset=UTF-8' }, // 表示跨域请求时是否需要使用凭证 withCredentials: true, validateStatus(status) { return status === 200 // 自定义状态码范围 }, paramsSerializer(params) { // 序列化请求参数,避免get请求参数出现[],&,空格等识别错误的问题 return qs.stringify(params, { arrayFormat: 'brackets' }) }, transformRequest: [ data => { // 转化为正常的json data = JSON.stringify(data) return data } ] }) // 请求拦截 http.interceptors.request.use( (config: AxiosRequestConfig) => { // do something return config }, (error: any) => { Promise.reject(error) } ) // 响应拦截 http.interceptors.response.use( async (response: AxiosResponse) => { return Promise.reject(response) }, (error: any) => { // do something return Promise.reject(error) } ) export default http
复制
在请求拦截和响应拦截根据自己的需求加以修,之后在发请求时,可以直接运用以上封装的工具函数.
七.状态管理pinia
安装
pnpm i pinia@next pnpm i pinia-plugin-persist 持久化插件
复制
在 src
下新增 store
文件夹, 接着在 store
中新增 index.ts
文件,内容如下:
import type { App } from 'vue' import { createPinia } from 'pinia' import piniaPluginPersist from 'pinia-plugin-persist' const store = createPinia() store.use(piniaPluginPersist) //使用插件持久化 export function setupStore(app: App<Element>) { app.use(store) } export default store
复制
并在入口文件下 main.ts
中增加
import store from './store' app.use(store)
复制
pinia
基本已经配置完毕,如何使用可以参考官网: https://pinia.web3doc.top/introduction.html
八.vue3中使用echarts图表
安装
pnpm i echarts
复制
使用方式如下所示:
<!-- 线 + 柱混合图 -->
<template>
<div id="poy-charts" style="height: 200px; width: 300px" />
</template>
<script setup lang="ts">
import { nextTick, onMounted } from 'vue'
// 引入 echarts
import { init, EChartsOption } from 'echarts'
import * as echarts from 'echarts'
// const { mounted, chart, beforeDestroy, activated, deactivated } = resize()
// 初始化图表
function initChart() {
const barChart = init(document.getElementById('poy-charts') as HTMLDivElement)
barChart.setOption({
title: {
show: true,
text: '业绩总览',
x: 'center',
padding: 15,
textStyle: {
fontSize: 18,
fontStyle: 'normal',
fontWeight: 'bold',
color: '#337ecc'
}
},
grid: {
left: '2%',
right: '2%',
bottom: '10%',
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
}
},
legend: {
x: 'center',
y: 'bottom',
data: ['收入', '毛利润', '收入增长率', '利润增长率']
},
xAxis: [
{
type: 'category',
data: ['浙江', '北京', '上海', '广东', '深圳'],
axisPointer: {
type: 'shadow'
}
}
],
yAxis: [
{
type: 'value',
min: 0,
max: 10000,
interval: 2000,
axisLabel: {
formatter: '{value} '
}
},
{
type: 'value',
min: 0,
max: 100,
interval: 20,
axisLabel: {
formatter: '{value}%'
}
}
],
series: [
{
name: '收入',
type: 'bar',
data: [7000, 7100, 7200, 7300, 7400],
barWidth: 20,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
}
},
{
name: '毛利润',
type: 'bar',
data: [8000, 8200, 8400, 8600, 8800],
barWidth: 20,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#25d73c' },
{ offset: 0.5, color: '#1bc23d' },
{ offset: 1, color: '#179e61' }
])
}
},
{
name: '收入增长率',
type: 'line',
yAxisIndex: 1,
data: [60, 65, 70, 75, 80],
itemStyle: {
color: '#67C23A'
}
},
{
name: '利润增长率',
type: 'line',
yAxisIndex: 1,
data: [70, 75, 80, 85, 90],
itemStyle: {
color: '#409EFF'
}
}
]
} as EChartsOption)
}
// 页面挂载时初始化图表
onMounted(() => {
nextTick(() => {
initChart()
})
})
</script>
复制