前言
目前为止,webpack 在 GitHub 上已拥有 48.8k 的 star,在前端代码打包器领域内,算得上是 时下最流行的前端打包工具。它可以分析各个模块的依赖关系,最终打包成我们常见的静态文件:.js 、 .css 、 .jpg 、.png,极大地提升了开发至发布过程的效率。所以,不少人称它为:模块打包机。
一、webpack4.x 的开发环境配置
1. 全局安装 webpack
npm install -g webpack
2. 全局安装 webpack-cli
npm install -g webpack-cli
3. 配置 webpack 的 mode 选项,进行打包
webpack 的默认入口文件是./src/index.js,默认输出文件./dist/main.js。
// 开发环境
webpack --mode development
// 生产环境
webpack --mode production
4. package.json文件
项目初始化之后,文件夹中会增加 package.json 文件,它用于保存关于项目的信息。我们可以在 package.json 文件中的 script 中配置参数。
配置 dev 和 build
这样可以直接运行 npm run dev / build 来进行打包。
"dev":"webpack --mode development",
"build":"webpack --mode production"
二、webpack4.x 下 babel 的安装、配置及使用
为了实现兼容,需要使用转换工具将ES6语法转换为ES5语法,babel就是最常用的一个工具。那么在webpack中如何使用babel呢?
1. 安装babel-loader、babel-core、babel-preset-env
(1)babel-loader
loader 的作用是让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
虽然 webpack 本身就能够处理 .js 文件
,但无法对ES2015+的语法进行转换,babel-loader 的作用正是实现对使用了ES2015+语法的 .js 文件进行处理。
npm install -D babel-loader
(2)babel-core
babel-core 的作用在于提供一系列API。这便是说,当 webpack 使用 babel-loader 处理文件时,babel-loader 实际上调用了babel-core 的 API,因此也必须安装 babel-core。
npm install -D babel-core
(3)babel-preset-env
babel-preset-env 的作用是告诉 babel 使用哪种转码规则进行文件处理。事实上,babel 有几种规则都可以实现对ES6语法的转码,如 babel-preset-es2015、babel-preset-latest、babel-preset-env,不过官方现已建议采用 babel-preset-env。
npm install -D babel-preset-env
2. 配置 babel 规则
安装完成之后,还需要配置 babel 规则。
第一种方式是通过 package.json。在 package.json 文件中增加一个“babel"属性,该属性是一个JSON对象,作用是设置项目中的babel 转码规则和使用到的 babel 插件,其基本格式如下:
"babel":{
"presets": [],
"plugins": []
}
”presets”属性字段设置转码规则,”plugins”属性设置使用到的插件。我们这里只需将”babel”属性 的”presets”设置为[“env”]即可,如下所示:
"babel":{
"presets": ["env"]
}
上面的设置告诉 npm 本项目将使用 babel,并且使用 bable-preset-env 规则进行转码,即实现对ES2015+语法进行转码。
除此之外,还有第二种方式,即通过.babelrc文件。在项目根目录下新建.babelrc文件,里面只需输入第一种方式中”babel”属性的值即可:
{
"presets": ["env"]
}
3. 建立并配置 webpack.config.js 文件
webpack.config.js 文件的作用是对 webpack 打包的参数进行配置。在根目录下新建 webpack.config.js 文件,在其中输入:
module.exports={
module:{
rules:[
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
}
这就告诉 webpack 打包时,一旦匹配到.js文件就使用babel-loader进行处理,如前文所述,babel-loader 调用 babel-core 的API,使用 bable-preset-env 的规则进行转码。这里并没有使用 entry、output 这样的参数,因为 webpack4.x 有默认的入口和出口,我们无须改变,因此便不必进行设置。
一、创建 webpack 配置文件
编写开发环境和生产环境彼此独立的 webpack 配置文件。
新建项目,项目初始化之后,会自动生成 package.json 文件,我们还需要添加三个文件:webpack.common.js、webpack.dev.js、webpack.prod.js。
webpack-demo
|- package.json
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js
|- /src
|- index.js
|- /node_modules
1. webpack.common.js
安装两个基本插件:
npm i clean-webpack-plugin html-webpack-plugin -D
clean-webpack-plugin:打包时自动清除输出文件夹中未用到的文件
html-webpack-plugin:打包时会自动生成index.html并替换已有的index.html,bundle.js也会自行添加到 html 中
// webpack.common.js
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'index'
})
],
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist') //定义输出文件夹dist路径
}
};
2. webpack.dev.js
安装 webpack-merge,作用是合并通用配置文件与开发环境配置文件。
npm i webpack-merge -D
安装开发服务器 devServer,作用是修改代码后实时重新加载(刷新浏览器)。
npm i webpack-dev-server -D
// webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
module.exports = merge(common,{
devServer: { //启用开发服务器
contentBase: './dist', //告诉服务器从哪提供内容,只有在想要提供静态文件时才需要
compress: true, //一切服务都启用gzip 压缩
host: '0.0.0.0', //指定使用一个host,可用ip地址访问,没有的话如果别人访问会被禁止。默认localhost。
port: '9999', //指定端口号,如省略,默认为”8080“
hot: true, //启用模块热替换特性
inline: true, //启用内联模式,一段处理实时重载的脚本被插入到bundle中,并且构建消息会出现在浏览器控制台
historyApiFallback: true,//开发单页应用时有用,依赖于HTML5 history API,设为true时所有跳转将指向index.html
},
plugins: [
new webpack.HotModuleReplacementPlugin(), //webpack内置的热更新插件
],
mode: 'development'
});
devServer的更多可选参数:https://www.webpackjs.com/configuration/dev-server/
HotModuleReplacementPlugin 模块热替换(Hot Module Replacement)插件,用以在运行时更新发生改变的模块,从而无需进行完全刷新。
3. webpack.prod.js
// webpack.prod.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common,{
mode: "production"
});
关于 mode
- --mode production 生产环境:不需要像旧版本一样定义 node 环境变量
自动开启一些插件,如:
uglifyjs-webpack-plugin—— js代码压缩(所以无需再单独使用)
NoEmitOnErrorsPlugin —— 编译出错时跳过输出,以确保输出资源不包含错误
ModuleConcatenationPlugin —— webpack3 添加的作用域提升(Scope Hoisting)
- --mode development 开发环境:自行定义 node 环境变量为 development
使用 eval 构建 module, 提升增量构建速度
自动开启一些插件,如:
NamedModulesPlugin 使用模块热替换(HMR)时会显示模块的相对路径
二、启动
在 package.json 文件的 "scripts" 中添加 npm 脚本,从而快捷运行开发服务器 | 打包生产环境代码。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --open --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
"start": "webpack-dev-server --open --config webpack.dev.js"
webpack-dev-server 启动开发服务器
--open 打开浏览器
--config webpack.dev.js 设置运行此脚本时执行的配置文件为 webpack.dev.js
"build": "webpack --config webpack.prod.js"
webpack 启动 webpack
--config webpack.prod.js 设置运行此脚本时执行的配置文件为 webpack.prod.js
执行 npm start
此时应该可以看到 项目输出 。
执行 npm run build
项目文件夹中自动生成打包后的文件目录(输出文件夹dist)
webpack-demo
|- package.json
|- webpack.common.js
|- webpack.dev.js
|- webpack.prod.js
|- /src
|- index.js
|- /dist
| - index.html
| - app.bundle.js
|- /node_modules
三、使用 sourcemap
sourcemap 能实现打包后的运行代码与源代码的映射,帮助我们debug到原始开发代码。
///webpack.dev.js
module.exports = merge(common,{
devtool: 'cheap-module-eval-source-map',
...
});
大多数时候开发环境用'cheap-module-eval-source-map'是最好的选择,想要完整的功能又不介意构建速度的话就直接用'source-map'。具体的配置项很多,可以是eval,source-map,cheap,module,inline的任意组合。
具体每个参数的作用请查阅官方API:https://www.webpackjs.com/configuration/devtool/
四、代码分离
把代码分离到不同的 bundle 中,可以按需加载或并行加载这些文件。这样可用于获取更小的 bundle,以及控制资源加载优先级。如果使用合理,会极大影响加载时间。
三种常用的代码分离方法:
1. 入口起点:使用 entry 配置手动地分离代码。
2. 防止重复:使用 SplitChunks 去重和分离 chunk。webpack4 之前版本用的是 CommonsChunkPlugin。
3. 动态导入:通过模块的内联函数调用来分离代码。
更多详情见webpack4.x 配置指南
五、分离CSS
分离 css 需要用到插件 mini-css-extract-plugin,这个插件将会提取 css 到单独的文件,根据每个包含 css 的 js 文件创建一个 css 文件,因此,你的样式将不再内嵌到 JS bundle 中。如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。同时还支持按需加载 css 和 SourceMaps。
相较于旧版 extract-text-webpack-plugin 插件,mini-css-extract-plugin 的优势有:
- 异步加载
- 没有重复的编译
- 更容易使用
- Specific to CSS
- 支持热更新
应用情景:
- 在单个文件中提取所有CSS
- 按照入口JS来分离CSS
- 压缩CSS
其他:
- 这个插件不兼容 style-loader
- 各种 loader 的处理
- 区分环境变量 development / production
更多详情见webpack4.x 配置指南
六、缓存
略,详情见webpack4.x 配置指南
七、Babel
略,详情见webpack4.x 配置指南
八、React
略,详情见webpack4.x 配置指南
面试题
-
有哪些常见的 loader?他们能解决什么问题?
-
webpack 的构建流程是什么? 从读取配置到输出文件的整个过程
-
是否写过 loader 和 Plugin ?描述一下编写 loader 或 Plugin 的思路?
-
webpack 的热更新是如何做到的?说明其原理?
-
如何利用 webpack 来优化前端性能?(提高性能和体验)
参考:
END