博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
③webpack 优化环境配置
阅读量:3966 次
发布时间:2019-05-24

本文共 34733 字,大约阅读时间需要 115 分钟。

webpack


本人是个新手,写下博客用于自我复习、自我总结。

如有错误之处,请各位大佬指出。
学习资料来源于:尚硅谷


优化环境配置 ※


优化配置介绍

webpack性能优化分为:

  • 开发环境性能优化
  • 生产环境性能优化

开发环境性能优化

  • 优化打包构建速度:HMR
  • 优化代码调试:source-map

生产环境性能优化

  • 优化打包构建速度
    • oneOf
    • babel缓存
    • 多进程打包
    • externals
    • dll
  • 优化代码运行的性能
    • 缓存(hash-chunkhash-contenthash)
    • tree shaking
    • code split
    • 懒加载/预加载
    • pwa

HMR

为什么要优化打包构建速度?当我们修改样式的时候,虽然只修改了css文件,但是重新打包时,也会重新打包js文件。也就意味着:如果有上百个模块,虽然你只修改了一个模块,但是所有模块都会被重新打包,可以想象速度有多慢。

这里使用HMR,hot module replacement 热模块替换 / 模块热替换

作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块),极大提升构建速度。

样式文件:可以直接使用HMR功能,因为style-loader内部实现了

js文件默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码

注意:HMR功能对js的处理,只能处理非入口js文件的其他文件

html文件: 默认不能使用HMR功能。(不用做HMR功能)

解决:修改entry入口,将html文件引入

webpack.config.js

/*  HMR: hot module replacement 热模块替换 / 模块热替换    作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)       极大提升构建速度            样式文件:可以使用HMR功能:因为style-loader内部实现了~      js文件:默认不能使用HMR功能 --> 需要修改js代码,添加支持HMR功能的代码        注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。      html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了~ (不用做HMR功能)        解决:修改entry入口,将html文件引入*/const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
entry: ['./src/js/index.js', './src/index.html'], output: {
filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: {
rules: [ // loader的配置 {
// 处理less资源 test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, {
// 处理css资源 test: /\.css$/, use: ['style-loader', 'css-loader'] }, {
// 处理图片资源 test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', // 关闭es6模块化 esModule: false, outputPath: 'imgs' } }, {
// 处理html中img资源 test: /\.html$/, loader: 'html-loader' }, {
// 处理其他资源 exclude: /\.(html|js|css|less|jpg|png|gif)/, loader: 'file-loader', options: {
name: '[hash:10].[ext]', outputPath: 'media' } } ] }, plugins: [ // plugins的配置 new HtmlWebpackPlugin({
template: './src/index.html' }) ], mode: 'development', devServer: {
contentBase: resolve(__dirname, 'build'), compress: true, port: 3000, open: true, // 开启HMR功能 // 当修改了webpack配置,新配置要想生效,必须重新webpack服务 // npx webpack-dev-server hot: true }};

print.js

console.log('print.js被加载了~');function print() {
const content = 'hello print'; console.log(content);}export default print;

index.js

// 引入import print from './print';console.log('index.js文件被加载了~');print();function add(x, y) {
return x + y;}console.log(add(1, 3));if (module.hot) {
// 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效 module.hot.accept('./print.js', function() {
// 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。 // 会执行后面的回调函数 print(); });}

index.html

  
doucument

hello

运行:npx webpack-dev-server


source-map

source-map: 一种提供源代码到构建后代码的映射技术

如果构建后代码出错了,通过映射可以追踪源代码错误
只需要在配置文件中,加一句话即可。devtool: 'source-map'

webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
entry: ['./src/js/index.js', './src/index.html'], output: {
filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: {
rules: [ // loader的配置 {
// 处理less资源 test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, {
// 处理css资源 test: /\.css$/, use: ['style-loader', 'css-loader'] }, {
// 处理图片资源 test: /\.(jpg|png|gif)$/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', // 关闭es6模块化 esModule: false, outputPath: 'imgs' } }, {
// 处理html中img资源 test: /\.html$/, loader: 'html-loader' }, {
// 处理其他资源 exclude: /\.(html|js|css|less|jpg|png|gif)/, loader: 'file-loader', options: {
name: '[hash:10].[ext]', outputPath: 'media' } } ] }, plugins: [ // plugins的配置 new HtmlWebpackPlugin({
template: './src/index.html' }) ], mode: 'development', devServer: {
contentBase: resolve(__dirname, 'build'), compress: true, port: 3000, open: true, hot: true }, devtool: 'eval-source-map'};

