vite vue3 typescript,vite工程化实践
vite是个啥
vite
是一个开发构建工具,开发过程中它利用浏览器native ES Module
特性导入组织代码,生产中利用rollup
作为打包工具,它有如下特点:
- 光速启动
- 热模块替换
- 按需编译
本文目标
说白了vite
就是为开发者量身定做的一套先进的开发工具,按需编译、热模块替换等特性使我们开发时免除了重新打包的等待时间,开发体验丝滑,默认还整合了vue3
,是居家旅行、杀人灭口之必备良药。目前vite
已经是正式版,相关的生态正在迅速繁荣起来,我也第一时间在工程化方面做了一些探索,希望能够抛砖引玉,欢迎广大掘友拍砖。
安装vite
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
<span class="copy-code-btn">复制代码</span>
代码组织形式分析
关键变化是index.html
中的入口文件导入方式
这样main.js中就可以使用ES6 Module方式组织代码
浏览器会自动加载这些导入,vite会启动一个本地服务器处理不同这些加载请求,对于相对地址的导入,要根据后缀名处理文件内容并返回,对于裸模块导入要修改它的路径为相对地址并再次请求处理。
再根据模块package.json中的入口文件选项获取要加载的文件。
对于开发者而言,整体没有大的变化。
资源加载方式解析
直接导入CSS文件
vite中可以直接导入.css
文件,样式将影响导入的页面,最终会被打包到style.css
。
在我们程序中,除了全局样式大部分样式都是以形式存在于SFC中
Scoped CSS
<style scoped>
/**/
</style>
复制代码
CSS Module
SFC中使用CSS Module
<style module>
/**/
</style>
复制代码
范例:修改组件样式为CSS Module形式
JS中导入CSS Module:将css文件命名为*.module.css
即可
CSS预处理器
安装对应的预处理器就可以直接在vite项目中使用。
<style lang="scss">
/* use scss */
</style>
复制代码
或者在JS中导入
import './style.scss'
复制代码
PostCSS
vite自动对 *.vue
文件和导入的.css
文件应用PostCSS配置,我们只需要安装必要的插件和添加 postcss.config.js
文件即可。
module.exports = {
plugins: [
require('autoprefixer'),
]
}
<span class="copy-code-btn">复制代码</span>
npm i postcss autoprefixer@8.1.4
<span class="copy-code-btn">复制代码</span>
资源URL处理
引用静态资源
我们可以在*.vue
文件的template, style和纯.css
文件中以相对和绝对路径方式引用静态资源。
<!-- 相对路径 -->
<img src="./assets/logo.png">
<!-- 绝对路径 -->
<img src="/src/assets/logo.png">
复制代码
<style scoped>
#app {
background-image: url('./assets/logo.png');
}
</style>
复制代码
public
目录
public
目录下可以存放未在源码中引用的资源,它们会被留下且文件名不会有哈希处理。
这些文件会被原封不动拷贝到发布目录的根目录下。
<img src="/logo.png">
复制代码
注意引用放置在
public
下的文件需要使用绝对路径,例如public/icon.png
应该使用/icon.png
引用
代码规范:eslint
我们借助eslint
规范项目代码,通过prettier
做代码格式化。
首先在项目安装依赖,package.json
{
"scripts": {
"lint": "eslint \"src/**/*.{js,vue}\""
},
"devDependencies": {
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^7.0.0-0",
"prettier": "^1.19.1"
}
}
复制代码
然后配置lint
规则,.eslintrc.js
module.exports = {
root: true,
env: {
node: true,
},
extends: ["plugin:vue/vue3-essential", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint",
},
rules: {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"prettier/prettier": [
"warn",
{
// singleQuote: none,
// semi: false,
trailingComma: "es5",
},
],
},
};
复制代码
如果有必要还可以配置prettier.config.js
修改prettier
的默认格式化规则
module.exports = {
printWidth: 80, // 每行代码长度(默认80)
tabWidth: 2, // 每个tab相当于多少个空格(默认2)
useTabs: false, // 是否使用tab进行缩进(默认false)
singleQuote: false, // 使用单引号(默认false)
semi: true, // 声明结尾使用分号(默认true)
trailingComma: 'es5', // 多行使用拖尾逗号(默认none)
bracketSpacing: true, // 对象字面量的大括号间使用空格(默认true)
jsxBracketSameLine: false, // 多行JSX中的>放置在最后一行的结尾,而不是另起一行(默认false)
arrowParens: "avoid", // 只有一个参数的箭头函数的参数是否带圆括号(默认avoid)
};
复制代码
测试环境
利用jest
和@vue/test-utils
测试组件
安装依赖
"jest": "^24.0.0",
"vue-jest": "^5.0.0-alpha.3",
"babel-jest": "^26.1.0",
"@babel/preset-env": "^7.10.4",
"@vue/test-utils": "^2.0.0-beta.9"
复制代码
配置babel.config.js
module.exports = {
presets: [
[
"@babel/preset-env", {
targets: {
node: "current"
}
}
]
],
};
复制代码
配置jest.config.js
module.exports = {
testEnvironment: "jsdom",
transform: {
"^.+\\.vue$": "vue-jest",
"^.+\\js$": "babel-jest",
},
moduleFileExtensions: ["vue", "js", "json", "jsx", "ts", "tsx", "node"],
testMatch: ["**/tests/**/*.spec.js", "**/__tests__/**/*.spec.js"],
moduleNameMapper: {
"^main(.*)$": "<rootDir>/src$1",
},
};
复制代码
启动脚本
"test": "jest --runInBand"
复制代码
测试代码,tests/example.spec.js
import HelloWorld from "main/components/HelloWorld.vue";
import { shallowMount } from "@vue/test-utils";
describe("aaa", () => {
test("should ", () => {
const wrapper = shallowMount(HelloWorld, {
props: {
msg: "hello,vue3",
},
});
expect(wrapper.text()).toMatch("hello,vue3");
});
});
复制代码
lint
配置添加jest
环境,要不然会有错误提示:
module.exports = {
env: {
jest: true
},
}
复制代码
将lint
、test
和git
挂钩
npm i lint-staged yorkie -D
<span class="copy-code-btn">复制代码</span>
"gitHooks": {
"pre-commit": "lint-staged",
"pre-push": "npm run test"
},
"lint-staged": {
"*.{js,vue}": "eslint"
},
复制代码
正常情况下安装 yorkie 后会自动安装提交钩子 如果提交钩子未生效可以手动运行
node node_modules/yorkie/bin/install.js
来安装。 当然,你也可以运行node node_modules/yorkie/bin/uninstall.js
来卸载提交钩子。
typescript整合
vite
可直接导入.ts
文件,在SFC中通过<script lang="ts">
使用
范例:使用ts创建一个组件
<script lang="ts">
import { defineComponent } from 'vue'
interface Course {
id: number;
name: string;
}
export default defineComponent({
setup() {
const state = ref<Course[]>([]);
setTimeout(() => {
state.value.push({ id: 1, name: "全栈架构师" });
}, 1000);
},
});
</script>
复制代码
ts
版本指定,package.json
{
"devDependencies": {
"typescript": "^3.9.7"
}
}
复制代码
ts
参考配置,tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"isolatedModules": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"experimentalDecorators": true,
"lib": ["dom", "esnext"]
},
"exclude": ["node_modules", "dist"]
}
复制代码
项目配置
项目根目录创建vite.config.js
,可以对vite
项目进行深度配置。
定义别名
导入的别名,避免出现大量相对路径,优雅且不易出错
给src/components
定义别名,vite.config.js
const path = require("path");
module.exports = {
alias: {
// 路径映射必须以/开头和结尾
"/comps/": path.resolve(__dirname, "src/components"),
},
};
复制代码
使用
import CourseAdd from "/comps/CourseAdd.vue";
import Comp from "/comps/Comp.vue";
复制代码
代理
配置服务器代理,vite.config.js
export default {
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: path => path.replace(/^\/api/, '')
}
}
}
复制代码
使用
fetch("/api/users")
.then(response => response.json())
.then(json => console.log(json));
复制代码
数据mock
安装依赖
npm i mockjs -S
npm i vite-plugin-mock cross-env -D
<span class="copy-code-btn">复制代码</span>
引入插件,vite.config.js
plugins: [
createMockServer({
// close support .ts file
supportTs: false,
}),
],
复制代码
设置环境变量,package.json
"dev": "cross-env NODE_ENV=development vite"
<span class="copy-code-btn">复制代码</span>
创建mock
文件,mock/test.js
export default [
{
url: "/api/users",
method: "get",
response: req => {
return {
code: 0,
data: [
{
name: "tom",
},
{
name: "jerry",
},
],
};
},
},
{
url: "/api/post",
method: "post",
timeout: 2000,
response: {
code: 0,
data: {
name: "vben",
},
},
},
];
复制代码
模式和环境变量
使用模式做多环境配置,vite serve
时模式默认是development
,vite build
时是production
。
创建配置文件.env.development
VITE_TOKEN=this is token
<span class="copy-code-btn">复制代码</span>
代码中读取
import.meta.env.VITE_TOKEN
<span class="copy-code-btn">复制代码</span>
打包和部署
打包
使用npm run build
执行打包
部署
手动上传dist中的内容到服务器,再配置好nginx当然可以,但是这一过程最好自动化处理,避免前面这些繁琐的操作。我们这里利用github actions实现ci/cd过程。
Github Actions让我们可以在Github仓库中直接创建自定义的软件开发生命周期工作流程。
准备工作:
阿里云linux
服务器
第一步:配置workflow,下面的配置可以在我们push代码时自动打包我们应用并部署到阿里云服务器上,在项目根目录下创建.github/workflows/publish.yml
name: 打包应用并上传阿里云
on:
push:
branches:
- master
jobs:
build:
# runs-on 指定job任务运行所需要的虚拟机环境(必填字段)
runs-on: ubuntu-latest
steps:
# 获取源码
- name: 迁出代码
# 使用action库 actions/checkout获取源码
uses: actions/checkout@master
# 安装Node10
- name: 安装node.js
# 使用action库 actions/setup-node安装node
uses: actions/setup-node@v1
with:
node-version: 14.0.0
# 安装依赖
- name: 安装依赖
run: npm install
# 打包
- name: 打包
run: npm run build
# 上传阿里云
- name: 发布到阿里云
uses: easingthemes/ssh-deploy@v2.1.1
env:
# 私钥
SSH_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }}
# scp参数
ARGS: "-avzr --delete"
# 源目录
SOURCE: "dist"
# 服务器ip:换成你的服务器IP
REMOTE_HOST: "47.98.252.43"
# 用户
REMOTE_USER: "root"
# 目标地址
TARGET: "/root/vue-in-action"
复制代码
第二步:在github当前项目下设置私钥选项
复制本地私钥,~/.ssh/id_rsa
# ssh秘钥生成过程自行百度
cd .ssh/
cat id_rsa
复制代码
复制并填写到github-secretes
第三步:在阿里云服务器上配置nginx
登录服务器
ssh root@47.98.252.43 # ip换成你的
复制代码
配置nginx
cd /etc/nginx/sites-enabled/
vi vue-in-action
复制代码
添加如下配置
server {
listen 8080;
server_name 47.98.252.43;
location / {
root /root/vue-in-action/dist/;
index index.html index.htm;
}
}
复制代码
重启
nginx:nginx -s reload
第四步:push代码,触发workflow
大功告成,激动,赶紧验证一下上传结果
访问:47.98.252.43:8080 试试效果吧!!!
配套视频演示
我专门录了一套视频演示本文所做的所有操作,喜欢看视频学习的小伙伴移步:
制作不易,求一个3连关注
不过分吧!?
后续计划
后续我打算把一系列项目实践内容整合进来,包括不限于如下内容:
- 样式管理
- 页面布局
- 权限控制
- 图标管理
- 请求封装
- 数据可视化
关于工程化就说到这里,这篇内容折腾了很久,踩了不少坑,掉了几根头发,小伙伴们点个赞👍鼓励一下。