1. css-loader、style-loader、sass-loader 的作用和执行顺序

  • css-loader:处理js中通过模块化语句import引入的css文件(webpack本身只认识.js.json文件),比如import ./index.css,打包后的css文件会被写入到js文件中。
  • style-loader:只通过css-loadercss打包进js文件中,页面样式并不会生效,使用style-loadercss-loader获取到的css代码,以<style></style>标签形式在html页面中插入css代码。
  • sass-loader:将scss文件编译成浏览器能识别的css文件。

三者的使用顺序为:

  1. module: {
  2. rules: [
  3. {
  4. test: /\.scss$/,
  5. use: [ 'style-loader', 'css-loader', 'sass-loader' ]
  6. }
  7. ]
  8. }

所以可以看到,多个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)是获取不到图片资源的。

解决办法:

  • webpack4mini-css-extract-pluginloader中配置publicPath
  1. module: {
  2. rules: [
  3. {
  4. test: /\.css$/,
  5. use: [
  6. {
  7. loader: MiniCssExtractPlugin.loader,
  8. options: {
  9. publicPath: '../',
  10. hmr: process.env.NODE_ENV === 'development',
  11. },
  12. },
  13. 'css-loader',
  14. ],
  15. },
  16. ],
  17. }

3. module、chunk、bundle 的区别

  1. 对于一份同逻辑的代码,当我们手写下一个一个的文件,它们无论是ESM还是commonJS或是AMD,他们都是module
  2. 当我们写的module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk文件,webpack会对这个chunk文件进行一些操作;
  3. webpack处理好chunk文件后,最后会输出bundle文件,这个bundle文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。

总结:

modulechunkbundle其实就是同一份逻辑代码在不同转换场景下的取了三个名字:我们直接写出来的是modulewebpack处理时是chunk,最后生成浏览器可以直接运行的bundle

4. fileName 和 chunkFileName 的区别

  1. filename就是对应于entry里面的输入文件,经过webpack打包后输出文件的文件名。比如说经过下面的配置,生成出来的文件名为index.min.js
  1. {
  2. entry: {
  3. index: "../src/index.js"
  4. },
  5. output: {
  6. filename: "[name].min.js", // index.min.js
  7. }
  8. }
  1. chunkFilename指未被列在entry中,却又需要被打包出来的chunk文件的名称。一般来说,这个 chunk文件指的就是要懒加载的代码。比如在路由中使用懒加载:
  1. {
  2. path: '/foo',
  3. name: 'foo',
  4. component: () => import('./Foo.vue')
  5. }

总结:

  • filename指列在entry中,打包后输出的文件的名称;
  • chunkFilename指未列在entry中,却又需要被打包出来的文件的名称。

5. hash、chunkhash、contenthash 的区别

哈希一般是结合CDN缓存来使用的。如果文件内容改变的话,那么对应文件哈希值也会改变,对应的HTML引用的URL地址也会改变,触发CDN服务器从源服务器上拉取对应数据,进而更新本地缓存。

  1. hash计算是跟整个项目的构建相关。在如下配置中:
  1. {
  2. entry: {
  3. index: "../src/index.js",
  4. utils: '../src/utils.js',
  5. },
  6. output: {
  7. filename: "[name].[hash].js", // 改为 hash
  8. },
  9. ......
  10. plugins: [
  11. new MiniCssExtractPlugin({
  12. filename: 'index.[hash].css' // 改为 hash
  13. }),
  14. ]
  15. }

因为hash是项目构建的哈希值,项目中如果有些变动,hash一定会变。比如说改动了utils.js的代码,index.js里的代码虽然没有改变,但是大家都是用的同一份hashindex.js打包出来的hash值也发生了改变,hash一变,缓存一定失效了,这样子是没办法实现CDN和浏览器缓存的。

  1. 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.jsindex.js打包出文件的文件hash值是不同的,这个时候改变utils.js中的文件再打包,index.js打包出来的hash值是不会发生改变的。

但是,index.jsindex.css同为一个chunk,如果 index.js内容发生变化,但是index.css没有变化,打包后他们的hash都发生变化,这对css文件来说是一种浪费。

  1. 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计算与文件内容本身相关。