1. 首页
  2. 技术知识

Vue3 + Vite 前端工程化-基础篇

前言

Vue3 距首次发布时间近一年了,Vite 也从 1.0 到了 2.x,周边的生态,技术方案足够成熟了,是时候使用新技术来优化开发体验了,因此有了本篇文章。


初始化项目

通过官方脚手架初始化项目

  1. # 输入项目名,选择模板,选择是否支持 ts
  2. npm init vite@latest
  3. # 也可直接指定快速生成
  4. npm init vite@latest json-cms –template vue-ts

复制代码 执行结束X入项目目录,安装依赖后执行 npm run dev 即可秒开项目

  1. cd json-cms
  2. npm install
  3. npm run dev

复制代码 查看项目结构,对于生成的目录结构不足以支持项目的复杂度,因此我们结构进行扩展,扩展后结构为右图


Vite 定制化配置

在初始化的项目中 vite.config.js 只是引入了提供 Vue 3 单文件组件支持的 plugin,接下来推荐一些优秀的 vite-plugin,更多 plugin 详见 awesome-vite。


@vitejs/plugin-legacy

为打包后的文件提供传统浏览器兼容性支持。因为 vite 是基于现代浏览器支持的 ESM 机制,所以构建后文件模块仍是 ESM,如果需要支持旧版浏览器就需要使用 @vitejs/plugin-legacy。

安装及使用

  1. npm i -D @vitejs/plugin-legacy
  2. // vite.config.ts
  3. import { defineConfig } from 'vite'
  4. import legacy from '@vitejs/plugin-legacy'
  5. export default defineConfig({
  6.   plugins: [
  7.     legacy({
  8.       targets: ['defaults', 'not IE 11'],
  9.     }),
  10.   ],
  11. })

复制代码
vite-plugin-element-plus

为 ElementPlus 提供按需引入能力。全量导入 ElementPlus 导致构建包的体积过大,按需引入有效的减小包的体积。此包的原理是动态将每个按需引入的组件 css 写入。

  1. import { ElButton } from 'element-plus'
  2.       ↓ ↓ ↓ ↓ ↓ ↓
  3. import { ElButton } from 'element-plus'
  4. import 'element-plus/es/components/button/style/css'

复制代码 安装及使用

  1. npm i -D vite-plugin-element-plus
  2. // vite.config.ts
  3. import { defineConfig } from 'vite'
  4. import importElementPlus from 'vite-plugin-element-plus'
  5. export default defineConfig({
  6.   plugins: [
  7.     // @ts-ignore 此处暂时需要使用 ignore
  8.     // 原因是包内部的 options 未做非必填兼容
  9.     // 目前已有人提了 PR,未合并,使用可以观望下
  10.     importElementPlus(),
  11.   ],
  12. })

复制代码
@vitejs/plugin-vue-jsx

提供 Vue 3 JSX & TSX 支持(通过 专用的 Babel 转换插件)。

安装及使用

  1. npm i -D @vitejs/plugin-vue-jsx
  2. // vite.config.ts
  3. import { defineConfig } from 'vite'
  4. import vueJsx from '@vitejs/plugin-vue-jsx'
  5. export default defineConfig({
  6.   plugins: [
  7.     vueJsx({
  8.       // options 参数将传给 @vue/babel-plugin-jsx
  9.     }),
  10.   ],
  11. })

复制代码
rollup-plugin-visualizer

可视化并分析构建包,查看哪些模块占用空间大小,以此来优化构建包的大小。这是一个 Rollup 的 plugin,推荐这个也是 vite 的一个特性,vite 默认已经支持大部分的 Rollup 的 plugin,从这点来看,vite 的 plugin 库更加丰富了。

安装及使用

  1. npm i -D rollup-plugin-visualizer
  2. // vite.config.ts
  3. import { defineConfig } from 'vite'
  4. import visualizer from 'rollup-plugin-visualizer'
  5. export default defineConfig({
  6.   plugins: [visualizer()],
  7. })

复制代码
vite-plugin-html

为 index.html 扩展了动态能力,提供压缩及 EJS 模板功能,动态注入,如果不了解 EJS,移步查看。

