webpack 手摸手学习系列之性能优化的 HRM、source-map 和 oneOf

一、webpack 性能优化之 HRM
  1. 创建空文件夹,通过 npm init 命令初始化 package.json 文件,通过 npm install webpack webpack-cli -g 命令全局下载 webpackwebpack-cli,通过 npm install webpack webpack-cli -D 命令本地下载 webpackwebpack-cli,通过 npm i style-loader css-loader -D 命令下载 style-loadercss-loader,通过 npm i less less-loader -D 命令下载 lessless-loader,通过 npm i html-webpack-plugin -D 命令下载 html-webpack-plugin 插件,通过 npm i url-loader file-loader -D 命令下载 url-loaderfile-loader,通过 npm i html-loader -D 命令下载 html-loader

  2. 创建 src 文件夹,在里面创建 cssjsimgsmedia 文件夹,以及 index.htmlcss 文件夹中放 index.less 和字体图标资源样式,js 文件夹中放 index.jsprint.jsimgs 文件夹中放图片资源,media 文件夹放字体图标的相关资源,代码如下所示:

  • index.less

    #box {
        width: 200px;
        height: 200px;
        background-image: url('../imgs/angular.jpg');
        background-repeat: no-repeat;
        background-size: 100% 100%;
    }
    
  • index.js

    // 引入
    import print from './print.js';
    import '../css/iconfont.css';
    import '../css/index.less';
    
    
    console.log('index.js 文件被加载')
    
    print()
    
    function add(x, y) {
      return x + y;
    }
    
    console.log(add(1, 2));
    
    if (module.hot) {
      module.hot.accept('./print.js', function () {
        print();
      })
    }
    
  • print.js

    // 引入
    import print from './print.js';
    import '../css/iconfont.css';
    import '../css/index.less';
    
    
    console.log('index.js 文件被加载')
    
    print()
    
    function add(x, y) {
      return x + y;
    }
    
    console.log(add(1, 2));
    
    if (module.hot) {
      module.hot.accept('./print.js', function () {
        print();
      })
    }
    
  • index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>开发环境</title>
    </head>
    <body>
        <h1>开发环境配置</h1>
        <span class="iconfont icon-icon-test"></span>
        <span class="iconfont icon-icon-test2"></span>
        <span class="iconfont icon-icon-test3"></span>
        <span class="iconfont icon-icon-test1"></span>
        <div id="box"></div>
        <img src="./imgs/vue.jpg" alt="vue">
        <img src="./imgs/react.png" alt="react">
    </body>
    </html>
    
  1. 创建 webpack.config.js 文件,通过 require 引入 pathhtml-webpack-pluginentry 是入口文件,output 是出口文件,filename 是打包输出后的文件名,path 是输出路径。moduleloader 的配置,rules 是详细的 loader 配置。第一个规则是处理 less 资源,匹配以 .less 结尾的文件,使用的 loaderstyle-loadercss-loaderless-loader。第二个规则是处理 css 资源, 匹配以 .css 结尾的文件,使用的 loaderstyle-loadercss-loader。第三个规则是处理图片资源,匹配以 jpgpnggif 结尾的文件图片资源,使用的 loaderurl-loader,进行 options 配置,limit 是限制图片的大小小于8kb,就会被base64处理,给图片做优化,name 给图片做重命名,取 hash 值的前 10 位,取文件的原来扩展名,关闭 es6 模块化,输出路径为 imgs。第四个规则是 处理 html 中的 img 资源,匹配以 .html 结尾的文件,使用的 loaderhtml-loader。由于处理图片资源采用的是 ES6 module 解析,而 html 文件中的 img 资源使用 commonJS 解析,会造成冲突,需要关闭 ES6 module。第五个规则是处理其它资源,使用 exclude 不包括上面的所有资源,html|css|js|less|jpg|png|gif,使用 file-loader,通过 options 配置 name 的值为取 hash 值的前 10 位,取文件的原来扩展名,通过 outputPath 设置输出路径为 mediaplugins 里面是一些插件配置,通过 new HtmlWebpackPlugin(),复制里面的文件,并自动引入打包输出的所有资源(JS/CSS),设置 mode 模式为 development 开发模式。在 devServer 中,contentBase 是项目构建后路径,compress 是启动 gzip 压缩,port 是端口号,open 是自动打开浏览器,hot 是开启 HRM 功能,代码如下所示:
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: [
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    hot: true
  }
};

  1. HMRhot module replacement,热模块替换,也可以说是模块热替换。作用是一个模块发生变化,只会重新打包这一个模块,而不是打包所有模块,极大提升构建速度,HRM 在样式文件、JS 文件和 HTML 文件的不同应用:
  • 样式文件:可以使用 HMR 功能,因为 style-loader 内部实现了
  • JS 文件:默认不能使用 HMR 功能,需要修改 js 代码,添加支持 HMR 功能的代码。注意的是,HMR 功能对 js 的处理,只能处理非入口 js 文件的其他文件,添加代码, 全局寻找 module 这个变量,有没有 hot 这个属性,一旦 module.hottrue,说明开启了 HMR 功能,方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建,会执行后面的回调函数,如下所示:
