最近在学习 Vue 3
的相关内容,而在 Vue 3
当中则采用了 Vite
来作为构建工具,所以在这里简单的梳理一下 Vite
的相关内容,也算是记录一下
什么是 Vite
Vite
是一款开发构建工具,在开发期,它是利用浏览的 Native ES Modules
特性来导入并且组织代码,生产环境中则是利用 Rollup
作为打包工具,它主要有以下几个特点
- 启动速度很快
- 热模块替换
- 按需编译
安装和使用的方式十分简单,并不需要过多的配置,安装流程如下
1 | npm install -g create-vite-app |
安装完成以后我们就可以使用它来初始化我们的 Vue 3
项目
1 | $ npm init vite-app <project-name> |
代码组织方式
这里我们来借助上面默认初始化完成以后的项目来进行简单的结构梳理,首先先从入口文件开始看起,也就是我们的 index.html
,它在代码当中的引入方式是下面这样的
1 | <script type="module" src="/src/main.js"></script> |
这里可以发现,我们引用了 /src/main.js
这个文件,但是是使用了 type="module"
的方式来进行引用,我们再来看看 main.js
1 | import { createApp } from 'vue' |
我们先来简单的梳理几个可能会有疑惑的地方,首先就是这里直接采用了裸模块的方式直接进行了引用,也就是 import { createApp } from 'vue'
这样的使用方式,我们在上面也提到过,Vite
是利用浏览的 Native ES Modules
特性来导入并且组织代码,但是浏览器是如何知道这个文件具体是在什么位置的呢?
另外对于 import App from './App.vue'
这样的相对路径的引入方式我们很熟悉,但是它又是如何解析 App.vue
这样的文件的呢?同理下面的 index.css
也是一样的道理,也许它是一个纯的 CSS
,但是如果是使用了预编译器的 Sass
,那又该如何对它进行处理呢?
关于上面的这些疑问,我们可以在项目启动以后,借助 Chrome
浏览的开发者工具当中的 Network
标签来进行分析,我们先来看它的加载过程,这里我们主要关注以下几个文件的加载解析过程,它们按序加载的顺序如下
localhost
main.js
vue.js
App.vue
index.css?import
HelloWorld.vue
App.vue?type=template
一开始首先加载 localhost
,也就是请求本地服务器上的 index.html
,它发现内部引用了 main.js
,所以又发送了另外一条请求去请求该文件,但是我们可以发现,现在返回的 main.js
有了一些变化,如下所示
1 | import { createApp } from '/@modules/vue.js' |
我们可以发现,返回的路径在 Vite
的处理下变成了 '/@modules/vue.js'
,所以此时浏览器便会再次发送请求,去请求一个相对路径下的 vue.js
,所以就会去 node_modules
下寻找 vue
文件夹,接下来就会去访问 vue
文件夹下的 package.json
,如下
1 | // node_modules/vue/package.json |
通过 package.json
我们可以知道,所谓的入口文件也就是 vue.runtime.esm-bundler.js
这个文件,从命名上我们也可以看出,就是一个运行时的使用 ES
模块来打包的 vue
版本
所以我们在调用了 import { createApp } from '/@modules/vue.js'
这行代码以后就相当于在我们的代码当中从 vue
当中导出了 createApp
这个方法,然后就可以在后续过程当中执行 createApp(App).mount('#app')
来进行程序的创建了
接下来我们再来看看 import App from '/src/App.vue'
这一行,因为是相对路径,所以会去请求当前目录下的 App.vue
,我们来对比看一下它的先后变化,首先是我们代码当中实现的 App.vue
,如下
1 | <template> |
而下面则是经过 Vite
处理后返回的 App.vue
1 | import HelloWorld from '/src/components/HelloWorld.vue' |
可以发现,我们之前所写的 <template></template>
,<script></script>
等相关代码全部被经过了一次编译,然后将它们组合,输出最终的代码,而返回的最终代码也就如上所示,简单梳理一下就是
- 首先将我们的路径替换成了相对路径
'/src/components/HelloWorld.vue'
- 之前
export default { }
的部分变成了一个名为__script
的组件配置对象 - 通过请求
App.vue
引入了render
函数,但是添加了type=template
的查询参数,这样Vite
就会对这个请求做特殊处理,也就是解析<template></template>
这个模板,将其变成渲染函数 - 得到渲染函数以后,再将其合并到我们之前的组件配置对象当中
- 以上就是任务的整个流程,剩余的一些都是一些标识文件,这里我们就不过多探讨了,然后最后将我们的组件配置对象导出
其实简单总结的话就是『解析当前组件,并且把我们的最终解析结果导出』,所以这样一来 App.vue
就变成了一个组件配置对象返回到了前台,所以我们在使用这个组件配置对象的时候就可以正常的去使用了
小结
最后我们来简单的总结一下上面梳理的内容,从开头部分开始说起,关键变化的是 index.html
中的入口文件的导入方式
1 | <script type="module" src="/src/main.js"></script> |
这样 main.js
中就可以使用 ES6 Module
的方式来组织代码
1 | import { createApp } from 'vue' |
浏览器会自动加载这些导入,Vite
会启动一个本地服务器处理这些不同的加载请求
- 对于相对地址的导入,要根据后缀名处理文件内容并返回
- 而对于裸模块导入要修改它的路径为相对地址并再次请求处理
1 | import { createApp } from '/@modules/vue.js' |
资源加载
下面我们再来看看 Vite
当中的资源加载,也就是图片,CSS
等一些静态资源是如何处理的,这也是工程化当中的一个十分重要的点
CSS 文件导入
Vite
中可以直接导入 CSS
文件,样式将影响导入的页面,还是以我们的 main.js
为例,它是以全局的方式进行引入的
1 | import { createApp } from 'vue' |
同 JavaScript
的处理方式类似,Vite
会对这个 CSS
进行开发阶段的预处理,将其转换成 JavaScript
代码,然后热更新到界面当中,但是需要注意的是,最后还是会被打包到 style.css
当中,下面是处理后的 main.js
文件当中的引入方式
1 | import '/src/index.css?import' |
可以发现,针对于 CSS
的处理,添加了 import
的标识,再来看看现在的 index.css
的样子,如下
1 | import { updateStyle } from "/vite/client" |
可以发现,其实最终的处理是以 JavaScript
的形式传递到前端,下面我们再来看看 CSS Module
的使用方式
CSS Module
我们除了在模版当中的 style
里直接定义对应 class
的样式以外,还可以使用 Module
的方式,我们通常的使用方式是下面这样的
1 | <template> |
但是我们可以将其调整为 CSS Module
的形式,这样一来我们的 CSS
在将来编译的时候会将我们的 style
变成计算属性,所以在模板当中使用的时候就不再是使用单纯的 class
了,而是使用 $style
来进行使用,也就是下面这样的使用方式
1 | <template> |
运行以后可以发现,结果是和上面是一致的,并且最终生成的样式结果当中会自动帮助我们加上 hash
,也就是下面这样的
1 | <img class="img_7ac74a55" alt="Vue logo" src="/src/assets/logo.png"> |
而这也是模块化带来的好处,因为有 hash
的存在,所以我们也不用担心它未来会重名,而这也是与使用 scoped
的区别
不过这里需要注意的一点就是,如果我们之前定义的 class
是使用 -
命名的话,则需要将其调整为驼峰命名,另外如果需要在 JavaScript
当中导入 CSS Module
,只需要将 CSS
文件命名为 *.module.css
,这样一来 Vite
也会自动对其进行模块化的处理
1 | import style from './HelloWorld.module.css' |
PostCSS
Vite
自动会对 *.vue
文件和导入的 .css
文件应用 PostCSS
配置,我们只需要安装必要的插件和添加 postcss.config.js
文件即可
1 | module.exports = { |
这里需要注意的一个地方就是,如果需要安装 autoprefixer
这个插件,最好保持和 PostCSS
相同的版本,否则会有不兼容的错误提示
资源 URL 处理
我们可以在 *.vue
文件的 <template>
,<style>
和纯的 .css
文件当中以相对和绝对路径方式引用静态资源,我们先来看看静态资源如何引用的
1 | <!-- 相对路径 --> |
另外一个就是 public
目录,public
目录下可以存放未在源码中引用的资源,它们会被留下且文件名不会有哈希处理,这些文件会原封不动的拷贝到发布目录的根目录下
1 | <img src="./logo.png"> |
但是需要注意的是,引用放置在 public
下的文件需要使用绝对路径,例如 public/icon.png
应该使用 /icon.png
进行引用
eslint
其实在 Vite
当中使用 eslint
并不会对我们进行约束,该怎么配置还是怎样配置,通常我们借助 eslint
规范项目代码,通过 prettier
做代码格式化,所以我们就需要进行两部分配置,而且我们希望两者是相匹配的,因为如果不匹配的话,在我们格式化以后是通过不了 eslint
的检查,这样就会引起很多的麻烦,下面我们来看看如何进行配置,首先在项目当中安装依赖,package.json
如下
1 | { |
接下来就是进行 lint
的规则配置,主要有两种方式,一种是采用 .eslintrc
的方式,也就是没有后缀的形式,但是这种方式需要写 JSON
,它的好处是代码提示很好,并且如果安装了 eslint
扩展可以很好的帮助我们来进行提示有哪些项是可以选择的,可以使用的
另外一种是采用 .eslintrc.js
的方式,也就是带后缀的方式,它的好处是可以在配置文件当中加上一些动态的配置,比如环境变量等,这里我们采用带有后缀的方式,如下
1 | module.exports = { |
另外如果有必要我们还可以配置 prettier.config.js
来修改 prettier
的默认格式化规则,因为开发工具的不一致可能导致格式化后的结果不一致,所以我们将其配置成一致的
1 | module.exports = { |
测试
当我们当项目规模变大,或者我们在写一些通用组件的时候,我们肯定需要实现一些测试,避免一些新的调整影响到了之前的代码,下面我们就来看看如何在 Vite
项目当中配置测试
这里我们采用的是 jest
测试框架和针对于组件测试的 @vue/test-utils
,我们先来看看需要安装哪些依赖,如下
1 | { |
依赖安装完成以后我们还需要配置 babel.config.js
1 | module.exports = { |
然后再来配置 jest.config.js
1 | module.exports = { |
启动脚本为
1 | // runInBand 代表按照序列的方式来执行 |
最后就是我们的测试代码,因为我们在上面配置了测试目录,所以我们直接将其放置在 tests/example.spec.js
目录下
1 | import HelloWorld from 'main/components/HelloWorld.vue' |
另外需要注意的就是我们还需要在 lint
配置当中添加 jest
环境
1 | module. exports = { |
完成以后就可以来执行我们的测试代码了
1 | npm run test |
但是这样我们每次都需要手动执行命令来进行测试,所以我们可以将 lint
,test
和 git
挂钩起来,让其每次在提交代码之前自动执行测试,首先我们需要来安装两个插件
1 | npm install lint-staged yorkie -D |
接着需要在 package.json
当中添加配置
1 | // yorkie 的配置项,监控我们的提交 |
这样一来,在我们提交代码之前就会自动的执行检查与测试,只有全部通过以后才会正常的提交
TypeScript 的整合
其实在 Vite
当中我们是可以直接导入 .ts
的文件来进行使用的,即直接在模版文件当中通过添加 <script lang="ts">
来进行使用,如下
1 | <template> |
另外在我们使用过程当中,最好指定一下 TypeScript
的版本号
1 | { |
然后在加上 TypeScript
的相关配置即可
1 | { |
项目配置
对于使用 Vite
构建的项目,我们只需要在项目的根目录下添加 vite.config.js
文件,即可针对项目进行深度配置,下面我们就来看看如何使用
定义别名
我们通常在导入组件的时候,可能会像下面这样使用
1 | import HelloWorld from './components/HelloWorld.vue' |
但在这样操作可能会出现大量相对路径,非常的不优雅和容易出错,所以我们可以通过导入别名的方式来进行简化,首先我们先来配置我们的 vite.config.js
,如下
1 | const path = require('path') |
比如上面的配置就表示我们给 src/components
进行别名定义,这样一来我们在导入组件的时候就可以直接使用别名当中定义的方式
1 | // 配置之前 |
代理配置
下面我们再来看看代理服务器的配置,这个也是使用较多的一个配置,配置如下
1 | proxy: { |
使用也很简单
1 | fetch('/api/users') |
数据配置
我们通常在开发过程当中会使用 mock
来进行数据模拟,下面我们来看看如何配置,首先安装依赖
1 | npm i mockjs -S |
依赖安装完成以后,我们就可以在我们的 vite.config.js
当中来进行插件配置
1 | const { createMockServer } = require('vite-plugin-mock') |
然后在 package.json
当中进行环境变量的配置,这个算是一个小坑,需要注意
1 | "scripts": { |
最后就可以来定义我们的 mock
数据了
1 | // mock/test.js |
然后重启我们的服务,这时就可以在控制台当中看到以下信息
1 | [vite: mock-server]: request invoke: /api/users |
说明此时已经检测到了我们的 mock
数据,这时我们就可以在浏览器当中来验证我们的数据了
模式和环境变量
我们在使用模式做多环境配置的时候,vite serve
的模式默认是 development
,而 vite build
的时候则是 production
,所以我们可以针对开发环境来创建对应的配置文件,我们可以在根目录当中创建一个名为 .emv.development
的文件,其中包括的内容如下
1 | // 需要注意的是,需要在前面添加 VITE_ 才可以在代码中去使用这个变量 |
这样在代码中我们就可以来使用它了,测试如下
1 | console.log(import.meta.env.VITE_TOKEN) |
类似这样的方式我们还可以去配置 .emv.production
文件,这时候两种不同的配置就可以分别在这两种环境下生效
打包和部署
打包的话很简单,直接执行如下命令即可
1 | npm run build |
这里我们主要来看看部署方面的内容,当然我们可以使用类似 FTP
,SSH
等工具直接连接到我们的服务器,然后将打包后的 dist
文件传上去即可,但是这里我们来看看如何使用自动化处理流程来避免前面的那些繁琐的操作,这里我们主要采用的是 GitHub
的 Actions
来实现 CI/CD
的过程
GitHub Actions
可以让我们在 GitHub
仓库中直接创建自定义的软件开发生命周期工作流程,如下图所示
待续