mini-css-extract-plugin源码解析
前⾔
前⾯我们写过⼏篇关于webpack的⽂章:
…
然后结合之前babel、eslint知识搭了⼀个⽐较复杂的vue项⽬:
在实战demo中我们有⽤到⼀个css的插件,今天我们结合demo来分析⼀下源码。
简介
This plugin extracts CSS into parate 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 u
Specific to CSS
以上是官⽹的介绍~
mini-css-extract-plugin主要功能就是:“抽离css到单独的⽂件”,跟“extract-text-webpack-plugin”插件相⽐有以下优点:异步加载
不会重复编译
使⽤很⽅便
特定于css
准备
四六级成绩查询官网我们还是⽤之前⽂章中实战的demo:,demo已经在webpack的配置⽂件中配置了“mini-css-extract-plugin”插件:fig.js
...
//rule的配置
.rule("sass")
.test(/\.(sass|scss)$/)//sass和scss⽂件
.u("extract-loader")//提取css样式到单独css⽂件
.loader(require('mini-css-extract-plugin').loader)
vampiro
.options({
hmr: isDev //开发环境开启热载
})
.end()
...
/
/插件的配置
.plugin("extract-css")//提取css样式到单独css⽂件
.u(require('mini-css-extract-plugin'),[{
filename:"css/[name].css",
chunkFilename:"css/[name].css"
}])
.end()
extract-text-webpack-plugin更多的⽤法和功能就不在这⾥介绍了,⼤家⾃⼰去看官⽹。开始
我们在demo的项⽬根⽬录执⾏build命令:plant
npm run build
然后可以看到输出的dist⽬录中多了⼀个css⽂件:
yytdist/css/app.css
.app-container[data-v-5ef48958]{
color: red;
width: 26.66667vw
}
body, html{
margin: 0;
padding: 0
}criticize
这⾥的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";
ur = {
name: "yasin"
};
created(): void {
// const name = this.ur?.name;
// console.log("name");
}
}
</script>
<style scoped lang="scss">
.app-container {
gladder
color: red;
width: 200fpx;
}
</style>
<style lang="scss">
html,
body {
margin: 0;
padding: 0;
}
</style>
可以看到,抽离出来的css样式就是app.vue⽂件中的style内容。
ok~ 看完了最终打包过后的效果,我们接下来就直接通过源码的⾓度来分析⼀下插件,看它是怎样把我们app.vue⾥⾯的style内容单独抽离到dist/app.css⽂件中的。
在开始分析之前先上⼀张我⾃⼰总结的webpack的编译流程图:
原理
这⾥我们提前说⼏个webpack的知识点:
Dependency
⽐如我们在代码中使⽤:
import'xx.js'或者require('xx.js')
的时候,webpack就会把“xx.js”看成⼀个依赖,然后会创建⼀个Dependency对象对这个依赖进⾏说明,包含了当前依赖的路径、context上下⽂等基本信息。
Module
模块,webpack会把⼀个个依赖创建成⼀个module对象,也就是说module的创建是依赖dependency对象的,
包含了当前模块的request路径、loaders(加载器集合)、loader加载过后的源码信息、依赖之间的关
runwinzip>年末结转分录
系等等。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.t(_CssDependency.default,new CssModuleFactory());
//CssDependencyTemplate主要是将CssModule编译成当前target环境(浏览器)能够执⾏的代码
compilation.dependencyTemplates.t(_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, t);
this.id ='';
this._identifier = dependency.identifier;
this._identifierIndex = dependency.identifierIndex;
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.derManifest.tap(pluginName,(result,{
chunk
})=>{
//从当前chunk中分离出所有的CssModule
const renderedModules = Array.dulesIterable).filter(module => pe ===MODULE_TYPE);
if(renderedModules.length >0){
//在当前chunk的清单⽂件中添加⼀个单独的css⽂件(抽离css样式到单独的file)
result.push({
英文优化render:()=&derContentAst(compilation, chunk, renderedModules, questShortener), filenameTemplate:({
chunk: chunkData
})=>duleFilename(chunkData),
pathOptions:{
chunk,
contentHashType:MODULE_TYPE
},
identifier: ${pluginName}.${chunk.id},
hash: tHash[MODULE_TYPE]
});
}
});
//异步chunk渲染清单⽂件
compilation.derManifest.tap(pluginName,(result,{
chunk
})=>{
//从当前chunk中分离出所有的CssModule
const renderedModules = Array.dulesIterable).filter(module => pe ===MODULE_TYPE);
if(renderedModules.length >0){
//在当前chunk的清单⽂件中添加⼀个单独的css⽂件(抽离css样式到单独的file)
result.push({
render:()=&derContentAst(compilation, chunk, renderedModules, questShortener), filenameTemplate:this.options.chunkFilename,
pathOptions:{
chunk,
contentHashType:MODULE_TYPE
},
identifier:`${pluginName}.${chunk.id}`,
hash: tHash[MODULE_TYPE]
});
fbi是什么}
});
...
webpack的异步和⾮异步模块是什么概念呢?
⽐如我们使⽤以下代码:
import("xxx.js")//webpack中的⼀个异步chunk
这样的异步chunk在⽣成清单⽂件的就会⾛: