webpack 手摸手学习系列之 PWA、多进程打包、externals 和 dll

一、webpack 性能优化之 PWA
  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,通过 npm i workbox-webpack-plugin -D 命令下载 workbox-webpack-plugin

  2. 在里面创建 src 文件夹,里面创建 cssjs 文件夹,index.html。在 css 中创建 index.css,在 js 中创建 index.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));
    
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', () => {
        navigator.serviceWorker
          .register('/service-worker.js')
          .then(() => {
            console.log('sw注册成功了~');
          })
          .catch(() => {
            console.log('sw注册失败了~');
          });
      });
    }
    
    
  • test.js
    
    export function mul(x, y) {
        return x * y;
    }
    
    export function count(x, y) {
        return x - y;
    }
    
    
  • index.css
    html, body {
      margin: 0;
      padding: 0;
      height: 100%;
      background-color: deeppink;
    }
    
  • 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-pluginmini-css-extract-pluginoptimize-css-assets-webpack-pluginworkbox-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 文件。通过 WorkboxWebpackPlugin 插件的 GenerateSW 生成一个 serviceworker 配置文件,通过设置 clientsClaimtrue,帮助 serviceworker快速启动,通过设置 skipWaitingtrue,删除旧的 serviceworker。设置 modeproduction,在生产环境下 webpack 会自动压缩 js 代码,设置 devtoolsource-map,提供源代码到构建后代码映射技术,代码如下所示:
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');

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
            }
        }),
        new WorkboxWebpackPlugin.GenerateSW({
            clientsClaim: true,
            skipWaiting: true
        })
    ],
    mode: 'production',
    devtool: 'source-map'
}
  1. PWA 是渐进式网络开发应用程序,离线可访问,需要用到 workbox-webpack-plugin 这个插件。通过 WorkboxWebpackPlugin.GenerateSW 生成配置文件,在 js 中注册serviceWorker,处理兼容性问题。这样当在有网的情况下就会生成 serviceWorker 配置文件,当在没网的情况下,就会访问这个 serviceWorker,实现离线访问。代码必须运行在服务器上,为了解决 eslint 不认识 windownavigator全局变量,需要修改 package.jsoneslintConfig 配置,代码如下所示:
"eslintConfig": {
    "extends": "airbnb-base",
    "env": {
      "browser": true
    }
  }
  1. 在命令行输入 webpack 命令,资源就会进行打包,以及离线实现离线访问的功能。
二、webpack 性能优化之多进程打包
  1. 在上面的 PWA 项目的基础上,进行修改。通过 npm i thread-loader -D 命令下载 thread-loader。在需要使用到多进程打包的地方,进行使用thread-loader,在 babel-loader 中使用多进程打包,通过 options 设置 workers,进程个数,代码如下所示:
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
        {
            loader: 'thread-loader',
            options: {
                workers: 2 
            }
        },
        {
            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
        }
        }
    ],
    
}
  1. 开启多进程打包需要使用thread-loader,程启动大概为600ms,进程通信也有开销,只有工作消耗时间比较长,才需要多进程打包。
三、webpack 性能优化之 externals
  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. 在项目文件中,创建 src 文件夹,里面创建 index.jsindex.html 文件,代码如下所示:

  • index.js
    import $ from 'jquery';
    
    console.log($);
    
    
  • index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=q, initial-scale=1.0">
        <title>webpack</title>
    </head>
    <body>
        <h1 id="title">hello webpack</h1>
        <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    </body>
    </html>
    
  1. 在主目录,创建 webpack.config.js 文件,通过 require 引入 pathhtml-webpack-pluginentry 是入口文件,output 是出口文件,filename 是打包输出后的文件名,path 是输出路径。moduleloader 的配置,rules 是详细的 loader 配置。plugins 里面是 plugins 的配置。html-webpack-plugin 是默认会创建一个空的 HTML,自动引入打包输出的所有资源(JS/CSS)。如果只使用 HtmlWebpackPlugin(),那么就会在 build 中自动生成一个 index.html,是无结构的 html文件。如果需要有结构的 HTML文件,那么就需要配置 HtmlWebpackPlugintemplate,复制里面的文件,并自动引入打包输出的所有资源(JS/CSS)。mode 是设置模式,production 是开发模式。通过 externals 忽略库名和 npm 包名等,比如拒绝 jQuery被打包进来,代码如下所示:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        })
    ],
    mode: 'production',
    externals: {
        jquery: 'jQuery'
    }
}
  1. externals 可以忽略打包的包,比如在构建项目的时候,将第三方的包进行移除打包,可以极大的提示打包的构建速度。externals 是更加适用于 CDN 打包。

  2. 在命令行输入 webpack 命令, html 资源就会进行打包,同时在 build 文件夹中,多出一个 built.js 的打包输出文件。

四、webpack 性能优化之 dll
  1. 在上面的 externals 项目的基础上,进行修改。新建 webpack.dll.js 文件,使用 dll 技术,对某些库(第三方库:jquery、react、vue...)进行单独打包。引入 pathwebpackentry 是入口文件,要打包的库是jqueryoutput 是出口文件,filename 是输出的文件名,path 是输出路径,library 是打包的库里面向外暴露出去的内容叫什么名字。通过 entryoutput,将 jQuery 打包进来并且输出出去。在 plugins 中放的是插件,通过 webpack.DllPlugin 包生成一个 manifest.json,提供和 jquery 映射,name 是映射库的暴露的内容名称,path 是输出文件路径。通过 plugins 生成 manifest.json, 建立与 jquery 之间的映射关系。设置 modeproduction,代码如下所示:

const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
    entry: {
        jquery: ['jquery']
    },
    output: {
        filename: '[name].js',
        path: resolve(__dirname, 'dll'),
        library: '[name]_[hash]'
    },
    plugins: [
        new webpack.DllPlugin({
            name: '[name]_[hash]',
            path: resolve(__dirname, 'dll/manifest.json')
        })
    ],
    mode: 'production'
}
  1. 修改 webpack.config.js 文件,通过 npm i add-asset-html-webpack-plugin -D 命令下载 add-asset-html-webpack-plugin,并且通过 require 引入 到里面。在 plugins 中,通过 webpack.DllReferencePlugin 告诉 webpack 哪些库不参与打包,同时使用时的名称也得变, 通过 AddAssetHtmlWebpackPlugin 将某个文件打包输出去,并在 html 中自动引入该资源,代码如下所示:

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'
        }),
        new webpack.DllReferencePlugin({
            manifest: resolve(__dirname, 'dll/manifest.json')
        }),
        new AddAssetHtmlWebpackPlugin({
            filepath: resolve(__dirname, 'dll/jquery.js')
        })
    ],
    mode: 'production'
}
  1. 在命令行输入 webpack 命令, html 资源就会进行打包,同时会多出 dll 文件目录,里面有 jquery.jsmanifest.json 文件。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值