用法:

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

source-map:外部

错误代码准确信息 和 源代码的错误位置

inline-source-map:内联

只生成一个内联source-map
错误代码准确信息 和 源代码的错误位置

hidden-source-map:外部

错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置

eval-source-map:内联

每一个文件都生成对应的source-map,都在eval
错误代码准确信息 和 源代码的错误位置

nosources-source-map:外部

错误代码准确信息, 但是没有任何源代码信息

cheap-source-map:外部

错误代码准确信息 和 源代码的错误位置
只能精确的行

cheap-module-source-map:外部

错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入

内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快

开发环境:速度快,调试更友好

速度比较:eval>inline>cheap>…
经过组合:eval-cheap-souce-map 比 eval-source-map 还快

调试是否友好的比较:

source-map > cheap-module-souce-map > cheap-souce-map

经过综合,选择 eval-source-map / eval-cheap-module-souce-map 比较好。

生产环境:源代码要不要隐藏? 调试要不要更友好

内联会让代码体积变大,所以在生产环境不用内联
速度比较:source-map > cheap-source-map / cheap-module-source-map > hidden-source-map / nosources-source-map

调试是否友好的比较:cheap-source-map / cheap-module-source-map 最好。

nosources-source-map 全部隐藏

hidden-source-map 只隐藏源代码,会提示构建后代码错误信息

经过综合:选择 source-map / cheap-module-source-map 比较好

大家可以拿之前代码尝试。npx webpack-dev-server


oneOf

在生产环境下的配置文件中,我们会设置很多loader,用来对各种类型的文件做兼容性处理和压缩。之前的时候,一个文件进来,肯定会所有的配置都过一遍,去判断文件类型,虽然之前有设置优先执行enforce: 'pre',这样肯定会消耗更多资源。现在只需要添加oneOf即可,能这么做,也是因为一个文件只能被一个loader处理(没必要将处理一个类型文件的配置分开写)。那么像这种:当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序,比如:先执行eslint 在执行babel。也正因为oneOf里,不能有两个配置处理同一种类型文件,所以这两个配置要拿出去一个。

webpack.config.js

