BREAK IN TO BREAK OUT Lyn
  1. 1 BREAK IN TO BREAK OUT Lyn
  2. 2 かかってこいよ NakamuraEmi
  3. 3 Life Will Change Lyn
  4. 4 Time Bomb Veela
  5. 5 Warcry mpi
  6. 6 Flower Of Life 发热巫女
  7. 7 Last Surprise Lyn
  8. 8 Libertus Chen-U
  9. 9 Hypocrite Nush
  10. 10 One Last You Jen Bird
2018-07-08 13:59:56

Webpack常用插件索引

这篇文章用来梳理Webpack使用各个流程中常用的一些插件并长期更新,以便以后使用的时候能够快速索引。

自动化

html-webpack-plugin

这个插件的功能可以说就是Webpack项目成功启动的最后一步。毕竟资源无论怎么样打包,最终成品都会输出为JS,CSS和静态资源,而这些文件必然要被HTML引用才能在浏览器中作为项目启动,这个插件的作用就是如此,将生成HTML模板并插入打包后的文件的流程自动化。使用方法相当简单,直接在plugins选项中实例化即可。

const webpackConfig = {
  ...
  plugins: [
    new HtmlWebpackPlugin()
  ]
};

如果不传入任何配置项,会将通过entry 配置的所有入口thunk和extract-text-webpack-plugin抽出的css文件都插入到一个默认的html文件中,当然我们也可以通过一些配置项使这个过程实现得更灵活。

配置项

  • title: 给HTML模板注入title标签,需要注意的是如果模板中已有title,则不会替换。
  • filename: 输出HTML的文件名称 ,默认为index.html,还可以指定输入位置,例如src/index.html,路径相对于webpackConfig.output.path
  • template: 指定自定义的HTML模板,支持加载器配置如ejs,undersore,jade等。
  • inject: 在模板中注入静态资源的位置。当为true或body时JS资源会被注射到body底部,head会注射到head中,false则不会注入。
  • favicon: 注射favicon图标文件的路径。
  • meta: 注射自定义meta标签。
  • minify: 传递给 html-minifier 的选项用于压缩html。
  • hash: 为所有注入的资源在webpack中的每次编译生成唯一hash,这一点在破坏缓存时很有用,比如每次发布时,自然不希望用户仍然使用缓存的旧文件。
  • showErrors: 将报错输出到html页面中。这个在开发时比较方便,便于调试。
  • chunks: 自定义插件到模板的一些chunk。不配置时则默认为所有。
  • chunksSortMode: 控制chunk插入顺序。
  • excludeChunks: 和chunks相反,配置不插入的chunks
  • xhtml: 将标签渲染为自闭合。

下面分别介绍几种该插件的常用场景。

多页配置

在单页应用中如果需要多个页面入口,多页应用配置多个入口时,或者生成主入口或测试入口时,多页配置就能派上用场。使用也很简单,在plugins配置项中实例化多次传入不同配置项即可。

const webpackConfig = {
    ...
    plugins: [
      new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'src/assets/index.html',
      ...
    }),
      new HtmlWebpackPlugin({
      filename: 'test.html',  
      template: 'src/assets/test.html',    
      ...
    })
  ]
};

自定义通用模板

大部分情况下,将thunk插入到html中可能就已经够用了,但有时候我们仍然需要更灵活的插入配置,例如多页面入口时,可能入口模板大部分数据是一样的,只是少部分数据根据配置会不一样,这种情况下很容易想到使用模板引擎来实现,也就是自定义通用模板了。例子如下:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
  </body>
</html>

可以看到在以上模板中我们使用了htmlWebpackPlugin数据,模板语法中能够访问到插件提供的相关数据来差异化生成HTML。具体能访问到的内容如下:

  • compilation: webpack编译对象,这是webpack每次编译都会生成的保存编译相关内容的对象,在webpack工作流程介绍中也提到过,详细内容可以查阅相关资料。
  • webpack: webpack的html文件生成时对应的stats对象。
  • webpackConfig: webpack的相关配置项。
  • htmlWebpackPlugin: 该插件工作时对应的数据内容,包括配置的注射chunk和css文件以及传递给插件的配置项。

事件钩子

