webpack:进阶用法(二)

一、在webpack中使用ESLint

在这里插入图片描述
ESLint如何落地?

  1. 和CI/CD系统集成
  2. 和webpack集成

webpack与ESLint的集成
使用eslint-loader,构建时检查JS规范。
下面我们操作一下,采用 eslint-config-airbnb,这个eslint-config-airbnb有很多依赖

第一步,安装依赖

npm install eslint eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y -D
npm install eslint-loader -D

第二步,在webpack.prod.js中将该loader加入

在这里插入图片描述

第三步:增加一个配置文件 .eslintrc.js

注:关于这个配置文件,可以是.eslintrc.json,可以是.eslintrc.js,可以是.eslintrc
这里我们采用.eslintrc.js,既然是js文件,我们就要采用module.exports的方式。

  • parser配置项是我们要用的解析器,我们使用babel-eslint,所以要安装babel-eslint
  • extends配置项用于继承,如果要继承多个,要用数组,我们继承了airbnb,所以按照eslint-config-airbnb
  • rules配置用于修改eslint规则,自定义,比如你觉得官方哪些规则不满意,你可以在这里改,可不要。
  • env配置指定启用的环境,比如设置node为true,你使用node的一些全局变量就不会报错
npm install babel-eslint eslint-config-airbnb -D
module.exports = {
    
    
  "parser": "babel-eslint",
  "extends": "airbnb",
  "env": {
    
    
    "browser": true,
    "node": true
  },
  "rules": {
    
    
    "semi": "error"
  }
}

我们执行一下npm run build
它会检测每个文件,然后依次列出每个文件不符合eslint规范的地方,比如:
在这里插入图片描述
在这里插入图片描述
比如我们看到上面有个提示是需要两个空格,却发现了四个。那是因为我故意的,第九行。
在这里插入图片描述
假如我们团队就是希望用四个空格,怎么办?修改规则,怎么修改?如下图修改,在eslint官网,用户指南,规则,选择indent,缩进。
在这里插入图片描述
在这里插入图片描述
我们只要修改.eslintrc.js中的rules规则即可。

module.exports = {
    
    
  "parser": "babel-eslint",
  "extends": "airbnb",
  "env": {
    
    
    "browser": true,
    "node": true
  },
  "rules": {
    
    
    "indent": ["error", 4] // 注意这里
  }
}

再比如,我们不希望在代码每条语句后面出现分号。这么配置:

  "rules": {
    
    
    "semi": "never"
  }

在这里插入图片描述

二、webpack打包库和组件

怎么用webpack打包一个组件或者基础库,其实打包用rollup最好,很纯粹,也更简单,但是webpack也是很强大的,今天讲的是webpack。

2.1 问题抛出

实现一个大整数加法库的打包
(1)需要打包压缩版和非压缩版
(2)支持AMD/CJS/ESM模块引入,

import * as largeNumber from 'large-number'
largeNumber.add('999', '1')

支持CJS:

const largeNumber = require('large-number')
largeNumber.add('999', '1')

支持AMD:

require(['large-number'], function(large-number) {
    
    
    largeNumber.add('999', '1')
})

支持

<script src="https://unpkg.com/large-number"></script>
<script>
    largeNumber.add('999', '1')  
</script>
// 如何将库暴露出去?
module.export = {
    
    
  mode: "production",
  entry: {
    
    
    "large-number": "./src/index.js",
    "large-number.min": "./src/index.js"
  },
  output: {
    
    
    filename: "[name].js",
    library: "largeNumber",
    libraryExport: "default",
    libraryTarget: "umd"
  }
}

2.2 举例

自己写一个库,用webpack打包并发布到npm,然后用于项目
在这里插入图片描述

第一步,创建目录large-number

mkdir large-number

cd large-number

npm init -y

npm i webpack webpack-cli -D

第二步,创建large-number/src/index.js

export default function add(a, b) {
    
    
  let i = a.length - 1
  let j = b.length - 1

  let carry = 0
  let ret = ''

  while (i >= 0 || j >= 0) {
    
    
    let x = 0
    let y = 0
    let sum
    if (i >= 0) {
    
    
      x = a[i] - '0'
      i --
    }
    if (j >= 0) {
    
    
      y = b[j] - '0'
      j --
    }
    
    sum = x + y + carry

    if (sum >= 10) {
    
    
      carry = 1
      sum -= 10
    } else {
    
    
      carry = 0
    }
    ret = sum + ret
  }
  if (carry) {
    
    
    ret = carry + ret
  }
  return ret
}

第三步,创建large-number/webpack.config.js

在这里插入图片描述