const {
resolve } = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');// 定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production';// 复用loaderconst commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', {
// 还需要在package.json中定义browserslist loader: 'postcss-loader', options: {
ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } }];module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: {
rules: [ {
// 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: {
fix: true } }, {
// 以下loader只会匹配一个 // 注意:不能有两个配置处理同一种类型文件 oneOf: [ {
test: /\.css$/, use: [...commonCssLoader] }, {
test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ {
test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: {
presets: [ [ '@babel/preset-env', {
useBuiltIns: 'usage', corejs: {
version: 3}, targets: {
chrome: '60', firefox: '50' } } ] ] } }, {
test: /\.(jpg|png|gif)/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, {
test: /\.html$/, loader: 'html-loader' }, {
exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: {
outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({
filename: 'css/built.css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], mode: 'production'};

缓存

为什么要对babel进行缓存:babel需要对我们的js代码进行编译处理,编译成浏览器能理解的语法。如果有好几百个js模块,其中只修改一个js模块,那么其他的js模块也会被重新编译,即类似于HMR功能,但是HMR功能只适用于开发环境。所以在这里开启babel缓存:它会把所有构建后的js模块缓存下来,如果这些模块没有改动,那么就会直接使用缓存。只需要在配置文件中加一句话:cacheDirectory: true

webpack.config.js

const {
resolve } = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');// 定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production';// 复用loaderconst commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', {
// 还需要在package.json中定义browserslist loader: 'postcss-loader', options: {
ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } }];module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.js', path: resolve(__dirname, 'build') }, module: {
rules: [ {
// 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: {
fix: true } }, {
// 以下loader只会匹配一个 // 注意:不能有两个配置处理同一种类型文件 oneOf: [ {
test: /\.css$/, use: [...commonCssLoader] }, {
test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ {
test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: {
presets: [ [ '@babel/preset-env', {
useBuiltIns: 'usage', corejs: {
version: 3 }, targets: {
chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true } }, {
test: /\.(jpg|png|gif)/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, {
test: /\.html$/, loader: 'html-loader' }, {
exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: {
outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({
filename: 'css/built.css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], mode: 'production', devtool: 'source-map'};

index.html

  
webpack

hello cache

index.js

import '../css/index.css';function sum(...args) {
return args.reduce((p, c) => p + c, 0);}// eslint-disable-next-lineconsole.log(sum(1, 2, 3, 4));

index.css

html, body {
margin: 0; padding: 0; height: 100%; background-color: deeppink;}

打包运行。

在这里插入图片描述
还有一种文件资源缓存:

server.js

/*  服务器代码  启动服务器指令:    npm i nodemon -g (第一种方法需要安装包)    nodemon server.js  或    node server.js (不需要安装包)  访问服务器地址:    http://localhost:3000*/const express = require('express');const app = express();// express.static向外暴露静态资源// maxAge 资源缓存的最大时间,单位msapp.use(express.static('build', {
maxAge: 1000 * 3600 }));app.listen(3000);

在使用时,输入node server.js即可。然后去网页中访问服务器地址:http://localhost:3000

但是这种方法有问题:当你修改js文件后,重新打包,在服务器页面刷新,不会有任何改动。这是因为:当前资源在强制缓存期间,它是不会访问服务器的,而是直接读取本地的缓存。这样一来,如果页面出现bug,需要紧急修复,这种情况下肯定不会修改成功。解决它的方法:在资源名称上做一些操作,比如在资源名称后加上版本号,这样一来只要资源名称发生了改变,它就会去重新请求,问题就解决了:

(使用哈希值是因为,只要对文件进行修改,重新打包时,就会重新生成新的哈希值,这样文件名发生了改变,就会改变缓存)
webpack.config.js

const {
resolve } = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');// 定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production';// 复用loaderconst commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', {
// 还需要在package.json中定义browserslist loader: 'postcss-loader', options: {
ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } }];module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: {
rules: [ {
// 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: {
fix: true } }, {
// 以下loader只会匹配一个 // 注意:不能有两个配置处理同一种类型文件 oneOf: [ {
test: /\.css$/, use: [...commonCssLoader] }, {
test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ {
test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: {
presets: [ [ '@babel/preset-env', {
useBuiltIns: 'usage', corejs: {
version: 3 }, targets: {
chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true } }, {
test: /\.(jpg|png|gif)/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, {
test: /\.html$/, loader: 'html-loader' }, {
exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: {
outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], mode: 'production', devtool: 'source-map'};

缓存:

babel缓存
cacheDirectory: true 让第二次打包构建速度更快

文件资源缓存:

hash: 每次wepack构建时会生成一个唯一的hash值。
问题: 因为js和css同时使用一个hash值。
如果重新打包,会导致所有缓存失效。

chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样。

问题: js和css的hash值还是一样的
因为css是在js中被引入的,所以同属于一个chunk

contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样。让代码上线运行缓存更好使用


tree shaking

简单理解:将应用程序想像成一颗树,引入的源代码和第三方库就像是树上“绿色”的树叶,这个库里肯定有你没用到的代码,就像是“灰色”的树叶,为了去掉这些“灰色”的树叶,我们需要摇晃这颗树去掉它,即tree shaking,去掉没有用到的代码,减小代码体积,提高效率。(开启生产环境即可)

webpack.config.js

const {
resolve } = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');/* tree shaking:去除无用代码 前提:1. 必须使用ES6模块化 2. 开启production环境 作用: 减少代码体积*/// 定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production';// 复用loaderconst commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', {
// 还需要在package.json中定义browserslist loader: 'postcss-loader', options: {
ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } }];module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: {
rules: [ {
// 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: {
fix: true } }, {
// 以下loader只会匹配一个 // 注意:不能有两个配置处理同一种类型文件 oneOf: [ {
test: /\.css$/, use: [...commonCssLoader] }, {
test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ {
test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: {
presets: [ [ '@babel/preset-env', {
useBuiltIns: 'usage', corejs: {
version: 3 }, targets: {
chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true } }, {
test: /\.(jpg|png|gif)/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, {
test: /\.html$/, loader: 'html-loader' }, {
exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: {
outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], mode: 'production', devtool: 'source-map'};

在package.json中,加一段话:

"sideEffects": [    "*.css"]

"sideEffects": false所有代码都可以进行tree shaking。

问题:可能会把css / @babel/polyfill 文件干掉
所以需要这么使用:"sideEffects": ["*.css", "*.less"]


code split

代码分割,可以将一个文件分割成多个文件,这样我们就可以分别去实现各项功能,同时分割后,多个文件可以并行加载,使运行更快,而且还可以实现按需下载,只有我们需要这个文件才会去下载。

index.html

  
webpack

hello

目录结构:

在这里插入图片描述
第一种分割方法:

webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
// 用于单页面应用 // 单入口 // entry: './src/js/index.js', entry: {
// 用于多页面应用 // 多入口:有几个入口,最终输出就会有几个bundle index: './src/js/index.js', test: './src/js/test.js' }, output: {
// [name]:取文件名 filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], mode: 'production'};

index.js

function sum(...args) {
return args.reduce((p, c) => p + c, 0);}// eslint-disable-next-lineconsole.log(sum(1, 2, 3, 4));

test.js

export function mul(x, y) {
return x * y;}export function count(x, y) {
return x - y;}

结果:

在这里插入图片描述
这种方式比较麻烦,因为需要手动设置这些入口,而且可能会有很多个入口。

第二种分割方法

在js中,可能会引入jQuery,这时通过配置,可以将node_modules中代码单独打包一个chunk最终输出,同时还可以自动分析多入口chunk中,有没有公共的文件。如果有,会打包成单独一个chunk。比如有两个js文件都引入jQuery,它就会将这个公共文件打包成单独的一个chunk。

webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
// 单入口 // entry: './src/js/index.js', entry: {
index: './src/js/index.js', test: './src/js/test.js' }, output: {
// [name]:取文件名 filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], /* 1. 可以将node_modules中代码单独打包一个chunk最终输出 2. 自动分析多入口chunk中,有没有公共的文件。如果有,会打包成单独一个chunk */ optimization: {
splitChunks: {
chunks: 'all' } }, mode: 'production'};

index.js

import $ from 'jquery';function sum(...args) {
return args.reduce((p, c) => p + c, 0);}// eslint-disable-next-lineconsole.log(sum(1, 2, 3, 4));// eslint-disable-next-lineconsole.log($);

test.js

import $ from 'jquery';// eslint-disable-next-lineconsole.log($);export function mul(x, y) {
return x * y;}export function count(x, y) {
return x - y;}

打包的效果:

在这里插入图片描述
可以看到只分成了三个文件,index,test和index+test共有的文件。

第三种分割方法:※

假如我们依然只想要单入口,但是却想让其他js文件,分隔开,可以这么做:

webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
// 单入口 entry: './src/js/index.js', output: {
// [name]:取文件名 filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], optimization: {
splitChunks: {
chunks: 'all' } }, mode: 'production'};

index.js

function sum(...args) {
return args.reduce((p, c) => p + c, 0);}/* 通过js代码,让某个文件被单独打包成一个chunk import动态导入语法:能将某个文件单独打包*/import(/* webpackChunkName: 'test' */'./test') .then(({
mul, count }) => {
// 文件加载成功~ // eslint-disable-next-line console.log(mul(2, 5)); }) .catch(() => {
// eslint-disable-next-line console.log('文件加载失败~'); });// eslint-disable-next-lineconsole.log(sum(1, 2, 3, 4));

test.js

export function mul(x, y) {
return x * y;}export function count(x, y) {
return x - y;}

lazy loading 懒加载

懒加载就是延迟加载,即它需要触发一些条件才会加载,并不是在最开始就加载。

webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/[name].[contenthash:10].js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }) ], optimization: {
splitChunks: {
chunks: 'all' } }, mode: 'production'};

test.js

console.log('test.js文件被加载了~');export function mul(x, y) {
return x * y;}export function count(x, y) {
return x - y;}

index.html

  
webpack

hello lazy loading

index.js

console.log('index.js文件被加载了~');document.getElementById('btn').onclick = function() {
// 懒加载:当文件需要使用时才加载 // 正常加载可以认为是并行加载(同一时间加载多个文件) // 预加载 prefetch:会在使用之前,提前加载js文件, // 具体是等其他资源加载完毕,浏览器空闲了,再偷偷加载资源 import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({
mul }) => {
console.log(mul(4, 5)); });};

pwa

pwa(渐进式网络开发应用程序)帮助我们实现,让浏览器离线也可以访问。

(因为兼容性问题,虽然pwa普及还需要时间,但是很多大厂已经在使用,比如淘宝,当你测试一下就知道了,其他网页离线就看不了了,但是淘宝页面,离线了依然可以访问。当然只是部分资源可以访问,其他资源肯定还是访问不了)

项目结构:

在这里插入图片描述
安装:
在这里插入图片描述
webpack.config.js

const {
resolve } = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');const WorkboxWebpackPlugin = require('workbox-webpack-plugin');/* PWA: 渐进式网络开发应用程序(离线可访问) workbox --> workbox-webpack-plugin*/// 定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production';// 复用loaderconst commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', {
// 还需要在package.json中定义browserslist loader: 'postcss-loader', options: {
ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } }];module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: {
rules: [ {
// 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: {
fix: true } }, {
// 以下loader只会匹配一个 // 注意:不能有两个配置处理同一种类型文件 oneOf: [ {
test: /\.css$/, use: [...commonCssLoader] }, {
test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, /* 正常来讲,一个文件只能被一个loader处理。 当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序: 先执行eslint 在执行babel */ {
test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', options: {
presets: [ [ '@babel/preset-env', {
useBuiltIns: 'usage', corejs: {
version: 3 }, targets: {
chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true } }, {
test: /\.(jpg|png|gif)/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, {
test: /\.html$/, loader: 'html-loader' }, {
exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: {
outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }), new WorkboxWebpackPlugin.GenerateSW({
/* 1. 帮助serviceworker快速启动 2. 删除旧的 serviceworker 生成一个 serviceworker 配置文件 */ clientsClaim: true, skipWaiting: true }) ], mode: 'production', devtool: 'source-map'};

index.js

import {
mul } from './test';import '../css/index.css';function sum(...args) {
return args.reduce((p, c) => p + c, 0);}// eslint-disable-next-lineconsole.log(mul(2, 3));// eslint-disable-next-lineconsole.log(sum(1, 2, 3, 4));/* 1. eslint不认识 window、navigator全局变量 解决:需要修改package.json中eslintConfig配置 "env": { "browser": true // 支持浏览器端全局变量 } 2. sw代码必须运行在服务器上 npm i serve -g serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去*/// 注册serviceWorker// 处理兼容性问题if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker .register('/service-worker.js') .then(() => {
console.log('sw注册成功了~'); }) .catch(() => {
console.log('sw注册失败了~'); }); });}

eslint不认识 window、navigator全局变量,在package.json中修改之前的信息:

"eslintConfig": {
"extends": "airbnb-base", "env": {
"browser": true }}

test.js

export function mul(x, y) {
return x * y;}export function count(x, y) {
return x - y;}

index.html

  
webpack

hello

index.css

html,body {
margin: 0; padding: 0; height: 100%; background-color: deeppink;}

安装:

在这里插入图片描述
打包。

运行:

在这里插入图片描述


多进程打包

因为主线程是单线程,所以如果任务比较多,可能要等很久很久,所以我们可以用多进程打包,优化速度。但是要使用多进程打包需要注意,进程启动大概为600ms,进程通信也有开销。假如我们直接打包可能只需要100ms,那么多进程打包就多余了。所以只有工作消耗时间比较长,才需要多进程打包。

安装:

在这里插入图片描述
webpack.config.js

const {
resolve } = require('path');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');const HtmlWebpackPlugin = require('html-webpack-plugin');const WorkboxWebpackPlugin = require('workbox-webpack-plugin');// 定义nodejs环境变量:决定使用browserslist的哪个环境process.env.NODE_ENV = 'production';// 复用loaderconst commonCssLoader = [ MiniCssExtractPlugin.loader, 'css-loader', {
// 还需要在package.json中定义browserslist loader: 'postcss-loader', options: {
ident: 'postcss', plugins: () => [require('postcss-preset-env')()] } }];module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.[contenthash:10].js', path: resolve(__dirname, 'build') }, module: {
rules: [ {
// 在package.json中eslintConfig --> airbnb test: /\.js$/, exclude: /node_modules/, // 优先执行 enforce: 'pre', loader: 'eslint-loader', options: {
fix: true } }, {
// 以下loader只会匹配一个 // 注意:不能有两个配置处理同一种类型文件 oneOf: [ {
test: /\.css$/, use: [...commonCssLoader] }, {
test: /\.less$/, use: [...commonCssLoader, 'less-loader'] }, {
test: /\.js$/, exclude: /node_modules/, use: [ /* 开启多进程打包。 进程启动大概为600ms,进程通信也有开销。 只有工作消耗时间比较长,才需要多进程打包 */ {
loader: 'thread-loader', options: {
workers: 2 // 进程2个 } }, {
loader: 'babel-loader', options: {
presets: [ [ '@babel/preset-env', {
useBuiltIns: 'usage', corejs: {
version: 3 }, targets: {
chrome: '60', firefox: '50' } } ] ], // 开启babel缓存 // 第二次构建时,会读取之前的缓存 cacheDirectory: true } } ] }, {
test: /\.(jpg|png|gif)/, loader: 'url-loader', options: {
limit: 8 * 1024, name: '[hash:10].[ext]', outputPath: 'imgs', esModule: false } }, {
test: /\.html$/, loader: 'html-loader' }, {
exclude: /\.(js|css|less|html|jpg|png|gif)/, loader: 'file-loader', options: {
outputPath: 'media' } } ] } ] }, plugins: [ new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css' }), new OptimizeCssAssetsWebpackPlugin(), new HtmlWebpackPlugin({
template: './src/index.html', minify: {
collapseWhitespace: true, removeComments: true } }), new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true, skipWaiting: true }) ], mode: 'production', devtool: 'source-map'};

