Beace Lee

Beace Blog

Written by Beace Lee who lives and works in China building useful things. You should follow him on Twitter

webpack 处理 css module的一种方式

February 08, 2018

在实际项目开发中,会经常遇到引入第三方库和开发者自己书写的css处理问题。首先我采用了postcss的语法,借助postcss的语法可以实现定义变量、全局css、自动添加浏览器内核的前缀等等,除此之外,借助css-loader,采用cssmodule的实现方式在react中定义className,并且为了防止重复的css代码,采用了`[name][local]-[hash:base64:5]`的命名方式。

场景

所有的css都通过style标签的方式注入到html文档中,并没有通过extract-text-webpack-plugin来处理成css文件

疑问

在该定义场景下,这里主要谈两个问题

  1. 一个是如何在webpack中分别处理vendor样式和开发者自己书写的样式
  2. 另一个是如何压缩vendor样式和自己书写的样式

处理方式

我采用了postcss的两个plugin来处理自己的css

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer'),
    require('cssnano')({
      preset: 'default',
    }),
  ],
};

autoprefixer 顾名思义,是添加浏览器内核前缀使用,主要处理兼容性问题,另外,cssnano主要来处理postcssminify。并且用css-loader来处理变量的命名和导入方式,

{
	test: /\.css$/,
	exclude: /node_modules/,
	use: [
		'style-loader',
		{
			loader: 'css-loader',
			options: {
				modules: true,
				localIdentName: '[name]_[local]-[hash:base64:5]',
			},
		},
		{
			loader: 'postcss-loader',
		},
	],
}

下图是最终的css。

在未使用cssnano处理之前的css

在处理完自身css的时候会出现一个问题,也就是当引用vendor的css时,并不希望用postcss来处理前缀等问题(因为vendor的css已经拥有了这些),甚至处理了会导致样式的错乱,即相当于手动修改了vendor的css,导致组件class与样式匹配不上的问题。

解决方式

其实,在webpack处理loader的时候,是会根据开发者的入口文件递归遍历,在使用loader时,同一个loader可以针对不同文件使用多次。loader的配置中可以通过这样的方式来区分不同文件

{ exclude: /node_modules/ }

通过这样的方式,排除了node_modules下的css,另一个问题又出现了,排除了node_modules中的css样式之后,此时引入的css还想进行压缩或者vendor的样式库并不支持css_module时该如何处理?

可以通过如下方式进行处理,排除掉源代码中的css,并且采用css-loader的options来定义它的处理。

{
	test: /\.css$/,
	exclude: [/src/],
	use: [
		'style-loader',
		{
			loader: 'css-loader',
			options: {
				minimize: true,
				importLoaders: 0,
			},
		},
	],
},

GitHub - postcss/postcss-loader: PostCSS loader for webpack的文档中解释了这里的内容

This loader cannot be used with CSS Modules out of the box due to the way css-loader processes file imports. To make them work properly, either add the css-loader’s importLoaders option.

css-loader中也有相同的解释

{
  test: /\.css$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        importLoaders: 2 // 0 => no loaders (default); 1 => postcss-loader; 2 => postcss-loader, sass-loader
      }
    },
    'postcss-loader',
    'sass-loader'
  ]
}

loader不能够用css_module的方式处理的时候,需要借助css-loaderimportLoaders选项来进行处理。

这样,webpack的build速度也会提升,我这里从12s提升到了10s,最终vendor的css如下

总结

开发过程中总有一些外部资源不是那么尽如人意,我们需要去尽量的解耦并且做到0冲突,对于不同的位置的资源进行不同的方式的处理,但他们最终的效果应该是相同的。殊途同归,弯路再多,也只是在过程上的不同。