人生当中总是有你能鞭长莫及的范畴,然而如果在你能力所及的领域内,你尽到了本人全副的致力,那你还有什么能够遗憾呢?
问题形容
在某次我的项目开发中,我发现了一个乏味的问题:当我在本地环境中拜访页面时,标签元素的布局看起来很失常;然而当我将我的项目部署到开发环境后,标签元素的布局有些偏上,不再是之前的失常布局。这个问题引起了我的留神,并且我开始进行排查。
定位剖析
为了找出标签布局不统一的起因,我比拟了两个环境下作用于标签的款式,以确定它们之间是否有差别:
本地环境 | 开发环境 |
---|---|
比照能够发现,作用于标签的类款式 .bre-label-inner_container
和.brand-item_discountTag__SlxDf
在两个环境下加载程序不一样,而它们都存在一个款式属性vertical-align
,会相互笼罩,所以才导致两个环境下标签布局不统一。
为什么类款式的加载程序会有所不同呢?
两个环境下代码都是一样的,惟一的区别是构建过程中对款式解决形式不同。
本地环境解决款式
本地环境构建采纳 Development
模式,采纳 style-loader
解决款式,当 webpack
解决 CSS
款式文件时,style-loader
会将 CSS
款式文件转换为 JavaScript
模块,并将这些模块嵌入到生成的 JavaScript bundle
文件中。在浏览器加载 JavaScript bundle
文件时,style-loader
会在 HTML
页面中动态创建 <style>
标签,并将 CSS
款式插入到这些标签中,从而使款式失效。
理论我的项目中组件嵌套层级构造和款式援用如以下示例:
// Main 组件
import Label from "@/components/Label";
import "@casstime/bre-label/styles/index.scss"; // 含有.bre-label-inner_container 款式
const Main = () => {return <Label />}
// Labe 组件
import styles from "./index.module.scss"; // 含有.brand-item_discountTag__SlxDf 款式
const Label = () => {return <div className=`bre-label-inner_container ${styles.discountTag}`>...</div>
}
当下面代码构建后,会编译成一个个代码块构造,由 webpack
内置函数 __webpack_require__
深度调用执行,调用栈过程如下:
不难发现 ./index.module.scss
款式文件 先于 @casstime/bre-label/styles/index.scss
款式文件通过 style
标签注入到页面中,因而,本地环境会看到 .bre-label-inner_container {vertical-align: middle;}
笼罩.brand-item_discountTag__SlxDf {vertical-align: top;}
<html>
<head>
<style>.brand-item_discountTag__SlxDf {vertical-align: top;}</style>
<style>.bre-label-inner_container {vertical-align: middle;}</style>
</head>
</html>
开发环境解决款式
开发环境构建采纳 Production
模式,采纳 mini-css-extract-plugin
解决款式,MiniCssExtractPlugin.loader
用于将 CSS
款式从 webpack
打包生成的 JavaScript
文件中提取到独自的文件中,这些文件的名称和门路由 MiniCssExtractPlugin
插件的 filename
和 chunkFilename
配置选项决定。为了确保生成的 CSS
文件可能正确地利用到 HTML
页面中,联合插件html-webpack-plugin
主动生成 HTML
文件,并主动以 link
标签引入生成的 CSS
文件。
MiniCssExtractPlugin
插件自身并没有拆分 CSS
的性能,它只负责将 CSS
款式提取到独自的文件中,并将这些文件与 webpack
打包生成的 JavaScript
文件一起输入到指定的目录中。
如果您心愿在应用 MiniCssExtractPlugin
插件时拆分生成的 CSS
文件,能够联合应用 optimization.splitChunks
配置选项来实现。具体来说,您能够将 optimization.splitChunks
配置选项设置为一个对象,并在其中指定要拆分的 chunk
类型、最小体积和最小应用次数等参数。这样,webpack
将会主动将符合条件的 chunk
拆分成多个文件,并将其中的 CSS
款式提取到独自的文件中。
须要留神的是,为了正确地应用 MiniCssExtractPlugin.loader
,您必须先在 webpack
配置文件中引入 MiniCssExtractPlugin
插件,并将其增加到插件列表中。只有在插件被实例化并增加到插件列表中后,MiniCssExtractPlugin.loader
能力正确地将 CSS
款式提取到文件中。
MiniCssExtractPlugin
插件提取款式的程序是依照 webpack
打包时模块的依赖关系程序提取的。具体来说,如果一个款式文件 A
依赖了另一个款式文件 B
,那么在打包时,会先提取款式文件 B
,而后再提取款式文件 A
。这是因为在 webpack
的打包过程中,每个模块的依赖关系会被剖析进去,并依照依赖关系程序生成依赖图,这个依赖图中包含了款式文件之间的依赖关系。因而,在提取款式文件时,会依照依赖图的程序顺次提取,以确保款式文件的依赖关系被正确地解决。
须要留神的是,在提取款式文件时,还会思考款式文件的援用程序。例如,如果在 HTML
页面中先援用了款式文件 A
,再援用款式文件 B
,那么在提取款式文件时,会依照这个援用程序先提取款式文件 A
,再提取款式文件 B
。这样能够确保在页面加载时,款式文件的加载程序与 HTML
页面中的援用程序统一,防止了款式被谬误地笼罩或重写的问题。
假如有三个款式文件:a.scss
、b.scss
和 c.scss
,其中 a.scss
依赖于 b.scss
,a.scss
在 c.scss
后面引入:
// a.scss
@import 'b.scss';
/* ... */
// c.scss
/* ... */
在 webpack
的配置文件中,应用 MiniCssExtractPlugin
插件提取款式文件:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({filename: '[name].[contenthash].css'
})
]
};
在提取款式文件时,b.scss
会先被提取,而后是 a.scss
,最初是 c.scss
。在生成的 CSS
文件中,款式规定的程序则取决于它们在款式文件中的呈现程序。
假如 b.scss
定义了以下款式规定:
/* b.scss */
.foo {color: red;}
a.scss
依赖于 b.scss
,并定义了以下款式规定:
// a.scss
@import 'b.scss';
.bar {font-size: 16px;}
c.scss
定义了以下款式规定:
/* c.scss */
.baz {text-align: center;}
在生成的 CSS
文件中,款式规定的程序会依照它们在款式文件中的呈现顺序排列,即先是 b.scss
中定义的款式规定,而后是 a.scss
中定义的款式规定,最初是 c.scss
中定义的款式规定。因而,生成的 CSS
文件中款式规定的程序如下:
/* styles.css */
/* b.scss */
.foo {color: red;}
/* a.scss */
.bar {font-size: 16px;}
/* c.scss */
.baz {text-align: center;}
咱们理论我的项目援用程序是这样的:
最终提取到 CSS
文件中的款式规定程序该当是这样子:
/* @casstime/bre-label/styles/index.scss */
.bre-label-inner_container {vertical-align: middle; // 被笼罩}
/* ./index.module.scss */
.brand-item_discountTag__SlxDf {vertical-align: top;}
.brand-item_discountTag__SlxDf {vertical-align: top;}
笼罩.bre-label-inner_container {vertical-align: middle;}
,因而开发环境标签布局有些偏上。
解决方案
解决办法也比较简单,将款式文件 @casstime/bre-label/styles/index.scss
调整到具体组件中引入,保障引入程序
// Main 组件
import Label from "@/components/Label";
const Main = () => {return <Label />}
// Labe 组件
import styles from "./index.module.scss"; // 含有.brand-item_discountTag__SlxDf 款式
import "@casstime/bre-label/styles/index.scss"; // 含有.bre-label-inner_container 款式
const Label = () => {return <div className=`bre-label-inner_container ${styles.discountTag}`>...</div>
}
参考
style-loader
MiniCssExtractPlugin
optimization.splitChunks