1. Vscode: 前端人必备写码神器
  2. Chrome:对开发者非常友好的浏览器 (反正我是很依赖它的)
  3. Nodejs&npm:配置本地开发环境,安装 Node 后你会发现 npm 也会一起安装下来
  4. Vue.js devtools:浏览器调试插件
  5. Vue Language Features (Volar):Vscode 开发 vue3 必备插件,提供语法高亮提示,非常好用
  6. Vue 3 Snippets:vue3 快捷输入

1. 使用 vite 快速创建脚手架

兼容性注意: Vite 需要 Node.js 版本 >= 12.0.0

  1. 第一步: 在需要创建项目文件目录下打开 cmd 运行以下命令 ```bash

    npm 6.x

    npm init vite@latest my-vue-app —template vue

npm 7+, 需要额外的双横线:

npm init vite@latest my-vue-app — —template vue

yarn

yarn create vite my-vue-app —template vue

pnpm

pnpm create vite my-vue-app — —template vue

  1. 这里我采用 `yarn` 来安装
  2. ```bash
  3. yarn create vite try-vite-vue-app -- --template vue-ts

image.png

第二步: cd 到项目文件夹, 安装依赖, 启动项目

  1. # 进入项目文件夹
  2. cd try-vite-vue-app
  3. # 安装依赖
  4. yarn
  5. # 启动
  6. yarn dev

image.png

2. Vite 常用基础配置

resolve.alias 配置文件引用路径别名

在我们项目开发过程中,会有很多嵌套层级的目录,所以要找到某个目录经常用相对路径../../..,层级一多就显得眼花缭乱,通过 alias 别名,我们可以快速地指定首层的目录,并且相比相对路径减少了路径索引的消耗,在性能上来说也是更优解。

  1. import { defineConfig } from "vite";
  2. import vue from "@vitejs/plugin-vue";
  3. // 指定解析路径
  4. import { resolve } from 'path'
  5. const pathResolve = (dir: string) => resolve(__dirname, dir)
  6. // 可以是一个对象
  7. resolve: {
  8. alias: {
  9. '@': pathResolve('src'), // 设置 `@` 指向 `src` 目录
  10. views: pathResolve('src/views'), // 设置 `views` 指向 `./src/views` 目录,下同
  11. components: pathResolve('src/components'),
  12. assets: pathResolve('src/assets')
  13. }
  14. },
  15. // 也可以是一个 { find, replacement } 的数组
  16. resolve: {
  17. alias: [
  18. // /@/xxxx => src/xxxx
  19. {
  20. find: /\/@\//,
  21. replacement: pathResolve('src') + '/',
  22. },
  23. // /#/xxxx => types/xxxx
  24. {
  25. find: /\/#\//,
  26. replacement: pathResolve('types') + '/',
  27. },
  28. ],
  29. },

指定解析路径使用的 path module 需要先安装nodejs的类型声明 @types/node
image.png
image.png

开发服务器选项 serve

当我们在没有任何配置的时候,在运行服务的时候,vite 是会自动跑在本地的3000端口,所以我们可以扩展下配置

  1. server: {
  2. port: 4000, // 设置服务启动端口号,如果端口已经被使用,Vite 会自动尝试下一个可用的端口
  3. open: true, // boolean | string 设置服务启动时是否自动打开浏览器,当此值为字符串时,会被用作 URL 的路径名
  4. cors: true, // 为开发服务器配置 CORS,配置为允许跨域
  5. // 设置代理,根据我们项目实际情况配置
  6. proxy: {
  7. '/api': {
  8. target: 'http://127.0.0.1:8000', // 后台服务地址
  9. changeOrigin: true, // 是否允许不同源
  10. secure: false, // 支持https
  11. rewrite: path => path.replace(/^/api/, '')
  12. }
  13. }
  14. }

构建选项 build

  1. build: {
  2. outDir: 'dist', // 指定打包路径,默认为项目根目录下的 dist 目录
  3. terserOptions: {
  4. compress: {
  5. keep_infinity: true, // 防止 Infinity 被压缩成 1/0,这可能会导致 Chrome 上的性能问题
  6. drop_console: true, // 生产环境去除 console
  7. drop_debugger: true // 生产环境去除 debugger
  8. },
  9. },
  10. chunkSizeWarningLimit: 1500 // chunk 大小警告的限制(以 kbs 为单位)
  11. }

3. TypeScript 配置

