初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件和生命周期,执行对象的 run 方法开始执行编译;确定入口:根据配置中的 entry 找出所有的入口文件编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译(转化为AST树进行处理),再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。hash:根据构建目录生成,只要项目中有文件修改,则整个项目构建出来的hash都会改变chunkhash:和webpack打包到chunk有关,不同entry会生成不同的ChunkHush。chunk之间互不影响。(一般用来打包公用js)contenthash:根据文件内容生成hash,只要文件内容不变,生成的ContentHash就不变(一般用来打包css)webpack 为前端的工程化开发提供了一套相对容易和完整的解决方案
.babelrc文件中或在webpack.config.js文件中设置modules: falseUglifyJsPlugin插件每个 Webpack 的 Loader 都需要导出一个函数,这个函数就是我们这个 Loader 对资源的处理过程,它的输入就是加载到的资源文件内容,输出就是我们加工后的结果。我们通过 source 参数接收输入,通过返回值输出
为了减少HTTP请求,通常我们会讲所有代码打包到一个文件中,但是如果文件过大,也会影响页面的加载速度,尤其是会增加白屏时间,所以我们不妨将代码分成一块一块的;并且只在需要的时候去加载它;还可以利用浏览器将这些模块缓存起来;一般情况下分离有两种
webpack优化的瓶颈主要有两个方面:构建过程耗时、打包的体积太大;
针对构建时间的优化(从依赖分析、依赖编译、处理输出三个方向):
hard-source-webpack-plugin,通过缓存编译过程优化启动速度cacheDirectory开启缓存webpack.module.rules:{
test: /\.js$/,
use: [
{
loader: resolve('babel-loader'),
options: {
babelrc: false,
// 将 babel 编译过的模块缓存在 webpack_cache 目录下,下次优先复用
cacheDirectory: true,
},
},
]
}
cache选项指定缓存路径;注:配置更后如果不起作用则需要将cache文件删除webpack.module.rules:{
test: /\.(js|vue)$/,
enforce: 'pre',
use: [
{
options: {
// 启动缓存
cache: true,
},
loader: require.resolve('eslint-loader'),
},
]
}
cache-loader辅助构建结果缓存webpack.resolve: { // 指定以下目录寻找第三方模块,避免webpack往父级目录递归搜索
modules: {
'node_modules',
path.resolve(projectDir, 'src'),
path.resolve(projectDir, 'node_modules')
}
}
webpack.module: {
noParse: /node_modules\/(jQuery|chart\.js)/,
}
alias配置别名, 减小查找过程 webpack.resolve: {
alias: {
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
}
}
include/exclude缩小babel的编译范围webpack.module.rules: {
test: /\.js$/,
use: [{
loader: 'babel-loader',
query: {
// 将 babel 编译过的模块缓存在 webpack_cache 目录下,下次优先复用
cacheDirectory: './webpack_cache/',
},
}],
// 减少 babel 编译范围,忘记设置会让 webpack 编译慢上好几倍
include: path.resolve(__dirname, 'src'),
}
uglifyjs-webpack-plugin插件开启多个子线程压缩代码Happypack开启多线程加速loader对文件的转换速度;使用 HappyPack 也有一些限制,它只兼容部分主流的 loaderopen in new window;webpack4已经弃用,推荐用thread-loadermodule.exports = {
module: {
rules: [
{
test: /\.js?$/,
use: ['happypack/loader?id=babel'],
include: path.resolve(__dirname, 'src'),
},
]
},
plugins: [
new HappyPack({
id: 'babel',
loaders: [{
loader: 'babel-loader',
query: {
cacheDirectory: './webpack_cache/',
},
}],
})
],
};
thread-loader放置在其他 loader 之前;放置在这个 loader 之后的 loader 就会在一个单独的 worker【worker pool】 池里运行;请仅在耗时的 loader 上使用;文档open in new windowmodule.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
// 创建一个 js worker 池
use: [
'thread-loader',
'babel-loader'
]
},
{
test: /\.s?css$/,
exclude: /node_modules/,
// 创建一个 css worker 池
use: [
'style-loader',
'thread-loader',
'css-loader'
},
'postcss-loader'
]
}
]
}
}
针对打包体积的优化
DllPlugin和DllReferencePlugin提前打包公共模块;注:不建议将所有公共模块放在dll中,否则无法用webpack的代码分割和按需加载功能ModuleConcatenationPlugin插件开启scope-hoisting(作用域提升)功能,尽可能的把打包出来的模块合并到一个函数中去(代码需要符合es6的module规范) 注意: webpack4中,去掉了这个插件改为
optimization.concatenateModules配置,默认在生产环境是开启的
tree shaking,css通过purgecss-webpack-plugin插件进行tree-shanking优化webpack-bundle-analyzer对打包文件进行分析,将大文件模块提取出来在html中单独加载// ...
externals: {
'element-ui': 'Element',
'v-charts': 'VCharts'
}
<script src="https://unpkg.com/element-ui@2.10.0/lib/index.js"></script>
HMR Server服务,同时在打包文件中注入HMR Runtime(用来启动websocket,来保持和server的通信,通过修改entry配置注入)// 修改后的entry入口
{ entry:
{ index:
[
// 添加websocket代码
'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080',
// 检查更新逻辑
'xxx/node_modules/webpack/hot/dev-server.js',
// 自己的入口
'./src/index.js'
],
},
}
compiler.hooks.done钩子中注册监听编译完成事件,当修改了一个或多个文件是,webpack会重新编译,编译完成后触发钩子setupHooks() {
const {done} = compiler.hooks;
// 监听webpack的done钩子,tapable提供的监听方法
done.tap('webpack-dev-server', (stats) => {
this._sendStats(this.sockets, this.getStats(stats));
this._stats = stats;
});
};
websoket想浏览器发送hash和ok事件// 通过websoket给客户端发消息
_sendStats() {
this.sockWrite(sockets, 'hash', stats.hash); // 发送最新hash
this.sockWrite(sockets, 'ok'); // 通知刷新
}
hash,发送xxx/${hash}.hot-update.json请求,获取下次更新的hash和更新模块{
"h": "0c256052432b51ed32c8", // 下次更新hash
"c": {
"302": true // 更新的模块,如果没有则不需要更新
}
}
hash,发送xxx/${hash}.hot-update.js请求,利用jsop方式返回更新模块代码,jsonp的方式对返回的代码可以直接执行参考open in new window、参考open in new window、参考open in new window
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server"
}
webpack --env.production // 设置 env.production == true
webpack --env.platform=web // 设置 env.platform == "web"
注意,这种方式需要对
webpack配置的module.exports原来指向对象的方式改为函数
module.exports = env => {
console.log('NODE_ENV: ', env.NODE_ENV) // 'local'
console.log('platform: ', env.platform) // web
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
}
DefinePlugin配置const env = {
BASE_API:'"https://www.xxxx.com"' // 其它一些参数配置
}
plugins: [
new webpack.DefinePlugin({
'process.env' : env,
'process.env.NODE_ENV' : JSON.stringify(process.env.NODE_ENV)
})
]
modemodule.exports = {
// 定义环境变量
mode: 'development',
// JavaScript 执行入口文件
entry: './main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
},
};
// 或者执行时设置
webpack --mode=production
注意: 设置
modewebpack内部会自动执行DefinePlugin设置变量open in new window
注意:npm脚本里的设置多用于配置相关;而DefinePlugin和mode选项定义的NODE_ENV 作用于webpack入口文件下的业务代码
初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件和生命周期,执行对象的 run 方法开始执行编译;确定入口:根据配置中的 entry 找出所有的入口文件编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译(转化为AST树进行处理),再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。hash:根据构建目录生成,只要项目中有文件修改,则整个项目构建出来的hash都会改变chunkhash:和webpack打包到chunk有关,不同entry会生成不同的ChunkHush。chunk之间互不影响。(一般用来打包公用js)contenthash:根据文件内容生成hash,只要文件内容不变,生成的ContentHash就不变(一般用来打包css)webpack 为前端的工程化开发提供了一套相对容易和完整的解决方案
.babelrc文件中或在webpack.config.js文件中设置modules: falseUglifyJsPlugin插件每个 Webpack 的 Loader 都需要导出一个函数,这个函数就是我们这个 Loader 对资源的处理过程,它的输入就是加载到的资源文件内容,输出就是我们加工后的结果。我们通过 source 参数接收输入,通过返回值输出
为了减少HTTP请求,通常我们会讲所有代码打包到一个文件中,但是如果文件过大,也会影响页面的加载速度,尤其是会增加白屏时间,所以我们不妨将代码分成一块一块的;并且只在需要的时候去加载它;还可以利用浏览器将这些模块缓存起来;一般情况下分离有两种
webpack优化的瓶颈主要有两个方面:构建过程耗时、打包的体积太大;
针对构建时间的优化(从依赖分析、依赖编译、处理输出三个方向):
hard-source-webpack-plugin,通过缓存编译过程优化启动速度cacheDirectory开启缓存webpack.module.rules:{
test: /\.js$/,
use: [
{
loader: resolve('babel-loader'),
options: {
babelrc: false,
// 将 babel 编译过的模块缓存在 webpack_cache 目录下,下次优先复用
cacheDirectory: true,
},
},
]
}
cache选项指定缓存路径;注:配置更后如果不起作用则需要将cache文件删除webpack.module.rules:{
test: /\.(js|vue)$/,
enforce: 'pre',
use: [
{
options: {
// 启动缓存
cache: true,
},
loader: require.resolve('eslint-loader'),
},
]
}
cache-loader辅助构建结果缓存webpack.resolve: { // 指定以下目录寻找第三方模块,避免webpack往父级目录递归搜索
modules: {
'node_modules',
path.resolve(projectDir, 'src'),
path.resolve(projectDir, 'node_modules')
}
}
webpack.module: {
noParse: /node_modules\/(jQuery|chart\.js)/,
}
alias配置别名, 减小查找过程 webpack.resolve: {
alias: {
react: path.resolve(__dirname, './node_modules/react/dist/react.min.js')
}
}
include/exclude缩小babel的编译范围webpack.module.rules: {
test: /\.js$/,
use: [{
loader: 'babel-loader',
query: {
// 将 babel 编译过的模块缓存在 webpack_cache 目录下,下次优先复用
cacheDirectory: './webpack_cache/',
},
}],
// 减少 babel 编译范围,忘记设置会让 webpack 编译慢上好几倍
include: path.resolve(__dirname, 'src'),
}
uglifyjs-webpack-plugin插件开启多个子线程压缩代码Happypack开启多线程加速loader对文件的转换速度;使用 HappyPack 也有一些限制,它只兼容部分主流的 loaderopen in new window;webpack4已经弃用,推荐用thread-loadermodule.exports = {
module: {
rules: [
{
test: /\.js?$/,
use: ['happypack/loader?id=babel'],
include: path.resolve(__dirname, 'src'),
},
]
},
plugins: [
new HappyPack({
id: 'babel',
loaders: [{
loader: 'babel-loader',
query: {
cacheDirectory: './webpack_cache/',
},
}],
})
],
};
thread-loader放置在其他 loader 之前;放置在这个 loader 之后的 loader 就会在一个单独的 worker【worker pool】 池里运行;请仅在耗时的 loader 上使用;文档open in new windowmodule.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
// 创建一个 js worker 池
use: [
'thread-loader',
'babel-loader'
]
},
{
test: /\.s?css$/,
exclude: /node_modules/,
// 创建一个 css worker 池
use: [
'style-loader',
'thread-loader',
'css-loader'
},
'postcss-loader'
]
}
]
}
}
针对打包体积的优化
DllPlugin和DllReferencePlugin提前打包公共模块;注:不建议将所有公共模块放在dll中,否则无法用webpack的代码分割和按需加载功能ModuleConcatenationPlugin插件开启scope-hoisting(作用域提升)功能,尽可能的把打包出来的模块合并到一个函数中去(代码需要符合es6的module规范) 注意: webpack4中,去掉了这个插件改为
optimization.concatenateModules配置,默认在生产环境是开启的
tree shaking,css通过purgecss-webpack-plugin插件进行tree-shanking优化webpack-bundle-analyzer对打包文件进行分析,将大文件模块提取出来在html中单独加载// ...
externals: {
'element-ui': 'Element',
'v-charts': 'VCharts'
}
<script src="https://unpkg.com/element-ui@2.10.0/lib/index.js"></script>
HMR Server服务,同时在打包文件中注入HMR Runtime(用来启动websocket,来保持和server的通信,通过修改entry配置注入)// 修改后的entry入口
{ entry:
{ index:
[
// 添加websocket代码
'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080',
// 检查更新逻辑
'xxx/node_modules/webpack/hot/dev-server.js',
// 自己的入口
'./src/index.js'
],
},
}
compiler.hooks.done钩子中注册监听编译完成事件,当修改了一个或多个文件是,webpack会重新编译,编译完成后触发钩子setupHooks() {
const {done} = compiler.hooks;
// 监听webpack的done钩子,tapable提供的监听方法
done.tap('webpack-dev-server', (stats) => {
this._sendStats(this.sockets, this.getStats(stats));
this._stats = stats;
});
};
websoket想浏览器发送hash和ok事件// 通过websoket给客户端发消息
_sendStats() {
this.sockWrite(sockets, 'hash', stats.hash); // 发送最新hash
this.sockWrite(sockets, 'ok'); // 通知刷新
}
hash,发送xxx/${hash}.hot-update.json请求,获取下次更新的hash和更新模块{
"h": "0c256052432b51ed32c8", // 下次更新hash
"c": {
"302": true // 更新的模块,如果没有则不需要更新
}
}
hash,发送xxx/${hash}.hot-update.js请求,利用jsop方式返回更新模块代码,jsonp的方式对返回的代码可以直接执行参考open in new window、参考open in new window、参考open in new window
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
"dev": "cross-env NODE_ENV=development webpack-dev-server"
}
webpack --env.production // 设置 env.production == true
webpack --env.platform=web // 设置 env.platform == "web"
注意,这种方式需要对
webpack配置的module.exports原来指向对象的方式改为函数
module.exports = env => {
console.log('NODE_ENV: ', env.NODE_ENV) // 'local'
console.log('platform: ', env.platform) // web
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
}
}
DefinePlugin配置const env = {
BASE_API:'"https://www.xxxx.com"' // 其它一些参数配置
}
plugins: [
new webpack.DefinePlugin({
'process.env' : env,
'process.env.NODE_ENV' : JSON.stringify(process.env.NODE_ENV)
})
]
modemodule.exports = {
// 定义环境变量
mode: 'development',
// JavaScript 执行入口文件
entry: './main.js',
output: {
// 把所有依赖的模块合并输出到一个 bundle.js 文件
filename: 'bundle.js',
// 输出文件都放到 dist 目录下
path: path.resolve(__dirname, './dist'),
},
};
// 或者执行时设置
webpack --mode=production
注意: 设置
modewebpack内部会自动执行DefinePlugin设置变量open in new window
注意:npm脚本里的设置多用于配置相关;而DefinePlugin和mode选项定义的NODE_ENV 作用于webpack入口文件下的业务代码