我们都知道,webpack有各种hash值,包括每次项目构建hash,不同入口的chunkhash、文件的内容contenthash,这么多hash,它们有什么区别呢?
hash类型 | 区别 |
---|---|
hash | hash是根据整个项目构建,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值 |
chunkhash | chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的代码块(chunk),生成对应的哈希值,某文件变化时只有该文件对应代码块(chunk)的hash会变化 |
contentHash | 每一个代码块(chunk)中的js和css输出文件都会独立生成一个hash,当某一个代码块(chunk)中的js源文件被修改时,只有该代码块(chunk)输出的js文件的hash会发生变化 |
hash是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值。
使用webpack构建时hash是使用最多的一种,webpack构建后整个项目的js和css输出文件的hash都相同;例如一个项目有6个组件,需要把组件1、2、3作为代码块(chunk)输出一组js和css文件,组件4、5作为代码块(chunk)输出一组js和css文件,webpack如下配置:
output: {
path: path.resolve(__dirname, OUTPUT_PATH),
filename: '[name].[hash].js',// 使用hash
publicPath: '/dist/webpack/'
}
通过webpack构建完后输出的第一组js、css文件的hash相同,并且第二组和第一组的hash也相同,下图是hash在项目中的效果:
所以只要某一个文件被修改,所有输出文件的hash都会跟着变化;因此它有一个弊端,一旦修改了某一个文件,整个项目的文件缓存都会失效。
chunkhash,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。简单来说这种是根据不同入口来配置的,比如react,react-router、resux等公共入口文件,只要这些没有改变,那么他对应生成的js的hash值也不会改变。
chunkhash相对hash影响范围比较小,使用chunkhash时,每一个代码块(chunk)输出文件对应一个hash,某源文件被修改后,只有该源文件所在代码块(chunk)的输出文件的hash会变化;例如一个项目有6个组件,需要把组件1、2、3作为代码块(chunk)输出一组js和css文件,组件4、5作为代码块(chunk)输出一组js和css文件,webpack如下配置:
output: {
path: path.resolve(__dirname, OUTPUT_PATH),
filename: '[name].[chunkhash].js', // 使用chunkhash
publicPath: '/dist/webpack/'
}
通过webpack打包构建完后输出的两组hash不同,但是每一组内部js和css的hash相同,下图是chunkhash在项目中的效果:
contenthash主要是处理关联性,比如一个js文件中引入css,但是会生成一个js文件,一个css文件,但是因为入口是一个,导致他们的hash值也相同,所以当只有js修改时,关联输出的css、img等文件的hash值也会改变,这种情况下就需要contenthash了。
当使用mini-css-extract-plugin插件时还可以使用contenthash来获取文件的hash,contenthash相对于chunkhash影响范围更小;每一个代码块(chunk)中的js和css输出文件都会独立生成一个hash,当某一个代码块(chunk)中的js源文件被修改时,只有该代码块(chunk)输出的js文件的hash会发生变化;例如一个项目有6个组件,需要把组件1、2、3作为代码块(chunk)输出一组js和css文件,组件4、5作为代码块(chunk)输出一组js和css文件,webpack如下配置:
output: {
path: path.resolve(__dirname, OUTPUT_PATH),
filename: '[name].[contenthash].js', // 使用contenthash
publicPath: '/dist/webpack/'
}
通过webpack打包构建完后输出的两组hash不同,而且每一组内部js和css的hash也不同,下图是contenthash在项目中的效果:
1、提升webpack打包速度和项目体积:
将webpack入口的chunk文件中所有公共的代码提取出来,减少代码体积;同时提升webpack打包速度。
2、利用缓存机制:
依赖的公共模块文件一般很少更改或者不会更改,这样独立模块文件提取出可以长期缓存。
3、利用浏览器缓存
方便我们在改动代码的时候,线上代码发版后及时读取最新的js文件,防止出现缓存问题。
webpack的hash是通过crypto加密和哈希算法实现的,webpack提供了hashDigest(在生成 hash 时使用的编码方式,默认为 'hex')、hashDigestLength(散列摘要的前缀长度,默认为 20)、hashFunction(散列算法,默认为 'md5')、hashSalt(一个可选的加盐值)等参数来实现自定义hash;下面依次讲述三种hash生成策略。
webpack的三种hash生成策略都是根据源码内容来生成,只是该源码已经被webpack封装成能在webpack环境中运行的代码了,包含每一个源文件的绝对路径;webpack会在build阶段根据源码给对应的模块(module)生成一个_buildHash(后续根据该值生成模块的hash),如下图所示可以看到源码中包含绝对路径。
webpack在seal阶段生成三种hash,最后根据output的配置决定使用哪种hash,webpack通过执行Compilation.createHash函数来生成hash。
下面主要讲一下hash的生成过程,其中chunkhash的生成过程包含在其中。webpack生成hash的第一步是获取Compilation下面的所有modules,把所有的module在build阶段生成的_buildHash作为内容生成一个新的hash值;然后获取到所有的代码块(chunks),分别把代码块(chunk)中包含的module的hash作为内容生成代码块(chunk)的hash,该hash就是配置chunkhash时需要使用的hash值;最后把所有代码块(chunks)的hash作为内容生成一个hash就是最终的hash,如下源码所示。
// 非源码,代码有删减
createHash() {
// 把所有的module根据在build阶段生成_buildHash来生成一个新的hash值
const modules = this.modules;
for (let i = 0; i < modules.length; i++) {
const module = modules[i];
const moduleHash = createHash(hashFunction);
module.updateHash(moduleHash);
}
// clone needed as sort below is inplace mutation
const chunks = this.chunks.slice();
// 给所有的chunks分别生成一个hash
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const chunkHash = createHash(hashFunction);
try {
chunk.updateHash(chunkHash);
// chunk中包含的所有module的hash作为内容生成一个hash值
template.updateHashForChunk(
chunkHash,
chunk,
this.moduleTemplates.javascript,
this.dependencyTemplates
);
chunk.hash = chunkHash.digest(hashDigest);
// 把所有的chunks的hash作为内容
hash.update(chunk.hash);
// 生成contentHash
this.hooks.contentHash.call(chunk);
} catch (err) {}
}
// 生成hash
this.fullHash = hash.digest(hashDigest);
this.hash = this.fullHash.substr(0, hashDigestLength);
}
contenthash生成跟前两种hash生成不一样,它是通过mini-css-extract-plugin和JavascriptModulesPlugin插件生成的hash;mini-css-extract-plugin是webpack打包构建时把css类型的module单独分类出来的插件,使用该插件时会为css类型的文件单独生成hash;它会把代码块(chunk)中所有类型为css/mini-extract的module的hash作为内容生成chunkhash。
// mini-css-extract-plugin插件的css文件hash生成的钩子函数
compilation.hooks.contentHash.tap(pluginName, chunk => {
const { outputOptions } = compilation;
const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
const hash = createHash(hashFunction);
// 把chunk中所有类型为`css/mini-extract`的module的hash作为内容生成hash
for (const m of chunk.modulesIterable) {
if (m.type === MODULE_TYPE) {
m.updateHash(hash);
}
}
const { contentHash } = chunk;
// 把生成的内容放入chunk对象的contentHash中
contentHash[MODULE_TYPE] = hash.digest(hashDigest).substring(0, hashDigestLength);
});
contentHash钩子触发时会调用JavascriptModulesPlugin插件注册的contentHash事件,把代码块(chunk)中所有类型为函数的module的hash作为内容生成hash。
// JavascriptModulesPlugin插件为js生成contentHash的钩子函数
compilation.hooks.contentHash.tap("JavascriptModulesPlugin", chunk => {
// ...此处有删减
for (const m of chunk.modulesIterable) {
if (typeof m.source === "function") {
hash.update(m.hash);
}
}
chunk.contentHash.javascript = hash
.digest(hashDigest)
.substr(0, hashDigestLength);
});
webpack的hash虽然给我们带来了极大的方便,但是也存在一些弊端;webpack的三种hash策略都依赖module的_buildHash,而_buildHash值又依赖module的源文件内容和绝对路径,所以同一份源码在不同的机器上构建出来的hash值不一定一样,除非两台机器上的项目路径完全相同;若线上存在多机器构建部署同一个项目时,可能hash值不同而导致访问js或者css时出现404现像。
若想多机器部署hash一样,下面是解决多机器构建生成hash的策略:
webpack 在前端工程中随处可见,当前流行的 vue, react, weex 等解决方案都推崇 webpack 作为打包工具。前端工具云集的时代,这是你值得选择的之一。
webpack是前端工程构建的一套工具,为什么一个程序称之为一套呢,是因为webpack其实是npm的一个模块,使用起来的话,这期间还需要很多其它模块来进行支持,所以我称之为一套工具。
本文从一个小Demo开始,通过不断增加功能来说明webpack的基本配置,只针对新手。webpack基本的配置就可以熟悉了,会引入loader,配置loader选项,会设置alias,会用plugins差不多。
Plugins是webpack的基础,我们都知道webpage的plugin是基于事件机制工作的,这样最大的好处是易于扩展。讲解如果扩展内置插件和其他插件,以及我们常用的Plugins插件
webpack技巧的总结:进度汇报、压缩、复数文件打包、分离app文件与第三方库文件、资源映射、输出css文件、开发模式、分析包的大小、更小的react项目、更小的Lodash、引入文件夹中所有文件、清除extract-text-webpack-plugin日志。
Webpack 作为目前最流行的前端构建工具之一,在 vue/react 等 Framework 的生态圈中都占据重要地位。在开发现代 Web 应用的过程中,Webpack 和我们的开发过程和发布过程都息息相关,如何改善 Webpack 构建打包的性能也关系到我们开发和发布部署的效率。
新版 Webpack 中我们所做的每一个更新目的都在于此,为了当大家在使用 Webpack 的时候敏捷连续毫无顿挫感。 webpack 4 进行构建性能测试,得出的结果非常有趣。结果很惊人,构建时间降低了 60%-98%!
Webpack 是一个现代 JavaScript 应用程序的模块打包器 (module bundler) 。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块
Tobias Koppers是一位自由软件开发者,家住德国纽伦堡。他因写出webpack这个已有数百万开发者使用的开源软件而名噪一时。他目前专注于JavaScript和开源项目。以下是我对他个人的专访,希望对大家有所启发。
本文讲述css-loader开启css模块功能之后,如何与引用的npm包中样式文件不产生冲突。比如antd-mobilenpm包的引入。在不做特殊处理的前提下,样式文件将会被转译成css module。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!