安装及使用

  1. npm i -D vite-plugin-html
  2. // vite.config.ts
  3. import { defineConfig } from 'vite'
  4. import html from 'vite-plugin-html'
  5. // 以下是实现动态设置标题,及注入 js 路径
  6. export default defineConfig({
  7.   plugins: [
  8.     html({
  9.       inject: {
  10.         injectData: {
  11.           title: 'JSON CMS',
  12.           tinymce: '/js/tinymce/tinymce.min.js',
  13.         },
  14.       },
  15.     }),
  16.   ],
  17. })

复制代码 编译前

  1. <head>
  2.   <meta charset="UTF-8" />
  3.   <link rel="icon" href="/favicon.ico" />
  4.   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  5.   <title><%- title %></title>
  6.   <script src="<%- tinymce %>" rel="preload"></script>
  7. </head>

复制代码 编译后

  1. <head>
  2.   <meta charset="UTF-8" />
  3.   <link rel="icon" href="/favicon.ico" />
  4.   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  5.   <title>JSON CMS</title>
  6.   <script src="/js/tinymce/tinymce.min.js" rel="preload"></script>
  7. </head>

复制代码
基于 husky + lint-staged 项目规范化

husky 是一个让 Git hooks 更简单好用的工具。安装后,它会自动在仓库中的 .git/ 目录下增加相应的钩子,比如 pre-commit 钩子就会在你执行 git commit 的触发。

那么我们可以在 pre-commit 中实现一些比如 lint 检查、单元测试、代码美化等操作。当然,pre-commit 阶段执行的命令当然要保证其速度不要太慢,每次 commit 都等很久也不是什么好的体验。

lint-staged,一个过滤出 Git 代码暂存区文件(被 git add 的文件)的工具。这个很实用,因为我们如果对整个项目的代码做一个检查,可能耗时很长,如果是老项目,要对之前的代码做一个代码规范检查并修改的话,这可能就麻烦了呀,可能导致项目改动很大。

所以 lint-staged,对团队项目和开源项目来说,是一个很好的工具,它是对个人要提交的代码的一个规范和约束。


编码规范

通过 eslint prittiter stylelint 进行编码规范,修复。

安装及使用

  1. npm i -D eslint prittiter stylelint eslint-config-prettier eslint-define-config eslint-plugin-prettier eslint-plugin-vue vue-eslint-parser @typescript-eslint/eslint-plugin @typescript-eslint/parser

复制代码 以下为 eslint 和 prittier 配置文件

  1. // .eslintrc.js
  2. const { defineConfig } = require('eslint-define-config')
  3. module.exports = defineConfig({
  4.   root: true,
  5.   env: {
  6.     browser: true,
  7.     node: true,
  8.     es6: true
  9.   },
  10.   parser: 'vue-eslint-parser',
  11.   parserOptions: {
  12.     parser: '@typescript-eslint/parser',
  13.     ecmaVersion: 2020,
  14.     sourceType: 'module',
  15.     jsxPragma: 'React',
  16.     ecmaFeatures: {
  17.       jsx: true
  18.     }
  19.   },
  20.   extends: [
  21.     'plugin:vue/vue3-recommended',
  22.     'plugin:@typescript-eslint/recommended',
  23.     'prettier'
  24.     'plugin:prettier/recommended'
  25.   ],
  26.   rules: {
  27.     // …
  28.   }
  29. })
  30. // prettier.config.js
  31. module.exports = {
  32.   printWidth: 100,
  33.   tabWidth: 2,
  34.   semi: false,
  35.   singleQuote: true,
  36.   bracketSpacing: true,
  37.   trailingComma: 'none',
  38.   jsxBracketSameLine: false,
  39.   jsxSingleQuote: false,
  40.   arrowParens: 'always',
  41.   insertPragma: false,
  42.   requirePragma: false,
  43.   proseWrap: 'never',
  44.   htmlWhitespaceSensitivity: 'strict',
  45. }

复制代码 有了相关的 lint 的配置文件,接下需要配置 husky 和 lint-staged,这里提供一行快速配置的命令

  1. npx mrm@2 lint-staged

复制代码 这条命令将根据 package.json 依赖中的 eslint 和 prettier 安装配置 husky 和 lint-staged,这样 Git hooks 就配好了,将新增的配置 git add 后,再执行 git commit 就会触发 lint 校验,会将有问题的文件修复并再次 git add 到暂存区最终完成 commit 操作。

  1. {
  2.   "scripts": {
  3.     "prepare": "husky install"
  4.   },
  5.   "devDependencies": {
  6.     // …
  7.     "eslint": "^7.32.0",
  8.     "husky": "^7.0.2",
  9.     "lint-staged": "^11.1.2",
  10.     "prettier": "^2.4.0"
  11.     // …
  12.   },
  13.   "lint-staged": {
  14.     "*.js": "eslint –cache –fix",
  15.     "*.{js,css,md}": "prettier –write"
  16.   }
  17. }