externals

externals,防止将某一些包,打包到我们最终输出的bundle中。比如,jquery我们可以从外部引入,这样可以降低代码打包后的大小。

webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
entry: './src/js/index.js', output: {
filename: 'js/built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({
template: './src/index.html' }) ], mode: 'production', externals: {
// 拒绝jQuery被打包进来 jquery: 'jQuery' }};

index.js

import $ from 'jquery';console.log($);

index.html

  
webpack

hello html


dll

dll(动态连接库),类似于externals,可以指定一些库不需要打包,不同的是,dll会单独对某些库进行单独打包,将多个库打包成一个chunk。

像之前,node_modules会被打包成一个chunk文件,第三方库又有很多,如果全部打包成一个文件,文件体积太大了。通过dll技术,可以将这些库单独拆出来,打包成不同的chunk,这样更有利于性能优化。

在这里插入图片描述

webpack.dll.js

/*  使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包    当你运行 webpack 时,默认查找 webpack.config.js 配置文件    需求:需要运行 webpack.dll.js 文件      --> webpack --config webpack.dll.js*/const {
resolve } = require('path');const webpack = require('webpack');module.exports = {
entry: {
// 最终打包生成的[name] --> jquery // ['jquery'] --> 要打包的库是jquery jquery: ['jquery'], }, output: {
filename: '[name].js', path: resolve(__dirname, 'dll'), library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字 }, plugins: [ // 打包生成一个 manifest.json --> 提供和jquery映射 new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库的暴露的内容名称 path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径 }) ], mode: 'production'};

运行:

在这里插入图片描述
在这里插入图片描述
这样一来,以后要使用jquery就不需要再打包了,因为已经打包过了。4

再安装:

在这里插入图片描述
webpack.config.js

const {
resolve } = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const webpack = require('webpack');const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');module.exports = {
entry: './src/index.js', output: {
filename: 'built.js', path: resolve(__dirname, 'build') }, plugins: [ new HtmlWebpackPlugin({
template: './src/index.html' }), // 告诉webpack哪些库不参与打包,同时使用时的名称也得变~ new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json') }), // 将某个文件打包输出去,并在html中自动引入该资源 new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js') }) ], mode: 'production'};

index.js

import $ from 'jquery';console.log($);

index.html

  
webpack

hello html

打包运行。


转载地址:http://zlyki.baihongyu.com/

你可能感兴趣的文章
【转】腾讯十年运维老兵:运维团队的五个“杀手锏”
查看>>
Iterator_traits
查看>>
Zedboard中的SPI通信记录文档(已实现)
查看>>
Android 发布到google Play的app搜索不到问题的解决
查看>>
Flutter 网络请求之基于dio的简单封装
查看>>
Flutter UI基础 - 路由之Navigator详解
查看>>
Flutter UI基础 - Widgets 之 InkWell 和 Ink
查看>>
Spring - sentinel和hystrix比较
查看>>
Flutter 日期插件date_format 中文 国际化 及flutter_cupertino_date_picker
查看>>
Flutter 插件笔记 | 屏幕适配 flutter_screenutil
查看>>
Flutter UI基础 - 侧拉抽屉菜单
查看>>
Flutter UI基础 - AppBar中标题文字如何居中
查看>>
Flutter UI基础 - Drawer 抽屉视图与自定义header
查看>>
Flutter UI基础 - GridView
查看>>
Flutter UI基础 - 使用InkWell给任意Widget添加点击事件
查看>>
OC WKWebView的使用
查看>>
Flutter UI基础 - Image.asset 图片铺满布局
查看>>
Flutter UI基础 - Row、Column详解
查看>>
Flutter UI基础 - 添加背景图片
查看>>
Flutter UI基础 - 布局之Row/Column/Stack
查看>>