Vue3 + Vite 前端工程化-基础篇
前言
Vue3 距首次发布时间近一年了,Vite 也从 1.0 到了 2.x,周边的生态,技术方案足够成熟了,是时候使用新技术来优化开发体验了,因此有了本篇文章。
初始化项目
通过官方脚手架初始化项目
- # 输入项目名,选择模板,选择是否支持 ts
- npm init vite@latest
- # 也可直接指定快速生成
- npm init vite@latest json-cms –template vue-ts
复制代码 执行结束X入项目目录,安装依赖后执行 npm run dev 即可秒开项目
- cd json-cms
- npm install
- npm run dev
复制代码 查看项目结构,对于生成的目录结构不足以支持项目的复杂度,因此我们结构进行扩展,扩展后结构为右图
Vite 定制化配置
在初始化的项目中 vite.config.js 只是引入了提供 Vue 3 单文件组件支持的 plugin,接下来推荐一些优秀的 vite-plugin,更多 plugin 详见 awesome-vite。
@vitejs/plugin-legacy
为打包后的文件提供传统浏览器兼容性支持。因为 vite 是基于现代浏览器支持的 ESM 机制,所以构建后文件模块仍是 ESM,如果需要支持旧版浏览器就需要使用 @vitejs/plugin-legacy。
安装及使用
- npm i -D @vitejs/plugin-legacy
- // vite.config.ts
- import { defineConfig } from 'vite'
- import legacy from '@vitejs/plugin-legacy'
- export default defineConfig({
- plugins: [
- legacy({
- targets: ['defaults', 'not IE 11'],
- }),
- ],
- })
复制代码
vite-plugin-element-plus
为 ElementPlus 提供按需引入能力。全量导入 ElementPlus 导致构建包的体积过大,按需引入有效的减小包的体积。此包的原理是动态将每个按需引入的组件 css 写入。
- import { ElButton } from 'element-plus'
- ↓ ↓ ↓ ↓ ↓ ↓
- import { ElButton } from 'element-plus'
- import 'element-plus/es/components/button/style/css'
复制代码 安装及使用
- npm i -D vite-plugin-element-plus
- // vite.config.ts
- import { defineConfig } from 'vite'
- import importElementPlus from 'vite-plugin-element-plus'
- export default defineConfig({
- plugins: [
- // @ts-ignore 此处暂时需要使用 ignore
- // 原因是包内部的 options 未做非必填兼容
- // 目前已有人提了 PR,未合并,使用可以观望下
- importElementPlus(),
- ],
- })
复制代码
@vitejs/plugin-vue-jsx
提供 Vue 3 JSX & TSX 支持(通过 专用的 Babel 转换插件)。
安装及使用
- npm i -D @vitejs/plugin-vue-jsx
- // vite.config.ts
- import { defineConfig } from 'vite'
- import vueJsx from '@vitejs/plugin-vue-jsx'
- export default defineConfig({
- plugins: [
- vueJsx({
- // options 参数将传给 @vue/babel-plugin-jsx
- }),
- ],
- })
复制代码
rollup-plugin-visualizer
可视化并分析构建包,查看哪些模块占用空间大小,以此来优化构建包的大小。这是一个 Rollup 的 plugin,推荐这个也是 vite 的一个特性,vite 默认已经支持大部分的 Rollup 的 plugin,从这点来看,vite 的 plugin 库更加丰富了。
安装及使用
- npm i -D rollup-plugin-visualizer
- // vite.config.ts
- import { defineConfig } from 'vite'
- import visualizer from 'rollup-plugin-visualizer'
- export default defineConfig({
- plugins: [visualizer()],
- })
复制代码
vite-plugin-html
为 index.html 扩展了动态能力,提供压缩及 EJS 模板功能,动态注入,如果不了解 EJS,移步查看。
安装及使用
- npm i -D vite-plugin-html
- // vite.config.ts
- import { defineConfig } from 'vite'
- import html from 'vite-plugin-html'
- // 以下是实现动态设置标题,及注入 js 路径
- export default defineConfig({
- plugins: [
- html({
- inject: {
- injectData: {
- title: 'JSON CMS',
- tinymce: '/js/tinymce/tinymce.min.js',
- },
- },
- }),
- ],
- })
复制代码 编译前
- <head>
- <meta charset=&#34;UTF-8&#34; />
- <link rel=&#34;icon&#34; href=&#34;/favicon.ico&#34; />
- <meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34; />
- <title><%- title %></title>
- <script src=&#34;<%- tinymce %>&#34; rel=&#34;preload&#34;></script>
- </head>
复制代码 编译后
- <head>
- <meta charset=&#34;UTF-8&#34; />
- <link rel=&#34;icon&#34; href=&#34;/favicon.ico&#34; />
- <meta name=&#34;viewport&#34; content=&#34;width=device-width, initial-scale=1.0&#34; />
- <title>JSON CMS</title>
- <script src=&#34;/js/tinymce/tinymce.min.js&#34; rel=&#34;preload&#34;></script>
- </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 进行编码规范,修复。
安装及使用
- 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 配置文件
- // .eslintrc.js
- const { defineConfig } = require(&#39;eslint-define-config&#39;)
- module.exports = defineConfig({
- root: true,
- env: {
- browser: true,
- node: true,
- es6: true
- },
- parser: &#39;vue-eslint-parser&#39;,
- parserOptions: {
- parser: &#39;@typescript-eslint/parser&#39;,
- ecmaVersion: 2020,
- sourceType: &#39;module&#39;,
- jsxPragma: &#39;React&#39;,
- ecmaFeatures: {
- jsx: true
- }
- },
- extends: [
- &#39;plugin:vue/vue3-recommended&#39;,
- &#39;plugin:@typescript-eslint/recommended&#39;,
- &#39;prettier&#39;
- &#39;plugin:prettier/recommended&#39;
- ],
- rules: {
- // …
- }
- })
- // prettier.config.js
- module.exports = {
- printWidth: 100,
- tabWidth: 2,
- semi: false,
- singleQuote: true,
- bracketSpacing: true,
- trailingComma: &#39;none&#39;,
- jsxBracketSameLine: false,
- jsxSingleQuote: false,
- arrowParens: &#39;always&#39;,
- insertPragma: false,
- requirePragma: false,
- proseWrap: &#39;never&#39;,
- htmlWhitespaceSensitivity: &#39;strict&#39;,
- }
复制代码 有了相关的 lint 的配置文件,接下需要配置 husky 和 lint-staged,这里提供一行快速配置的命令
- npx mrm@2 lint-staged
复制代码 这条命令将根据 package.json 依赖中的 eslint 和 prettier 安装配置 husky 和 lint-staged,这样 Git hooks 就配好了,将新增的配置 git add 后,再执行 git commit 就会触发 lint 校验,会将有问题的文件修复并再次 git add 到暂存区最终完成 commit 操作。
- {
- &#34;scripts&#34;: {
- &#34;prepare&#34;: &#34;husky install&#34;
- },
- &#34;devDependencies&#34;: {
- // …
- &#34;eslint&#34;: &#34;^7.32.0&#34;,
- &#34;husky&#34;: &#34;^7.0.2&#34;,
- &#34;lint-staged&#34;: &#34;^11.1.2&#34;,
- &#34;prettier&#34;: &#34;^2.4.0&#34;
- // …
- },
- &#34;lint-staged&#34;: {
- &#34;*.js&#34;: &#34;eslint –cache –fix&#34;,
- &#34;*.{js,css,md}&#34;: &#34;prettier –write&#34;
- }
- }
复制代码
代码提交规范
目前提交规范使用最广泛的就是 conventional commits (约定式提交), coding 平台使用的也是约定式提交,coding 平台的校验是开发者 push 的时候才会触发,这时候已经执行过 commit 了,因此我们需要将提交规范前置,在 commit 时进行校验并给出修改提示。
以上效果主要需要 commitizen 实现,全局安装或作为 npm script 使用,官方推荐 Commitizen-friendly 方式使用,全局安装后初始化配置。
- # 全局安装
- npm install -g commitizen
- # Commitizen-friendly 方式
- commitizen init cz-conventional-changelog –save-dev –save-exact
- // package.json
- {
- &#34;scripts&#34;: {
- &#34;commit&#34;: &#34;cz&#34;
- },
- &#34;config&#34;: {
- &#34;commitizen&#34;: {
- &#34;path&#34;: &#34;cz-conventional-changelog&#34;
- }
- }
- }
复制代码 通过以上配置,可以用 git cz 或 npm run commit 替换 git commit 命令,实现规范提交的选择。
但是如果有人执意要使用 git commit 自定义提交信息也是没有办法,因此需要增加 commitlint 来检查提交信息是否符合规范。
安装使用过程
- npm i -D @commitlint/config-conventional @commitlint/cli
- # 因为上面已经安装了 husky,所以我们只要再新增一个 hook
- npx husky add .husky/commit-msg &#39;npx –no-install commitlint –edit &#34;$1&#34;&#39;
复制代码 这样就实现了提交信息的检查。另外可以通过 cz-customizable 设置成中文的提交提示。
根据版本生成 changelog
正是因为有了以上的“约定式提交”规范,我们还可以通过 conventional-changelog-cli 生成对应版本的 ChangeLog。
- {
- &#34;scripts&#34;: {
- &#34;log&#34;: &#34;conventional-changelog -p angular -i CHANGELOG.md -s&#34;
- }
- }
复制代码
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> 中,使用更方便。
- <script setup>
- import { ref } from &#39;vue&#39;
- const color = ref(&#39;red&#39;)
- </script>
- <template>
- <button @click=&#34;color = color === &#39;red&#39; ? &#39;green&#39; : &#39;red&#39;&#34;>
- Color is: {{ color }}
- </button>
- </template>
- <style scoped>
- button {
- color: v-bind(color);
- }
- </style>
复制代码
- 组件的 props 和 emit 写法发生变化以及在 TypeScript 中的类型定义。
- // js 写法
- <script setup>
- const props = defineProps({
- foo: String
- })
- const emit = defineEmits([&#39;change&#39;, &#39;delete&#39;])
- // setup code
- </script>
- // ts 写法
- <script lang=&#34;ts&#34; setup>
- const props = defineProps<{
- foo: string
- bar?: number
- }>()
- const emit = defineEmits<{
- (e: &#39;change&#39;, id: number): void
- (e: &#39;update&#39;, value: string): void
- }>()
- </script>
复制代码
Vue3 废弃 filter 及替代方案
从 Vue 3.0 开始,过滤器已移除,且不再支持。
- 在 2.x 中,可以使用过滤器来处理通用文本格式。
- <template>
- <p>今天是 {{ date | weeekFormat }}</p>
- </template>
- <script>
- export default {
- props: {
- date: {
- type: [Date | String | Number],
- required: true,
- },
- },
- filters: {
- /**
- * 返回 星期五
- * */
- weeekFormat(value) {
- return new Intl.DateTimeFormat(&#39;zh-CN&#39;, { weekday: &#39;long&#39; }).format(
- new Date(value)
- )
- },
- },
- }
- </script>
复制代码
- 在 3.x 中,需要用方法调用或计算属性来替换它们。
- <template>
- <p>今天是 {{ date | weeekFormat }}</p>
- </template>
- <script>
- export default {
- props: {
- date: {
- type: [Date | String | Number],
- required: true,
- },
- },
- computed: {
- /**
- * 返回 星期五
- * */
- weeekFormat() {
- return new Intl.DateTimeFormat(&#39;zh-CN&#39;, { weekday: &#39;long&#39; }).format(
- new Date(this.date)
- )
- },
- },
- }
- </script>
复制代码 另外全局过滤器可以使用 globalProperties 实现。较好的实践方式是实现自定义 plugin,在 plugin 中定义全局属性。
- // /plugin/filters.ts
- import type { APP } from &#39;vue&#39;
- import { timeFormat } from &#39;@/utils&#39;
- export default {
- install: (app: App) => {
- app.config.globalProperties.$filters = {
- format(value: string | Date | number) {
- return timeFormat(value)
- },
- }
- },
- }
- // main.ts
- import { createApp } from &#39;vue&#39;
- import filters from &#39;@/plugins/filters&#39;
- // 这里的类型定义是必须的,否则会在 build 时类型出错
- declare module &#39;@vue/runtime-core&#39; {
- interface ComponentCustomProperties {
- $filters: Record<string, any>
- }
- }
- createApp(App).use(filters).mount(&#39;#app&#39;)
复制代码 经过以上配置即可在支持全局使用,关于类型问题可以参考相关PR和源码。
- <el-table-column label=&#34;创建时间&#34;>
- <template #default=&#34;{ row }&#34;>
- {{ $filters.format(row.createTime) }}
- </template>
- </el-table-column>
复制代码
关于 global is not defined 问题
因为 Vite 是 ESM 机制,有些包内部使用了 node 的 global 对象,解决此问题可以通过自建 pollfill,然后在 main.ts 顶部引入,不是最优解,有想法的同学可以相互交流下:)。
- // polyfills
- if (typeof (window as any).global === &#39;undefined&#39;) {
- ;(window as any).global = window
- }
- // main.ts
- import &#39;./polyfills&#39;
- import { createApp } from &#39;vue&#39;
复制代码
总结
至此,使用 Vue3 + Vite 的工程化基础篇搭建已完成,后续会深入在业务上提高效率及规范,已达到工程化的目的。
原创文章,作者:starterknow,如若转载,请注明出处:https://www.starterknow.com/126955.html