复制代码
代码提交规范

目前提交规范使用最广泛的就是 conventional commits (约定式提交), coding 平台使用的也是约定式提交,coding 平台的校验是开发者 push 的时候才会触发,这时候已经执行过 commit 了,因此我们需要将提交规范前置,在 commit 时进行校验并给出修改提示。

以上效果主要需要 commitizen 实现,全局安装或作为 npm script 使用,官方推荐 Commitizen-friendly 方式使用,全局安装后初始化配置。

  1. # 全局安装
  2. npm install -g commitizen
  3. # Commitizen-friendly 方式
  4. commitizen init cz-conventional-changelog –save-dev –save-exact
  5. // package.json
  6. {
  7.   "scripts": {
  8.     "commit": "cz"
  9.   },
  10.   "config": {
  11.     "commitizen": {
  12.       "path": "cz-conventional-changelog"
  13.     }
  14.   }
  15. }

复制代码 通过以上配置,可以用 git cz 或 npm run commit 替换 git commit 命令,实现规范提交的选择。

但是如果有人执意要使用 git commit 自定义提交信息也是没有办法,因此需要增加 commitlint 来检查提交信息是否符合规范。

安装使用过程

  1. npm i -D @commitlint/config-conventional @commitlint/cli
  2. # 因为上面已经安装了 husky,所以我们只要再新增一个 hook
  3. npx husky add .husky/commit-msg 'npx –no-install commitlint –edit "$1"'

复制代码 这样就实现了提交信息的检查。另外可以通过 cz-customizable 设置成中文的提交提示。


根据版本生成 changelog

正是因为有了以上的“约定式提交”规范,我们还可以通过 conventional-changelog-cli 生成对应版本的 ChangeLog。

  1. {
  2.   "scripts": {
  3.     "log": "conventional-changelog -p angular -i CHANGELOG.md -s"
  4.   }
  5. }

复制代码
Vue3 开发生态

路由

vue-router4.x 第一个 4.0 版本已经在 20 年 12 月已经发布,至今已相对稳定,使用方式相对 Vue2 时变化不是很大。通过官方提供的迁移指南能够对比出与 3.x 版本的区别,详情移步至迁移指南。


状态管理

状态管理有了新的选择,可以选择 pina 和 vuex。

  • pinia 是根据 vuex5.x 的提案设计的,可以说是面向未来的 vuex,该提案提出了非集中式的 store 管理模型,没有嵌套的 store 模块,移除了 mutations,留下 state、getters 和 actions,我个人也比较看好这种模式。
  • vuex5.x 提案
  • 提案 API 设计   –
  • vuex4.x 正式版发布在 21 年年初,作为官方状态管理,想必大家也都很熟悉,就不做过多介绍,使用方式相对 Vue2 时变化也不大。
  • 迁移指南

UI 组件库

可供选择的 UI 组件库也很丰富

  • PC 端:
  • 曾一度被怀疑弃更的 ElementUI,现在名为 elementPlus
  • 很早就支持 Vue3 的 Ant Design Vue
  • 出自图森未来的,一个名字很特别的 Naive UI
  • M 端:
  • 出自京东零售团队的 nutui
  • 出自有赞的 vant

另外与 CSS 相关的好用的工具

  • tailwindcss 一个可以让你不写一行 CSS 就能实现布局等一系列操作的工具
  • windcss 也是同类型的 css 工具,兼容 tailwindcss
  • 现代浏览器 CSS 样式重置 modern-normalize

Vue3 & Vite 开发注意事项

本节会介绍在实际项目遇到的问题,以此作为开发注意事项,仅组合式 API 相关。


Vue3.2 发布,SFC 新特性

  • 支持 <script setup> 语法糖,在组合式 API 中,无需将模板需要的值 return 处理,使代码更简洁。
  • 支持 <style> v-bind 语法糖,可以直接传值至 <style> 中,使用更方便。
  1. <script setup>
  2.   import { ref } from 'vue'
  3.   const color = ref('red')
  4. </script>
  5. <template>
  6.   <button @click="color = color === 'red' ? 'green' : 'red'">
  7.     Color is: {{ color }}
  8.   </button>
  9. </template>
  10. <style scoped>
  11.   button {
  12.     color: v-bind(color);
  13.   }
  14. </style>

