美文网首页
【webpack】插件 plugins

【webpack】插件 plugins

作者: 前端菜篮子 | 来源:发表于2019-08-21 11:06 被阅读0次

    插件(plugins)

    插件是 webpack 的支柱功能。插件目的在于解决 loader 无法实现的其他事。

    webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。如ConsoleLogOnBuildWebpackPlugin.js

    class ConsoleLogOnBuildWebpackPlugin {
        apply(compiler) {
        //compiler hook 的 tap 方法的第一个参数,
        //应该是驼峰式命名的插件名称
            compiler.hooks.run.tap(pluginName, compilation => {
                console.log("webpack 构建过程开始!");
            });
        }
    }
    

    一些插件简介

    Name Description
    AggressiveSplittingPlugin 将原来的 chunk 分成更小的 chunk
    BabelMinifyWebpackPlugin 使用 babel-minify进行压缩
    BannerPlugin 在每个生成的 chunk 顶部添加 banner
    CommonsChunkPlugin 提取 chunks 之间共享的通用模块
    CompressionWebpackPlugin 预先准备的资源压缩版本,使用 Content-Encoding 提供访问服务
    ContextReplacementPlugin 重写 require 表达式的推断上下文
    CopyWebpackPlugin 将单个文件或整个目录复制到构建目录
    DefinePlugin 允许在编译时(compile time)配置的全局常量
    DllPlugin 为了极大减少构建时间,进行分离打包
    EnvironmentPlugin DefinePlugin 中 process.env 键的简写方式。
    ExtractTextWebpackPlugin 从 bundle 中提取文本(CSS)到单独的文件
    HotModuleReplacementPlugin 启用模块热替换(Enable Hot Module Replacement - HMR)
    HtmlWebpackPlugin 简单创建 HTML 文件,用于服务器访问
    I18nWebpackPlugin 为 bundle 增加国际化支持
    IgnorePlugin 从 bundle 中排除某些模块
    LimitChunkCountPlugin 设置 chunk 的最小/最大限制,以微调和控制 chunk
    LoaderOptionsPlugin 用于从 webpack 1 迁移到 webpack 2
    MinChunkSizePlugin 确保 chunk 大小超过指定限制
    NoEmitOnErrorsPlugin 在输出阶段时,遇到编译错误跳过
    NormalModuleReplacementPlugin 替换与正则表达式匹配的资源
    NpmInstallWebpackPlugin 在开发时自动安装缺少的依赖
    ProvidePlugin 不必通过 import/require 使用模块
    SourceMapDevToolPlugin 对 source map进行更细粒度的控制
    EvalSourceMapDevToolPlugin 对 eval source map 进行更细粒度的控制
    UglifyjsWebpackPlugin 可以控制项目中 UglifyJS 的版本
    ZopfliWebpackPlugin 通过 node-zopfli 将资源预先压缩的版本

    更多第三方插件,请查看 awesome-webpack 列表。


    用法案例:CommonsChunkPlugin

    The CommonsChunkPlugin 已经从 webpack v4 legato 中移除。想要了解在最新版本中如何处理 chunk,请查看 SplitChunksPlugin(该插件配置过程更为便捷高效)。

    image
    element-ui初始化工程为例看下vendor.js
    import Vue from 'vue'
    import ElementUI from 'element-ui'
    

    由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins 属性传入 new 实例。根据你的 webpack 用法,这里有多种方式使用插件。

    //package.json中引入依赖
    "devDependencies": {
        ...
        "html-webpack-plugin": "^2.24.1",
        "webpack": "^2.4.1",
        ...
     }
    
    //webpack.config.js中引入
    //通过 npm 直接安装插件
    const HtmlWebpackPlugin 
        = require('html-webpack-plugin');
    //访问webpack内置的插件
    //CommonsChunkPlugin就是内置插件之一
    const webpack = require('webpack'); 
    

    功能:该插件用于提取 chunks 之间共享的通用模块。使用 CommonsChunkPlugin 从「应用程序 bundle」中提取 vendor 引用(vendor reference) 到 vendor bundle,并把引用 vendor 的部分替换为 __webpack_require__() 调用

    image

    Node API(O(∩_∩)O~,这个是啥)

    TODO」即便使用 Node API,用户也应该在配置中传入 plugins 属性。compiler.apply 并不是推荐的使用方式。

    //some-node-script.js
    //访问 webpack 运行时(runtime)
    const webpack = require('webpack'); 
    const configuration = require('./webpack.config.js');
    
    let compiler = webpack(configuration);
    compiler.apply(new webpack.ProgressPlugin());
    
    compiler.run(function(err, stats) {
    // ...
    });
    

    你知道吗:以上看到的示例和 webpack 自身运行时(runtime) 极其类似。wepback 源码中隐藏有大量使用示例,你可以用在自己的配置和脚本中。


    官网插件

    HtmlWebpackPlugin

    根据配置的入口和出口,自动生成index.html,且会自动引入相关的依赖。

    • 如果你想要了解更多 HtmlWebpackPlugin 插件提供的全部功能和选项,那么你就应该多多熟悉 HtmlWebpackPlugin 仓库。
    • 你还可以看一下 html-webpack-template,除了默认模板之外,还提供了一些额外的功能。

    CleanWebpackPlugin

    • 你可能已经注意到,由于过去的指南和代码示例遗留下来,导致我们的 /dist 文件夹相当杂乱。
    • webpack 会生成文件,然后将这些文件放置在 /dist 文件夹中,但是 webpack 无法追踪到哪些文件是实际在项目中用到的。
    • 通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法,因此只会生成用到的文件。让我们完成这个需求。
    • clean-webpack-plugin 是一个比较普及的管理插件,让我们安装和配置下。
    npm install clean-webpack-plugin --save-dev
    
    • webpack.config.js中配置:
      const path = require('path');
      const HtmlWebpackPlugin = require('html-webpack-plugin');
    + const CleanWebpackPlugin = require('clean-webpack-plugin');
    
      module.exports = {
        entry: {
          app: './src/index.js',
          print: './src/print.js'
        },
        plugins: [
    +     new CleanWebpackPlugin(['dist']),
          new HtmlWebpackPlugin({
            title: 'Output Management'
          })
        ],
        output: {
          filename: '[name].bundle.js',
          path: path.resolve(__dirname, 'dist')
        }
      };
    
    • 现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!

    WebpackManifestPlugin

    该插件能够在项目根目录生成一份manifest.json的文件,通过该文件的映射关系可以让我们知道webpack是如何追踪所有模块并映射到输出bundle中的。

    解读webpack-manifest-plugin

    //安装插件:
    npm install --save-dev webpack-manifest-plugin
    
    //在 webpack.config.js中引入,大致如下:
    var ManifestPlugin = require('webpack-manifest-plugin');
    module.exports = {
       // ...
       plugins: [
         new ManifestPlugin() 
         // ManifestPlugin方法可以接受一些选项参数options,如
         // new ManifestPlugin(options)
       ]
    };
    
    //3.可选参数如下:
    options.fileName 表示要生成文件的名称,默认为manifest.json
    options.publicPath 表示生成映射文件的路径,默认为output.publicPath
    
    还有其他的参数见官网
    https://github.com/danethurber/webpack-manifest-plugin
    

    HotModuleReplacementPlugin

    开发环境启用 HMR:启用此功能实际上相当简单。而我们要做的,就是更新 webpack-dev-server 的配置,和使用 webpack 内置的 HMR 插件

    image

    IgnorePlugin

    importrequire 调用时,忽略以下正则表达式匹配的模块:

    // requestRegExp 匹配(test)资源请求路径的正则表达式。
    // contextRegExp (可选)匹配(test)资源上下文(目录)的正则表达式。
    new webpack.IgnorePlugin(
        requestRegExp, [contextRegExp]
    )
    

    举例:moment

    比如我们要使用moment这个第三方依赖库,该库主要是对时间进行格式化,并且支持多个国家语言。

    import moment from 'moment'
    
    //设置语言
    moment.locale('zh-cn');
    let r = moment().endOf('day').fromNow();
    console.log(r);
    
    • 这样打印出来的就是中文的时间,因为我在使用这个API的时候,前面设置了语言类型moment.locale为中文zh-cn
    • 但是,虽然我设置了语言为中文,但是在打包的时候,是会将所有语言都打包进去的。这样就导致包很大,打包速度又慢
    • 所以,最好能够少打包一些没用的依赖目录进去
    • moment的包含./locale/该字段路径的文件目录就是各国语言的目录,比如./locale/zh-cn就是中文语言
    • 使用IgnorePlugin
    let webpack = require('webpack');
    plugins:[
        //moment这个库中,如果引用了./locale/目录的内容,
        //就忽略掉,不会打包进去
        new webpack.IgnorePlugin(/\.\/locale/,/moment/),
    ]
    
    • 我们虽然按照上面的方法忽略了包含./locale/该字段路径的文件目录,但是也使得我们使用的时候不能显示中文语言了,所以这个时候可以手动引入中文语言的目录
    import moment from 'moment'
    //手动引入所需要的语言包
    import 'moment/locale/zh-cn';
    moment.locale('zh-cn');
    let r = moment().endOf('day').fromNow();
    console.log(r);
    
    • 这样就OK了。既能够显示中文,又把不必要的语言包都忽略打包了

    DllPlugin

    DLLPluginDLLReferencePlugin 用某种方法实现了拆分 bundles,同时还大大提升了构建的速度dll是一种最简单粗暴并且极其有效的优化方式)。

    1. Webpack 打包的时候,对于一些不经常更新的第三方库,比如 reactlodash,我们希望能和自己的代码分离开,Webpack 社区有两种方案
      CommonsChunkPluginDLLPlugin
    2. 对于 CommonsChunkPlugin
      • webpack 每次打包实际还是需要去处理这些第三方库,只是打包完之后,能把第三方库和我们自己的代码分开。
      • 且每次工程重新打包,verdors也会重新打包,打包的chunkhash就会改变,导致每次发布,vendors的缓存都会失效。这样增加了用户的流量消耗和首屏加载时间。
    3. DLLPlugin 则是能把第三方代码完全分离开,即每次只打包项目自身的代码。引入的第三方包没有改变就不需要重新打包。
    4. DLLPlugin具体用法:
    /**
    要使用 DLLPlugin,需要额外新建一个配置文件。
    所以对于用这种方式打包的项目,
    一般会有下面两个配置文件:
    webpack.config.js
    webpack.dll.config.js
    
    先来看下 webpack.dll.config.js
    */
    const webpack = require('webpack')
    const library = '[name]_lib'
    const path = require('path')
    module.exports = {
        entry: {
            vendors: ['react', 'lodash']
            //上面的vendors,看到有人有的libs
        },
        output: {
            filename: '[name].dll.js',
            path: 'dist/',
            library
        },
        plugins: [
            new webpack.DllPlugin({
            //执行的上下文环境,对之后DllReferencePlugin有用
                context: __dirname,
                path: path.join(
                    __dirname, 'dist/[name]-manifest.json'
                ),
                // This must match the output.library option above
                name: library
            }),
        ],
    }
    
    
    //再改下 webpack.config.js 文件
    const webpack = require('webpack')
    module.exports = {
        entry: {
          app: './src/index'
        },
        output: {
          filename: 'app.bundle.js',
          path: 'dist/',
        },
        plugins: [
            new webpack.DllReferencePlugin({
                context: __dirname,
                //下面的vendors有人用libs
                manifest: require('./dist/vendors-manifest.json')
            })
        ]
    }
    
    /**
    manifest: require('./dist/vendors-manifest.json') 
    这里的路径要和 webpack.dll.config.js 里面的对应。
    然后运行:
    */
    $ webpack --config webpack.dll.config.js
    $ webpack --config webpack.config.js
    
    //然后你的 html 文件像下面这样引用
    <script src="/dist/vendors.dll.js"></script>
    <script src="/dist/app.bundle.js"></script>
    
    /**
    上面的用法也存在一些不方便的地方,
    比如在 webpack.config.js 中要明确
    指出对应的 manifest.json 文件。
    还有当 DLL 需要更新的时候,
    比如 react 升级了,或者加入新的第三方库,
    都需要手动像下面这样编译一次。
    */
    $ webpack --config webpack.dll.config.js
    
    
    /**
    ---非官方插件(不需关注,没有缓存作用了吧?)----
    因为上面这些问题,
    所以基于官方的 DllReferencePlugin,
    有一个 Dll Link Plugin。链接如下:
    www.npmjs.com/package/dll-link-webpack-plugin
    使用这个插件,
    只需要对 webpack.config.js 作下小小的改动
    */
    const webpack = require('webpack')
    const DllLinkPlugin = require('dll-link-webpack-plugin')
    
    module.exports = {
        // ...
        plugins: [
            new DllLinkPlugin({
                config: require('webpack.dll.config.js')
            })
        ]
    }
    /**
    直接替换掉 DllReferencePlugin,
    然后传入对应的 DLL 配置文件就可以了。
    每次打包的时候,只需要运行
    */
    $ webpack --config webpack.config.js
    /**
    上面的命令便会自动生成对应的 vendors 文件,
    需要更新的时候,也会自动更新。
    */
    

    SplitChunksPlugin

    1. 在默认情况下,SplitChunksPlugin 仅仅影响按需加载的代码块,因为更改初始块会影响HTML文件应包含的脚本标记以运行项目。

    2. webpack将根据以下条件自动拆分代码块:

      • 会被共享的代码块或者 node_mudules 文件夹中的代码块
      • 体积大于30KB的代码块(在gz压缩前)
      • 按需加载代码块时的并行请求数量不超过5个
      • 加载初始页面时的并行请求数量不超过3个
      //举例:
      
      // 文件一:impvue.js
      import 'vue'
      ...
      
      // 文件二:index.js
      // 动态加载 impvue.js
      import('./impvue')
      ...
      
      /**
      打包之后的结果会创建一个包含 vue 的独立代码块,
      当包含 impvue.js 的原始代码块被调用时,
      这个独立代码块会并行请求进来。
      原因:
          1) vue 来自 node_modules 文件夹
          2) vue 体积超过30KB
          3) 导入调用时的并行请求数为2
          4) 不影响页面初始加载
      */
      
      
    3. 我们这样做的原因是因为,vue代码并不像你的业务代码那样经常变动,把它单独提取出来就可以和你的业务代码分开缓存,极大的提高效率。

    4. SplitChunksPlugin的默认配置

    splitChunks: {
        /**
        chunks属性用来选择分割哪些代码块,
        可选值有:
            'all'(所有代码块)
            'async'(按需加载的代码块)
            'initial'(初始化代码块)
        */
        chunks: "async",
        minSize: 30000, // 模块的最小体积
        minChunks: 1, // 模块的最小被引用次数
        maxAsyncRequests: 5, // 按需加载的最大并行请求数
        maxInitialRequests: 3, // 一个入口最大并行请求数
        automaticNameDelimiter: '~', // 文件名的连接符
        name: true,
        /**
        缓存组是个有趣的功能:
        在默认设置中,
        会将`node_mudules`中的模块打包进`vendors`,
        引用超过两次的模块分配到`default` `bundle` 中。
        更可以通过 `priority` 来设置优先级。
        */
        cacheGroups: { // 缓存组
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10
            },
            default: {
                minChunks: 2,
                priority: -20,
                reuseExistingChunk: true
            }
        }
    }
    
    1. 在项目中添加SplitChunksPlugin
    /**
    为了方便演示,我们先安装两个类库: lodash 和 axios,
    npm i lodash axios -S
    修改 main.js,引入 lodash 和axios 并调用相应方法:
    */
    import Modal from './components/modal/modal'
    import './assets/style/common.less'
    import _ from 'lodash'
    import axios from 'axios'
    const App = function () {
      let div = document.createElement('div')
      div.setAttribute('id', 'app')
      document.body.appendChild(div)
      let dom = document.getElementById('app')
      let modal = new Modal()
      dom.innerHTML = modal.template({
        title: '标题',
        content: '内容',
        footer: '底部'
      })
    }
    const app = new App()
    console.log(_.camelCase('Foo Bar'))
    axios.get('aaa')
    
    /**
    -----------------------------
    使用SplitChunksPlugin不需要安装任何依赖,
    只需在 webpack.config.js 中的 config对象
    添加 optimization 属性:
    */
    optimization: {
        splitChunks: {
          chunks: 'initial',
          automaticNameDelimiter: '.',
          cacheGroups: {
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: 1
            }
          }
        },
        //一般都配合runtimeChunkPlugin使用
        runtimeChunk: {
          name: entrypoint => `manifest.${entrypoint.name}`
        }
      }
    /**
    配置 runtimeChunk 会给每个入口添加一个只包含runtime的额外的代码块,
    name 的值也可以是字符串,不过这样就会给每个入口添加相同的 runtime,
    配置为函数时,返回当前的entry对象,即可分入口设置不同的runtime。
    */
    
    /**
    我们再安装一个 webpack-bundle-analyzer,
    这个插件会清晰的展示出打包后的各个bundle所依赖的模块:
    引入:
    */
    const BundleAnalyzerPlugin =
    require('webpack-bundle-analyzer').BundleAnalyzerPlugin
    //使用,在plugins数组中添加即可:
    new BundleAnalyzerPlugin()
    
    1. 可以看看Webpack4SplitChunksPlugin这里提及的问题

    BannerPlugin

    为每个 chunk 文件头部添加 banner(注释/版权)。

    new webpack.BannerPlugin(options)
    //options选项:
    {
      banner: string, // 其值为字符串,将作为注释存在
      raw: boolean, // 如果值为 true,将直出,不会被作为注释
      entryOnly: boolean, // 如果值为 true,将只在入口 chunks 文件中添加
      test: string | RegExp | Array,
      include: string | RegExp | Array,
      exclude: string | RegExp | Array,
    }
    

    CopyWebpackPlugin:拷贝静态文件

    将单个文件或整个目录直接从源目录拷贝到构建目录.

    //config:配置了一些参数
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      },
      //上面的static目录先不管,
      //我们先看这个,文件中是一些客户可自行修改的配置
      //独立出来,方便修改
      {
        from: path.resolve(config.directory.root, 'sysconfig.js'),
        to: 'sysconfig.js'
      },
    ])
    

    打包好的工程下:


    image
    image

    ExtractTextWebpackPlugin:分离CSS文件

    MiniCssExtractPlugin这个呢?

    const ExtractTextPlugin 
        = require("extract-text-webpack-plugin");
    
    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ExtractTextPlugin.extract({
              fallback: "style-loader",
              use: "css-loader"
            })
          }
        ]
      },
      plugins: [
        new ExtractTextPlugin("styles.css"),
        /**
        我看我们的工程中是这样配置的
        具体的后续再好好看下:
        new ExtractTextPlugin({
          filename: utils.assetsPath('css/[name].[contenthash].css'),
          allChunks: true,
        }),
        */
      ]
    }
    

    它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件。因此,你的样式将不再内嵌到 JS bundle 中,而是会放到一个单独的 CSS 文件(即 styles.css)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。

    再来看个CSS优化(压缩)相关的另一个插件optimize-css-assets-webpack-plugin:

    /**这个插件可以接受下列配置(均为可选):
    assetNameRegExp: 正则,用于匹配需要优化的资源名。默认值是 /\.css$/g
    
    cssProcessor: 用于压缩和优化CSS 的处理器,默认是 cssnano.这是一个函数,
    应该按照 cssnano.process 接口(接受一个CSS和options参数,返回一个Promise)
    
    canPrint: {bool} 表示插件能够在console中打印信息,默认值是true
    */
    
    // webpack4+
    const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    // webpack4 
    module.exports = {
      optimization: {
        minimizer: [
          new UglifyJsPlugin({
            cache: true,
            parallel: true,
            sourcMap: true
          }),
          new OptimizeCssAssetsPlugin({
            assetNameRegExp: /\.optimize\.css$/g,
            cssProcessor: require('cssnano'),
            cssProcessorOptions: { safe: true, discardComments: { removeAll: true } },
            canPrint: true
          }),
        ],
      },
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              MiniCssExtractPlugin.loader,
              'css-loader'
            ],
          },
        ],
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: "[name].css",
          chunkFilename: "[id].css"
        }),
      ]
    };
    
    //webpack3中一般配合 ExtractTextPlugin一起使用。
    

    UglifyjsWebpackPlugin

    此插件使用uglify-js进行js文件的压缩。

    • 下面说明是来自:关闭开发环境的代码压缩UglifyJsPlugin
    • 开发环境是不需要去压缩代码,主要是因为太耗性能了,每修改一个地方就要花几秒去等待页面渲染,非常浪费开发时间,解决办法就是配置不同的环境变量
    • 去在开发环境的时候不要这个UglifyJsPlugin插件,为此,webpack4又增加了新的Mode,且默认值是production;而且更新后的webpack默认是有UglifyJsPlugin这个配置的
    • 也就是说在不设置任何环境变量的情况下,始终会有压缩代码的行为出现,导致编译极其耗时,那我的解决办法就是在package.json文件启动时设置环境变量:
      image

    Tree Shaking:一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。新的webpack 4 正式版本,扩展了这个检测能力,通过 package.jsonsideEffects 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 pure(纯的 ES2015 模块),由此可以安全地删除文件中未使用的部分。

    • 这种方式是通过 package.jsonsideEffects 属性来实现的。
    {
      "name": "your-project",
      "sideEffects": false
    }
    
    • 如果你的代码确实有一些副作用,那么可以改为提供一个数组:
    {
      "name": "your-project",
      "sideEffects": [
        "./src/some-side-effectful-file.js"
      ]
    }
    
    • webpack 4 开始,也可以通过 mode 配置选项轻松切换到压缩输出,只需设置为 production
    • 配置webpack.config.js
    const path = require('path');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
     },
     mode: "production"
    };
    

    DefinePlugin

    实用webpack插件之DefinePlugin

    DefinePlugin 允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。如果在开发构建中,而不在发布构建中执行日志记录,则可以使用全局常量来决定是否记录日志。这就是 DefinePlugin 的用处,设置它,就可以忘记开发和发布构建的规则。

    new webpack.DefinePlugin({
    /**
    在vue-cli创建的项目中,
    凡是src下的文件,都可以访问到下面这些变量,如VERSION,
    例如main.js,App.vue等等
    */
      PRODUCTION: JSON.stringify(true),
      VERSION: JSON.stringify("5fa3b9"),
      BROWSER_SUPPORTS_HTML5: true,
      TWO: "1+1",
      //根据process.env.NODE_ENV区分环境,引入配置文件
      process.env: {NODE_ENV: "development"},
    })
    

    EnvironmentPlugin: 是一个通过 DefinePlugin 来设置 process.env 环境变量的快捷方式。

    new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG'])
    //上面的写法和下面这样使用 DefinePlugin 的效果相同:
    new webpack.DefinePlugin({
     'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
     'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
    })
    

    HashedModuleIdsPlugin

    该插件会根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境。

    //其他具体说明再看看
    new webpack.HashedModuleIdsPlugin({
      hashFunction: 'sha256',
      hashDigest: 'hex',
      hashDigestLength: 20
    })
    

    ZipPlugin(好像官网没列)

    发布的时候可以使用 压缩插件将资源(图片,配置文件等)压缩成一个 .zip 文件

    //参数中配置了需要将工程打包
    if (config.build.productionToZip) {
      const ZipPlugin = require('zip-webpack-plugin')
      webpackConfig.plugins.push(
        new ZipPlugin({
          path: config.build.assetsZipRoot,
          filename: 'build.zip',
          extension: 'zip',
        })
      )
    }
    

    ModuleConcatenationPlugin

    • webpack2处理后的每个模块均被一个函数包裹,如下:
    /* 50 */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
        window.lib = {}
        ...   
    /* harmony default export */ __webpack_exports__["a"] = (window.lib);
     
    /***/ }),
    
    • 这样会带来一个问题:降低浏览器中JS执行效率,这主要是闭包函数降低了JS引擎解析速度。
    • 于是webpack团队参考Closure CompilerRollup JS,将一些有联系的模块,放到一个闭包函数里面去,通过减少闭包函数数量从而加快JS的执行速度。
    • webpack3通过设置ModuleConcatenationPlugin使用这个新特性:
    module.exports = { 
      plugins: [
        new webpack.optimize.ModuleConcatenationPlugin()
      ]
    }
    
    • 记住,此插件仅适用于由 webpack 直接处理的 ES6 模块。在使用转译器(transpiler)时,你需要禁用对模块的处理(例如 Babel 中的 modules 选项)。

    NpmInstallWebpackPlugin

    在开发时自动安装缺少的依赖:package.json中需要配置了才会自动安装哦

    //安装
    npm install --save-dev npm-install-webpack-plugin
    
    //用法
    //不要忘记引入哦
    const NpmInstallPlugin = require('npm-install-webpack-plugin')
    //插件配置:
    plugins: [
      new NpmInstallPlugin()
    ],
    
    //相当于:
    plugins: [
      new NpmInstallPlugin({
        // 使用 --save 或者 --save-dev
        dev: false,
        // 安装缺少的 peerDependencies
        peerDependencies: true,
        // 减少控制台日志记录的数量
        quiet: false,
        // npm command used inside company, yarn is not supported yet
        npm: 'tnpm'
      });
    ],
    
    //可以提供一个 Function 来动态设置 dev:
    plugins: [
      new NpmInstallPlugin({
        dev: function(module, path) {
          return [
            "babel-preset-react-hmre",
            "webpack-dev-middleware",
            "webpack-hot-middleware",
          ].indexOf(module) !== -1;
        },
      }),
    ],
    

    ProvidePlugin

    自动加载模块,而不必到处 importrequire

    //要自动加载 jquery,我们可以将两个变量都指向对应的 node 模块:
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
    //然后在我们任意源码中:
    $('#item'); // <= 起作用
    jQuery('#item'); // <= 起作用
    
    //使用:Lodash Map
    new webpack.ProvidePlugin({
      _map: ['lodash', 'map']
    })
    
    //使用:Vue.js
    new webpack.ProvidePlugin({
      Vue: ['vue/dist/vue.esm.js', 'default']
    })
    

    LimitChunkCountPlugin

    当你在编写代码时,可能已经添加了许多代码分离点(code split point)来实现按需加载(load stuff on demand)。在编译完之后,你可能会注意到有一些很小的 chunk - 这产生了大量 HTTP 请求开销。幸运的是,此插件可以通过合并的方式,后处理你的 chunk,以减少请求数。

    //改善`chunk`传输的插件
    
    //LimitChunkCountPlugin
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 5, // 必须大于或等于 1
      minChunkSize: 1000
    })
    
    /**MinChunkSizePlugin:
    通过合并小于 minChunkSize 大小的 chunk,
    将 chunk 体积保持在指定大小限制以上。
    */
    new webpack.optimize.MinChunkSizePlugin({
      // Minimum number of characters
      minChunkSize: 10000 
    })
    
    /**
    AggressiveSplittingPlugin可以将 bundle拆分成更小的chunk,
    直到各个 chunk 的大小达到 option 设置的 maxSize。
    它通过目录结构将模块组织在一起。
    */
    new webpack.optimize.AggressiveSplittingPlugin({
        minSize: 30000, // 字节,分割点。默认:30720
        maxSize: 50000, // 字节,每个文件最大字节。默认:51200
        chunkOverhead: 0, // 默认:0
        entryChunkMultiplicator: 1, // 默认:1
    })
    

    SourceMapDevToolPlugin

    开发模式下的插件工具了解下

    生成source map的两种配置

    1. 配置devtool
    //最简单的生成source map的方式是,如下配置webpack.config.js,
    const path = require('path');
    
    module.exports = {
        devtool: 'source-map',
        entry: {
            index: path.resolve(__dirname, 'src/index.js'),
        },
        output: {
            devtoolModuleFilenameTemplate: '[resource-path]',
            path: path.resolve(__dirname, 'dist/'),
            filename: 'index.js',
        },
        module: {
            rules: [
                { 
                    test: /\.js$/, 
                    use: { 
                        loader: 'babel-loader', 
                        query: { presets: ['@babel/preset-env'] } 
                    } 
                    
                },
            ]
        },
    };
    /**
    其中,devtool默认值为false,
    配置为source-map表示以独立的文件形式生成source map。
    因此,dist/ 文件夹下,会产生两个文件,
    index.js
    index.js.map
    
    index.js文件末尾,webpack会自动添加一行注释,
    //# sourceMappingURL=index.js.map
    
    浏览器解析到这里,会自动根据index.js的相对路径,请求map文件并加载它。
    */
    
    1. SourceMapDevToolPlugin
    /**
    除了直接配置devtool之外,
    还可以使用webpack官方插件SourceMapDevToolPlugin,
    进行更细粒度的source map配置。
    */
    const path = require('path');
    const webpack = require('webpack');
    
    module.exports = {
        // devtool: 'source-map',
        entry: {
            index: path.resolve(__dirname, 'src/index.js'),
        },
        output: {
            // devtoolModuleFilenameTemplate: '[resource-path]',
            path: path.resolve(__dirname, 'dist/'),
            filename: 'index.js',
        },
        module: {
            rules: [
                { 
                    test: /\.js$/, 
                    use: { 
                        loader: 'babel-loader', 
                        query: { presets: ['@babel/preset-env'] } 
                    } 
                },
            ]
        },
        plugins: [
            new webpack.SourceMapDevToolPlugin({
                filename: '[file].map',
                moduleFilenameTemplate: '[resource-path]',
                append: '\n//# sourceMappingURL=http://127.0.0.1:8080/dist/[url]'
            }),
        ],
    };
    /**
    以上配置中,我们注释掉了
        devtool和devtoolModuleFilenameTemplate,
    将它们配置到了SourceMapDevToolPlugin中。
    这样配置,dist/目录最后也会生成两个文件,
    index.js
    index.js.map
    
    由于我们使用了该插件的append功能,
    修改了sourceMappingURL地址,
    因此,index.js末尾source map文件的地址就变成了,
    //# sourceMappingURL=http://127.0.0.1:8080/dist/index.js.map
    */
    
    1. 注:这里值得一提的是
    同时配置devtool和SourceMapDevToolPlugin是不行的,
    index.js文件末尾会被添加两行sourceMappingURL,
    //# sourceMappingURL=http://127.0.0.1:8080/dist/index.js.map
    //# sourceMappingURL=index.js.map
    
    而且map文件的内容也不正确,是一个空的map文件,
    
    1. EvalSourceMapDevToolPlugin:对 eval source map 进行更细粒度的控制
    2. webpack——devtool里的7SourceMap模式
      我们先来看看文档对这 7 种模式的解释:
    模式 解释
    eval 每个module会封装到 eval 里包裹起来执行,并且会在末尾追加注释 //@ sourceURL.
    source-map 生成一个SourceMap文件.
    hidden-source-map 和 source-map 一样,但不会在 bundle 末尾追加注释.
    inline-source-map 生成一个 DataUrl 形式的 SourceMap 文件.
    eval-source-map 每个module会通过eval()来执行,并且生成一个DataUrl形式的SourceMap
    cheap-source-map 生成一个没有列信息(column-mappings)的SourceMaps文件,不包含loader的 sourcemap(譬如 babel 的 sourcemap)
    cheap-module-source-map 生成一个没有列信息(column-mappings)的SourceMaps文件,同时 loader 的 sourcemap 也被简化为只包含对应行的。
    - 注1:webpack 不仅支持这 7 种,
    而且它们还是可以任意组合上面的eval、inline、hidden关键字,
    就如文档所说,你可以设置 souremap 选项为:
        cheap-module-inline-source-map。
    
    - 注2:如果你的modules里面已经包含了SourceMaps,
    你需要用source-map-loader 来和合并生成一个新的 SourceMaps。
    

    还有很多其他的,需要时自行搜索学习即可

    ......


    Webpack 4进阶

    相关文章

      网友评论

          本文标题:【webpack】插件 plugins

          本文链接:https://www.haomeiwen.com/subject/obkbsctx.html