Vite

345 阅读6分钟

Vite优点

Vite快速的意思,基于esm打包,Vite并非和vue绑定的工具,只支持esmodule模块方式,不支持commonjs模块写法

打包工具

  • Webpack
  • rollup
  • parcel
  • gulp

存在的问题

  • 缓慢的服务启动
  • 缓慢的更新

传统的打包

image.png

ESM打包器

image.png 分布启动,避免一开始启动所有的文件,按需加载

esm加载对比

传统export模块都会启动相对应的方法,导致收起请求大量的js,vite通过自身封装了入口js,导致只调用首次请求的js文件

Vite vs create-react-app

Vite初始化速度快 打包时间快 包较小

image.png

  • webpack缺陷 项目越大加载时间越长,需要HMR几秒钟才能生效,webpack多种模块的的特性,需要对所有模块代码统一模块,所以时间较长
  • vite 按需加载,包大小不会影响加载时间
  • yarn create vite执行过程
  1. 全局安装create-vite(vite脚手架)
  2. 直接运行create-vite bin目录下的执行配置
  • yarn init -y 按照默认配置package
  • vite打包搜索自身再往上寻找包地址,vite在生产上打包使用rollup
  1. vite针对非esmodule规范的代码通过esbuild(对js处理的一个库),转换为esmodule规范,然后放到当前目录下的node_modules/.vite/deps下
  • 解决不同第三方包会有不同的导出格式
  • 对路径处理上可以直接使用node_modules/.vite/deps,方便路径重写
  • 解决网络多包的传输问题,其他包里可能导入了其他exports导出的包,使用预构建(这也是原生esmodule不敢支持node_module的原因之一),有了预构建,无论有多少其他的export和import ,vite会经可能进行一个集成一个或几个模块
  1. 如何获取代码提示
方法1import { defineConfig } from 'vite' 
方法2/** @type {import('vite').UserConfig} */
  1. 切换环境
//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]()
  })
  1. 环境变量

使用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配置修改
  1. vite为何能解析.vue文件

服务端解析:ctx.response.set('Content-Type',"text/javascript") 强制解析vue成js

  1. 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
  1. CSS Modules
  • 解决重名css覆盖问题
  • 原理:modules.css结尾约定需要开启模块化,会将模块化中的类名以一定规则哈希替换(.footer->footer_122st_1),同时创建一个映射对象(.footer->footer_122st_1),然后替换head中的样式字符串并默认导出
  1. 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
}

  1. 配置预处理器
preprocessorOptions:{
   less:{
   math:'always';  //在webpack中配置less-loader
       globalVars:{ //定义less全局变量
          mainColor:'red'
       }
   }
}
//lessc 对less文件进行编译
// lessc index.less
  1. css.devSourcemap
  • true定位源码中出错的地方,false只会反应打包后报错的文件
  1. 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:[]
}
  1. 未来的、全局的一些语法不适用预处理器,使用css后缀进行定义
  2. 为什么用path.resolve
node指向过程中如果发现使用的路径是相对路径,会使用precess.cwd()去拼接
process.cwd() 获取当前node的执行目录
commonjs规范会注入几个规范 __dirname:当前执行路径
如果不适用path,需要处理不同系统路径书写的兼容性问题“/”,‘\’
  1. 静态资源加载
vite支持大部分静态文件
静态资源导入经量按需导入,这样方便tree shaking  (import _ from 'lodash' ->import {deepClone} from 'lodash')
vite.config.js->resolve->alias
  1. resolve.alias原理
alias做字符串替换
  1. 打包后的文件为什么有哈希
浏览器有缓存机制,如果名字同一个会触发缓存
  1. vite-aliases
检测包括src下所有文件夹并生成别名
  1. nvm node版本管理工具
  2. 自定义插件,引入plugins中,返回的config会merge合并覆盖配置中的config
const mutateConfigPlugin = () => 
({ 
name: 'mutate-config', 
config(config, { command }) 
{
   resolve: { alias: { foo: 'bar' } }
} 
})
  1. 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)
  }
  }
}
}

  1. 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()
      }
    })
  }
})

  1. 如何将ts报错到控制台
//插件
vite-plugin-checker
//ts三斜线指令
///<reference types="vite/client" />
相当于import
  1. 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"
   }
}
  1. gzip压缩
vite-plugin-compression
plugins->viteCompression()
  1. 动态导入
//import 返回promise,不会立即执行调用,异步加载会导致chunk分包
const module = await import(`./dir/${file}.js`)
  1. 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`,
                },
            ]
})
  1. 跨域
跨域是服务器有响应,只是浏览器拦截了