复制代码

  • 组件的 props 和 emit 写法发生变化以及在 TypeScript 中的类型定义。
  1. // js 写法
  2. <script setup>
  3. const props = defineProps({
  4.   foo: String
  5. })
  6. const emit = defineEmits(['change', 'delete'])
  7. // setup code
  8. </script>
  9. // ts 写法
  10. <script lang="ts" setup>
  11. const props = defineProps<{
  12.   foo: string
  13.   bar?: number
  14. }>()
  15. const emit = defineEmits<{
  16.   (e: 'change', id: number): void
  17.   (e: 'update', value: string): void
  18. }>()
  19. </script>

复制代码
Vue3 废弃 filter 及替代方案

从 Vue 3.0 开始,过滤器已移除,且不再支持。

  • 在 2.x 中,可以使用过滤器来处理通用文本格式。
  1. <template>
  2.   <p>今天是 {{ date | weeekFormat }}</p>
  3. </template>
  4. <script>
  5.   export default {
  6.     props: {
  7.       date: {
  8.         type: [Date | String | Number],
  9.         required: true,
  10.       },
  11.     },
  12.     filters: {
  13.       /**
  14.        * 返回 星期五
  15.        * */
  16.       weeekFormat(value) {
  17.         return new Intl.DateTimeFormat('zh-CN', { weekday: 'long' }).format(
  18.           new Date(value)
  19.         )
  20.       },
  21.     },
  22.   }
  23. </script>

复制代码

  • 在 3.x 中,需要用方法调用或计算属性来替换它们。
  1. <template>
  2.   <p>今天是 {{ date | weeekFormat }}</p>
  3. </template>
  4. <script>
  5.   export default {
  6.     props: {
  7.       date: {
  8.         type: [Date | String | Number],
  9.         required: true,
  10.       },
  11.     },
  12.     computed: {
  13.       /**
  14.        * 返回 星期五
  15.        * */
  16.       weeekFormat() {
  17.         return new Intl.DateTimeFormat('zh-CN', { weekday: 'long' }).format(
  18.           new Date(this.date)
  19.         )
  20.       },
  21.     },
  22.   }
  23. </script>

复制代码 另外全局过滤器可以使用 globalProperties 实现。较好的实践方式是实现自定义 plugin,在 plugin 中定义全局属性。

  1. // /plugin/filters.ts
  2. import type { APP } from 'vue'
  3. import { timeFormat } from '@/utils'
  4. export default {
  5.   install: (app: App) => {
  6.     app.config.globalProperties.$filters = {
  7.       format(value: string | Date | number) {
  8.         return timeFormat(value)
  9.       },
  10.     }
  11.   },
  12. }
  13. // main.ts
  14. import { createApp } from 'vue'
  15. import filters from '@/plugins/filters'
  16. // 这里的类型定义是必须的,否则会在 build 时类型出错
  17. declare module '@vue/runtime-core' {
  18.   interface ComponentCustomProperties {
  19.     $filters: Record<string, any>
  20.   }
  21. }
  22. createApp(App).use(filters).mount('#app')

复制代码 经过以上配置即可在支持全局使用,关于类型问题可以参考相关PR和源码。

  1. <el-table-column label="创建时间">
  2.   <template #default="{ row }">
  3.     {{ $filters.format(row.createTime) }}
  4.   </template>
  5. </el-table-column>

复制代码
关于 global is not defined 问题

因为 Vite 是 ESM 机制,有些包内部使用了 node 的 global 对象,解决此问题可以通过自建 pollfill,然后在 main.ts 顶部引入,不是最优解,有想法的同学可以相互交流下:)。

  1. // polyfills
  2. if (typeof (window as any).global === 'undefined') {
  3.   ;(window as any).global = window
  4. }
  5. // main.ts
  6. import './polyfills'
  7. import { createApp } from 'vue'

复制代码
总结

至此,使用 Vue3 + Vite 的工程化基础篇搭建已完成,后续会深入在业务上提高效率及规范,已达到工程化的目的。

原创文章,作者:starterknow,如若转载,请注明出处:https://www.starterknow.com/126955.html

联系我们