一、什么是构建工具
ts
:如果遇到ts文件,我们需要使用tsc把ts转换为jsreact/vue
: 安装react-compiler、vue-conplier 将我们写的jsx或者vue文件转换成render函数less/sass/postcss/somponent-style
:我们又需要less-loader、sass-loader等一系列编译工具语法降级
:babel—>将es的新语法转换成旧版浏览器可以接受的语法体积优化
:uglifyjs—>将代码进行压缩变成体积更小性能更高的文件- …
每次稍微改一点点东西,就会非常麻烦
我们需要把App.tsx通过tsc转换为App.jsx,再通过React-complier转换为js文件
有一个东西能够帮我们把tsc,react-complier,less,babel,uglifyjs全部集成到一起,我们只需要关心我们写的代码,如果代码一变化,就会全部把上述流程全部走一遍,这个东西就是构建工具
一个构建工具到底承担了哪些?
- 模块化开发支持:支持直接从node_modules中引入代码以及多种模块化支持
- 处理代码兼容性:比如babel语法降级、less、ts语法转换(不是构建工具做的,而是构建工具将这些语法对应的处理工具集成进来自动化处理)
- 提高项目性能 :压缩文件,代码分割
- 优化开发体验:
- 构建工具会自动监听文件的变化,当文件变化会自动调用对应的集成工具进行重新打包,然后在浏览器重新运行,整个过程叫做热更新
- 开发服务器:跨越问题,用react-cli,create-react-element、vue-cli 解决跨域问题
每次改一点,都需要走一遍这个构建的流程,顺序还不能错
构建工具就是为了让我们不用关心代码在浏览器如何运行,我们只需要首次给构建工具提供一个配置文件(这个配置文件也不是必须的,他会有默认的),有个这个集成的配置文件之后,我们就可以在下次需要更新的时候调用一次对应的命令就好了,如果我们再结合热更新,我们就不用关心生产的代码也不用关心代码如何在浏览器如何运行,只需要关心我们怎么写就行
市面上主流的构建工具
webpack
vite
parcel
esbuild
rollup
grunt
gulp
二、vite相较于webpack的优势
当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感
如果我们的项目越大,webpack所要处理的js代码就越多,跟webpack的构建过程有关,造成需要很长时间才能启动开发服务器
webpack支持多种模块化:工程不止跑在浏览器端
他一开始必须要统一模块化代码,所以意味着他需要将所有的依赖全部读一遍
vite是基于esmodule的,不能写commonjs代码,就不需要讲所有的依赖都读一遍,就以为不需要很长的时间启动开发服务器
根据下图:webpack先从入口开始把所有的模块构建在开启开发服务器,而vite是先开启服务器,从入口只加载当前路由的模块,按需加载,所以很快
三、vite启动项目初体验
yarn create vite
- 会帮我们全局安装一个
create-vite(vite脚手架)
- 会直接运行
create-vite
bin
目录下的一个执行配置
很多人会误区 认为官网中使用yarn create
构建项目的过程是vite
在做的事情,create-vite
只是一个脚手架
create-vite
和vite
的关系是什么?
create-vite
内置了vite
就像是使用vue-cli
也会内置webpack
预设
我们自己搭建一个项目,需要下载vite、vue、post-scc、less、babel
等,而create-vite/vue-cli
会帮我们集成好这些东西,这个就叫预设
四、vite依赖预构建
在默认情况下,esmodule导入资源的时候要么是绝对路径,要么是相对路径,既然我们现在的最佳实践是node_modules,为什么在es官方在我们导入非绝对路径和非相对路径资源的时候不帮我们搜寻node_modules呢?
原因就是太消耗性能了,在浏览器端,我们想要获取资源,都是通过http请求去加载资源,而一个资源往往也要引用好多个资源,这样一次就需要加载成千上万个资源,如果每个引入的资源都需要去加载,那么就会非常消耗性能
那么vite是怎么处理能够获取到这种非绝对路径或者相对路径的资源呢?
官网回答:https://cn.vitejs.dev/guide/features#npm-dependency-resolving-and-pre-bundling
vite在处理过程中如果遇到了非绝对路径或者相对路径的引用,则会尝试开启路径补全,比如我们引入import { cloneDeep } from "lodash";
,会修改成如下图
找寻依赖的过程中,是自当前目录依次向上查找的过程,直到搜寻到根目录或者搜寻到对应依赖为止
而为何会把路径补齐成上图这样?
这是因为我们在导入第三方的包的时候,并不确定第三方包是以何种形式导出的,有的是commonjs
(axios)规范导出,有的是esmodule
规范导出,所以vite
在导入这些第三方包的时候,就需要统一处理,vite
会找到对应的依赖,然后用esbuild(对js语法进行处理的一个库)
,将其他规范的代码转换成esmodule
规范的代码,然后放到当前目录下的node_modules/.vite/deps
文件夹中,同时对
依赖预构建解决了什么问题
-
不同的第三方包会有不同的导出格式(这个是vite没法约束的事情),导入时统一格式
-
对路径的处理上可以直接使用
.vite/deps
,方便路径重写 -
网络多包传输的性能问题(也是原生esmodule不敢解决node_modules的问题),有了依赖预构建以后,无论有多少的额外的export和import,vite都会尽可能的将他们进行继承,最后生成一个或者几个模块
假设loadsh又依赖了很多其他的模块,并且这些模块都是用export导出,比如lodash-es这个库,我们可以看到下图有很多导出
而在浏览器中则会看到vite已经把lodash-es做了一层转换,转换成一个一个的函数
export { default as add } from "./add.js" //相当于 import add from "./add.js" export const add = add
vite重写以后
function add(){}
如果我们不让lodash-es进行预构建,可以再vite.config.js中配置
我们可以看到改变,足足有649个请求,并且lodash-es
的代码也是没有打包过的
从上图不难看出,使用与构建之后只需要请求一,而不预构建则需求请求600多次,可想而知差距有多大,这也是为什么说解决了网络多包传输的性能问题
开发环境中,每次依赖预构建所重新构建的相对路径都是正确的
生产环境中,vite
会全权交给rollup
去完成生产环境的打包