3.1. tsconfig.json 配置项

在初始化 vite 项目的时候可以看到,项目根目录下生成了 tsconfig.jsontsconfig.node.json 两个文件。

tsconfig.json 指定了用来编译这个项目的根文件和编译选项

  1. {
  2. "compilerOptions": {
  3. "target": "esnext",
  4. "useDefineForClassFields": true,
  5. "module": "esnext",
  6. "moduleResolution": "node",
  7. "strict": true,
  8. "jsx": "preserve",
  9. "sourceMap": true,
  10. "resolveJsonModule": true,
  11. "esModuleInterop": true,
  12. "lib": ["esnext", "dom"]
  13. },
  14. "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  15. "references": [{ "path": "./tsconfig.node.json" }]
  16. }

可以看到tsconfig.node.json中的"include": ["vite.config.ts"]表明这个单独拆分出来的配置文件只是负责编译 vite 的配置文件vite.config.ts

  1. {
  2. "compilerOptions": {
  3. "composite": true,
  4. "module": "esnext",
  5. "moduleResolution": "node"
  6. },
  7. "include": ["vite.config.ts"]
  8. }

include 指定需要编译的文件范围

默认是对 src 下的.ts、.d.ts、.tsx、.vue结尾的文件都需要进行编译

exclude 配置不需要编译的文件范围

"exclude": ["node_modules","dist"]

references 将 TypeScript 程序拆分

属性是 TypeScript 3.0 的新特性,允许将 TypeScript 程序拆分结构化。这和我们写 vue 组件的思想有着异曲同工之妙,避免了在一个文件中处理过多的内容,而是通过拆分成多个文件,分别配置不同的部分,达到更加清晰,可维护性更高的目的。

compilerOptions 编译选项

skipLibCheck 忽略声明文件检查

忽略所有的声明文件( *.d.ts)的类型检查

  1. "compilerOptions": {
  2. "skipLibCheck": true
  3. }

这个属性不但可以忽略 npm 不规范带来的报错,还能最大限度的支持类型系统。设置为 true 就不用怕使用的第三方库不规范了。

可能有人好奇,为什么要跳过这些第三方库的检查?

我们做类型检查(以及代码规范等)的目的是为了对团队内业务代码开发保持统一和规范,以保证开发成员之间的快速上手和后续维护。所以我们要做的是将各种规则集成到业务代码模块,而一些框架上的或者第三方库的内容就不用多此一举了。

baseUrl

作用:设置baseUrl来告诉编译器到哪里去查找模块。所有非相对模块导入都会被当做相对于 baseUrl

注意相对模块的导入不会被设置的baseUrl所影响,因为它们总是相对于导入它们的文件.
这个设置的作用和 vite 构建选项 build 中的 base 是类似的,我们配置为当前根目录即可

paths 路径映射列表

作用:模块名到基于baseUrl的路径映射的列表。

请注意"paths"是相对于"baseUrl"进行解析。

在 vite 配置中设置了路径别名resolve.alias,为了让编译 ts 时也能够解析对应的路径,我们还需要配置 paths 选项

  1. "paths": {
  2. "@/*": ["src/*"],
  3. "views/*": ["src/views/*"],
  4. "components/*": ["src/components/*"],
  5. "assets/*": ["src/assets/*"],
  6. "api/*": ["src/api/*"],
  7. "utils/*": ["src/utils/*"],
  8. }

isolatedModules 将每个文件作为单独模块

typescript 将没有导入/导出的文件视为旧脚本文件。因为这样的文件不是模块和它们在全局命名空间中合并的任何定义。该配置项会禁止此类文件。将任何导入或导出添加的文件都视为模块

这个设置在 vite 官方文档中是被要求应该设置为 true 的,但项目初始化的时候并没有默认加上这条

  1. "isolatedModules": true,

image.png
https://vitejs.cn/guide/features.html#typescript-compiler-options

types 客户端类型

作用:添加要包含的类型声明文件名列表,只有在这里列出的模块的声明文件才会被加载进来

  1. {
  2. "compilerOptions": {
  3. "types": ["vite/client"]
  4. }
  5. }

这将会提供以下类型定义补充:

  • 资源导入 (例如:导入一个 .svg 文件)
  • import.meta.env 上 Vite 注入的环境变量的类型定义
  • import.meta.hot 上的 HMR API 类型定义

