前言
看了很多webpack相关文章,一直读不进去(或者记不住),无非将webpack官文档的配置说明,重新梳理下顺序或者翻译不好部分的再重新处理一下。很少以项目角度去讨论一个webpack工程项目如何从0到1搭建,搭建完成之后随着项目的复杂度提高又是如何一步步优化,达到最佳实践过程。
本系列是基于以下webpack版本来展开的讨论的
"webpack": "^4.46.0",
"webpack-dev-server": "^3.11.2"
复制代码
【一】 项目初始化
新建一个文件夹webpack-demo
,执行下面命令。
npm init -y
npm install webpack webpack-cli -D
复制代码
【二】 配置打包的输入(entry)与输出(output)
在src/index.js
路径下写点代码,配置webpack.config.js
module.exports = {
entry:'./src/index.js', // 输入输出
output: {
path: path.resolve(__dirname, "./dist"), // 打包后的目录
filename:"bundle.js"
}
}
复制代码
【三】 配置构建命令
1、 在 package.json scripts 添加构建打包命令
scripts:{
"build": "webpack --config webpack.config.js"
}
复制代码
2、验证:
终端执行打包命令npm run build
将会在dist目录下生成一个bundle.js文件。
【四】自动引入打包生成的文件bundle.js
- 安装依赖:
html-webpack-plugin
和clean-webpack-plugin
npm i -D html-webpack-plugin clean-webpack-plugin
复制代码
- 配置 webpack.config.js,以插件形式引入
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
// ... 省略其它配置项目
plugins:[
new CleanWebpackPlugin({ // 每次打包前清空打包输出目录
// cleanOnceBeforeBuildPatterns:['**/*', '!dll', '!dll/**'] // 不删除dll目录下的文件
}), // 清空打包输出目录
new HtmlWebpackPlugin({
template: './index.html' // 源模板文件
// filename: './index.html' // 指定输出文件
})
]
}
复制代码
验证:
终端执行命令npm run build
将打包dist生成文件除了bundle.js,还有index.html 。这样打包之后bundle.js自动注入到index.html
【五】搭建本地服务器
- 安装依赖
webpack-dev-server
npm i -D webpack-dev-server
复制代码
2、配合服务启动目录、端口
module.exports = {
// ... 省略其它配置项目
devServer: {
port: 3001,
hot: true,
contentBase: "./dist",
}
}
复制代码
- 在 package.json scripts 配置本地服务启动命令
scripts:{
"serve": "webpack-dev-server --config webpack.config.js --open"
}
复制代码
4、验证:
在终端执行npm run serve
,会自动开口浏览器启动本地服务
【六】 配置解析和转换文件的策略 module
常见loader
loder | 功能 |
---|---|
babel-loader | 解析 .js 和 .jsx 文件 |
tsx-loader | 处理 ts 文件 |
less-loader | 处理 less 文件,并将其编译为 css |
sass-loader | 处理 sass、scss 文件,并将其编译为 css |
css-loader | 处理 css 文件 |
style-loader | 将 css 注入到 DOM |
file-loader | 将文件上的import / require 解析为 url,并将该文件输出到输出目录中 |
url-loader | 用于将文件转换成 base64 uri 的 webpack 加载程序 |
html-loader | 将 HTML 导出为字符串, 当编译器要求时,将 HTML 最小化 |
6.1 引入babel-loader处理js
由于不同浏览器对高级语法的支持性并不是非常好,为了向下兼容考虑将他们转换为ES5标准
- 安装依赖
babel-loader
()、@babel/core
和@babel/preset-env
npm install -D babel-loader @babel/core @babel/preset-env
复制代码
- 配置
webpack.config.js
module.exports={
module:{
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/ //
}
}
}
复制代码
- 根目录新建.babelrc
{
"presets": [
"@babel/preset-env" // 官方推荐使用,包含了所有现代js(es2015 es2016等)的所有新特性
]
}
复制代码
- 如果你希望 加入懒加载 TODO
npm i -D @babel/plugin-syntax-dynamic-import
复制代码
同时更新 .babelrc 文件,加入plugins配置
{
"presets": [
"@babel/preset-env"
],
"plugins": [
"syntax-dynamic-import"
]
}
复制代码
验证:
在index.js 加入箭头函数,执行构建命令查看bundle.js
是否将ES6箭头函数转换为普通函数
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);
// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
return n + 1;
});
复制代码
6.2 引入css(scss less),这里以scss举例
- 为支持css import,安装依赖
style-loader css-loader
npm i -D style-loader css-loader
复制代码
- 如果我们使用sass来构建样式,则需要多安装两个node-sass sass-loader
npm i -D node-sass sass-loader
复制代码
3、为css增加浏览器前缀
npm i -D postcss-loader autoprefixer
复制代码
3、配置 webpack.config.js,通过loader形式让webpack 知道以何种loader加载对应后缀名(.css
和scss
)的文件,从而打包进bundle.js
module.exports = {
// ... 省略其它配置项目
module:{
rules:[
{
test:/\.css$/,
use:['style-loader','css-loader'] // 从右向左解析原则
},
{
test:/\.scss$/,
use:['style-loader','css-loader','postcss-loader','sass-loader'] // 从右向左解析原则
}
]
}
}
复制代码
4、验证:
新建一个.sccs文件,写一些sass 语法样式,以及css3语法,查看实际输出结果
// 变量定义
$color: blue;
body {
/*嵌套*/
.test {
height: 100px;
display: flex;
align-items: center;
justify-content: center;
background-color: $color;
color: #fff;
}
}
复制代码
6.3 图片/字体文件处理
- 为支持import 字体、图片等其它多媒体格式文件,安装依赖
url-loader file-loader
npm i -D url-loader file-loader
复制代码
- 配置 webpack.config.js,通过loader形式让webpack知道以何种loader加载这些多媒体格式文件的
注意:url-loader
一般与file-loader
搭配使用,功能与file-loader
类似,如果文件小于限制的大小。则会返回base64
编码,否则使用 file-loader
将文件移动到输出的目录中
module.exports = {
// ... 省略其它配置
module:{
rules:{
{
test: /\.(jpe?g|png|gif)$/i, //图片文件
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'media/[name].[hash:8].[ext]'
}
}
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体
use: [
{
loader: 'url-loader',
options: {
limit: 10240,
fallback: {
loader: 'file-loader',
options: {
name: 'fonts/[name].[hash:8].[ext]'
}
}
}
}
]
},
]
}
}
}
复制代码
【七】】其它
7.1 mode
mode 配置选项告用于告诉webpack相应地使用其内置的优化。可选配置有 development
、production
或 none
,具体可查看官方文档
7.2 devtool
在了解Source Map用处之后,你就会容易理解通过devtool的配置 webpack 如何生成 Source Map,用来增强调试过程。不同的值会明显影响到构建(build)和重新构建(rebuild)的速度。
生产环境(mode=production)
:默认为 null ,一般不设置( none )或 nosources-source-map开发环境(mode=development)
:默认为 eval ,一般设置为 eval、cheap-eval-source-map 、cheap-module-eval-source-map
sourcemap 生成几个策略:
值 | 原理 | 优缺点 |
---|---|---|
eval | 通过eval包裹每一个module模块,编译后不会生成sourcemap 文件,仅仅是在每一个模块后,增加sourceURL来关联模块处理前后对应的关系 |
优点是:打包速度非常快,因为不需要生成sourcemap文件。缺点是:由于会映射到转换后的代码,而不是映射到原始代码,所以不能正确的显示行数。 |
source-map | 为每一个打包后的模块生成独立的sourcemap文件,打包后的代码最后面一句代码是 // #sourceMappingURL=bundle.js.map ,同时在dist目录下会针对每一个模块生成响应的 .map文件 |
拥有完整的源代码信息,便于调试,但影响持续构建 |
inline | 不会生成独立的 .map文件,而是将.map 文件以dataURL的形式插入 |
缺点:打包输出文件变大 |
cheap | 打包后同样会为每一个文件模块生成 .map 文件,但是与source-map 的区别在于cheap-source-map 生成的 map文件会忽略原始代码中的列信息 |
可以大幅提高souremap 生成的效率,但没有列信息(会映射到转换后的代码,而不是映射到原始代码),通常我们调试并不关心列信息 |
module | 同样生成一个没有列的信息的sourcemap文件,同时loader的sourcemap也被简化成为只包含对应行 | 支持babel 这种预编译工具,找到最初源码信息 |
如何根据不同环境选择相应sourceMap 生成策略?
-
- 源代码中的列信息是没有任何作用,因此我们打包后的文件不希望包含列相关信息,只有行信息能建立打包前后的依赖关系。因此不管是开发环境或生产环境,我们都希望添加
cheap
的基本类型来忽略打包前后的列信息。
- 源代码中的列信息是没有任何作用,因此我们打包后的文件不希望包含列相关信息,只有行信息能建立打包前后的依赖关系。因此不管是开发环境或生产环境,我们都希望添加
-
- 不管是开发环境还是正式环境,我们都希望能定位到bug的源代码具体的位置,比如说某个vue文件报错了,我们希望能定位到具体的vue文件,因此我们也需要module配置。
-
- 我们需要生成map文件的形式,因此我们需要增加
source-map
属性。
- 我们需要生成map文件的形式,因此我们需要增加
-
- 我们介绍了eval打包代码的时候,知道eval打包后的速度非常快,因为它不生成map文件,但是可以对eval组合使用
eval-source-map
使用会将map文件以DataURL的形式存在打包后的js文件中
- 我们介绍了eval打包代码的时候,知道eval打包后的速度非常快,因为它不生成map文件,但是可以对eval组合使用
因此建议:
// 在开发环境中我们可以使用 , eval加快打包效率
module.exports = {
devtool: 'cheap-module-eval-source-map'
}
// 在正式环境中我们可以使用
module.exports = {
devtool: 'cheap-module-source-map';
}
复制代码