第四步,package.json中添加

在这里插入图片描述
打包:npm run build
在这里插入图片描述
在这里插入图片描述

第五步,修正:安装terser-webpack-plugin

显然,mode是不可以为production的,因为这样打包会被压缩,所以,我们需要将mode设置为none,然后通过其他配置去决定哪些压缩哪些不压缩。
这里我们采用一个插件:terser-webpack-plugin,这个插件通过配置来压缩large-number.min.js,而不压缩large-number.js,注意terser-webpack-plugin的也是webpack4.x自带的内置插件,它的内核就是uglifyjs-webpack-plugin.

npm install terser-webpack-plugin -D

webpack.config.js

const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
    
    
  mode: 'none',
  entry: {
    
    
    'large-number': './src/index.js',
    'large-number.min': './src/index.js'
  },
  output: {
    
    
    filename: '[name].js', // name是占位符: large-number.js&large-number.min.js
    library: 'largeNumber', // 打包出来的库的名字
    libraryTarget: 'umd', // umd 可以amd,cjs,esm ,script标签引用
    libraryExport: 'default' // 不设置default,引用库的时候麻烦,要largeNumber.default
  },
  // 优化
  optimization: {
    
    
    minimize: true,
    minimizer: [
      new TerserWebpackPlugin({
    
    
        include: /\.min\.js$/  // 只针对min.js进行压缩
      })
    ]
  }
}

在这里插入图片描述
在这里插入图片描述
npm: package.json中的main属性
简而言之,此属性定义了当我们引用依赖时的文件地址。
平时开发中基本用不到,只有我们在引用或者开发某个依赖包的时候才派上用场。不使用main属性的话我们可能需要这样写引用:require(“some-module/dist/app.js”),如果我们在main属性中指定了dist/app.js的话,我们就可以直接引用依赖就可以了:require(“some-module”)

第六步,新建 large-number/index.js

if (process.env.NODE_ENV === 'production') {
    
    
  module.exports = require('./dist/large-number.min.js')
} else {
    
    
  module.exports = require('./dist/large-number.js')
}

同时,在package.json中设置如下:
在这里插入图片描述

第七步,登录 npm login

在这里插入图片描述

第八步,发布

npm publish执行的时候,触发钩子prepublish
在这里插入图片描述
在这里插入图片描述
记住,如果自己写的库升级了,那么一定要在package.json的version配置里改动版本号,然后再次npm publish
第九步,切换到业务项目中去用这个库

npm install large-number-guoyu -S

在这里插入图片描述
项目中引用
在这里插入图片描述
在这里插入图片描述

三、webpack之DLLPlugin和DLLReferencePlugin

3.1 什么是DLL

DLL(Dynamic Link Library)文件为动态链接库文件,在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。
举个例子:很多产品都用到螺丝,但是工厂在生产不同产品时,不需要每次连带着把螺丝也生产出来,因为螺丝可以单独生产,并给多种产品使用。在这里螺丝的作用就可以理解为是dll。

3.2 为什么要使用DLL

通常来说,我们的代码都可以至少简单区分成业务代码和第三方库。如果不做处理,每次构建时都需要把所有的代码重新构建一次,耗费大量的时间。然后大部分情况下,很多第三方库的代码并不会发生变更(除非是版本升级),这时就可以用到dll:把复用性较高的第三方模块打包到动态链接库中,在不升级这些库的情况下,动态库不需要重新打包,每次构建只重新打包业务代码。
还是上面的例子:把每次构建,当做是生产产品的过程,我们把生产螺丝的过程先提取出来,之后我们不管调整产品的功能或者设计(对应于业务代码变更),都不必重复生产螺丝(第三方模块不需要重复打包);除非是产品要使用新型号的螺丝(第三方模块需要升级),才需要去重新生产新的螺丝,然后接下来又可以专注于调整产品本身。

3.3 使用步骤

3.3.1 创建webpack.dll.js

根目录下创建一个webpack.dll.js文件

const path = require('path')
const webpack = require('webpack')

module.exports = {
    
    
  entry: {
    
    
    // 第三方库
  	library: [
  	   'react',
      'react-dom'
    ]
  },
  output: {
    
    
    // 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
    filename: '[name]_[hash:8].dll.js',
    path: path.join(__dirname, 'build/library'),
    // library必须和后面dllplugin中的name一致 后面会说明
    library: '[name]_dll_[hash:8]'
  },
  plugins: [
    new webpack.DllPlugin({
    
    
      // 动态链接库的全局变量名称,需要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      name: '[name]_dll_[hash:8]',
      // 描述动态链接库的 manifest.json 文件输出时的文件名称
      path: path.join(__dirname, 'build/library/[name].manifest.json')
    })
  ]
}