tsconfig.json 完整配置内容

  1. {
  2. "compilerOptions": {
  3. "target": "esnext",
  4. "isolatedModules": true,
  5. "useDefineForClassFields": true,
  6. "module": "esnext",
  7. "moduleResolution": "node",
  8. "strict": true,
  9. "jsx": "preserve",
  10. "sourceMap": true,
  11. "resolveJsonModule": true,
  12. "esModuleInterop": true,
  13. "skipLibCheck": false, // 忽略所有的声明文件( *.d.ts)的类型检查,跳过第三方库的类型检查
  14. "lib": [
  15. "esnext",
  16. "dom"
  17. ],
  18. "baseUrl": ".",
  19. "types": [
  20. "vite/client"
  21. ],
  22. "paths": {
  23. "@/": [
  24. "src/"
  25. ]
  26. }
  27. },
  28. "include": [
  29. "src/**/*.ts",
  30. "src/**/*.d.ts",
  31. "src/**/*.tsx",
  32. "src/**/*.vue"
  33. ],
  34. "exclude": [
  35. "node_modules",
  36. "dist"
  37. ],
  38. "references": [
  39. {
  40. "path": "./tsconfig.node.json"
  41. }
  42. ]
  43. }

3.2. env.d.ts 全局类型声明

打开 src 文件夹下的 env.d.ts文件,可以看到

  1. /// <reference types="vite/client" />
  2. declare module '*.vue' {
  3. import type { DefineComponent } from 'vue'
  4. // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
  5. const component: DefineComponent<{}, {}, any>
  6. export default component
  7. }

image.png
请注意:三斜杠指令是包含单个XML标签的单行注释,注释的内容会做为编译器指令使用,只有在文件的最顶部才会生效,可以查看TypeScript: Documentation - Triple-Slash Directives学习相关内容。

所以这个文件的作用就呼之欲出了:帮助编译器识别类型
TypeScript 相比 JavaScript 增加了类型声明。原则上,TypeScript 需要做到先声明后使用。这就导致开发者在调用很多原生接口(浏览器、Node.js)或者第三方模块的时候,因为某些全局变量或者对象的方法并没有声明过,导致编译器的类型检查失败。

当我们遇上Property xxx does not exist on type …报错的时候,就可以定位到是没有声明过这个方法/属性。我们就可以在这个文件作全局声明。
可以看到,该文件已经默认为所有的 vue 文件声明了 DefineComponent的组件类型,这就意味着只要我们的单文件组件使用

  1. <script lang="ts">
  2. import { defineComponent } from 'vue'
  3. export default defineComponent({
  4. ...
  5. })
  6. </script>

的写法,就能避免 vue 文件中大多数类型声明的报错,比如使用路由的this.$router、this.$route命令

eslint + prettier 约束代码风格

Eslint 支持

安装

  1. # 1. eslint 安装
  2. yarn add eslint --dev
  3. # 2. typescript parser ESLint 默认使用的是 Espree 进行语法解析,所以无法对部分 typescript 语法进行解析,因此我么还需要安装 @typescript-eslint/parser 替换掉默认的解析器
  4. yarn add @typescript-eslint/parser --dev
  5. # 3. eslint 插件安装
  6. # 借用这个 plugin 的能力才能让 eslint 识别 vue 文件
  7. yarn add eslint-plugin-vue --dev
  8. # eslint-plugin 作为 eslint 默认规则的补充,提供了一些额外的适用于 ts 语法的规则
  9. yarn add @typescript-eslint/eslint-plugin --dev

注意: 如果 eslint 安装报错:
image.png
可以尝试运行以下命令:

  1. yarn config set ignore-engines true

运行成功后再次执行 eslint 安装命令

1. 项目下新建 .eslintrc.js