if (module.hot) {
  module.hot.accept('./print.js', function () {
    print();
  })
}
  • HTML 文件:默认不能使用 HMR 功能.同时会导致问题,html 文件不能热更新了,不用做 HMR 功能。如果需要做,修改 entry 入口,将 html文件引入,代码如下所示:
entry: ['./src/js/index.js', './src/index.html']
  1. 在命令行输入 npx webpack-dev-server 命令,项目就会自动打包编译。每次在更改完代码后,自动编译看到最新的显示内容。在 build 打包文件中,也可以看到打包后的 cssjsimgsmedia 文件夹,以及 index.html
二、webpack 性能优化之 source-map
  1. 在上面 HRM 热模块替换的项目中,修改 webpack.config.js 文件,添加 devtool 来指定不同的 source-map,代码如下所示:

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: [
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  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'
};

  1. source-map 是一种 提供源代码到构建后代码映射 技术,如果构建后代码出错了,通过映射可以追踪源代码错误,不同 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. 内联和外部的区别:
  • 外部生成了文件,内联没有
  • 内联构建速度更快
  1. 开发环境需要速度快,调试更友好。在速度快中,eval>inline>cheap>...,而 eval-source-map 优于 eval-cheap-souce-map。在调试友好中,cheap-souce-map 优于 cheap-module-souce-map。所以,在开发环境中,最快调试,调试更加友好,可以选择 eval-source-map 或者是 eval-cheap-module-souce-map

  2. 在生产环境中,需要考虑源代码的隐藏和调试的友好。内联会让代码体积变大,所以在生产环境不用内联。nosources-source-map 是全部隐藏, hidden-source-map 是只隐藏源代码,会提示构建后代码错误信息。所以,在生成环境中,可以选择 source-map 或者是 cheap-module-souce-map

  3. 在命令行输入 npx webpack-dev-server 命令,项目就会自动打包编译。每次在更改完代码后,自动编译看到最新的显示内容。在 build 打包文件中,也可以看到打包后的 cssjsimgsmedia 文件夹,以及 index.html

三、webpack 性能优化之 oneOf
  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 文件夹,在里面创建 index.jsindex.htmla.cssb.css 文件,代码如下所示:

  • index.js
    import '../css/a.css';
    import '../css/b.css';
    
    function add(x, y) {
      return x + y;
    }
    
    console.log(add(2, 5));
    
    
  • 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 webpack</h1>
        <div id="box1"></div>
        <div id="box2"></div>
    </body>
    </html>
    
  • a.css
    #box1 {
        width: 100px;
        height: 100px;
        background-color: pink;
    }
    
  • b.css
    #box2 {
        width: 200px;
        height: 200px;
        background-color: deeppink;
    }
    
  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 这个预设。通过 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: 'built.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'
                                    }
                                }
                            ]
                        }
                    },
                    {
                        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.css'
        }),
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './src/index.html',
            minify: {
                collapseWhitespace: true,
                removeComments: true
            }
        })
    ],
    mode: 'production'
}
  1. package.json 这个文件中,进行配置 browserslist,在 css 兼容处理的时候,需要帮 postcss 找到 package.jsonbrowserslist 里面的配置,通过配置加载指定的css兼容性样式,development 为开发环境配置,last 1 chrome version 表示兼容最近的 chrome 版本,last 1 firefox version 表示兼容最近的 firefox 版本,last 1 safari version 表示兼容最近的 safari 版本。production 为生产环境配置,>0.2% 表示大于 99.8% 的浏览器,not dead 表示不要已经死的浏览器,not op_mini all 表示不要使用 op_mini all 浏览器。 eslint 并不知道要检查什么,所有就有 airbnb 风格指南,airbnb 需要使用 eslint-config-airbnb-base 这个库,这个库依赖于 eslint-plugin-importeslint。所有需要在 package.jsoneslintConfig 中设置检查规则,设置 extendsairbnb-base,代码如下所示:
"browserslist": {
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]
  },
  "eslintConfig": {
    "extends": "airbnb-base"
  }
  1. 在命令行输入 webpack 命令,资源就会进行打包,以及相应的压缩代码。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值