3.3.2 在项目中使用(引用)打好的DLL文件

在webpack.prod.js中引用上面打好的dll文件

const webpack = require('webpack')

new webpack.DllReferencePlugin({
    
    
  manifest: require('./build/library/library.manifest.json')
})

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由此可见,是否做基础包的分离,对构建效率影响很大

3.3.3 add-asset-html-webpack-plugin将dll.js文件引入到html文件中

npm i add-asset-html-webpack-plugin --save
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
new AddAssetHtmlWebpackPlugin({
    
    
  filepath: path.join(__dirname, './build/library/library_fa48c66e.dll.js')
})
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')

const files = fs.readdirSync(path.resolve(__dirname, './build/library'));
files.forEach(file => {
    
    
  if(/.*\.dll.js/.test(file)) {
    
    
    //将打包好的dll文件挂载到html中
    plugins.push(new AddAssetHtmlWebpackPlugin({
    
    
        filepath: path.resolve(__dirname, './build/library', file)
    }))
  }
  if(/.*\.manifest.json/.test(file)) {
    
    
    //分析第三方模块是否已经在dll文件里,如果里面有就不用再node_modules在分析打包了
    plugins.push(new webpack.DllReferencePlugin({
    
    
      manifest: path.resolve(__dirname, './build/library', file)
    }))
  }
})

四、多进程/多线程 打包

4.1 基本原理和背景

在webpack3的时代,最活跃的多线程打包方案是 HappyPack,后来由于作者的原因,不再维护,到了webpack4.x时代,官方自带了thread-loader,可以解决。
HappyPack会创建一个线程池,每个模块以及该模块的依赖都会获得worker线程。线程会处理各自的模块,处理完成之后,再通过自己的一个通讯机制,返回给主线程,完成整个构建过程。
thread-loader其实原理和HappyPack差不多,将打包任务划分成多个(node)进程,把模块依次分给这些线程,一起完成打包任务。
在这里插入图片描述

4.2 怎么使用tread-loader

第一步:安装 thread-loader

npm install thread-loader -D

第二步:配置webpack,在rules下配置

{
    
    
  test: /\.js$/,
  use: [
    {
    
    
      loader: 'thread-loader',
      options: {
    
    
        workers: 3 // 采用3个线程
      }
    },
    'babel-loader',
    'eslint-loader'
  ]
}

在这里插入图片描述

4.3 多进程并行压缩

压缩有多种方式,这里推荐一种方法: terser-webpack-plugin 开启 parallel 参数。(注:parallel 是平行的,同时发生的 意思)
安装:

npm install terser-webpack-plugin --save-dev

使用:
下面parallel设置是4,如果不设置,那么默认就是2倍CPU数量减去1.

const TerserPlugin = require('terser-webpack-plugin')
 
module.exports = {
    
    
  optimization: {
    
    
    minimizer: [new TerserPlugin({
    
    
        parallel: 4
    })],
  },
}

parallel
Type: Boolean|Number Default: true
Use multi-process parallel running to improve the build speed. Default number of concurrent runs: os.cpus().length - 1.
通过某实例对比,将上述parallel: false的时候,打包时间为111秒。如果将parallel: true的时候,打包时间为79秒,效果很明显.

五、利用缓存提升二次构建速度

5.1 使用babel-loader插件的缓存

在使用缓存前
在这里插入图片描述
如何使用?
在这里插入图片描述

use: [
  {
    
    
    loader: 'thread-loader',
    options: {
    
    
      workers: 3
    }
  },
  'babel-loader?cacheDirectory=true'
]

然后: npm run build
在这里插入图片描述
可以发现,node_modules多了一个 .cache/babel-loader 的文件夹
在这里插入图片描述
再次执行npm run build
在这里插入图片描述

5.2 利用terser-webpack-plugin的缓存

npm install terser-webpack-plugin

const TerserPlugin = require('terser-webpack-plugin')

optimization: {
    
    
  minimizer: [
    new TerserPlugin({
    
    
      parallel: 4,
      cache: true
    })
  ]
}

执行 npm run build
在这里插入图片描述
第一次执行约3.3秒,再次执行,1.2秒
在这里插入图片描述

5.3 利用hard-source-webpack-plugin

npm install hard-source-webpack-plugin -S

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')

plugins: [new HardSourceWebpackPlugin()]

执行 npm run build
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最终时间降低到0.47秒

猜你喜欢

转载自blog.csdn.net/GY_U_YG/article/details/122348030