配置 eslint 校验规则:

  1. module.exports = {
  2. root: true,
  3. env: {
  4. browser: true,
  5. node: true,
  6. es2021: true,
  7. },
  8. parser: 'vue-eslint-parser',
  9. extends: [
  10. 'eslint:recommended',
  11. 'plugin:vue/vue3-recommended',
  12. 'plugin:@typescript-eslint/recommended',
  13. 'plugin:prettier/recommended',
  14. // eslint-config-prettier 的缩写
  15. 'prettier',
  16. ],
  17. parserOptions: {
  18. ecmaVersion: 12,
  19. parser: '@typescript-eslint/parser',
  20. sourceType: 'module',
  21. ecmaFeatures: {
  22. jsx: true,
  23. },
  24. },
  25. // eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写
  26. plugins: ['vue', '@typescript-eslint', 'prettier'],
  27. rules: {
  28. '@typescript-eslint/ban-ts-ignore': 'off',
  29. '@typescript-eslint/no-unused-vars': 'off',
  30. '@typescript-eslint/explicit-function-return-type': 'off',
  31. '@typescript-eslint/no-explicit-any': 'off',
  32. '@typescript-eslint/no-var-requires': 'off',
  33. '@typescript-eslint/no-empty-function': 'off',
  34. '@typescript-eslint/no-use-before-define': 'off',
  35. '@typescript-eslint/ban-ts-comment': 'off',
  36. '@typescript-eslint/ban-types': 'off',
  37. '@typescript-eslint/no-non-null-assertion': 'off',
  38. '@typescript-eslint/explicit-module-boundary-types': 'off',
  39. 'no-var': 'error',
  40. 'prettier/prettier': 'error',
  41. // 禁止出现console
  42. 'no-console': 'warn',
  43. // 禁用debugger
  44. 'no-debugger': 'warn',
  45. // 禁止出现重复的 case 标签
  46. 'no-duplicate-case': 'warn',
  47. // 禁止出现空语句块
  48. 'no-empty': 'warn',
  49. // 禁止不必要的括号
  50. 'no-extra-parens': 'off',
  51. // 禁止对 function 声明重新赋值
  52. 'no-func-assign': 'warn',
  53. // 禁止在 return、throw、continue 和 break 语句之后出现不可达代码
  54. 'no-unreachable': 'warn',
  55. // 强制所有控制语句使用一致的括号风格
  56. curly: 'warn',
  57. // 要求 switch 语句中有 default 分支
  58. 'default-case': 'warn',
  59. // 强制尽可能地使用点号
  60. 'dot-notation': 'warn',
  61. // 要求使用 === 和 !==
  62. eqeqeq: 'warn',
  63. // 禁止 if 语句中 return 语句之后有 else 块
  64. 'no-else-return': 'warn',
  65. // 禁止出现空函数
  66. 'no-empty-function': 'warn',
  67. // 禁用不必要的嵌套块
  68. 'no-lone-blocks': 'warn',
  69. // 禁止使用多个空格
  70. 'no-multi-spaces': 'warn',
  71. // 禁止多次声明同一变量
  72. 'no-redeclare': 'warn',
  73. // 禁止在 return 语句中使用赋值语句
  74. 'no-return-assign': 'warn',
  75. // 禁用不必要的 return await
  76. 'no-return-await': 'warn',
  77. // 禁止自我赋值
  78. 'no-self-assign': 'warn',
  79. // 禁止自身比较
  80. 'no-self-compare': 'warn',
  81. // 禁止不必要的 catch 子句
  82. 'no-useless-catch': 'warn',
  83. // 禁止多余的 return 语句
  84. 'no-useless-return': 'warn',
  85. // 禁止变量声明与外层作用域的变量同名
  86. 'no-shadow': 'off',
  87. // 允许delete变量
  88. 'no-delete-var': 'off',
  89. // 强制数组方括号中使用一致的空格
  90. 'array-bracket-spacing': 'warn',
  91. // 强制在代码块中使用一致的大括号风格
  92. 'brace-style': 'warn',
  93. // 强制使用骆驼拼写法命名约定
  94. camelcase: 'warn',
  95. // 强制使用一致的缩进
  96. indent: 'off',
  97. // 强制在 JSX 属性中一致地使用双引号或单引号
  98. // 'jsx-quotes': 'warn',
  99. // 强制可嵌套的块的最大深度4
  100. 'max-depth': 'warn',
  101. // 强制最大行数 300
  102. // "max-lines": ["warn", { "max": 1200 }],
  103. // 强制函数最大代码行数 50
  104. // 'max-lines-per-function': ['warn', { max: 70 }],
  105. // 强制函数块最多允许的的语句数量20
  106. 'max-statements': ['warn', 100],
  107. // 强制回调函数最大嵌套深度
  108. 'max-nested-callbacks': ['warn', 3],
  109. // 强制函数定义中最多允许的参数数量
  110. 'max-params': ['warn', 3],
  111. // 强制每一行中所允许的最大语句数量
  112. 'max-statements-per-line': ['warn', { max: 1 }],
  113. // 要求方法链中每个调用都有一个换行符
  114. 'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 3 }],
  115. // 禁止 if 作为唯一的语句出现在 else 语句中
  116. 'no-lonely-if': 'warn',
  117. // 禁止空格和 tab 的混合缩进
  118. 'no-mixed-spaces-and-tabs': 'warn',
  119. // 禁止出现多行空行
  120. 'no-multiple-empty-lines': 'warn',
  121. // 禁止出现;
  122. semi: ['warn', 'never'],
  123. // 强制在块之前使用一致的空格
  124. 'space-before-blocks': 'warn',
  125. // 强制在 function的左括号之前使用一致的空格
  126. // 'space-before-function-paren': ['warn', 'never'],
  127. // 强制在圆括号内使用一致的空格
  128. 'space-in-parens': 'warn',
  129. // 要求操作符周围有空格
  130. 'space-infix-ops': 'warn',
  131. // 强制在一元操作符前后使用一致的空格
  132. 'space-unary-ops': 'warn',
  133. // 强制在注释中 // 或 /* 使用一致的空格
  134. // "spaced-comment": "warn",
  135. // 强制在 switch 的冒号左右有空格
  136. 'switch-colon-spacing': 'warn',
  137. // 强制箭头函数的箭头前后使用一致的空格
  138. 'arrow-spacing': 'warn',
  139. 'prefer-const': 'warn',
  140. 'prefer-rest-params': 'warn',
  141. 'no-useless-escape': 'warn',
  142. 'no-irregular-whitespace': 'warn',
  143. 'no-prototype-builtins': 'warn',
  144. 'no-fallthrough': 'warn',
  145. 'no-extra-boolean-cast': 'warn',
  146. 'no-case-declarations': 'warn',
  147. 'no-async-promise-executor': 'warn',
  148. },
  149. globals: {
  150. defineProps: 'readonly',
  151. defineEmits: 'readonly',
  152. defineExpose: 'readonly',
  153. withDefaults: 'readonly',
  154. },
  155. }

