高级篇:webpack应用指南一

webpack应用指南一(高级篇)

  • 介绍
  • 提升代码体验
    • SourceMap*
  • 提升打包构建速度
    • HRM(热模块替换)【开发环境】
    • oneOf*
    • Include/Exclude*
    • cache*
    • 多进程打包*
  • 减少代码体积
    • TreeShaking*

根据尚硅谷视频教程及webpack5官网部分内容整理。
尚硅谷Web前端之Webpack5教程文档
Webpack中文官网

介绍

高级篇主要是进行webpack优化,使代码在编译或者运行时性能更加优异,主要从以下角度进行优化。

  • 提升开发体验
  • 提升打包构建速度
  • 减少代码体积
  • 优化代码运行性能

提升代码体验

SourceMap*

SourceMap(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案。
它会生成一个xxx.map文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过xxx.map文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到错误根源。
实际开发时我们只需要关注两种情况即可。

  • 开发模式:cheap-module-source-map
  1. 优点:打包编译速度快,只包含行映射
  2. 缺点:没有列映射
    配置方法,配置属性devtool: "cheap-module-source-map"
// webpack.dev.js
module.exports = {
  // 其他省略
  mode: "development",
  devtool: "cheap-module-source-map",
};
  • 生产模式:source-map
  1. 优点:包含行/列映射
  2. 缺点:打包编译速度更慢
    配置方法,配置属性devtool: "source-map"
// webpack.prod.js
module.exports = {
  // 其他省略
  mode: "production",
  devtool: "source-map",
};

提升打包构建速度

HRM(热模块替换)【开发环境】

开发环境下如果修改了其中一个模块代码,Webpack默认会将所有模块全部重新打包编译,如果文件过多,会导致速度很慢。所以需要做到修改某个模块代码,仅需重新打包编译该模块,其他模块不变,提升打包速度。
HotModuleReplacementHMR/热模块替换):在程序运行中,替换、添加或删除模块,而无需重新加载整个页面。
配置方式如下,设置devServer中的hot属性为true,即可。(默认条件下该属性时开启状态)

module.exports = {
  // 其他省略
  devServer: {
    host: "localhost", // 服务器域名
    port: "3000", // 端口号
    open: true, // 是否自动打开浏览器
    hot: true, // 开启HMR功能(只能用于开发环境)
  },
};

但仅开启hot属性对js文件是无效的,修改js文件依旧是会重新打包整个文件,所以需要对js文件做手动判断,使用module.hot.accept('文件路径'[,callback])方法,callback方法可省略,修改js文件会触发该方法。

// main.js
import count from "./js/count";
import sum from './js/sum';
// 需要引入才能被打包
import './css/index.css';
import './less/index.less'

console.log(count(1,2));
console.log(sum(1,2,3,4,5));

// 判断是否开启热模块替换
if(module.hot){
  // 有回调函数
  module.hot.accept('./js/count.js',function(){
    const result1 = count(2, 1);
    console.log(result1);
  });
  // 无回调函数
  module.hot.accept('./js/sum.js');
}

实际开发中,各前端框架的loader基本都已封装该方式,无需手动配置,比如vue-loaderreact-hot-loader等。

oneOf*

打包时每个文件都会经过所有loader处理,虽然因为test正则原因实际没有处理上,但是都要过一遍。比较慢。使用oneOf属性可以实现只要匹配上一个loader, 剩余的就不再匹配。配置方法如下:

...

module.exports = {
  ...,
  // 模块
  module: {
    rules: [
      {
        oneOf: [
          {
            test:/\.css$/i,
            use: ['style-loader', 'css-loader']
          },
          ...
        ]
      }
    ]
  },
  // 插件
  plugins:[...],
  // 开发服务器不会输出文件,只存在内存中
  devServer: {...},
  // 模式
  mode: 'development',
  devtool: 'cheap-module-source-map'
};

Include/Exclude*

开发项目时需要使用第三方的库或插件,所有文件都下载到node_modules中了。而这些文件是不需要编译可以直接使用的。所以在对js文件处理时,要排除node_modules下面的文件。
loader或者plugins中可以使用include或者exclude对文件进行筛选,顾名思义include表示仅处理的文件,exclude表示需要处理的文件,两者是互斥的,即同一个loader或者plugins中只能使用其中的一个属性。
如上述babel-loader中使用的exclude。一般plugins插件中会默认开启这个功能,排除node_module中的文件。对于css-loader一般是自定义的文件,即使引用了第三方的样式包,同样也需要引入开发文件中所以一般不需要设置include或者exclude属性。

cache*

每次打包时js文件都要经过Eslint检查 和Babel编译,速度比较慢。所以缓存之前的Eslint检查 和Babel编译结果,在第二次或者多次打包时速度就会更快(第一次打包除外,第一次不会有缓存)。
对对Eslint检查 和Babel编译结果进行缓存的配置方法如下:

// webpack.dev.js/webpack.prod.js
...

function getCommonStyleSets(pre) {...}

module.exports = {
  ...,
  // 模块
  module: {
    rules: [
      {
        oneOf: [
          ...,
          {
            test: /\.m?js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                cacheDirectory: true, // 开启babel编译缓存
                cacheCompression: false, // 缓存文件不要压缩
              },
            }
          }
        ]
      }
    ]
  },
  // 插件
  plugins:[
  	new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules", // 默认值
      cache: true, // 开启缓存
      // 缓存目录及文件名称
      cacheLocation: path.resolve(
        __dirname,
        "../node_modules/.cache/.eslintcache"
      ),
    }),
    ...
  ],
  // 模式
  mode: 'production',
  devtool: 'source-map'
};

多进程打包*

当项目越来越庞大时,打包速度越来越慢,要继续提升打包速度,本质就是要提升js的打包速度(其他文件相对都比较少)。而对js文件处理主要就是eslintbabelTerser(生产模式下自动激活,无需配置该工具) 三个工具,所以需要提升它们的运行速度。可以开启多进程同时处理js文件,更快提升打包速度。
多进程打包就是开启电脑的多个进程同时干一件事,速度更快。
由于每个电脑的cpu核数都不一样,所以首先需要获取cpu的核数。使用nodejsos模块获取,然后借助thread-loader进行处理,使用方法如下。

# 安装thread-loader
npm i thread-loader -D

修改webpack配置文件:

// webpack.dev.js/webpack.prod.js
const os = require('os');
...
// terser插件
const TerserPlugin = require("terser-webpack-plugin");

const threads = os.cpus().length-1;

function getCommonStyleSets(pre) {...}

module.exports = {
  ...,
  // 模块
  module: {
    rules: [
      {
        oneOf: [
          ...,
          {
            ...,
            use: [
              {
                loader: "thread-loader", // 开启多进程
                options: {
                  workers: threads, // 数量
                },
              },
              {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: true, // 开启babel编译缓存
                  cacheCompression: false, // 缓存文件不要压缩
                }
              },
            ]
          }
        ]
      }
    ]
  },
  // 插件
  plugins:[
    ...,
    // 加压缩文件可以统一放置到optimization中
    // new CssMinimizerPlugin(),
    // new TerserPlugin({
    //   parallel: threads // 开启多进程
    // }),
  ],
  optimization: {
  	// 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer定义的插件压缩 bundle。
    minimize: true,
    // 允许通过提供一个或多个定制过的 TerserPlugin 实例,覆盖默认压缩工具(minimizer)。
    minimizer: [
      // css压缩也可以写到optimization.minimizer里面,效果一样的
      new CssMinimizerPlugin(),
      // 当生产模式会默认开启TerserPlugin,但需要进行其他配置,就要重新写
      new TerserPlugin({
        parallel: threads // 开启多进程
      })
    ],
  },
  // 模式
  mode: 'production',
  devtool: 'source-map'
};

注意:每个worker都是一个独立的node.js进程,其开销大约为600ms左右。同时会限制跨进程的数据交换。请仅在耗时的操作中使用此loader

减少代码体积

TreeShaking*

开发时如果定义了一些工具函数库,或者引用第三方工具函数库或组件库。如果没有特殊处理,打包时会引入整个库,但是实际上可能只用上极小部分的功能。这样将整个库都打包进来,打包的文件体积就变大了。
Tree Shaking是一个术语,通常用于描述移除JavaScript中的没有使用上的代码,它依赖ES Module(对commonJS无效)。
Webpack已经默认开启了这个功能,无需其他配置。该属性的名称为:usedExports。可以在optimization属性中开启或关闭。

...

module.exports = {
 ...,
 mode: 'production',
 optimization: {
 	// 默认开启,可省略
   usedExports: true,
 },
};

举例,在我们的项目中添加一个新的通用模块文件src/math.js,并导出两个函数,但在main.js中仅引用了其中一个:

// src/js/math.js
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}

// main中应用math中的cube方法
...
import { cube } from './js/math';
...

...
console.log(cube(3));
...

打包的文件中不会包含square方法。
在一个纯粹的ESM模块世界中,很容易识别出哪些文件有副作用。然而,我们的项目无法达到这种纯度,所以,此时有必要提示 webpack编译器哪些代码是“纯粹部分”。通过package.json的 “sideEffects” 属性,来实现这种方式。
side effect(副作用)” 的定义是,在导入时会执行特殊行为的代码,而不是仅仅暴露一个export或多个export。举例说明,例如 polyfill,它影响全局作用域,并且通常不提供export。具体配置方式请参考webpack官网。

你可能感兴趣的:(笔记,webpack,前端,javascript)