构建工具
Vite
构建生产版本
自定义 chunk 及静态资源路径
使用 vite 默认选项打包出来的产物位于项目根目录下的 dist 文件夹中,里面包含了
assets 文件夹
index.html
原 public 文件夹中的内容
assets 文件夹中包含了所有的 js、css 以及图片文件。如果我们想要将它们分开,可以进行如下配置:
1 | import { defineConfig } from 'vite' |
assetFileNames
用于自定义构建结果中的静态资源名称(不包含 chunk),其有以下几个占位符:
[name]:静态资源的名称;
[hash]:基于静态资源内容的哈希值;
[extname]:包含
.
的文件后缀名;[ext]:不包含
.
的文件后缀名。
此外,可以通过 ”/“ 将其划分到子目录。
详细内容见 配置选项 | Rollup 中文文档。
chunkFileNames
用于对代码分割中产生的 chunk 自定义命名,用法同上。
详细内容见配置选项 | Rollup 中文文档。
基于以上配置打包后,assets 文件夹中包含了 js、css、png 等文件夹。
这时,你可能会发现一个问题:打包后有一个 js 文件会比其他文件大很多。其实这个文件包含了 node_modules 中的所有依赖。那怎么将 node_modules 中的依赖拆分开来呢?我们可以自定义 chunk,将 node_modules 中的每种依赖打包成一个 chunk。代码如下:
1 | export default defineConfig({ |
manualChunks
的参数 id 是文件的绝对路径,比如:
1 | E:/develop/projects/work/document-managment/node_modules/lodash-es/methodOf.js |
如果你想在打包后将 node_modules 中的依赖放入 node_modules 文件夹中,可以这样做:
1 | export default defineConfig({ |
Gzip 压缩
当用户访问我们的 web 站点时,前端资源通过 Gzip 压缩后传输给客户端,比纯文本体积减少大概 60% 左右,能够节约客户端网络资源,提升响应速度,解决网络导致的资源加载瓶颈,和减小服务器压力。
gzip 工作原理
- 浏览器请求 url,并在请求头中设置属性
accept-encoding:gzip
。表明浏览器支持 gzip,这个参数是浏览器自动会携带的请求头信息。 - 服务器收到浏览器发送的请求之后,服务器会返回压缩后的文件,并在响应头中包含
content-encoding: gzip
。如果没有压缩文件,服务器会返回未压缩的请求文件。 - 浏览器接收到服务器的响应,根据
content-encoding: gzip
响应头使用 gzip 策略去解压压缩后的资源,通过 content-type 内容类型决定怎么编码读取该文件内容。
gzip 压缩方案
nginx 端压缩
浏览器请求资源时,nginx 服务器对文件进行 gzip 压缩后返回给浏览器。前端不用做任何修改正常打包,但这样会消耗服务器性能。nginx 开启 gzip 压缩,代码如下:
1 | http { |
nginx 想要开启 gzip 压缩需要确保 nginx 安装了 ngx_http_gzip_module
模块,gzip_static on
配置项需用到 http_gzip_static_module
模块,可使用命令 nginx -V
查看。
此外,系统还必须安装 zlib 库,因为 nginx 使用 zlib 对 http 包的内容进行 gzip:
1 | sudo apt-get install zlib zlib-devel |
若报错:”E:无法定位软件包 zlib-devel“,是因为在 ubuntu 软件源里 zlib 和 zlib-devel 叫作 zlib1g 和 zlib1g.dev。
1 | sudo apt-get install zlib1g zlib1g.dev |
前端压缩
打包时通过 vite/webpack 配置生成的对应的 .gz 文件,浏览器请求文件后会自动解压为 js 或者 css 等资源文件。
前端打包借助 vite-plugin-compression 插件,插件地址 GitHub - vbenjs/vite-plugin-compression: Use gzip or brotli to compress resources。
安装
1 | npm install vite-plugin-compression |
使用
1 | import { defineConfig } from 'vite' |
打包后,压缩后的文件与原文件会同时存在,可以通过配置 deleteOriginFile: true
删除未打包资源,但我们推荐保留未打包资源。
此时,nginx 只需配置
1 | gzip_static on; |
即可。
注意: 如果 vue 项目的路由模式使用的是 history 且压缩后原文件被删除,那么当 nginx 配置了
1 | try_files $uri /$uri /index.html |
时,浏览器会报错:
1 | Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “text/html”. Strict MIME type checking is enforced for module scripts per HTML spec. |
这是因为浏览器请求 .js 或 .css 文件时,服务器只有 .js.gz 或 .css.gz 文件,找不到时就会返回 index.html 文件。
解决方法就保留源文件,浏览器获取的依然是打包后的资源文件。
PostCSS
什么是 postcss
postcss 是使用 js 插件转换样式的工具。这些插件可以校验 css,添加浏览器前缀,编译未来 css 语法,内联图片等等。
postcss 本身只是一个 API,自身并不会改变 CSS,作为一个 API 它可以创建任何需要的插件和工具来改变 css。
那它是如何处理 css 的呢?分为以下三步:
- parser:将 css 字符串解析成抽象语法树(AST);
- processor:我们应用 postcss 插件,或是自定义插件,都是在这个过程中。根据 postcss 提供的API,对 parser 生成的 AST 做相应调整;
- stringfier:从 root 开始,层序遍历 AST,根据节点类型,拼接节点的数据为 css 字符串。
css 预处理器(sass/less)可以和 postcss 一起使用,postcss 处理的是 sass/less 处理之后的 css。
postcss 插件
- autoprefixer:自动添加浏览器前缀;
- cssnext:将最新的 css 语法转换成大多数浏览器都能理解的语法;
- cssnano:压缩 css;
- stylelint:css 代码检查工具,支持新 css 语法校验;
- postcss-sprites:生成雪碧图;
- postcss-modules:避免命名冲突;
- …
更多插件见 https://www.postcss.parts/。
使用
在使用这些 postcss 插件前,要先安装:
1 | npm install autoprefixer, cssnext, cssnano |
然后配置 vite.config.ts:
1 | import { defineConfig } from 'vite' |
使用 autoprefixer 插件需要配置 browserslist
,可以在配置插件时配置(如上面代码),也可以在 package,json
中配置:
1 | "browserslist": [ |
配置规则见 https://browsersl.ist/。
Webpack
自定义 chunk 路径
webpack 自定义 chunk 路径要在 output 选项中:
1 | module.exports = { |
filename
:控制入口文件对应的出口文件名,比如入口文件名为 main.js,那么[name]
就是 main;chunkFilename
:控制按需加载(动态导入)的文件对应的出口文件名,默认[name]
和[id]
相同,是数字,如果想用文件名,可以在导入时使用魔法注释确定 chunk 名。
1 | const App = lazy(() => import(/* webpackChunkName: "app" */"../App")); |
自定义 css 文件路径
style-loader 会将 css 文件打包到 js 文件中,想要将 css 文件提取到单独的文件中可以用一下插件:
1 | npm install mini-css-extract-plugin -D |
配置 webpack:
1 | module.exports = { |
这里需要搭配插件的 css loader。
自定义图片路径
图片打包后的路径可以在 fil-loader 中配置:
```js
module: {
rules: [
{
test: /.(png|jpe?g|gif)$/i,
loader: “file-loader”,
options: {
name: “[name].[ext]”,
outputPath: “./img”
}
}
]
}