关于webpack:webpack不同环境构建导致样式规则顺序不同

3次阅读

共计 4213 个字符,预计需要花费 11 分钟才能阅读完成。

人生当中总是有你能鞭长莫及的范畴,然而如果在你能力所及的领域内,你尽到了本人全副的致力,那你还有什么能够遗憾呢?

问题形容

在某次我的项目开发中,我发现了一个乏味的问题:当我在本地环境中拜访页面时,标签元素的布局看起来很失常;然而当我将我的项目部署到开发环境后,标签元素的布局有些偏上,不再是之前的失常布局。这个问题引起了我的留神,并且我开始进行排查。

定位剖析

为了找出标签布局不统一的起因,我比拟了两个环境下作用于标签的款式,以确定它们之间是否有差别:

本地环境 开发环境

比照能够发现,作用于标签的类款式 .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 插件的 filenamechunkFilename 配置选项决定。为了确保生成的 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.scssb.scssc.scss,其中 a.scss 依赖于 b.scssa.scssc.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

正文完
 0