我们前面已经提到过,webpack整套机制都是运行于插件事件机制之上的。htmlwebpackplugin插件同样也提供了一些事件钩子,供我们在该插件运行过程中使用其他插件自定义一些逻辑。事件具体如下:

  • 同步钩子:htmlWebpackPluginAlterChunks
  • 异步钩子:htmlWebpackPluginBeforeHtmlGeneration, htmlWebpackPluginBeforeHtmlProcessing,htmlWebpackPluginAlterAssetTags,htmlWebpackPluginAfterHtmlProcessing,htmlWebpackPluginAfterEmit

clean-webpack-plugin

随着每次构建,构建目录中会持续积累文件,这肯定不是开发者想要的。这个问题当然可以通过npm脚本解决,但更合理的办法还是将所有特性都集成到webpack的工作中,这个插件就是用来做这件事。使用上也没什么特别需要注意的,配置目录实例化即可。

copy-webpack-plugin

有的时候某些数据可能并不需要webpack直接处理,但需要带到当前构建目录中使用,这种情况下就需要用到该插件来执行复制粘贴操作。使用上通常在实例化时传入源文件地址和目录地址即可。

ProvidePlugin

该插件用来设置自由变量和模块依赖的对应关系,当主代码中使用到自由变量时,能够自动化加载对应依赖。实例如下:

new webpack.ProvidePlugin({
    $: 'jquery',
    jquery: 'jquery'
})

然后在我们的主代码中使用到$jQuery的部分都能够自动化引用jquery模块了。

开发优化

HotModuleReplacementPlugin

HMR可以说是Webpack的看家特性之一了,主要表现就是当你对代码进行修改并保存后,在不刷新浏览器的情况下就能对应用进行更新,大大加快了开发效率。在HMR出现之前,主要是通过live reload监听文件变化,然后在浏览器端刷新页面,而HMR相对于其的优势在于更新手段并不是刷新页面只是实现局部替换,由此可以保存应用的当前状态,保证开发的流畅性。

使用方法

由于结合webpack开发时通常配合webpack-dev-server使用,其内部已经集成了HMR相关功能,所以使用起来相当简单,只需要两步:

  • 在plugin选项中实例化HotModuleReplacementPlugin
  • devServer选项中将hot选项设置为true

如此就开启了HMR,但需要注意一点,这只是HMR的前置工作,要在业务代码中实现指定模块的HMR还需要使用webpack提供的HMR相关API,好在目前社区中已经有工具帮我们自动化了这一过程,比如React Hot Loader,Vue-loader等,这样一套工作流能够完全自动化HMR相关配置,使得我们集中精力于业务逻辑中。

原理

  1. webpack-dev-server中的webpack-dev-middleware调用webpack的API监听文件系统,当模块文件变动时,webpack重新编译打包并利用memory-fs输出到内存中而不输出到磁盘,减少写入开销。
  2. 启动webpack-dev-server时,会利用sockjs在服务端和浏览器端建立一个webSocket长连接,当重编译完成后,通过_sendStatus方法将新模块的hash值发送到客户端。
  3. webpack-dev-server/client修改webpack配置的entry使得客户端bundle中有接收webSocket消息的逻辑代码,当客户端bundle接收到hash型消息后将其暂存,当接受到ok型消息后执行接下来的HMR步骤,如果未配置HMR,则回退到reload方法刷新页面。
  4. 上一步webpack-dev-server/client接受到ok型消息后,发送webpackHotUpdate消息给webpack/hot/dev-server调用HMR runtime中的check方法检测是否在新的模块中有代码更新。该方法会向服务端发送hotDownloadManifest请求确认所有更新模块的hash值列表,然后再根据该列表使用名为hotDownloadUpdateChunk的jsonp请求获取最新模块代码。
  5. HMR runtime执行HMR替换步骤,首先找出过期模块和过期依赖,再从缓存从删除,最后添加新的模块。如果替换过程中出现错误,则回退到reload方法刷新浏览器。

error-overlay-webpack-plugin

我们知道配置devServer时可以通过overlay选项设置编译时错误提示,通过该插件可以更加精确和美观的显示错误来源。使用同样只需要实例化。

new ErrorOverlayPlugin()

构建时优化

duplicate-package-checker-webpack-plugin

当项目变大时,一种很可能出现的情况是,由于依赖复杂,某个包的不同版本被多次引用导致重复打包,该插件就会发出警告。

HappyPack

当项目变大时,构建过程中就会出现构建速度过慢的问题,毕竟构建是对文件进行读写和密集型操作的过程。HappyPack就是为了解决这一点。

原理

