webpack 手摸手学习系列之性能优化的缓存、tree-shaking 、code-split 和 预加载以及懒加载

一、webpack 性能优化之缓存
  1. 创建空文件夹,通过 npm init 命令初始化 package.json 文件,通过 npm install webpack webpack-cli -g 命令全局下载 webpackwebpack-cli,通过 npm install webpack webpack-cli -D 命令本地下载 webpackwebpack-cli,通过 npm i html-webpack-plugin mini-css-extract-plugin optimize-css-assets-webpack-plugin -D 命令下载 html-webpack-pluginmini-css-extract-pluginoptimize-css-assets-webpack-plugin,通过 npm i eslint-loader eslint -D 命令下载 eslint-loadereslint,通过 npm i eslint-config-airbnb-base eslint-plugin-import eslint -D 命令下载 eslint-config-airbnb-baseeslint-plugin-importeslinteslint-config-airbnb-base 依赖于 eslint-plugin-importeslint,通过 npm i postcss-loeader postcss-preset-env -D 命令下载 postcss-loeaderpostcss-preset-env,通过 npm i css-loader less-loader -D 命令下载 css-loaderless-loader,通过 npm i url-loader file-loader html-loader -D 命令下载 url-loaderfile-loaderhtml-loader,通过 npm i babel-loader @babel/core @babel/preset-env -D 命令下载 babel-loader@babel/core@babel/preset-env,通过 npm i @babel/polyfill core-js -D 命令下载 @babel/polyfillcore-js

  2. 在里面创建 src 文件夹,里面创建 cssjs 文件夹,index.htmlserver.js。在 css 中创建 index.css,在 js 中创建 index.js,代码如下所示:

  • index.css
    html, body {
      margin: 0;
      padding: 0;
      height: 100%;
      background-color: deeppink;
    }
    
  • index.js
    import '../css/index.css';
    
    function sum(...args) {
      return args.reduce((p, c) => p + c, 0);
    }
    
    // eslint-disable-next-line
    console.log(sum(1, 2, 3, 4));
    
    
  • index.html
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>webpack</title>
    </head>
    
    <body>
      <h1>hello cache</h1>
    </body>
    
    </html>
    
  • server.js
    
    const express = require('express');
    
    const app = express();
    
    app.use(express.static('build', { maxAge: 1000 * 3600 }));
    
    app.listen(3000);
    
    
  1. 创建 webpack.config.js 文件,通过 require 引入 pathhtml-webpack-pluginmini-css-extract-pluginoptimize-css-assets-webpack-plugin。通过 process.env.NODE_ENV 定义 nodejs 环境变量:决定使用 browserslist 的哪个环境。定义一个复用 loader,在下面匹配 .css.less 文件的时候,需要重复使用。通过 MiniCssExtractPlugin.loader 提取 css 单独文件,使用 css-loader,使用 postcss-loadercss 的兼容性处理,同时使用 postcss-preset-envcss 的按需加载的兼容性处理,optionspostcss-loader 的配置项,ident 是固定写法,plugins 是使用 postcss-preset-env 这个插件。entry 是入口文件,output 是出口文件,filename 是输出的文件名,path 是文件的输出路径。moduleloader 的配置,rules 是详细的 loader 配置。在 rules 中使用 oneOf,以下的 loader 只会匹配一个,不能有两个配置处理同一种类型文件,如果有需要提取出来。第一个规则是匹配以 .css 结尾的文件,通过 use 使用 commonCssLoader 这个抽出复用的 loader。第二个规则是匹配以 .less 结尾的文件,通过 use 使用 commonCssLoader 这个抽出复用的 loaderless-loader。第三个规则是匹配以 .js 结尾的文件,通过 exclude 不包括 node_modules,通过 enforce 优先执行,通过 loader 使用 eslint-loader,通过 options 设置自动修复 eslint 的错误。第四个规则是匹配以 .js 结尾的文件,通过 exclude 不包括 node_modules,通过 loader 使用 babel-loader,通过 options 进行配置,presets 为预设,指示 babel 做怎么样的兼容性处理,使用 babel/preset-env 这个预设,cacheDirectory 是开启 babel 缓存,第二次构建时,会读取之前的缓存。通过 useBuiltIns 按需加载,通过 corejs 指定 core-js 版本,通过 targets 指定兼容性做到哪个版本浏览器。js 兼容性处理需要使用到 babel-loader@babel/core@babel/preset-env。如果是基本 js 兼容性处理,需要使用 @babel/preset-env,但是只能转换基本语法,如promise高级语法不能转换。如果使用全部 js 兼容性处理,需要使用 @babel/polyfill ,但是只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了,所有就需要做兼容性处理的就做做兼容性处理的就做,使用 core-js。第五个规则是匹配以 .jpgpnggif 格式的图片资源,通过 loader 使用 url-loaderurl-loader 依赖于 file-loader。进行 options 配置,设置 limit8 * 1024,图片大小小于 8kb,就会被 base64 处理,给图片做优化。这样做的优点是减少请求数量(减轻服务器压力),缺点是图片体积会更大(文件请求速度更慢)。设置 esModulefalse,由于 url-loader 默认使用 es6 模块化解析,而 html-loader 引入图片是 commonjs ,解析时会出问题为 [object Module],所以关闭 url-loaderes6 模块化,使用commonjs 解析。设置 name[hash:10].[ext],给图片进行重命名,因为图片值为 hash[hash:10] 取图片的 hash 的前 10 位,[ext]取文件原来扩展名。第三个规则是匹配以 .html 结尾的文件,使用 loaderhtml-loader,处理 html 文件的 img 图片(负责引入 img,从而能被 url-loader 进行处理)。第六个规则是匹配以 .html 结尾的文件,通过 loader 使用 html-loader。第七个规则是匹配其它资源,通过 loader 使用 file-loader,通过 options 进行输出路径配置,输出文件夹为 mediaplugins 里面是一些插件配置,通过 MiniCssExtractPlugin 插件提取 css 以及对输出的 css 文件进行重命名,通过 OptimizeCssAssetsWebpackPlugincss 文件进行压缩,通过 HtmlWebpackPlugin 复制里面的文件,并自动引入打包输出的所有资源(JS/CSS),进行 minify 配置,通过 collapseWhitespace 移除空格,通过 removeComments 移除注释,压缩 html 文件。设置 modeproduction,在生产环境下 webpack 会自动压缩 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');


process.env.NODE_ENV = 'production'

const commonCssLoader = [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
        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:[
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                loader: 'eslint-loader',
                options: {
                    fix: true
                }
            },
            {
                oneOf: [
                    {
                        test: /\.css$/,
                        use: [...commonCssLoader]
                    },
                    {
                        test: /\.less$/,   
                        use: [...commonCssLoader, 'less-loader']
                    },
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                                {
                                    useBuiltIns: 'usage',
                                    corejs: {
                                        version: 3
                                    },
                                    targets: {
                                        chrome: '60',
                                        firefox: '60',
                                        ie: '9',
                                        safari: '10',
                                        edge: '17'
                                    }
                                }
                            ],
                            cacheDirectory: true
                        }
                    },
                    {
                        test: /\.(jpg|png|gif)/,
                        loader: 'url-loader',
                        options: {
                            limit: 8 * 1024,
                            name: '[hash:].[ext]',
                            outputPath: 'imgs',
                            esModule: false
                        }
                    },
                    {
                        test: /\.html$/,
                        loader: 'html-loader'
                    },
                    {
                        test: /\.(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'
}
  1. 缓存是分为两种,babel 缓存和文件资源缓存。 babel 缓存是需要在 options 中设置 cacheDirectorytrue,让第二次打包构建速度更快。文件资源缓存是让代码上线运行缓存更好使用,通过对输出的文件进行设置不同的 hash 值。

  2. 文件资源缓存的分类,如下所示:

类型类型描述类型问题
hash每次wepack构建时会生成一个唯一的hash值因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效,但是却只改动一个文件
chunkhash根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk
contenthash根据文件的内容生成hash值。不同文件hash值一定不一样效果最好
  1. 在命令行输入 webpack 命令,资源就会进行打包,以及相应的压缩代码,同时通过 node server.js 命令启动服务器服务,观察缓存的变化。
二、webpack 性能优化之 tree-shaking
  1. 在上面缓存的项目中,修改 js 文件夹中的 index.js,同时新增 test.js,删除 server.js 文件,代码如下所示:
  • index.js
    import '../css/index.css';
    import {  mul } from './test.js'
    
    function sum(...args) {
      return args.reduce((p, c) => p + c, 0);
    }
    
    // eslint-disable-next-line
    console.log(mul(2, 3));
    // eslint-disable-next-line
    console.log(sum(1, 2, 3, 4));
    
    
  • test.js
        
    export function mul(x, y) {
        return x * y;
    }
    
    export function count(x, y) {
        return x - y;
    }
    
    
  1. tree shaking 的本质是去除无用代码,摇树优化。无用代码消除在广泛存在于传统的编程语言编译器中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为 DCEdead code elimination), tree shakingDCE 的一种新的实现。tree shaking 的前提是必须使用 ES6 模块化和开启 production 环境,作用是减少代码体积。在 package.json 中配置,通过 "sideEffects": false 是所有代码都没有副作用,都可以进行 tree shaking。但是问题是可能会把 css / @babel/polyfill 这些副作用文件干掉,这些还是有需要的。所有,可以通过 "sideEffects": ["*.css", "*.less"]tree shaking 就不会对这些文件进行处理,没有副作用的代码,代码如下所示:
    "sideEffects": [
        "*.css",
        "*.less"
     ]
  1. 在命令行输入 webpack 命令,资源就会进行打包,以及相应的压缩代码和去除无用的代码。
