2020-12-02 CSS 文件的代码分割(4.92)

之前总结的代码分割都是针对JS代码的,
如果要对CSS代码进行代码分割,要怎么办呢?
借助MiniCssExtractPlugin插件

image
  • 在我们没有配置这个插件的时候,webpack是怎么对css代码进行打包的呢?我们来试一下

    image
image
  • 然后运行打包命令npm run dev-build
    1.png

然后运行代码会发现,样式生效了。
由此可以得知,`webpack`在打包的时候,它会把CSS文件直接打包到JS文件里。
这也就是我们常听说的`CSS IN JS`这样一个概念

但是这并不是我们希望的,
我们希望的是,在打包生成代码的时候,
如果我们引入的是CSS文件,那么把CSS文件单独打包到`dist`目录下,生成一个CSS文件,
而不是打包到`js文件里`,
这个时候,我们就要借助`MiniCssExtractPlugin`插件了;
具体的使用步骤如下:

  • 先安装
  npm install --save-dev mini-css-extract-plugin

此版本官方文档显示这个插件有个缺陷,
TODO:
HMR support
也就是现在这个模块是不支持模块热更新的,

这就意味着在开发环境使用这个插件,
改变了css的样式,这个样式不会及时的更新到我们的项目中,
我们需要手动刷新页面,
开发效率低


所以我们一般在线上环境的打包过程中使用,

  
  • 安装好以后,要配置一下webpack.prod.js
//dist目录下 webpack.prod.js配置如下

const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //引入这个插件
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const prodConfig = {
    mode: "production",
    devtool:'cheap-module-source-map',
    plugins: [           //使用这个插件,还需要对loader进行一些配置
      new MiniCssExtractPlugin({
        filename: '[name].css',
        chunkFilename: '[id].css',
      }),
}

module.exports = merge(commonConfig,prodConfig)

之前我们最终会通过style-loader把css样式挂载到页面上,
module: {
   rules: [
        {
            test: /\.scss$/,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: {
                            importLoaders: 2
                        }
                    },
                    'sass-loader',
                    'postcss-loader'
                ]
        },{
            test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'postcss-loader'
                ]
        }]
    },

module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              publicPath: (resourcePath, context) => {
       
              },
            },
          },
          'css-loader',
        ],
      },
    ],
  },

src目录下,webpack.common.js文件中,
rules中对应的css文件,scss文件这部分代码剪切下


const path = require('path')   
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin  = require('clean-webpack-plugin')

module.exports = {
    entry: {
        main: "./src/index.js"
    },  
    module: {
        rules: [
        {
            test: /\.js$/, 
            exclude: /node_modules/,  
            loader: 'babel-loader'
        },
        {
            test: /\.(jpg|png|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name]_[hash].[ext]',
                    outputPath: 'images/',
                    limit: 10240
                }
            }
        },
        {
            test: /\.(eot|ttf|svg|woff)$/,
            use: {
                loader: 'file-loader'
            }
        },
//剪切到,分别放入webpack.dev.js和webpack.config.js中
      //  {
     //       test: /\.scss$/,
     //           use: [
      //              'style-loader',
       //             {
      //                  loader: 'css-loader',
     //                   options: {
     //                       importLoaders: 2
     //                   }
    / /               },
    //                'sass-loader',
    //                'postcss-loader'
    //            ]
   //     },{
  //          test: /\.css$/,
   //             use: [
   //                 'style-loader',
    //                'css-loader',
     //               'postcss-loader'
   //             ]
  //      }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template:'./src/index.html'
        }),
        new CleanWebpackPlugin()
    ],
    output: {
        path:path.resolve(__dirname,'dist'),   
        filename: '[name].js'   
    }
}

webpack.dev.js中关于css文件,scss文件如下配置:


//webpack.dev.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const devConfig = {
    mode: "development",
    devtool:'cheap-module-eval-source-map',
    devServer: {
        contentBase: './dist',
        open: true,
        port: 8080,
        hot: true,  //让webpack开启  Hot Module Replace 的功能
    },
module: {
//在开发环境中还是使用style-loader
      rules:[
        {
          test: /\.scss$/,
          use:[
            'style-loader',
            {
              loader: 'css-loader',
              options: {
                            importLoaders: 2
                        }
            },            
            'sass-loader',
            'postcss-loader'
          ]
        },
        {
          test: /\.css$/,
          use: [
              'style-loader',
              'css-loader',
              'postcss-loader'
          ]
        }
      ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin()
    ],
    optimization: {
        usedExports: true  
    }
}

module.exports = merge(commonConfig,devConfig)

webpack.prod.js还剩下如下配置

//webpack.prod.js
const merge = require('webpack-merge');
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const prodConfig = {
    mode: "production",
    devtool:'cheap-module-source-map',
    module: {
  //在线上环境中
      rules:[
        {
          test: /\.scss$/,
          use:[
           MiniCssExtractPlugin.loader, //使用这个插件提供给我们的loader替换掉style-loader
            {
              loader: 'css-loader',
              options: {
                            importLoaders: 2
                        }
            },            
            'sass-loader',
            'postcss-loader'
          ]
        },
        {
          test: /\.css$/,
          use: [
             MiniCssExtractPlugin.loader,
              'css-loader',
              'postcss-loader'
          ]
        }
      ]
    },
plugins: [
        new MiniCssExtractPlugin({})
    ],
}

