前端必备技能 webpack - 4. webpack处理CSS资源

每篇文章纯属个人经验观点,如有错误疏漏欢迎指正。转载请附带作者信息及出处。

点这里查看 webpack 系列文章目录

博客中的代码位于 码云Git仓库,如有需要可自行前往下载。

前端必备技能 webpack - 4. webpack处理CSS资源

  因为 webpack 本身只具有识别 JS 的能力,所以涉及到其他资源,需要我们通过 loader 来进行特殊处理,针对不同的样式资源,需要以下几个 loader

  • style-loader:用于加载 css 文件,会通过 <style> 标签将相应内容插入到页面中;
  • css-loader:用于处理 .css 文件,帮助 webpack 识别资源;
  • less-loader:用于处理 .less 文件,将 .less 文件编译成 .css 文件;
  • sass-loader:用于处理 .sass/.scss 文件,将 .sass 文件编译成 .css 文件;
  • postcss-loader:通过 PostCSS 处理 .css/.sss 文件;
  • stylus-loader:用于处理 Stylus 文件;

PostCSS 和 Stylus:

  PostCSS 官网地址:https://www.postcss.com.cn/

  Stylus 官网地址:https://stylus.bootcss.com/

  这里推荐张鑫旭大大提供翻译的 Stylus 中文文档: Stylus 中文文档

一、打包资源

  我们先创建项目并进行相应的初始化,同时创建一个简单的 webpack 配置文件,对创建项目过程有疑问的同学,可以查看 前端必备技能 webpack - 2. webpack环境安装 的第四部分。

// webpack.config.js

// 引入 NodeJS 中的 Path 模块暴露的 resolve 方法来处理路径问题 
// 同样可以使用 join 方法
const { resolve } = require('path');

// 采用 CommonJS 语法暴露配置文件
module.exports = {
    // 开发模式
    mode: "development",
    // 入口文件
    entry: "./src/index.js",
    // 打包出口
    output: {
        // 设置打包文件名
        filename: "[name]-[chunkhash:8].js",
        // 设置打包后的文件路径
        // __dirname 为 NodeJs 中的变量,代表当前文件的绝对目录
        path: resolve(__dirname, "dist")
    },
    // loader 配置
    module: {
        rules: [
            // 详细配置
        ]
    },
    plugins: []
}

  这里推荐先将需要的 loader 都装好,将下列命令中的 loadername 替换为相应名称即可:

npm i loadername -D

  准备工作完成后,我们来尝试打包各种类型的样式文件吧。

1.1 打包 .css 资源

  在 src 下新建一个 index.css 文件,简单书写一些样式,并在入口文件 index.js 中将资源引入:

// index.js

import "./index.css";

  对于普通的 css 资源,只需要引入 style-loadercss-loader 即可处理。所以没安装 loader 的需要先进行安装:

npm i style-loader css-loader -D

  接下来我们对 webpack.config.js 进行修改:

rules: [
	{
		// 使用正则匹配所有 .css 结尾的文件
		test: /\.css$/,
		// 注意 loader 的处理顺序是从下到上,从右到左
		// 需要特别注意, `loader` 在引入时对顺序是有要求的!!!
		use: [
			// style-loader 会通过 style 标签将 JS 文件中的 css 资源添加到页面中
			"style-loader",
			// css-loader 会将 css 文件变成 CommonJS 的模块资源,加载到 JS 文件中
			"css-loader"
		],
	}
]

  然后打包:

在这里插入图片描述

  打包完成后,我们可以手动创建一个 html 文件将生成的 js 文件引入,查看效果是否正常。

1.2 打包 .less 资源

  同样在 src 下新建一个 index.less 文件,并将其引入到 index.js 中:

在这里插入图片描述

  我们知道 less 文件是不能被浏览器直接解析的,需要编译成 css 文件。webpack 同样需要将其进行编译,这里需要借助 lessless-loader

npm i less less-loader -D

  接下来我们修改配置文件:

rules: [
// 详细配置
	{
		test: /\.less$/,
		// less-loader: 将 .less 文件编译成 .css 文件
		use: ["style-loader", "css-loader", "less-loader"]
	}
]

  然后打包:

在这里插入图片描述

1.3 打包 .sass 资源

  sassless 的处理步骤十分类似,只不过负责编译 sass 文件需要: sasssass-loader

npm i sass sass-loader -D

  同样修改配置文件以匹配 sass 文件:

rules: [
// 详细配置
	{
		test: /\..sass$/,
		// sass-loader: 将 .sass 文件编译成 .css 文件
		use: ["style-loader", "css-loader", "sass-loader"]
	}
]

  然后打包:

在这里插入图片描述