三、webpack 性能优化之 code-split
  1. 创建空文件夹,通过 npm init 命令初始化 package.json 文件,通过 npm install webpack webpack-cli -g 命令全局下载 webpackwebpack-cli,通过 npm install webpack webpack-cli -D 命令本地下载 webpackwebpack-cli,通过 npm i html-webpack-plugin -D 命令下载 html-webpack-plugin

  2. 在里面创建 js 文件夹,里面创建 index.jstest.js,然后创建 index.html,代码如下所示:

  • index.js
    function sum(...args) {
      return args.reduce((p, c) => p + c, 0);
    }
    
    // eslint-disable-next-line
    console.log(sum(1, 2, 3, 4));
    
    
  • test.js
        
    export function mul(x, y) {
      return x * y;
    }
    
    export function count(x, y) {
      return x - y;
    }
    
    
  • index.html
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>webpack</title>
    </head>
    
    <body>
      <h1>hello cache</h1>
    </body>
    
    </html>
    
  1. 创建 webpack.config.js 文件,通过 require 引入 pathhtml-webpack-pluginentry 是入口文件,入口文件分为单入口文件和多入口文件。如果是单入口,输出只有一个 bundle,单页面。如果是多入口,多个 bundle,多页面。output 是出口文件,filename 是输出的文件名,path 是文件的输出路径。[name] 是取文件名,[contenthash:10] 是取内容的 hash 值前 10 位。plugins 里面是一些插件配置,通过 HtmlWebpackPlugin 复制里面的文件,并自动引入打包输出的所有资源(JS/CSS),进行 minify 配置,通过 collapseWhitespace 移除空格,通过 removeComments 移除注释,压缩 html 文件,设置production,在生产环境下 webpack 会自动压缩 js 代码,代码如下所示:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        main: './js/index.js',
        test: './js/test.js'
    },
    output: {
        filename: 'js/[name].[contenthash:10].js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        })
    ],
    mode: 'production'
}
  1. 第二种方式,修改 index.jstest.js,代码如下所示:
  • index.js
    import $ from 'jquery';
    
    function sum(...args) {
      return args.reduce((p, c) => p + c, 0);
    }
    
    // eslint-disable-next-line
    console.log(sum(1, 2, 3, 4));
    // eslint-disable-next-line
    console.log($);
    
    
  • test.js
    import $ from 'jquery';
    
    // eslint-disable-next-line
    console.log($);
    
    export function mul(x, y) {
      return x * y;
    }
    
    export function count(x, y) {
      return x - y;
    }
    
    
  1. 修改 webpack.config.js 文件,添加 optimization,在 splitChunks 中设置 chunksalloptimization 可以将 node_modules 中代码单独打包一个 chunk 最终输出,自动分析多入口 chunk 中,有没有公共的文件,如果有会打包成单独一个 chunk,代码如下所示:
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: './index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        })
    ],
    optimization: {
        splitChunks: {
            chunks: 'all'
        }
    },
    mode: 'production'
}
  1. 第三种方式,修改 index.jstest.js,代码如下所示:
  • index.js
        
    function sum(...args) {
      return args.reduce((p, c) => p + c, 0);
    }
    
    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-line
    console.log(sum(1, 2, 3, 4));
    
    
  • test.js
        
    // eslint-disable-next-line
    console.log($);
    
    export function mul(x, y) {
      return x * y;
    }
    
    export function count(x, y) {
      return x - y;
    }
    
    
  1. 通过 js代码,让某个文件被单独打包成一个chunkimport 动态导入语法:能将某个文件单独打包,核心代码如下:
import(/* webpackChunkName: 'test' */'./test')
  .then(({ mul, count }) => {
    // eslint-disable-next-line
    console.log(mul(2, 5));
  })
  .catch(() => {
    // eslint-disable-next-line
    console.log('文件加载失败~');
  });

  1. 在命令行输入 webpack 命令,资源就会进行打包,以及相应的压缩代码,以及代码分割。
四、webpack 性能优化之预加载和懒加载
  1. 在上面的 code-split 的项目中,修改 index.jstest.js 文件,代码如下所示:
  • index.js
    console.log('index.js文件被加载了~');
    
    document.getElementById('btn').onclick = function() {
      import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
        console.log(mul(4, 5));
      });
    };
    
    
  • test.js
    console.log('test.js文件被加载了~');
    
    // eslint-disable-next-line
    console.log($);
    
    export function mul(x, y) {
      return x * y;
    }
    
    export function count(x, y) {
      return x - y;
    }
    
    
  1. webpack.config.js 文件不变,懒加载是当文件需要使用时才加载,预加载是会在使用之前,提前加载 js 文件,正常加载是并行加载,同一时间加载多个文件。需要注意的是,预加载 prefetch,等其他资源加载完毕,浏览器空闲了,再偷偷加载资源。webpackChunkName 是懒加载,webpackPrefetch 是预加载,核心代码如下:
    document.getElementById('btn').onclick = function() {
      import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
        console.log(mul(4, 5));
      });
    };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值