vite的深入学习
记录我对vite原理的学习,这里是学习所参考的视频
构建工具的作用
- 模块化开发支持:支持直接从node_modules中引入代码以及多种模块化支持
- 比如babel语法降级、less、ts语法转换(不是构建工具做的,而是构建工具将这些语法对应的处理工具集成进来自动化处理)
- 提高项目性能 :压缩文件,代码分割
- 优化开发体验:如热更新、解决跨域问题等
vite脚手架
之前一直困惑我的pnpm create vite
,实际上它只是安装了create-vite
脚手架并内置了vite,就像使用vue-cli也会内置webpack
vite的预构建
Vite 为了加快开发服务器启动速度和提高依赖模块加载效率的一项优化机制。它的核心思想是利用 esbuild
在启动时预先对 第三方依赖 进行快速打包,生成一个更高效的依赖模块,从而加快项目开发中的依赖加载。
为什么要依赖预构建?
- 提高开发启动速度:现代 JavaScript 应用通常依赖多个第三方库,这些库通常包含许多模块、深层依赖和模块格式(如 CommonJS 和 ESModule)。直接加载这些依赖会使开发服务器变慢。预构建将这些复杂的依赖提前打包成单一模块,提升加载效率。
- 统一模块格式:一些第三方库仍然使用 CommonJS 格式,这与 ESModule 不兼容。Vite 在预构建阶段会将所有依赖统一转换为 ESModule 格式,以确保浏览器原生支持。
- 优化依赖解析:通过预构建,Vite 可以将大型依赖压缩为较小的文件,从而减少 HTTP 请求次数,提高依赖的解析速度。
vite使用pnpm run dev
启动开发服务器的原理
类似使用nodejs开启一个后端服务器,然后服务器会读取本地的文件并且呈现在浏览器页面。读取本地的vue文件时会进行一些处理转化为JS文件从而让浏览器可以识别并且加载
vite对CSS的处理
vite天生支持对CSS文件的处理。vite在读取到main.js中引用到了Index.css,会直接使用fs模块去读取index.css的文件内容,再直接创建一个style标签将index.css的内容复制到里面。再将style标签插入到index.html(入口文件)中的head中,将该CSS文件中的内容直接替换为JS脚本(方便热更新或者CSS模块化),同时设置Content-Type为js从而使浏览器以JS的方式解析后缀为CSS的文件。
关于CSS模块化(module.css):CSS 模块化的核心思想是将样式与组件或模块绑定,而不是在全局作用域中定义。这意味着每个组件都有自己的样式,避免了命名冲突和样式污染。CSS Modules 会将每个 CSS 类名转换为唯一的标识符,以确保样式的局部作用域。:
vite在服务端处理路径的时候用到path
在node端读取文件时遇到相对路径时会尝试去拼接成绝对路径,拼接规则为process.cwd() + 相对路径
,其中process.cwd()
为执行node命令时的当前目录。这时候需要引入path来处理路径的问题。
vite对静态资源的处理
在服务端,除了动态API,其他基本上都被视为静态资源。vite对静态资源基本上是开箱即用。
学到的其他知识
在js中导入图片时,import img from "./assets/images/img.png?raw"
后面的raw参数,?raw 参数通常用于指示构建工具(如 Vite 或 Webpack)以原始文本形式导入图片文件。这意味着你会获得该图片的 Base64 编码字符串,而不是图片文件的路径。这样raw参数在处理SVG的时候有作用。
buffer对象:Buffer 对象用于处理二进制数据。它是 Node.js 提供的一个全局对象,主要用于在网络通信、文件读写等场景中处理原始数据流。
打包后的文件(生产环境)
打包后的dist目录中的index.html引用其他静态文件是通过绝对路径,所以能否访问到文件是由根目录/
决定的,因此直接浏览器打开index.html或者在vue项目中打开live server无法正确访问。而直接打开dist目录或者部署到服务器上后是正常的。
为什么打包后的静态资源名称带有hash值?
浏览器有缓存机制,只要静态资源名字不改,就会直接使用缓存的。
vite插件
在vite运行的生命周期的不同阶段进行调用以达到不同目的。
插件学习从简到繁,视频中实现插件的方式可以多看几次。
vite和ts的结合
使用vite-plugin-checker插件,在vite.config.ts中配置。
在tsconfig.json文件下配置ts检查手段和规则:
1 | { |
在package.json文件中script选项的build选项中改为"build":"tsc __noEmit && vite build"
可以在打包前进行类型检查,未通过就不打包。
在vite-env.d.ts通过三斜线命令配置环境变量。
学到的其他知识
import.meta 是一个特定于模块的对象,提供有关当前模块的信息。它在 JavaScript 的 ES模块(ESM)中可用。
vite处理跨域
Vite 使用配置代理 (Proxy) 的方式来实现跨域请求。 提供了 server.proxy 配置选项,允许我们设置请求代理,将前端的请求转发到指定的后端服务器,以绕过浏览器的跨域限制。
1 | export default defineConfig({ |
vite性能的优化
性能优化包括以下几个方面:
开发时构建速度优化
页面性能指标:和我们怎么去写代码有关
- 首屏渲染时:
- 懒加载:需要用代码去实现
- http优化:协商缓存和强缓存
- 协商缓存:是否缓存需要和后端协商。
- 强缓存:服务端给响应头添加字段,客户端会记住字段,在失效时间之前无论如何刷新页面,浏览器都不会重新请求,而是从缓存里面取
- 页面中最大元素渲染时长
- ……
- 首屏渲染时:
js逻辑:
- 副作用的清除:组件是会频繁挂载和卸载。如设置了计时器,在卸载时不清除,下次开启计时器相当于开启了两个线程。
- 浏览器渲染的原理(这里需要后面再学习了)
- 实现如防抖节流或者使用原生js的API没有性能的优化(可以使用lodash.js库)
- 对作用域的控制:
如for(let i=0, len = arr.length; i < len;i++)
只会访问一次arr.length所在的window作用域,如果写成for(let i = 0; i < arr.length; i++)
就会访问很多次。
CSS:关注继承和嵌套,能继承的就不要重复写
构建(rollup)的优化:优化体积、压缩图片、CDN加载、分包等。
构建优化
分包策略
比如说引入一个依赖,代码被build后有很一部分的代码是属于依赖,就算修改一点代码再重新打包又会导致整个文件hash值改变,然而依赖部分打包后的代码时不变的,浏览器却又需要重新请求,造成资源浪费。
所以分包就是把一些不会常规更新的代码单独打包处理。
gzip压缩
可以通过插件来实现。服务端直接读取gzip文件(.gz后缀),需要设置响应头
动态导入
在 JavaScript 中,当使用 import 关键字导入一个模块时,导入的模块会立即执行其所有顶层代码。
动态导入(Dynamic Import)是一种在代码运行时按需加载模块的方式。动态导入可以通过 import() 函数实现,它返回一个 Promise,用于异步加载模块。这在需要按需加载、分离代码、优化加载性能的场景中非常有用,比如路由懒加载或按需加载组件。
1 | import('./module.js') |
CDN加速
vite的配置文件
配置文件的语法提示
对配置文件做一些特殊处理,如import { defineConfig } from 'vite'
或者使用注释+@
vite环境变量的配置
(这里需要多看几遍,也可以参考vite官方文档)
这里的环境变量指的不是配置下载的软件的环境变量,而是指的如:开发环境、生产环境、测试环境等。
vite配置文件
1 | export default defineConfig({ |
关于postcss
用于处理 CSS 的工具,它允许开发者通过 JavaScript 插件来转换 CSS。其核心理念是将 CSS 解析成抽象语法树(AST),然后通过各种插件对其进行操作和转换。postcss对于CSS如babel对于js。目前在vite中专门配置较少