vite是个啥
vite是一个开发构建工具,开发过程中它利用浏览器native ES Module特性导入组织代码,生产中利用rollup作为打包工具,它有如下特点:
- 光速启动
- 热模块替换
- 按需编译
本文目标
说白了vite就是为开发者量身定做的一套先进的开发工具,按需编译、热模块替换等特性使我们开发时免除了重新打包的等待时间,开发体验丝滑,默认还整合了vue3,是居家旅行、杀人灭口之必备良药。目前vite已经是正式版,相关的生态正在迅速繁荣起来,我也第一时间在工程化方面做了一些探索,希望能够抛砖引玉,欢迎广大掘友拍砖。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5COmNzN2EmNzkjM4ATZjljM4ETMwkDZ5gDZ5cjZjRGZ48CX2IzLcJTMwIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL0M3Lc9CX6MHc0RHaiojIsJye.png)
安装vite
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev复制代码
代码组织形式分析
关键变化是index.html中的入口文件导入方式
这样main.js中就可以使用ES6 Module方式组织代码
浏览器会自动加载这些导入,vite会启动一个本地服务器处理不同这些加载请求,对于相对地址的导入,要根据后缀名处理文件内容并返回,对于裸模块导入要修改它的路径为相对地址并再次请求处理。
再根据模块package.json中的入口文件选项获取要加载的文件。
对于开发者而言,整体没有大的变化。
资源加载方式解析
CSS文件导入
vite中可以直接导入.css文件,样式将影响导入的页面,最终会被打包到style.css。
在我们程序中,除了全局样式大部分样式都是以形式存在于SFC中
CSS Module
SFC中使用CSS Module
<style module>复制代码
范例:修改组件样式为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'),
]
}复制代码
npm i postcss [email protected]复制代码
资源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复制代码
"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整合
ite可直接导入.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复制代码
引入插件,vite.config.js
plugins: [
createMockServer({// close support .ts filesupportTs: false,
}),
],复制代码
设置环境变量,package.json
"dev": "cross-env NODE_ENV=development vite"复制代码
创建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复制代码
代码中读取
import.meta.env.VITE_TOKEN复制代码
打包和部署
打包
使用npm run build执行打包
部署
手动上传dist中的内容到服务器,再配置好nginx当然可以,但是这一过程最好自动化处理,避免前面这些繁琐的操作。我们这里利用github actions实现ci/cd过程。
Github Actions让我们可以在Github仓库中直接创建自定义的软件开发生命周期工作流程。
准备工作:
阿里云linux服务器
linux操作
阿里云相关操作
第一步:配置workflow,下面的配置可以在我们push代码时自动打包我们应用并部署到阿里云服务器上,在项目根目录下创建.github/workflows/publish.yml
name: 打包应用并上传阿里云on:
push:branches: - masterjobs:
build:# runs-on 指定job任务运行所需要的虚拟机环境(必填字段)runs-on: ubuntu-lateststeps: # 获取源码 - name: 迁出代码# 使用action库 actions/checkout获取源码uses: actions/checkout@master # 安装Node10
- name: 安装node.js# 使用action库 actions/setup-node安装nodeuses: actions/setup-node@v1with: node-version: 14.0.0 # 安装依赖 - name: 安装依赖run: npm install # 打包 - name: 打包run: npm run build # 上传阿里云 - name: 发布到阿里云uses: easingthemes/[email protected]: # 私钥 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 [email protected] # 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 试试效果吧!!!![]()
备战2021:vite工程化实践,建议收藏
配套视频演示
我专门录了一套视频演示本文所做的所有操作,喜欢看视频学习的小伙伴移步:
「村长」vite工程化
制作不易,求一个3连关注不过分吧!?
后续计划
- 样式管理
- 页面布局
- 权限控制
- 图标管理
- 请求封装
- 数据可视化