env 预定义的环境变量

作用:提供预定义的环境变量。
因为node 或者浏览器中的全局变量很多,如果我们一个个声明会显得繁琐,因此就需要用到env,这是对环境定义的一组全局变量的预设。

  1. env: {
  2. browser: true,
  3. es2021: true, // 添加所有 ECMAScript 2021 全局变量并自动将 ecmaVersion 解析器选项设置为 12
  4. node: true,
  5. },

parser 指定要使用的解析器

作用:指定要使用的解析器。我们指定为vue-eslint-parser即可

  1. parser: 'vue-eslint-parser',

parserOptions 解析器配置

作用:给解析器传入一些其他的配置参数
比如我们之前安装的@typescript-eslint/parser就可以在这里进行配置

  1. parserOptions: {
  2. parser: '@typescript-eslint/parser',
  3. ecmaVersion: "latest", // 支持的es版本
  4. sourceType: 'module', // 代模块类型,默认为script,我们设置为module
  5. },

extends 使用预设配置

作用:使用预设的 lint 包
如果要我们自己去设置各个规则未免会显得繁琐,所以可以直接使用业界的最佳实践作为基础规则

  1. extends: [
  2. 'plugin:vue/vue3-recommended',
  3. 'plugin:@typescript-eslint/recommended'
  4. ],

plugins 增强 ESlint 功能

作用:增强 ESlint 功能
还记得我们最开始安装的@typescript-eslint/eslint-plugin吗?它就是用来给 eslint 提供一些额外的适用于 ts 语法的规则插件名中的 eslint-plugin- 可以省略:

  1. // eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写
  2. plugins: ["vue", "@typescript-eslint", "prettier"],

rules 自定义规则

作用:创建自定义规则。

规则定义值:

  • off 或 0 - 关闭规则
  • warn 或 1 - 开启规则, 使用警告 程序不会退出
  • error 或 2 - 开启规则, 使用错误 程序退出

虽然我们通过 extends 引入了行业内最佳实践的一些规则,但是每个团队还是有每个团队自己的开发习惯,所以我们可以通过 rules 添加或者修改 extends 中的规则。

2. 项目下新建 .eslintignore

  1. # eslint 忽略检查 (根据项目需要自行添加)
  2. node_modules
  3. dist

prettier 支持

  1. # 安装 prettier
  2. yarn add prettier --dev

解决 eslint 和 prettier 冲突

