每篇文章纯属个人经验观点,如有错误疏漏欢迎指正。转载请附带作者信息及出处。 点这里查看 webpack 系列文章目录 博客中的代码位于 码云Git仓库,如有需要可自行前往下载。
因为 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准备工作完成后,我们来尝试打包各种类型的样式文件吧。
在 src 下新建一个 index.css 文件,简单书写一些样式,并在入口文件 index.js 中将资源引入:
// index.js import "./index.css";对于普通的 css 资源,只需要引入 style-loader 和 css-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 文件引入,查看效果是否正常。
同样在 src 下新建一个 index.less 文件,并将其引入到 index.js 中:
我们知道 less 文件是不能被浏览器直接解析的,需要编译成 css 文件。webpack 同样需要将其进行编译,这里需要借助 less 和 less-loader:
npm i less less-loader -D接下来我们修改配置文件:
rules: [ // 详细配置 { test: /\.less$/, // less-loader: 将 .less 文件编译成 .css 文件 use: ["style-loader", "css-loader", "less-loader"] } ]然后打包:
sass 和 less 的处理步骤十分类似,只不过负责编译 sass 文件需要: sass 和 sass-loader:
npm i sass sass-loader -D同样修改配置文件以匹配 sass 文件:
rules: [ // 详细配置 { test: /\..sass$/, // sass-loader: 将 .sass 文件编译成 .css 文件 use: ["style-loader", "css-loader", "sass-loader"] } ]然后打包:
关于 PostCSS 和 Stylus 文件的处理这里就不加掩饰,我们可以通过总结一下上方三种样式资源的处理方式,得出关于资源编译的特性,无论处理任何资源都可以按照这个步骤来进行
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-loader 将 js 文件中相应的 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:
我们发现,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.css 和 b.css 都被打包成了单独的文件,但 c.css 却被分别打包进了两个文件内。
在工作中我们常常会将公共部分提取出来,单独引用着一个文件,在上方的实例中,c.css 就是一个公共资源,应该单独打包,而不是分别打包到每个引用他的文件内,这里需要借助另外一个插件: SplitChunksPlugin;
SplitChunksPlugin 是 webpack4.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() ] }我们在开发时一般都会引入各种框架来辅助开发,而我们往往只会用到其中的一部分,而不是所有的样式,这就造成了代码冗余。就算是我们自己写的样式,有时候也会因为页面更新等原因导致忘记删除无用的部分,现在我们可以借用 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 需要将配置分别写在 loader 和 plugins 中:
// 省略... 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仓库,如有需要可自行前往下载。
下一篇博客我们来介绍一下 webpack 对 js 资源的处理。
感谢大家的观看,点赞和收藏,我们下篇博客再见。
每篇文章纯属个人经验观点,如有错误疏漏欢迎指正。 转载请附带作者信息及出处。您的评论和关注是我更新的动力!
下面是我的个人微信公众号,我会在里面定期更新前端的相关技术文章,欢迎大家前来讨论学习。如果有什么问题需要老王帮忙或者想看关于某个主题的文章,也可以通过留言等方式来联系老王。 都看到这里了,三连一下呗~~~。点个收藏,少个 Bug 。