构建流程中,最耗时的任务是loader中的文件转换操作,而Webpack的运行又是基于JS的单线程模型,使得文件转换只能单队列处理,由此造成了性能瓶颈。HappyPack将这部分任务分解到多个并行进程,其核心调度器的逻辑在主进程中,每一次实例化该插件时,都会分配一个空闲子进程处理loader任务,处理完毕后通过进程通信通知核心调度器,核心调度器再通知Webpack。

配置

简单的示例用法配置如下:

module: {
    rules: [
        {
            test: /\.js$/,
              use: ['happypack/loader?id=babel']
        },
          {
            test: /\.css$/,
              use: ['happypack/loader?id=css']
        }
    ]
},
  plugins: [
      new HappyPack({
          id: 'babel',
          loaders: [
              ...
          ]
      }),
      new HappyPack({
          id: 'css',
          loaders: [
              ...
          ]
      })
  ]

可以看到和平常配置loaders时的主要几点区别如下:

  • loader配置中将处理代理给happypack并通过id确认使用哪个happypack实例处理
  • happypack实例化中传入id标识对应真实loader配置,loader选项和真实loader配置完全相同,happypack插件可以看作是一层代理

除了如上简单配置外,还可以通过相关参数更精确的配置处理不同loader的进程数量或者共享进程处理,详细使用方法就不赘述了,可查阅官方文档。

DllPlugin/DllReferencePlugin

原理

在Windows操作系统中有很多.dll类型的文件,这种文件被称作动态链接库,即可以在其他模块中被调用的函数。这个插件在Webpack中的作用也是如此,使用该插件可以生成一个manifest.json文件存储依赖模块索引并将依赖模块单独打包,打包后的文件本身不能运行,只是给主代码通过manifest.json存储的索引关系来引用。

这样抽出依赖代码的构建形式有如下几点好处:

  • 只要依赖库没有变化,主代码更新后再构建速度能够明显提升
  • 如果多个项目有重叠依赖库,可以共用dll管理依赖
  • 主代码更新时如果不更新依赖,依赖bundle的hash不会变化,降低更新频率,用户能够有效利用缓存加载依赖bundle

使用

这个插件的使用分两步,第一步先使用DllPlugin打包依赖,生成manifest.json。简单配置如下,假设依赖为antdreact等。

const webpack = require('webpack');
const vendors = ['antd', 'react', ...];

module.exports = {
    output: {
        path: 'build',
          filename: '[name].[chunkhash].js',
          library: '[name]_[chunkhash]' // dll包名,用于主代码调用
    },
      entry = {
        vendor: vendors
    },
      plugins: [
        new webpack.Dllplugin({
            path: 'manifest.json', // 存放manifest.json的路径
              name: '[name]_[chunkhash]', // dll暴露对象名,需要和library保持一致
              context: __dirname  // 解析包路径的上下文,需要和接下来配置的dll user保持一致
        })
    ]
};

运行后会输出两个文件,一个是依赖bundle,一个是记录索引关系的manifest.json,数据结构如下:

{
    "name": "vendor_....",
    "content": {
        "./node_modules/antd/dist/antd.js": 1,
        "./node_nodules/react/react.js": 2,
        ...
    }
}

可以看到对每个依赖都设置了id索引,之后的dll user就是通过该id来查找引用。下面设置dll user的打包配置,这一步需要用到DllReferencePlugin查找引用。

const webpack = require('webpack');

module.exports = {
    output: {
        path: 'build',
          filename: '[name].[chunkhash].js'
    },
      entry: {
        app: './src/index.js'
    },
      plugins: [
        new webpack.DllReferencePlugin({
            context: __dirname, // 和前一步打包保持一致
              manifest: require('./manifest.json')
        })
    ]
}

至此就完成了三方依赖和主代码的分离打包。

构建后优化

OccurenceOrderPlugin

该插件的作用是为模块分配ID,使用频率越多的模块ID越小,被查找到的速度就会越快。使用很简单,直接实例化即可。

new webpack.optimize.OccurenceOrderPlugin()

mini-css-extract-plugin

在Webpack3中使用ExtractTextPlugin提取公共CSS模块为单一CSS文件,Webpack4中使用了该插件取代。该插件应该只用于生产环境并且不能和styles-loader共同作用。使用起来也很简单,在插件选项中实例化后,再在rules配置链中设置即可。