当 ESLint 的规则和 Prettier 的规则相冲突时,就会发现一个尴尬的问题,用其中一种来格式化代码,另一种就会报错。

prettier 官方提供了一款工具 eslint-config-prettier 来解决 ESLint 中的样式规范和 prettier 中样式规范的冲突,以 prettier 的样式规范为准,使 ESLint 中的样式规范自动失效

  1. # 安装插件 eslint-config-prettier
  2. yarn add eslint-config-prettier --dev

上面介绍的工具,仅仅只是将部分 ESLint 规则给禁用了,避免 Prettier 格式化之后的代码导致 ESLint 报错而已,如何将两者结合起来使用呢?

prettier 官方提供了一个 ESLint 插件 eslint-plugin-prettier ,这个插件的主要作用就是将 prettier 作为 ESLint 的规则来使用,相当于代码不符合 Prettier 的标准时,会报一个 ESLint 错误,同时也可以通过 eslint —fix 来进行格式化

  1. yarn add eslint-plugin-prettier --dev

项目下新建 .prettierrc.js

配置 prettier 格式化规则:

  1. module.exports = {
  2. printWidth: 100, // 超过最大值换行
  3. tabWidth: 2, // 缩进字节数
  4. useTabs: false, // 缩进不使用tab,使用空格
  5. semi: false, // 末尾不加尾号
  6. singleQuote: true, // 默认单引号
  7. quoteProps: 'as-needed', // 仅在需要时在对象属性添加引号
  8. jsxSingleQuote: false, // jsx中使用单引号代替双引号
  9. trailingComma: 'none', // es5对象属性最后加逗号。none不加
  10. bracketSpacing: true, // 在对象,数组括号与文字之间加空格
  11. jsxBracketSameLine: false, // jsx中把'>' 是否单独放一行
  12. arrowParens: 'always', // 箭头函数参数只有一个时是否要有小括号
  13. htmlWhitespaceSensitivity: 'ignore', // HTML空白灵敏度,空格被认为是不敏感的
  14. vueIndentScriptAndStyle: false, // scriptstyle标签不缩进
  15. endOfLine: 'auto',
  16. // 覆写默认配置,为某些特定文件制定特定配置
  17. overrides: [
  18. {
  19. files: '*.json',
  20. options: {
  21. printWidth: 200
  22. }
  23. }
  24. ]
  25. }

package.json 配置命令

  1. {
  2. "script": {
  3. "lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
  4. "prettier": "prettier --write ."
  5. }
  6. }

上面配置完成后, 可以运行以下命令测试下代码检查个格式化效果:

  1. # eslint 检查
  2. yarn lint
  3. # prettier 自动格式化
  4. yarn prettier

配置 husky + lint-staged

使用husky + lint-staged助力团队编码规范, husky&lint-staged 安装推荐使用 mrm, 它将根据 package.json 依赖项中的代码质量工具来安装和配置 husky 和 lint-staged,因此请确保在此之前安装并配置所有代码质量工具,如 Prettier 和 ESlint

配置 css 预处理器 less

虽然 vite 原生支持 less/sass/scss/stylus,但是你必须手动安装他们的预处理器依赖

安装

  1. yarn add -D less
  2. yarn add --dev less-loader

配置全局 less 样式文件

src/ 下新增 styles 文件夹,用于存放全局样式文件

新建 main.less, 设置一个颜色变量 :

@primary-color: #1890ff;

如何将这个全局样式文件全局注入到项目中呢?配置 Vite 即可:

  1. css: {
  2. preprocessorOptions: {
  3. less: {
  4. additionalData: '@import "@/styles/main.less";', // 没有 resolve.alias 别名的话 路径为./src/styles/main.less
  5. },
  6. },
  7. },

组件中使用

  1. <style scoped lang="less">
  2. a {
  3. color: @primary-color;
  4. }
  5. </style>

image.png

stylelint 规范 css 的书写风格

安装

  • stylelint — 运行工具,
    - stylelint-config-standard — stylelint的推荐配置
    - stylelint-order — 排序插件(先写定位,再写盒模型,再写内容区样式,最后写 CSS3 相关属性)
    - stylelint-config-prettier 关闭所有不必要的或可能与Prettier冲突的规则


  1. yarn add stylelint --dev
  2. yarn add stylelint-config-prettier stylelint-config-recommended stylelint-config-standard stylelint-config-html --dev

