1. css-loader、style-loader、sass-loader 的作用和执行顺序
css-loader:处理js中通过模块化语句import引入的css文件(webpack本身只认识.js、.json文件),比如import ./index.css,打包后的css文件会被写入到js文件中。style-loader:只通过css-loader将css打包进js文件中,页面样式并不会生效,使用style-loader将css-loader获取到的css代码,以<style></style>标签形式在html页面中插入css代码。sass-loader:将scss文件编译成浏览器能识别的css文件。
三者的使用顺序为:
module: {rules: [{test: /\.scss$/,use: [ 'style-loader', 'css-loader', 'sass-loader' ]}]}
所以可以看到,多个loader配合使用时,处理顺序是:从下到上或者从右到左的顺序。
2. webpack 对图片路径的处理
我们在本地开发环境中,对图片使用相对地址的图片路径是没有问题的,因为本地开发环境产出的css样式内容通过style-loader内联到html文档中,这是背景图片的路径是相对于html文档目录,所以是正确的。
而经过webpack打包到生产环境时,遇到相对路径的图片,会找到需要的图片文件提取出来放到相应的文件夹中,然后以这个文件夹名作为图片资源的相对路径。比如,图片一开始图片的引用路径为background-url: (./imgs/1.png),打包后图片存放的文件夹为images,图片的路径变为background-url: (images/1.png),而css文件打包在一个css文件夹中,所以在css文件中通过background-url: (images/1.png)是获取不到图片资源的。
解决办法:
webpack4在mini-css-extract-plugin的loader中配置publicPath
module: {rules: [{test: /\.css$/,use: [{loader: MiniCssExtractPlugin.loader,options: {publicPath: '../',hmr: process.env.NODE_ENV === 'development',},},'css-loader',],},],}
3. module、chunk、bundle 的区别
- 对于一份同逻辑的代码,当我们手写下一个一个的文件,它们无论是
ESM还是commonJS或是AMD,他们都是module; - 当我们写的
module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk文件,webpack会对这个chunk文件进行一些操作; webpack处理好chunk文件后,最后会输出bundle文件,这个bundle文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。
总结:
module、chunk和bundle其实就是同一份逻辑代码在不同转换场景下的取了三个名字:我们直接写出来的是module,webpack处理时是chunk,最后生成浏览器可以直接运行的bundle。
4. fileName 和 chunkFileName 的区别
filename就是对应于entry里面的输入文件,经过webpack打包后输出文件的文件名。比如说经过下面的配置,生成出来的文件名为index.min.js:
{entry: {index: "../src/index.js"},output: {filename: "[name].min.js", // index.min.js}}
chunkFilename指未被列在entry中,却又需要被打包出来的chunk文件的名称。一般来说,这个chunk文件指的就是要懒加载的代码。比如在路由中使用懒加载:
{path: '/foo',name: 'foo',component: () => import('./Foo.vue')}
总结:
filename指列在entry中,打包后输出的文件的名称;chunkFilename指未列在entry中,却又需要被打包出来的文件的名称。
5. hash、chunkhash、contenthash 的区别
哈希一般是结合CDN缓存来使用的。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。
hash计算是跟整个项目的构建相关。在如下配置中:
{entry: {index: "../src/index.js",utils: '../src/utils.js',},output: {filename: "[name].[hash].js", // 改为 hash},......plugins: [new MiniCssExtractPlugin({filename: 'index.[hash].css' // 改为 hash}),]}
因为hash是项目构建的哈希值,项目中如果有些变动,hash一定会变。比如说改动了utils.js的代码,index.js里的代码虽然没有改变,但是大家都是用的同一份hash,index.js打包出来的hash值也发生了改变,hash一变,缓存一定失效了,这样子是没办法实现CDN和浏览器缓存的。
chunkhash解决了hash存在的问题,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。在如下配置中:
{
entry: {
index: "../src/index.js",
utils: '../src/utils.js',
},
output: {
filename: "[name].[chunkhash].js", // 改为 chunkhash
},
......
plugins: [
new MiniCssExtractPlugin({
filename: 'index.[chunkhash].css' // 改为 chunkhash
}),
]
}
utils.js和index.js打包出文件的文件hash值是不同的,这个时候改变utils.js中的文件再打包,index.js打包出来的hash值是不会发生改变的。
但是,index.js和index.css同为一个chunk,如果 index.js内容发生变化,但是index.css没有变化,打包后他们的hash都发生变化,这对css文件来说是一种浪费。
contenthash更细化的解决了chunkhash存在的问题,它将根据资源内容创建出唯一hash,也就是说文件内容不变,hash就不变。在如下配置中:
{
entry: {
index: "../src/index.js",
utils: '../src/utils.js',
},
output: {
filename: "[name].[chunkhash].js",
},
......
plugins: [
new MiniCssExtractPlugin({
filename: 'index.[contenthash].css' // 改为 contenthash
}),
]
}
将index.css打包后哈希改为contenthash,这个时候无论修改index.js中的内容多少次,都不会改变index.css打包后的hash。
总结:
hash计算与整个项目的构建相关;chunkhash计算与同一chunk内容相关;contenthash计算与文件内容本身相关。
