Vite优点
Vite快速的意思,基于esm打包,Vite并非和vue绑定的工具,只支持esmodule模块方式,不支持commonjs模块写法
打包工具
- Webpack
- rollup
- parcel
- gulp
存在的问题
- 缓慢的服务启动
- 缓慢的更新
传统的打包
ESM打包器
分布启动,避免一开始启动所有的文件,按需加载
esm加载对比
传统export模块都会启动相对应的方法,导致收起请求大量的js,vite通过自身封装了入口js,导致只调用首次请求的js文件
Vite vs create-react-app
Vite初始化速度快 打包时间快 包较小
- webpack缺陷 项目越大加载时间越长,需要HMR几秒钟才能生效,webpack多种模块的的特性,需要对所有模块代码统一模块,所以时间较长
- vite 按需加载,包大小不会影响加载时间
- yarn create vite执行过程
- 全局安装create-vite(vite脚手架)
- 直接运行create-vite bin目录下的执行配置
- yarn init -y 按照默认配置package
- vite打包搜索自身再往上寻找包地址,vite在生产上打包使用rollup
- vite针对非esmodule规范的代码通过esbuild(对js处理的一个库),转换为esmodule规范,然后放到当前目录下的node_modules/.vite/deps下
- 解决不同第三方包会有不同的导出格式
- 对路径处理上可以直接使用node_modules/.vite/deps,方便路径重写
- 解决网络多包的传输问题,其他包里可能导入了其他exports导出的包,使用预构建(这也是原生esmodule不敢支持node_module的原因之一),有了预构建,无论有多少其他的export和import ,vite会经可能进行一个集成一个或几个模块
- 如何获取代码提示
方法1:import { defineConfig } from 'vite'
方法2:/** @type {import('vite').UserConfig} */
- 切换环境
//vite build /vite serve
//引入 开发 生产 base三个配置文件
const envResolver={
"build":()=>{
//生产环境
return ({...viteBaseConfig,...viteProdConfig})
},
"serve":()=>{
//开发环境
return ({...viteBaseConfig,...viteDevConfig})
}
}
export default defineConfig(config:({command:"build"|"serve"})=>{
return envResolver[command]()
})
- 环境变量
使用dotenv,识别.env,对文件中的数据进行split切割成对象,并且注册到process对象下, 但是考虑和其他配置冲突问题,
root
envDir
不会直接注册到process下,补偿措施,在首次加载的时候执行方法
//直接执行可以注入配置
const env=loadEnv(mode,process.cwd(),prefix:'')
- loadEnv作用
- 1.直接找到.env文件,并解析其中的环境变量,并放到一个对象里
- 2.会将传进来的mode变量进行拼接,
.env.development,并根据我们提供的目录去取对应的配置文件并解析,并放进对象中,将合并的对象覆盖默认.env的对象 客户端vite将环境变量注入**import.meta.env** 客户端可以使用console.log,但是客户端是能识别以VITE_为前缀的变量,VITE_可以通过envPrefix配置修改
- vite为何能解析.vue文件
服务端解析:ctx.response.set('Content-Type',"text/javascript") 强制解析vue成js
- vite天生支持处理css
- vite直接main.js引用index.css
- 使用fs读取css中的内容
- 直接创建style标签,将index.css内容直接copy到style标签中
- 将style标签插入到index.html的head中
- 将该css文件内容直接替换为js脚本(方便热更新或者css模块化),同时设置content-type为js,让浏览器以js脚本的形式执行css
- CSS Modules
- 解决重名css覆盖问题
- 原理:modules.css结尾约定需要开启模块化,会将模块化中的类名以一定规则哈希替换(.footer->footer_122st_1),同时创建一个映射对象(.footer->footer_122st_1),然后替换head中的样式字符串并默认导出
- css.modules配置会给postcss.modules,
interface CSSModulesOptions {
scopeBehaviour?: 'global' | 'local' //模块化
globalModulePaths?: RegExp[] //不想参与class模块化的配置
generateScopedName?: //注入main中的样式类名字
| string
| ((name: string, filename: string, css: string) => string)
hashPrefix?: string //hash补充的前缀,会参与到最终的hash生成中,哈希只要字符串不一样就会不一样
/**
* 默认:null
*/
localsConvention?: //定义样式驼峰还是“-”
| 'camelCase'
| 'camelCaseOnly'
| 'dashes'
| 'dashesOnly'
| null
}
- 配置预处理器
preprocessorOptions:{
less:{
math:'always'; //在webpack中配置less-loader
globalVars:{ //定义less全局变量
mainColor:'red'
}
}
}
//lessc 对less文件进行编译
// lessc index.less
- css.devSourcemap
- true定位源码中出错的地方,false只会反应打包后报错的文件
- postcss确保css能兼容多版本、babel保证js能兼容多版本
postcss->对语法、变量、函数编译成原生css->对高级的css语法降级->浏览器前缀补全
babel->将最新ts语法进行转换为js->语法降级
- 使用postcss
1.安装postcss-cli postcss
2.书写postcss配置文件 postcss.config.js
postcss-low-level 语法降级
postcss-compiler 编译插件
postcss-preset-env 支持css变量和css未来语法,支持自动补全
3.使用 postcss index.css
//配置 vit.config.js中定义的postcss配置优先级高于postcss.config.js中的配置
css.postcss:{
plugins:[]
}
- 未来的、全局的一些语法不适用预处理器,使用css后缀进行定义
- 为什么用path.resolve
node指向过程中如果发现使用的路径是相对路径,会使用precess.cwd()去拼接
process.cwd() 获取当前node的执行目录
commonjs规范会注入几个规范 __dirname:当前执行路径
如果不适用path,需要处理不同系统路径书写的兼容性问题“/”,‘\’
- 静态资源加载
vite支持大部分静态文件
静态资源导入经量按需导入,这样方便tree shaking (import _ from 'lodash' ->import {deepClone} from 'lodash')
vite.config.js->resolve->alias
- resolve.alias原理
alias做字符串替换
- 打包后的文件为什么有哈希
浏览器有缓存机制,如果名字同一个会触发缓存
- vite-aliases
检测包括src下所有文件夹并生成别名
- nvm node版本管理工具
- 自定义插件,引入plugins中,返回的config会merge合并覆盖配置中的config
const mutateConfigPlugin = () =>
({
name: 'mutate-config',
config(config, { command })
{
resolve: { alias: { foo: 'bar' } }
}
})
- vite内部自动调用了许多插件 vite-plugin-html 自定义plugins,引入plugins中
//动态的控制整个html文件内容
module.exports=(options)=>{
return {
transformIndexHtml:{
enforce:"pre",
transform:(html,ext)=>{
return html.replace(/<%= title %>/g,options.inject.data.title)
}
}
}
}
- vite-plugin-mock mock数据
plugins执行引入
//原理
自定义plugins,引入plugins中
//获取mock文件夹中文件
const mockStat=fs.statSync('mock')
const isDirectery=mockStat.isDirectory() //判断是文件夹
if(isDirectery){
//process.cwd() 获取执行的根目录
const result=require(path.resolve(process.cwd(),"mock/index.js"))
......
}
//新建plugin文件,然后引入到plugins中
const myPlugin = () => ({
name: 'configure-server',
configureServer(server) {
server.middlewares.use((req, res, next) => {
// 自定义请求处理...
if(res.url==='/uesr/list'){
//匹配返回mock中的数据
}else{
next()
}
})
}
})
- 如何将ts报错到控制台
//插件
vite-plugin-checker
//ts三斜线指令
///<reference types="vite/client" />
相当于import
- vite性能优化
//分包策略 把不经常改的文件单独打包处理
build->rollupOptions->input:{
main:path.resolve(__dirname,"./index.html"),
product:path.resolve(__dirname,"./src/product.html")
}
build->rollupOptions->output->manualChunks:(id:string)=>{
if(id.includes('mode_modules')){
return "vender"
}
}
- gzip压缩
vite-plugin-compression
plugins->viteCompression()
- 动态导入
//import 返回promise,不会立即执行调用,异步加载会导致chunk分包
const module = await import(`./dir/${file}.js`)
- cdn加速
寻找最近的网络包,加载速度加快
//操作
import viteCDNPlugin frin 'vite-plugin-cdn-import'
plugins中引用
viteCDNPlugin({
modules: [
{
name: 'react',
var: 'React',
path: `umd/react.production.min.js`,
},
{
name: 'react-dom',
var: 'ReactDOM',
path: `umd/react-dom.production.min.js`,
},
]
})
- 跨域
跨域是服务器有响应,只是浏览器拦截了