官方提供了stylelint-config-recommended和stylelint-config-standard两种css标准:
stylelint-config-recommended包含可能报错的rule,code format的css标准
stylelint-config-standard继承于recommend,一些常见的css书写标准

image.png

Stylelint 14+ 不再包含 Scss,Sass,Less 或者 SugarSS 等预编译器的解析了,所以我们可以通过 extends 引入公共规则

  1. "extends": ["stylelint-config-standard", 'stylelint-config-prettier'],

为了让 stylelint 能够读 vue (.html, .xml, .svelte, .vue 等)文件,我们还需要安装postcss-html

  1. yarn add postcss-html --dev
  1. "customSyntax": "postcss-html"

stylelint-order 是CSS属性排序插件,并且每个规则都支持自动修复(stylelint —fix)

  1. yarn add stylelint-order --dev

package.json 配置命令

  1. "stylelint": "npx stylelint --aei .vue src",
  2. "stylint:fix": "npx stylelint ./src/**/*.vue --fix"

项目下新建 stylelint.config.js

项目下新建 .stylelintignore

.stylelintignore 用来配置不需要通过 stylelint 约束的文件

  1. # 其他类型文件
  2. *.js
  3. *.ts
  4. *.jpg
  5. *.woff
  6. # 测试和打包目录
  7. /dist/*
  8. /public/*
  9. public/*
  10. /node_modules/

路由 vue-router@4

  1. # 安装路由
  2. yarn add vue-router@4

src 文件下新增 router 文件夹 => index.ts 文件, 内容如下:

  1. import { createRouter, createWebHistory } from 'vue-router'
  2. import type { RouteRecordRaw } from 'vue-router'
  3. const basicRouters: RouteRecordRaw[] = [
  4. {
  5. path: '/',
  6. name: 'Root',
  7. redirect: '/login'
  8. },
  9. {
  10. path: '/login',
  11. name: 'Login',
  12. component: () => import('@/views/login/Index.vue'),
  13. }
  14. ]
  15. const router = createRouter({
  16. history: createWebHistory(),
  17. routes: basicRouters,
  18. })
  19. export default router

修改入口文件 mian.ts :

  1. import { createApp } from 'vue'
  2. import App from './App.vue'
  3. import router from './router/index'
  4. const app = createApp(App)
  5. app.use(router)
  6. app.mount('#app')

vue-router4.x 支持 typescript,配置路由的类型是 RouteRecordRaw,这里 meta 可以让我们有更多的发挥空间,这里提供一些参考:

  • title:string; 页面标题,通常必选。
  • icon?:string; 图标,一般配合菜单使用。
  • auth?:boolean; 是否需要登录权限。
  • ignoreAuth?:boolean; 是否忽略权限。
  • roles?:RoleEnum[]; 可以访问的角色
  • keepAlive?:boolean; 是否开启页面缓存
  • hideMenu?:boolean; 有些路由我们并不想在菜单中显示,比如某些编辑页面。
  • order?:number; 菜单排序。
  • frameUrl?:string; 嵌套外链。

这里只提供一些思路,每个项目涉及到的业务都会存在些差异,这里就不作详细讲解了,根据自己业务需求做配置即可。

axios 统一请求封装

  1. # 安装 axios
  2. yarn add axios
  3. # 安装 nprogress 用于请求 loading
  4. # 也可以根据项目需求自定义其它 loading
  5. yarn add nprogress
  6. # 类型声明,或者添加一个包含 `declare module 'nprogress'
  7. yarn add @types/nprogress --dev

实际使用中可以根据项目修改,比如RESTful api中可以自行添加putdelete请求,ResType也可以根据后端的通用返回值动态的去修改

新增 service 文件夹,service 下新增 http.ts 文件以及 api 文件夹:

Vite2   Vue3   TypeScript   Pinia - 图10

http.ts : 用于axios封装

  1. import axios, { AxiosRequestConfig } from 'axios'
  2. import NProgress from 'nprogress'
  3. // 设置请求头和请求路径
  4. axios.defaults.baseURL = '/api'
  5. axios.defaults.timeout = 10000
  6. axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
  7. axios.interceptors.request.use(
  8. (config): AxiosRequestConfig<any> => {
  9. const token = window.sessionStorage.getItem('token')
  10. if (token) {
  11. //@ts-ignore
  12. config.headers.token = token
  13. }
  14. return config
  15. },
  16. (error) => {
  17. return error
  18. }
  19. )
  20. // 响应拦截
  21. axios.interceptors.response.use((res) => {
  22. if (res.data.code === 111) {
  23. sessionStorage.setItem('token', '')
  24. // token过期操作
  25. }
  26. return res
  27. })
  28. interface ResType<T> {
  29. code: number
  30. data?: T
  31. msg: string
  32. err?: string
  33. }
  34. interface Http {
  35. get<T>(url: string, params?: unknown): Promise<ResType<T>>
  36. post<T>(url: string, params?: unknown): Promise<ResType<T>>
  37. upload<T>(url: string, params: unknown): Promise<ResType<T>>
  38. download(url: string): void
  39. }
  40. const http: Http = {
  41. get(url, params) {
  42. return new Promise((resolve, reject) => {
  43. NProgress.start()
  44. axios
  45. .get(url, { params })
  46. .then((res) => {
  47. NProgress.done()
  48. resolve(res.data)
  49. })
  50. .catch((err) => {
  51. NProgress.done()
  52. reject(err.data)
  53. })
  54. })
  55. },
  56. post(url, params) {
  57. return new Promise((resolve, reject) => {
  58. NProgress.start()
  59. axios
  60. .post(url, JSON.stringify(params))
  61. .then((res) => {
  62. NProgress.done()
  63. resolve(res.data)
  64. })
  65. .catch((err) => {
  66. NProgress.done()
  67. reject(err.data)
  68. })
  69. })
  70. },
  71. upload(url, file) {
  72. return new Promise((resolve, reject) => {
  73. NProgress.start()
  74. axios
  75. .post(url, file, {
  76. headers: { 'Content-Type': 'multipart/form-data' },
  77. })
  78. .then((res) => {
  79. NProgress.done()
  80. resolve(res.data)
  81. })
  82. .catch((err) => {
  83. NProgress.done()
  84. reject(err.data)
  85. })
  86. })
  87. },
  88. download(url) {
  89. const iframe = document.createElement('iframe')
  90. iframe.style.display = 'none'
  91. iframe.src = url
  92. iframe.onload = function () {
  93. document.body.removeChild(iframe)
  94. }
  95. document.body.appendChild(iframe)
  96. },
  97. }
  98. export default http

除了自己手动封装 axios , 这里还推荐一个 vue3 的请求库: VueRequest, 非常好用, 下面来看看 VueRequest有哪些比较好用的功能吧!!!

  • 🚀 所有数据都具有响应式
  • 🔄 轮询请求
  • 🤖 自动处理错误重试
  • 🗄 内置请求缓存
  • 💧 节流请求与防抖请求
  • 🎯 聚焦页面时自动重新请求
  • ⚙️ 强大的分页扩展以及加载更多扩展
  • 📠 完全使用 Typescript 编写,具有强大的类型提示
  • ⚡️ 兼容 Vite
  • 🍃 轻量化
  • 📦 开箱即用

官网链接: https://www.attojs.com/

状态管理 pinia

由于 vuex 4 对 typescript 的支持让人感到难过,所以状态管理弃用了 vuex 而采取了 pinia. pinia 的作者是 Vue 核心团队成员

安装 pinia

Pinia 与 Vuex 的区别:

  • id 是必要的,它将所使用 store 连接到 devtools。
  • 创建方式:new Vuex.Store(...)(vuex3),createStore(...)(vuex4)。
  • 对比于 vuex3 ,state 现在是一个函数返回对象
  • 没有 mutations,不用担心,state 的变化依然记录在 devtools 中。
  1. # 安装 yarn add pinia@next

main.ts 中增加

# 引入 import {createPinia} from "pinia" # 创建根存储库并将其传递给应用程序 app.use(createPinia())

src 文件夹下新增 store 文件夹, 接在在 store 中新增 main.ts

创建 store, mian.ts :

`import {defineStore} from ‘pinia’

export const useMainStore = defineStore({
id: ‘mian’,
state: () =>({
name: ‘超级管理员’
})
})
`

组建中获取 store :

<br /> <br />

getters 用法介绍

Pinia 中的 getter 与 Vuex 中的 getter 、组件中的计算属性具有相同的功能

store => mian.ts

`import {defineStore} from ‘pinia’

export const useMainStore = defineStore({
id: ‘mian’,
state: () => ({
name: ‘超级管理员’,
}),
// getters
getters: {
nameLength: (state) => state.name.length,
}
})
`

组件中使用:

`