//最终导出的是 prodConfig 和 commonConfig 结合的配置
module.exports = merge(commonConfig,prodConfig)
  • 然后打包生成·线上·的代码
    npm run build,
    发现仍然没于对CSS做单独的打包
image
  • 分析下原因,可能是下边几个因素造成的,之前我们在webpack.dev.js里做了下边的配置

    image
    • 这个参数是给tree shaking使用的。
      在做tree shaking的时候,webpack会对所有的模块都去tree shaking
      但是有些模块我们不希望进行tree shaking
      所以我们配置了usedExports: true
      然后在package.json里,我们就可以在sideEffects去写一些内容来代替false值。
    image
    • 之前tree shaking对我们引入的style.css起了作用,
      发现我们虽然引入了文件,但是并没于使用它,
      所以直接就把style.css给干掉了,
      所以要解决这个问题,我们要对tree shaking做一下修改。
    image
    • 还有就是,之前我们把tree shaking的配置写在了dev开发环境里,
      实际上我们应该写在common这里,
      因为不管是线上代码,还是本地测试代码,
      我们都需要,使用这个参数来告诉我们的webpack
      有一些css文件不要去做tree shaking的处理,
      所以,我们需要把usedExports: truedev里剪切到common
    image
    • 然后我们再运行打包npm run build,发现可以单独对CSS进行打包了,
      dist目录下main.css就是我们这个页面上的 css文件对应的内容,
      main.css.map是一个sourceMap,里面存储了一些映射关系,
      可以方便我们对代码的调试
image
还可以对MiniCssExtractPlugin插件进行更详细的配置
image
  • 然后运行打包命令,发现打包生成的CSS文件的文件名,还是走的filename这个配置,
    而不是chunkFilename这个配置。
    也就是说,打包生成的CSS文件,如果是直接被页面引用,
    那么在使用这个插件的时候,就会走filename这个配置。
    如果是间接要被引用的CSS文件,就会走chunkFilename这个配置

    image
  • 如果我们再增加一个CSS文件,并引入到index.js文件里,进行打包,
    那么webpack会把这两个CSS文件的内容合并到一起。

    image
    image
    image
虽然我们实现了打包单独生成CSS文件,

但是我们可以看到,生成的文件代码并不是压缩状态的。
如果我们希望单独生成CSS文件是压缩状态的,可以用`optimize-css-assets-webpack-plugin这个插件

  • 先安装
npm install --save-dev optimize-css-assets-webpack-plugin

  • 然后引入和配置
//dist目录下的webpack.prod.js

const merge = require('webpack-merge');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); //引入这个插件
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const prodConfig = {
    mode: "production",
    devtool:'cheap-module-source-map',
    module: {
  //在线上环境中
      rules:[
        {
          test: /\.scss$/,
          use:[
           MiniCssExtractPlugin.loader, //使用这个插件提供给我们的loader替换掉style-loader
            {
              loader: 'css-loader',
              options: {
                            importLoaders: 2
                        }
            },            
            'sass-loader',
            'postcss-loader'
          ]
        },
        {
          test: /\.css$/,
          use: [
             MiniCssExtractPlugin.loader,
              'css-loader',
              'postcss-loader'
          ]
        }
      ]
    },
optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin({})], //创建的时候,参数为空,空对象
  },
plugins: [
        new MiniCssExtractPlugin({})
    ],
}

//最终导出的是 prodConfig 和 commonConfig 结合的配置
module.exports = merge(commonConfig,prodConfig)

然后我们再运行打包npm run build,
发现它不单单帮我们把样式做了合并,
还把代码做了压缩显示在了一行

image
更高级的用法
  • 之前我们是在只有一个入口文件的情况下,对CSS文件进行单独的打包和压缩。如果我们现在有多个入口文件,并且我们希望所有的入口文件引入的CSS文件都能够打包生成到一个CSS样式文件里。该怎么做呢?

    image
  • 看官方文档可以知道,我们现在使用的MiniCssExtractPlugin这个插件,它的底层也是借助了splitChunks这个插件的。我们可以在cacheGroups组里额外增加一个组叫做styles,只要发现打包的文件是CSS为后缀的文件,那么不管它是同步加载的还是异步加载的文件,统一的都把它打包到名字叫做styles的文件里去。
    enforce:true的意思是,忽略掉默认的一些参数,比如说minSize,maxSize等。这些参数都不管,只要是一个CSS文件,就会做代码的拆分,把代码分割到名字叫做styles的文件里去。

    image
如果我们想根据入口文件的不同,把CSS文件打包到不同的CSS文件里边去,该怎么办?
image

链接:https://www.jianshu.com/p/440b97da2262

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容