mini-css-extract-plugin源码解析

这篇博客详细解析了webpack插件mini-css-extract-plugin的源码,介绍了该插件如何将CSS抽离到单独文件,对比了它与extract-text-webpack-plugin的区别,并通过实例演示了其工作流程,包括Dependency、Module、ModuleFactory和DependencyTemplate等核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前面我们写过几篇关于webpack的文章:

然后结合之前babel、eslint知识搭了一个比较复杂的vue项目:

在实战demo中我们有用到一个css的插件mini-css-extract-plugin,今天我们结合demo来分析一下源码。

简介

This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.

It builds on top of a new webpack v4 feature (module types) and requires webpack 4 to work.

Compared to the extract-text-webpack-plugin:

  • Async loading
  • No duplicate compilation (performance)
  • Easier to use
  • Specific to CSS

以上是官网的介绍~

mini-css-extract-plugin主要功能就是:“抽离css到单独的文件”,跟“extract-text-webpack-plugin”插件相比有以下优点:

  • 异步加载
  • 不会重复编译
  • 使用很方便
  • 特定于css

准备

我们还是用之前文章中实战的demo:webpack-vue-demo,demo已经在webpack的配置文件中配置了“mini-css-extract-plugin”插件:

webpack.config.js

...
//rule的配置
.rule("sass")
            .test( /\.(sass|scss)$/)//sass和scss文件
            .use("extract-loader")//提取css样式到单独css文件
                .loader(require('mini-css-extract-plugin').loader)
                .options({
   
                    hmr: isDev //开发环境开启热载
                })
                .end()
...
//插件的配置
 .plugin("extract-css")//提取css样式到单独css文件
        .use(require('mini-css-extract-plugin'), [{
   
            filename: "css/[name].css",
            chunkFilename: "css/[name].css"
        }])
        .end()

extract-text-webpack-plugin更多的用法和功能就不在这里介绍了,大家自己去看官网mini-css-extract-plugin

开始

我们在demo的项目根目录执行build命令:

npm run build

然后可以看到输出的dist目录中多了一个css文件:

dist/css/app.css

.app-container[data-v-5ef48958] {
   
    color: red;
    width: 26.66667vw
}

body, html {
   
    margin: 0;
    padding: 0
}

这里的css代码其实就是我们项目中抽离出来的css样式,在demo的,

src/app.vue:

<template>
  <div class="app-container">{
  { msg }}</div>
</template>

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";

@Component
export default class App extends Vue {
  msg = "hello world";
  user = {
    name: "yasin"
  };
  created(): void {
    // const name = this.user?.name;
    // console.log("name");
  }
}
</script>

<style scoped lang="scss">
.app-container {
  color: red;
  width: 200fpx;
}
</style>
<style lang="scss">
html,
body {
  margin: 0;
  padding: 0;
}
</style>

可以看到,抽离出来的css样式就是app.vue文件中的style内容。

ok~ 看完了最终打包过后的效果,我们接下来就直接通过源码的角度来分析一下mini-css-extract-plugin插件,看它是怎样把我们app.vue里面的style内容单独抽离到dist/app.css文件中的。

在开始分析之前先上一张我自己总结的webpack的编译流程图:

webpack的编译流程图

原理

这里我们提前说几个webpack的知识点:

Dependency

比如我们在代码中使用:

import 'xx.js'或者 require('xx.js')

的时候,webpack就会把“xx.js”看成一个依赖,然后会创建一个Dependency对象对这个依赖进行说明,包含了当前依赖的路径、context上下文等基本信息。

Module

模块,webpack会把一个个依赖创建成一个module对象,也就是说module的创建是依赖dependency对象的,

包含了当前模块的request路径、loaders(加载器集合)、loader加载过后的源码信息、依赖之间的关系等等。

ModuleFactory

模块工厂,正如其名一样“创建模块的工厂”,主要用于module对象的创建。

DependencyTemplate

依赖模版,主要就是把loaders加载过后的代码编译成当前环境,比如浏览器环境能够执行的代码。

第一步:添加CssModuleFactory和CssDependencyTemplate

mini-css-extract-plugin/dist/index.js:

... 
apply(compiler) {
   
    compiler.hooks.thisCompilation.tap(pluginName, compilation => {
   
      //添加CssModuleFactory,CssModuleFactory主要用于把css代码转换成webpack中的module(CssModule)
      compilation.dependencyFactories.set(_CssDependency.default, new CssModuleFactory());
      //CssDependencyTemplate主要是将CssModule编译成当前target环境(浏览器)能够执行的代码
      compilation.dependencyTemplates.set(_CssDependency.default, new CssDependencyTemplate());
      ...

CssModuleFactory:

class CssModuleFactory {
   
  create({
   
    dependencies: [dependency]
  }, callback) {
   
    //直接返回一个自定义的CssModule对象
    callback(null, new CssModule(dependency));
  }

}

CssModule:

class CssModule extends _webpack.default.Module {
   
  constructor(dependency) {
   
    super(MODULE_TYPE, dependency.context);
    this.id = '';
    this._identifier = dependency.identifier;
    this._identifierIndex = dependency.identifierIndex;
    this.content = dependency.content;
    this.media = dependency.media;
    this.sourceMap = dependency.sourceMap;
  } // no source() so webpack doesn't do add stuff to the bundle

 ...
	//build方法直接执行callback返回给webpack,告诉webpack当前模块已经加载完成
  build(options, compilation, resolver, fileSystem, callback) {
   
    this.buildInfo = {
   };
    this.buildMeta = {
   };
   
    callback();
  }
	
 ...

}

第二步:在chunk获取清单文件的时候分离出CssModule到单独的file

mini-css-extract-plugin/dist/index.js:

...
//非异步chunk渲染清单文件
compilation.mainTemplate.hooks.renderManifest.tap(pluginName, (result, {
   
        chunk
      }) => {
   
  //从当前chunk中分离出所有的CssModule
        
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值