做电商网站都需要学什么,上海加盟网网站建设,网站搭建工作怎么样,宠物网站推广怎么做概述
在我们的项目代码中#xff0c;我们所说的模块代码其实分为两部分 一部分是源代码#xff0c;也就是业务代码另一部分是第三方依赖的代码#xff0c;即 node_modules 中的代码 Vite 是一个提倡 no-bundle 的构建工具#xff0c;相比于传统的 Webpack能做到开发时的模…概述
在我们的项目代码中我们所说的模块代码其实分为两部分 一部分是源代码也就是业务代码另一部分是第三方依赖的代码即 node_modules 中的代码 Vite 是一个提倡 no-bundle 的构建工具相比于传统的 Webpack能做到开发时的模块按需编译而不用先打包完再加载所谓的 no-bundle 只是对于源代码而言对于第三方依赖而言Vite 还是选择 bundle(打包)并且使用速度极快的打包器 Esbuild 来完成这一过程达到秒级的依赖编译速度
为何需要预构建 1 ) 第三方库
为什么在开发阶段我们要对第三方依赖进行预构建呢?首先 Vite 是基于浏览器原生 ES 模块规范实现的 Dev Server不论是应用代码还是第三方依赖的代码理应符合 ESM 规范才能够正常运行但是我们没有办法控制第三方的打包规范就目前来看有相当多的第三方库仍然没有 ES 版本的产物比如 react// react 入口文件
if (process.env.NODE_ENV production) {module.exports require(./cjs/react.production.min.js);
} else {module.exports require(./cjs/react.development.js);
}可见这是 CommonJS 规范这种 CommonJS 格式的代码在 Vite 当中无法直接运行我们需要将它转换成 ESM 格式的产物
2 函数的依赖
举个例子知名的 loadsh-es 库我们在调用其中的一个函数的时候这个函数可能需要用到其他的函数也就是在这个函数的内部会 import 其他的文件在其他文件中同样还会import更多这样就会造成一个问题它在加载时会发出特别多的请求导致页面加载的前几秒几都乎处于卡顿状态因此在这种 依赖层级深 、 涉及模块数量多的情况下会触发成百上千个网络请求巨大的请求量加上 Chrome 对同一个域名下只能同时支持 6 个 HTTP 并发请求的限制导致页面加载十分缓慢与 Vite 主导性能优势的初衷背道而驰不过在进行依赖的预构建之后 lodash-es 这个库的代码被打包成了一个文件这样请求的数量会骤然减少页面加载也快了许多
3 综上
依赖预构建主要做了两件事情 一是将其他格式(如 UMD 和 CommonJS)的产物转换为 ESM 格式使其在浏览器通过 script typemodulescript 的方式正常加载二是打包第三方库的代码将各个第三方库分散的文件合并到一起减少 HTTP 请求数量避免页面加载性能劣化 这两件事情全部由性能优异的 Esbuild (基于 Golang 开发)完成而不是传统的Webpack/Rollup所以也不会有明显的打包性能问题反而是 Vite 项目启动飞快(秒级启动)的一个核心原因注意 Vite 1.x 使用了 Rollup 来进行依赖预构建在 2.x 版本将 Rollup 换成了Esbuild编译速度提升了近 100 倍
开启预构建
在 Vite 中有两种开启预构建的方式分别是 自动开启 和 手动开启
1 ) 自动开启
首先是 自动开启, 当我们在第一次启动项目的时候 $ pnpm run dev, 可以在命令行窗口看见如下的信息Pre-bundling dependencies:reactreact-domreact/jsx-dev-runtime
(this will be run only when your dependencies or config have changed)同时在项目启动成功后, 你可以在根目录下的 node_modules 中发现 .vite 目录这就是预构建产物文件存放的目录内容如下 在浏览器访问页面后打开 Dev Tools 中的网络调试面板你可以发现第三方包的引入路径已经被重写 并且对于依赖的请求结果Vite 的 Dev Server 会设置强缓存 缓存过期时间被设置为一年表示缓存过期前浏览器对 react 预构建产物的请求不会再经 过 Vite Dev Server直接用缓存结果当然除了 HTTP 缓存Vite 还设置了本地文件系统的缓存所有的预构建产物默认缓 存在 node_modules/.vite 目录中如果以下 3 个地方都没有改动Vite 将一直使用缓存文件: package.json 的 dependencies 字段各种包管理器的 lock 文件optimizeDeps 配置内容
2 手动开启
上面提到了预构建中本地文件系统的产物缓存机制而少数场景下不希望用本地的缓存文件比如需要调试某个包的预构建结果我推荐使用下面任意一种方法清除缓存还有手动开启预构建: 删除 node_modules/.vite 目录。在 Vite 配置文件中将 server.force 设为 true命令行执行 npx vite --force 或者 npx vite optimize Vite 项目的启动可以分为两步 第一步是依赖预构建第二步才是 Dev Server 的启动 $ npx vite optimize 相比于其它的方案仅仅完成第一步的功能
预构建的自定义配置
通过 Vite 提供的配置项来定制预构建的过程Vite 将预构建相关的配置项都集中在 optimizeDeps 属性上我们来一一拆解这些子配置项背后的含义和应用场景
1 入口文件——entries
第一个是参数是 optimizeDeps.entries通过这个参数你可以自定义预构建的入口文 件实际上在项目第一次启动时Vite 会默认抓取项目中所有的 HTML 文件如当前脚手 架项目中的 index.html 将 HTML 文件作为应用入口然后根据入口文件扫描出项目中用到的第三方依赖, 最后对这些依赖逐个进行编译那么当默认扫描 HTML 文件的行为无法满足需求的时候比如项目入口为 vue 格式文件时你可以通过 entries 参数来配置:// vite.config.ts
{optimizeDeps: {// 为一个字符串数组entries: [./src/main.vue];}
}entries 配置也支持 glob 语法非常灵活如:// 将所有的 .vue 文件作为扫描入口
entries: [**/*.vue];不光是 .vue 文件Vite 同时还支持各种格式的入口包括: html 、 svelte 、 astro 、js 、 jsx 、 ts 和 tsx可以看到只要可能存在 import 语句的地方Vite 都可以解析并通过内置的扫描机制搜集到项目中用到的依赖通用性很强
2 添加一些依赖——include
除了 entries include 也是一个很常用的配置它决定了可以强制预构建的依赖项使用方式很简单:// vite.config.ts
optimizeDeps: {// 配置为一个字符串数组将 lodash-es 和 vue两个包强制进行预构建include: [lodash-es, vue];
}它在使用上并不难真正难的地方在于如何找到合适它的使用场景前文中我们提到Vite 会根据应用入口( entries )自动搜集依赖然后进行预构建这是不是说明 Vite 可以百分百准确地搜集到所有的依赖呢事实上并不是某些情况下 Vite 默认的扫描行为并不完全可靠这就需要联合配置 include 来达到完美的预构建效果了接下来我们好好梳理一下到底有哪些需要配置 include 的场景
2.1 场景一: 动态 import
在某些动态 import 的场景下由于 Vite 天然按需加载的特性经常会导致某些依赖只能在运行时被识别出来// src/locales/zh_CN.js
import objectAssign from object-assign;
console.log(objectAssign);
// main.tsx
const importModule (m) import(./locales/${m}.ts);
importModule(zh_CN);在这个例子中动态 import 的路径只有运行时才能确定无法在预构建阶段被扫描出来因此我们在访问项目时控制台会出现下面的日志信息[vite] new dependencies found: object-assign, updating...
[vite] ✨ dependencies updated, reloading page...这段 log 的意思是: Vite 运行时发现了新的依赖随之重新进行依赖预构建并刷新页 面这个过程也叫二次预构建。在一些比较复杂的项目中这个过程会执行很多次如下面的日志信息所示:[vite] new dependencies found: material-ui/icons/Dehaze, material-ui/core/Box, material-ui
[vite] ✨ dependencies updated, reloading page...
[vite] new dependencies found: material-ui/core/Dialog, material-ui/core/DialogActions, upd
[vite] ✨ dependencies updated, reloading page...
[vite] new dependencies found: material-ui/core/Accordion, material-ui/core/AccordionSummar
[vite] ✨ dependencies updated, reloading page...然而二次预构建的成本也比较大不仅需要把预构建的流程重新运行一遍还得重 新刷新页面并且需要重新请求所有的模块尤其是在大型项目中这个过程会严重拖慢应用的加载速度因此我们要尽力避免运行时的二次预构建具体怎么做呢你可以通过 include 参数提前声明需要按需加载的依赖:// vite.config.ts
{optimizeDeps: {include: [// 按需加载的依赖都可以声明到这个数组里object-assign,];}
}2.2 场景二: 某些包被手动 exclude
exclude 是 optimizeDeps 中的另一个配置项与 include 相对用于将某些依赖从预构 建的过程中排除不过这个配置并不常用也不推荐使用如果真遇到了要在预构建中排除某个包的情况需要注意它所依赖的包是否具有 ESM 格式如下面这个例子// vite.config.ts
{optimizeDeps: {exclude: [loadable/component];}
}这时候可以看到浏览器控制台会出现报错因为我们刚刚手动 exclude 的包 loadable/component 本身具有 ESM 格式的产物但它的某个依赖 hoist-non-react-statics 的产物并没有提供 ESM 格式导致运行时加载失败这个时候 include 配置就派上用场了我们可以强制对 hoist-non-react-statics 这个间接依赖进行预构建:// vite.config.ts
{optimizeDeps: {include: [// 间接依赖的声明语法通过分开, 如a b表示 a 中依赖的 bloadable/component hoist-non-react-statics,];}
}在 include 参数中我们将所有不具备 ESM 格式产物包都声明一遍这样再次启动项目 就没有问题了
2.3 场景三: 自定义 Esbuild 行为
Vite 提供了 esbuildOptions 参数来让我们自定义 Esbuild 本身的配置常用的场景是加入一些 Esbuild 插件:// vite.config.ts
{optimizeDeps: {esbuildOptions: {plugins: [// 加入 Esbuild 插件];}}
}这个配置主要是处理一些特殊情况
2.4 场景四第三方包出现问题
由于我们无法保证第三方包的代码质量在某些情况下我们会遇到莫名的第三方库报错我举一个常见的案例—— react-virtualized 库这个库被许多组件库用到但它的ESM 格式产物有明显的问题在 Vite 进行预构建的时候会直接抛出错误原因是这个库的 ES 产物莫名其妙多出了一行无用的代码其实我们并不需要这行代码但它却导致 Esbuild 预构建的时候直接报错退出了那这一类的问题如何解决呢
A 改第三方库代码
直接修改第三方库的代码不过这会带来团队协作的问题你的改动需要同步到团队所有成员比较麻烦好在我们可以使用 patch-package 这个库来解决这类问题一方面它能记录第三方库代码的改动另一方面也能将改动同步到团队每个成员patch-package 官方只支持 npm 和 yarn而不支持 pnpm不过社区中已经提供了支持 pnpm 的版本这里我们来安装一下相应的包: $ pnpm i milahu/patch-package -D注意: 要改动的包在 package.json 中必须声明确定的版本不能有 ~ 或者 ^ 的前缀 接着我们进入第三方库的代码中进行修改先删掉无用的 import 语句再在命令行输 入: $ npx patch-package react-virtualized现在根目录会多出 patches 目录记录第三方包内容的更改随后我们在 package.json 的 scripts 中增加如下内容{scripts: {// 省略其它 scriptpostinstall: patch-package}
}这样一来每次安装依赖的时候都会通过 postinstall 脚本自动应用 patches 的修改 解决了团队协作的问题
B 加入 Esbuild 插件
第二种方式是通过 Esbuild 插件修改指定模块的内容这里展示一下新增的配置内容:// vite.config.ts
const esbuildPatchPlugin {name: react-virtualized-patch,setup(build) {build.onLoad({filter: /react-virtualized\/dist\/es\/WindowScroller\/utils\/onScroll.js$/,},async (args) {const text await fs.promises.readFile(args.path, utf8);return {contents: text.replace(import { bpfrpt_proptype_WindowScroller } from ../WindowScroller.js;,),};});},
};// 插件加入 Vite 预构建配置
{optimizeDeps: {esbuildOptions: {plugins: [esbuildPatchPlugin];}}
}总结
预构建的相关配置—— entries 、 include 、 exclude 和 esbuldOptions 还有第三方包出现了问题的两个解决思路: 通过 patch-package 修改库代码编写 Esbuild 插件 修改模块加载的内容