plugins: [
    new MiniCssExtractPlugin({
        filename: '[name].css'
    })
],
module: {
    rules: [
        {
            test: /\.css$/,
              use: [
                MiniCssExtractPlugin.loader,
                  'css-loader'
            ]
        }
    ]
}

purifycss-webpack

类似于tree shaking,对于CSS也有移除未使用部分的功能就是PurifyCSS,该插件就是在Webpack中集成该功能。使用时需要注意的几点如下:

  • 不能和CSS Modules类共同作用,可以通过whitelist选项设置白名单隔离CSS Modules作用部分。
  • 必须先提取CSS再purify化。
  • 使用该特性会丢失CSS source map。

uglifyjs-webpack-plugin

生产环境中压缩代码减小体积可以说是必备的,webpack4默认使用该插件处理这个问题。使用上需要配合webpack选项的optimization。实例如下:

{
    optimization: {
        minimizer: [new UglifyWebpackPlugin({sourceMap: true})]
    }
}

需要注意如果构建时启用了sourceMap,在使用该插件压缩时也应该传入sourceMap选项。

optimize-css-assets-webpack-plugin

同样的,CSS压缩也是必不可少的一部分,目前最好的解决方案就是该插件,其可以将选定的压缩器应用于CSS,通常使用cssnano压缩器来处理。实例如下:

{
    plugins: [
        new OptimizeCSSAssetsPlugin({
            cssProcessor: cssnano
        })
    ]
}

DefinePlugin

应用中很可能会有区分生产环境和开发环境执行的代码,比如生产环境中记录日志,开发环境中开启实验性功能等等。通过控制环境变量,区分出这部分代码打包能够很好的优化性能,这个插件就是做这件事。常用场景可以分为如下几类。

  • 区分环境
new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.DEBUG': JOSN.stringify(process.env.DEBUG)
})

也可以通过EnvironmentPlugin更方便的做这件事,该插件相当于DefinePlugin针对于区分环境的一个上层封装。

new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG'])

经过变量替换后,判断为false的条件语句后续将不会被打包到最终代码,这也是该插件优化性能的核心。

  • 特征功能标记
new webpack.DefinePlugin({
      'NICE_FEATURE': JSON.stringify(true),
    'EXPERIMENTAL_FEATURE': JSON.stringify(false)
})
  • 不同环境服务URL区分
new webpack.DefinePlugin({
      'SERVICE_URL': JSON.stringify("http://dev.example.com")
})

stats-webpack-plugin

分析构建统计信息有利于我们进一步优化打包,该插件可以灵活的管理统计信息的输出。

SplitChunksPlugin

在Webpack4中使用该插件替代了commonChunksPlugin来提取公共代码,并且提供了一份项目通用的默认配置,我们就从默认配置来分析这个插件的用法。

splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
        default: {
              minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
        }
    }
}
  • chunks: 该选项有3个取值: async,initial,all。在解释这3个选项之前必须先知道chunks是什么。首先webpack通过入口文件entry启动模块打包,打包过程首先定义了runtime中webpack要用到的函数以便后续过程使用,此时如果设置了多入口并有合理的公共代码提取配置,会将公共依赖抽离并和runtime代码打包形成一个新的代码块,此代码块就叫做entry chunk,再然后分析入口文件并打包模块依赖所形成的代码块就叫做initial chunk,而在initial chunk中如果设置了异步加载分割点,会从分割点再抽出chunk打包,形成的代码块就叫做async chunk
  • minSize/minChunks/maxInitialRequests/maxAsyncRequests: 当然,并不是所有公共代码块都需要被提取,这些选项限制了提取出公共代码块的条件。顾名思义,就是公共代码的体积,引用次数和并行请求数。
  • name: 该选项用来控制公共代码块命名,当不同公共代码块被赋予相同名称时会被合并。如果被设置为true,webpack会基于缓存组的key自动设置名称。
  • cacheGroups: 该选项可以配置提取公共代码的细节设定,和splitChunks相同key的配置会默认继承。数据结构为一个对象,键值对则代表着提取公共代码名称和对应的提取配置。如下三个配置是缓存组特有的:
  • test: 控制哪些模块被缓存组匹配到并按其规则提取,如果不设置则默认为所有模块。
  • priority: 当模块被多个缓存组匹配到时,则按该优先级设置值较高的规则提取。
  • reuseExistingChunk: 设置是否允许复用已经存在的代码块。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「工程化」「Webpack」「性能优化」 标签。