1. Webpack简介
1.1 Webpack是什么?
Webpack是一种前端资源构建工具,是一个静态模块打包器(module bundler)。 在Webpack看来,前端的所有资源文件(js/json/css/img/less...)都会作为模块处理。它根据模块的依赖关系构建一个依赖图,进行静态分析,打包生成对应的静态资源(bundle)。
为什么使用webpack?
现代JavaScript应用程序的最大特点可能就是模块化了,这里面涉及很多新技术,比如ES6,Less等,但是不是所有浏览器都适用这些技术,webpack的作用是将代码转化为直接在浏览器上运行的代码。
1.2 核心概念
1.2.1 入口(entry)
页面的入口文件,打包从哪个文件开始。有几个入口文件,就有几个依赖图。
入口起点指示webpack应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的。
也可以在webpack.config.js中配置入口起点文件:
3种写法:
- 字符串
module.exports = {
entry: './path/file.js'
}
- 对象
module.exports = {
entry: {
index: path.resolve(__dirname, './src/main.js')
}
}
- 数组(多个入口文件)
module.exports = {
entry: ['./src/index.js', './src/main.js']
}
为什么会有多个入口文件?
- 分离app(应用程序)和vendor(第三方库)入口
- 多页面应用
1.2.2 输出(output)
output属性告诉webpack在哪里输出它所创建的bundle,以及如何命名这些文件。默认值:./dist/main.js,其它生成文件默认放在./dist文件夹中。
也可以在webpack.config.js中指定一个output字段:
const path = require('path')
module.exports = {
entry: './path/file.js'
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'mybundle.js'
}
}
当有多个入口文件时,这样配置:
const path = require('path')
module.exports = {
entry: './path/file.js'
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
}
}
使用CDN和hash:这里我就不懂了,先写在这
module.exports = {
output: {
path: '/home/proj/cdn/assets/[hash]',
publicPath: 'http://cdn.example.com/assets/[hash]/'
}
}
1.2.3 loader
webpack只能理解JS文件和JSON文件。 loader使得webpack能够处理其它类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。 在更高层面,在 webpack 的配置中 loader 有两个属性:
test属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。use属性,表示进行转换时,应该使用哪个 loader。 在webpack.config.js中配置:
const path = require('path')
module.exports = {
output: {
filename: 'bundle.js'
},
module: {
rules: [
{test: /\.txt$/, use: 'raw-loader'}
]
}
}
告诉webpack编译器,当你碰到「在 require()/import 语句中被解析为 '.txt' 的路径」时,在你对它打包之前,先使用raw-loader转换一下。
1.2.4 插件(plugin)
loader用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量等。
通过在webpack.config.js文件里配置,使用plugins。
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
module.exports = {
module: {
rules: [
{ test: /.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
1.2.5 模式(mode)
通过选择 development, production 或 none 之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production。
用法:
- webpack.config.js文件里配置:
module.exports = {
mode: 'production'
}
- 从CLI参数中传递:
webpack --mode=production
1.2.6 浏览器兼容(browser compatibility)
webpack支持所有符合ES5标准的浏览器(不支持IE8及以下版本)。 webpack 的 import() 和 require.ensure() 需要 Promise。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill。
2. 入口起点
在webpack.config.js文件中有多种配置entry属性的方式。
2.1 单个入口(简写)语法
用法:entry: string|Array<string>
webpack.config.js文件简写配置:
module.exports = {
entry: './path/entry.js'
}
详细写法:
module.exports = {
entry{
main: './path/entry.js'
}
}
2.2 对象语法
用法:entry: {[entryChunkName: string]: string|Array<string>}
webpack.config.js文件中配置:
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js'
}
}
入口对象的完整写法
dependOn:当前入口所依赖的入口。它们必须在该入口被加载前加载filename:指定要输出的文件名称import:启动时需加载的模块library:指定library选项,为当前entry构建一个libraryruntime:运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为false以避免一个新的运行时 chunk。publicPath:当该入口的输出文件在浏览器中被引用时,为它们指定一个公共URL地址
注意:
runtime和dependOn不应在同一个入口上同时使用runtime不能指向已存在的入口名称dependOn不能循环引用
2.3 常见场景
分离app(应用程序)和vendeor(第三方库)入口
多页面应用程序
webpack.config.js文件配置:
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
3. 输出
loader用于对模块的源代码进行转换。loader可以使得你在import或加载模块时预处理文件。
loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!
2.1 使用loader
1. 配置:在webpack.config.js文件中指定loader
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
}
2. 内联:在每个import语句中显示指定loader
直接在import语句中指定loader。使用 ! 将资源中的 loader 分开。每个部分都会相对于当前目录解析。
import Styles from 'style-loader!css-loader?modules!./styles.css';
选项可以传递查询参数,例如 ?key=value&foo=bar,或者一个 JSON 对象,例如 ?{"key":"value","foo":"bar"}。
3. CLI:在shell命令中指定它们
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
2.2 loader特性
- loader支持链式传递。
- loader可以是同步的,可以是异步的。
- loader运行在Node.js中,并且能够执行任何 Node.js 能做到的操作。
- loader可以通过
options对象配置(这个配置项都有哪些属性?网上怎么查不到) 我见过的options配置:
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|jpg|jpeg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8 * 1024,
// 关掉es6的模块化解析
esModule: false,
name: '[hash:10].[ext]'
}
}
]
}
]
}
}
- 插件可以为loader带来更多特性
- loader能够产生额外的任意文件
4. 插件(plugin)
插件的目的在于解决loader无法实现的其他事
4.1 剖析
webpack插件是一个具有apply方法的JavaScript对象。apply方法会被webpack compiler调用,并且compiler对象可在整个编译生命周期访问。
4.2 配置
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装
const webpack = require('webpack'); //访问内置的插件
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.(js|jsx)$/,
use: 'babel-loader'
}
]
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
4.3 Node API
在使用Node API时,还可以通过配置中的plugins属性传入插件
const webpack = require('webpack'); //访问 webpack 运行时(runtime)
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
new webpack.ProgressPlugin().apply(compiler);
compiler.run(function(err, stats) {
// ...
});
5. webpakc性能优化
5.1 开发环境性能优化
1. 优化打包构建速度
2. 优化代码调试
5.2 生产环境性能优化
1. 优化打包构建速度
2. 优化代码运行性能
5.3 模块热替换(Module Hot Replacement)
模块热替换允许运行时更新(替换、添加或删除)所有类型的模块,而无需完全刷新。 主要通过以下几个方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态
- 只更新变更内容,以节省宝贵的开发时间
- 在源代码中对CSS/JS进行修改,会立刻在浏览器中进行更新
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度。
样式文件:可以使用HMR功能,因为
style-loader内部实现了 js文件:默认不使用HMR功能;-> 修改js代码,添加👇的代码,注意HMR对js的处理,只能处理非入口js文件的其它文件。 html文件:默认不使用HMR功能,这个不用热更新
if(module.hot){
module.hot.accept('./js/print.js', function(){
console.log(1)
})
}
10. 项目中用过的配置
10.1 配置过的插件(plugins)
10.1.1 html-webpack-plugin
说明:
该插件将生成一个HTML5文件,在body中使用script标签引入webpack生成的所有bundle。
安装:
npm install --save-dev html-webpack-plugin
用法:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [new HtmlWebpackPlugin({
template: path.resolve(__dirname, './src/index.html),
// 压缩html
minify: {
collapseWhitespace: true,
removeComments: true
}
})],
};
如果有多个webpack入口,它们都会在已生成的HTML文件中的<script>标签内引入。
如果在 webpack 的输出中有任何 CSS 资源(例如,使用 MiniCssExtractPlugin 提取的 CSS),那么这些资源也会在 HTML 文件 <head> 元素中的 <link> 标签内引入。
10.1.2 mini-css-extract-plugin
说明:
将css单独打包成一个文件的插件,它为每个包含css的js文件都创建一个css文件。它支持css和sourceMaps的按需加载。
安装:
npm install --save-dev mini-css-extract-plugin
用法:
// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
module: {
rules: [
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/style.css'
})
]
}
10.1.3 postcss-preset-env
说明:
postcss-preset-env允许我们将现代CSS转换为大多数浏览器可以理解的内容,即兼容浏览器,具体兼容哪些浏览器需要在package.json文件中配置browserslist字段。
安装:
需要跟postcss一起使用。
npm install postcss-preset-env --save-dev
用法:
// webpack.config.js
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
indent: 'postcss',
plugins: () => [
require('postcss-preset-env')()
]
}
}
]
}
]
}
}
10.2 loader
10.2.1 postcss-loader
说明: 使用postcss处理css的loader 安装:
npm install --save-dev postcss-loader
用法:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /.css$/i,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// Options
},
],
],
},
},
},
],
},
],
},
};