1.4 总结

  关于 PostCSSStylus 文件的处理这里就不加掩饰,我们可以通过总结一下上方三种样式资源的处理方式,得出关于资源编译的特性,无论处理任何资源都可以按照这个步骤来进行

  • Step 1:下载安装用于处理资源的相关 loader
  • Step 2: 通过 module.rules.resource 提供的各种方式来匹配需要处理的资源;
  • Step 3: 使用 module.rules.use 等来加载负责处理匹配到资源的 loader
  • Step 4: 如有需要,可以通过 options 来设置对 loader 的配置;

需要特别注意, loader 在引入时对顺序是有要求的!!!

  以 .sass 文件为例,sass-loader 负责将 .sass 文件编译为 .css 文件;接着 css-loader.css 文件转化为 CommonJS 模块加载到相应的 js 文件中;最后 style-loaderjs 文件中相应的 css 通过 <style> 标签加载到页面中,所以 use 中的内容要严格按照这个顺序:

use: ["style-loader", "css-loader", "sass-loader"]

  最后附上最终的 webpack.config.js 文件代码:

/**
 *  @filename: webpack.config.js
 *  @Author: wang
 *  @Description: webpack的配置文件,采用 CommonJS 语法 
 */

// 引入 NodeJS 中的 Path 模块暴露的 resolve 方法来处理路径问题 
// 同样可以使用 join 方法来拼接路径
const { resolve } = require('path');

// CommonJS 语法暴露配置文件
module.exports = {
    // 开发模式
    mode: "development",
    // 入口文件
    entry: {
        index: "./src/index.js"
    },
    // 打包出口
    output: {
        // 设置打包后的文件名为 入口名 - chunkhash 前八位 .js
        filename: '[name]-[chunkhash:8].js',
        // 设置打包后的文件位于 dist 文件夹
        // __dirname 为 NodeJs 中的变量,代表当前文件的绝对目录
        path: resolve(__dirname, "dist")
    },
    // loader 配置
    module: {
        rules: [
            // 详细配置
            {
                // 使用正则匹配所有 .css 结尾的文件
                test: /\.css$/,
                // 通过 style-loader 和 css-loader 处理匹配到的文件
                // loader 的执行顺序是从下到上的 千万注意 loader 的书写顺序
                use: [
                    // 通过 style 标签将 JS 文件中的 css 资源添加到页面的 head 中
                    "style-loader",
                    // 将 css 文件变成 CommonJS 的模块资源,加载到 JS 文件中
                    "css-loader"
                ],
            },
            {
                test: /\.less$/,
                // less-loader: 将 .less 文件编译成 .css 文件
                use: ["style-loader", "css-loader", "less-loader"]
            },
            {
                test: /\.scss$/,
                // sass-loader: 将 .sass 文件编译成 .css 文件
                use: ["style-loader", "css-loader", "sass-loader"]
            },

        ]
    },
    // 插件
    plugins: []
}

项目目录及package.json:

项目目录及package.json内容

二、提取

2.1 提取样式到单独 css 文件

  我们发现,webpack 在打包完成后,css 资源被打包到了 js 当中,并没有将表现层和行为层进行分离,显然,这并不符合我们的开发习惯,在慢网速下也会由于 js 文件加载缓慢造成样式丢失等问题。这个时候,我们需要借助一个插件将 css 文件单独提取出来:

npm i mini-css-extract-plugin -D

  安装完毕后我们修改一下项目的目录,并创建一些文件:

在这里插入图片描述

  接下来修改配置文件,将插件引入进来:

/**
 *  @filename: webpack.config.js
 *  @Author: wang
 *  @Description: webpack的配置文件,采用 CommonJS 语法 
 */

// 与 loader 不同,使用插件需要提前引入
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const { resolve } = require('path');

module.exports = {
    // 开发模式
    mode: "development",
    // 多入口文件
    entry: {
        a: "./src/js/a.js",
        b: "./src/js/b.js"
    },
    // 打包出口
    output: {
        filename: '[name]-[chunkhash:8].js',
        path: resolve(__dirname, "dist")
    },
    // loader 配置
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                
                    // 这里我们需要使用 MiniCssExtractPlugin 取代 style-loader 
                    // style-loader : 将 css 通过 style 标签添加到页面中 
                    // MiniCssExtractPlugin.loader: 将 JS 文件中的 css 资源提取到单独的 css 文件中
                   
                    //MiniCssExtractPlugin.loader,
					{
						loader:MiniCssExtractPlugin.loader,
						options:{
							// 如果打包时背景图片等资源的路径与开发时不一致 可以通过 publicPath 设置公共路径
							publicPath:""
						}
					},
                    "css-loader"
                ],
            },

        ]
    },
    // 插件
    plugins: [
    	// 插件都需要通过 new 的方式来调用
        new MiniCssExtractPlugin({
        	// 设置生成的文件路径及文件名
            filename: "/static/css/[name]-[chunkhash:8].css"
        })
    ]
}

  搞定,我们来执行一下打包命令:

在这里插入图片描述

  我们发现 a.cssb.css 都被打包成了单独的文件,但 c.css 却被分别打包进了两个文件内。

2.2 打包公共样式

  在工作中我们常常会将公共部分提取出来,单独引用着一个文件,在上方的实例中,c.css 就是一个公共资源,应该单独打包,而不是分别打包到每个引用他的文件内,这里需要借助另外一个插件: SplitChunksPlugin

  SplitChunksPluginwebpack4.0 以上版本的内置插件,无须安装,可以直接配置使用:

module.exports = {
	
	// 省略......

	optimization: {
		splitChunks: {
			cacheGroups: {
				//打包公共模块
				commons: {
					// initial 表示提取每个入口文件中的公共部分
					chunks: 'initial', 
					// 提取的 chunk 最少被引用一次才允许被提取
					minChunks: 1, 
					// 设置提取公共部分最小的大小
					// 假设值为 1* 1024 则表示 1KB 以下的文件不参与提取
					minSize: 0, 
					// 提取出来的文件命名
					name: 'common' 
				}
			}
		}
	}
}

  我们删除掉之前打包出的文件夹,重新进行打包:

在这里插入图片描述

  这时候,我们已经成功的将公共样式文件单独打包成了一个文件,最后的 webpack.config.js 文件代码:

/**
 *  @filename: webpack.config.js
 *  @Author: wang
 *  @Description: 通过 mini-css-extract-plugin 提取样式资源到单独的文件
 */

// 与 loader 不同,使用插件需要提前引入
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const { resolve } = require('path');

module.exports = {
    // 开发模式
    mode: "development",
    // 入口文件
    entry: {
        a: "./src/js/a.js",
        b: "./src/js/b.js"
    },
    // 打包出口
    output: {
        filename: '[name]-[chunkhash:8].js',
        path: resolve(__dirname, "dist/static/js")
    },
    // loader 配置
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [

                    // 这里我们需要使用 MiniCssExtractPlugin 取代 style-loader 
                    // style-loader : 将 css 通过 style 标签添加到页面中 
                    // MiniCssExtractPlugin.loader: 将 JS 文件中的 css 资源提取到单独的 css 文件中

                    MiniCssExtractPlugin.loader,
                    "css-loader"
                ],
            },

        ]
    },
    // 插件
    plugins: [
        // 插件都需要通过 new 的方式来调用
        new MiniCssExtractPlugin({
            // 设置生成的文件路径及文件名
            filename: "../css/[name]-[chunkhash:8].css"
        })
    ],
	optimization: {
		splitChunks: {
			cacheGroups: {
				//打包公共模块
				commons: {
					// initial 表示提取每个入口文件中的公共部分
					chunks: 'initial', 
					// 设置公共部分提取的最少文件数为 2
					minChunks: 2, 
					// 设置提取公共部分最小的大小
					// 假设值为 1* 1024 则表示 1KB 以下的文件不参与提取
					minSize: 0, 
					// 提取出来的文件命名
					name: 'common' 
				}
			}
		}
	}
}

三、压缩

  为了减少文件大小,提高访问速度,一般我们会在上线时对文件进行压缩处理,压缩 css 文件时,需要安装一个插件:

npm i optimize-css-assets-webpack-plugin -D

  安装完毕后,我们只需要安装插件的使用方法将其引入即可:

// 与 loader 不同,使用插件需要提前引入
// 提取 css 插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 压缩 css 插件
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

const { resolve } = require('path');

module.exports = {
	
	// 省略......
	
    plugins: [
        // 插件都需要通过 new 的方式来调用
        new MiniCssExtractPlugin({
            // 设置生成的文件路径及文件名
            // filename: resolve(__dirname, "./css/[name]-[chunkhash:8].css")
            filename: "../css/[name]-[chunkhash:8].css"
        }),
        // 直接引入即可
        new OptimizeCssAssetsPlugin()
    ]
}

四、消除无用 css

  我们在开发时一般都会引入各种框架来辅助开发,而我们往往只会用到其中的一部分,而不是所有的样式,这就造成了代码冗余。就算是我们自己写的样式,有时候也会因为页面更新等原因导致忘记删除无用的部分,现在我们可以借用 webpack 将无用部分直接去除:

npm i purifycss-webpack purify-css -D

  因为涉及到 css 的引用,所以我们还需要对 html 资源进行处理:

npm i html-webpack-plugin -D

  关于使用 html-webpack-plugin 打包处理 html 的内容,我们在后面的章节另行介绍,这里先知道这个插件用于打包 html 内容,自动引入文件即可。

  引入完毕后,我们给 a.css | b.css 随意添加一些样式,:

在这里插入图片描述

  然后修改配置文件:


// 省略......

plugins: [
	// 打包 html 资源
    new HtmlWebpackPlugin({
        // 复制 './src/a.html' 文件结构
        template: './src/a.html',
        // 在打包生成的页面中自动引入 a 模块输出的所有资源
        chunks: ['a'],
        // 设置打包路径及文件名
        filename: resolve(__dirname,"./dist/a.html")
    }),
    new HtmlWebpackPlugin({
        template: './src/b.html',
        chunks: ['b'],
        filename: resolve(__dirname,"./dist/b.html")
    }),
    // 去除无用 css
    new PurifyPlugin({
    	// 通过 NodeJs 中的 glob 模块扫码 html 文件,找出没有用到的样式
        paths: glob.sync(resolve(__dirname, 'src/*.html'))
    })
],

  修改完成后打包查看输出结果:

打包文件展示
  我们发现直接随意添加的两个类名由于并没有在页面中引入,所以直接被 webpack 干掉了。

五、兼容性处理

  我们知道有些属性在各个浏览器中拥有不同的前缀,我们在开发时需要将每一个浏览器的前缀都加上,例如说我们常用的弹性布局 display:flex,为了处理兼容性常常需要写成以下形式:

.box1{
	display: -moz-box;  		/* Firefox */
	display: -ms-flexbox;  		/* IE10 */
	display: -webkit-box;    	/* Safari */ 
	display: -webkit-flex;    	/* Chrome, WebKit */
	display: flex;
}

  这让我们在开发时还需要手动添加额外的属性,感觉上十分的麻烦,所以这时候,我们可以选择通过 webpack 来处理:

npm i postcss postcss-loader postcss-preset-env -D

  在之前版本中,相关配置可以直接写在 loader 中:


// 省略...
rules: [
	{
		loader: 'postcss-loader',
		options: {
			// 固定写法 使用 postcss 方式识别
			ident: 'postcss',
			plugins: () => [
				// 引用 postcss-preset-env 插件
				require('postcss-preset-env')()
			]
		}
	}
]

  但目前版本的 postcss 需要将配置分别写在 loaderplugins 中:


// 省略...
	
module: {
     rules: [
         {
             test: /\.css$/,
             use: [
                 MiniCssExtractPlugin.loader,
                 "css-loader",
                 // postcss-loader 需要在 css-loader 前, less/sass-loader 后引用
                 "postcss-loader"
             ],
         },

     ]
}
plugins: [
	require('postcss-preset-env')()
]

  修改完配置文件后,还需要我们提供一个相应的配置文件,决定如何处理兼容性,之前版本可以直接在 package.json 中声明 browserslist 属性即可:

"browserslist": [
    "defaults",
    "not ie < 10",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]

  但更新后需要在项目根目录新建一个 postcss.config.js 的配置文件来进行设置:

/**
 *  @filename: postcss.config.js
 *  @Author: wang
 *  @Description: postcss 配置文件 指示那些情况下需要处理 那些情况不需要处理兼容性
 */
module.exports = {
	plugins: [
		require('autoprefixer')({
			"browsers": [
			"defaults",
			"not ie < 11",
			"last 2 versions",
			"> 1%",
			"iOS 7",
			"last 3 iOS versions"
			]
		})
	]
}

  如果还是想将相应内容写到 package.json 中,则需要将 postcss.config.js 修改为:

/**
 *  @filename: postcss.config.js
 *  @Author: wang
 *  @Description: postcss 配置文件 指示那些情况下需要处理 那些情况不需要处理兼容性
 */

module.exports = {
    plugins: [
        require('autoprefixer')()
    ]
}

  打包完成后,我们发现 webpack 已经为我们自动加上了前缀。

在这里插入图片描述

  以上就是本篇博客的内容,博客内容较长,建议无基础的同学分段阅读。关于对 css 资源的处理,大体上就是以上的内容,博客中的代码位于 码云Git仓库,如有需要可自行前往下载。

   下一篇博客我们来介绍一下 webpackjs 资源的处理。


  感谢大家的观看,点赞和收藏,我们下篇博客再见。

每篇文章纯属个人经验观点,如有错误疏漏欢迎指正。
转载请附带作者信息及出处。您的评论和关注是我更新的动力!

下面是我的个人微信公众号,我会在里面定期更新前端的相关技术文章,欢迎大家前来讨论学习。如果有什么问题需要老王帮忙或者想看关于某个主题的文章,也可以通过留言等方式来联系老王。
在这里插入图片描述
都看到这里了,三连一下呗~~~。点个收藏,少个 Bug 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值