前言 没想到吧,这个系列还能出4,这篇会介绍Webpack中Css的相关内容,一些JavaScript的内容留到下个文章了。
CSS工程化 为什么要进行css工程化 在前端项目开发中,CSS是不可或缺的一部分,在前端项目不断变得庞大,复杂的今天,传统的CSS在开发中也暴露了一些问题,这驱使前端开发者不断寻找CSS工程化的最佳实践
传统开发会使用一个link标签引入CSS样式
1 <link rel ="stylesheet" href ="index.css" >
这种开发模式有下面几个弊端
类名冲突:当页面复杂时,如果层级太浅,很容易导致类名冲突,但是过深的层级会导致代码的编写,阅读和复用变得困难,在打包时也难以压缩代码体积
重复样式:在页面中,有一些重复的值(比如主题色)会重复出现在代码中,难以维护和复用
CSS文件细分问题:在大型项目里,我们希望把CSS按照不同的模块来拆分,这样更有利于维护,比如一个轮播图模块,我们希望依赖的css样式只关心轮播图,这就要求css可以按照不同的模块,功能来拆分,css会比过去拆分的更细。
怎么解决上面的问题 这篇文章会介绍下面列出的方法,这里只是进行一个大致的介绍
类名冲突 命名约定 即提供一种命名的标准,来解决冲突,常见的标准有:
CSS in JS 这种方式用js对象来表示样式,然后把样式直接应用到元素的style中,这样一来,css变成了一个一个的对象,就可以利用js的一些特性对css进行操作
通过一个函数返回一个样式对象
把公共的样式提取到公共模块中返回
应用js的各种特性操作对象,比如:混合、提取、拆分
React-Native中就使用了这个方式
css module 非常好用的css模块化方案,这个方案会根据css文件的路径和类名生成一个新类名,在页面上应用这个新类名就可以避免重名了
解决重复样式 CSS in JS 既然是在JS里写CSS,自然可以解决重复样式,因为所有的公共部分都可以提取出来,然后需要时通过函数混合就行
预处理语言 预编译语言,你可以理解成CSS的高级版,它支持变量,函数等语法,然后在打包成项目时,会通过编译器转化成正常的CSS
常见的预处理语言有
解决CSS文件细分问题 这部分可以使用构建工具解决,比如webpack
在webpack中使用一些loader或者plugin用来处理,打包,合并,压缩CSS文件
在Webpack处理CSS 使用css-loader和style-loader插入样式 安装css-loader和style-loader
1 npm install css-loader style-loader -D
修改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 const path = require ("path" );const {CleanWebpackPlugin} = require ("clean-webpack-plugin" );const HtmlWebpackPlugin = require ('html-webpack-plugin' );const CopyWebpackPlugin = require ('copy-webpack-plugin' );const webpack = require ("webpack" );module .exports = { entry: { index: "./index.js" }, mode: "development" , output: { path: path.resolve(__dirname, "dist" ), filename: "js/[name].[chunkhash:5].js" , publicPath: "/" }, devServer: { contentBase: path.resolve(__dirname, 'dist' ), port: 9000 , open : true , }, context: path.resolve(__dirname, "src" ), module : { rules: [ { test: /\.css$/ , use: ["style-loader" , "css-loader" ] }, { test: /\.(png)|(gif)|(jpg)$/ , use: [{ loader: "url-loader" , options: { limit: 10 * 1024 , name: "imgs/[name].[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html" , filename: "html/index.html" , chunks: ["index" ] }), new CopyWebpackPlugin({ patterns: [ { from : "./assets" , to: "./assets" }, ], }), new webpack.ProvidePlugin({ $ : 'jquery' }) ], resolve: { alias: { "@" : path.resolve(__dirname, 'src' ) }}, externals: { jquery : "$" } }
这里我们只修改了css的处理方式,改为使用css-loader和style-loader来处理,运行下面的命令
打包成功
1 2 3 4 5 6 7 8 9 10 11 12 13 E:\Workspaces\Project\WebPro\WebpackTest>npm run build > webpacktest@1.0.0 build E:\Workspaces\Project\WebPro\WebpackTest > webpack [webpack-cli] Compilation finished asset assets/img.png 189 KiB [emitted] [from: assets/img.png] [copied] asset js/index.0a600.js 4.84 KiB [emitted] [immutable] (name: index) asset html/index.html 299 bytes [emitted] runtime modules 931 bytes 4 modules built modules 427 bytes [built] ./index.js 385 bytes [built] [code generated] external "$" 42 bytes [built] [code generated] webpack 5.10.1 compiled successfully in 225 ms
查看打包的页面,可以看到页面中多了一个style元素
这样打包就成功了
简单的css-loader和style-loader解析 我们来看看这两个loader的作用吧,先看css-loader
The css-loader interprets @import and url() like import/require() and will resolve them.
从介绍中我们可以看出,css-loader会解析@import和url这样的语法,举个例子你就知道了
经过css-loader转换后变成js代码:
1 2 3 module .exports = `.red{ color:"#f40"; }`
再例如:
1 2 3 4 .red { color :"#f40" ; background :url ("./bg.png" ) }
经过css-loader转换后变成:
1 2 3 4 5 var import1 = require ("./bg.png" );module .exports = `.red{ color:"#f40"; background:url("${import1} ") }` ;
webpack会把上面的代码转化成AST,然后进行依赖查找,这样Webpack会把依赖./bg.png添加到模块列表,最后把代码转化为
1 2 3 4 5 var import1 = __webpack_require__("./src/bg.png" );module .exports = `.red{ color:"#f40"; background:url("${import1} ") }` ;
再例如:
1 2 3 4 5 @import "./reset.css" ;.red { color :"#f40" ; background :url ("./bg.png" ) }
在css-loader处理后会变成
1 2 3 4 5 6 7 var import1 = require ("./reset.css" );var import2 = require ("./bg.png" );module .exports = `${import1} .red{ color:"#f40"; background:url("${import2} ") }` ;
webpack进行处理后
1 2 3 4 5 6 7 var import1 = __webpack_require__("./reset.css" );var import2 = __webpack_require__("./bg.png" );module .exports = `${import1} .red{ color:"#f40"; background:url("${import2} ") }` ;
上面给出的css-loader导出结果是经过简化过的,实际上css-loader会导出更复杂的结果,但是核心思想还是这个
总结一下,css-loader做了两件事
将css文件的内容作为字符串导出
将css中的其他依赖作为require导入,以便webpack分析依赖
style-loader
Inject CSS into the DOM.
style-loader会把经过css-loader导出的结果经过处理后插入到页面上
例如:
经过css-loader转换后变成js代码:
1 2 3 module .exports = `.red{ color:"#f40"; }`
经过style-loader转换后变成:
1 2 3 4 5 6 7 8 module .exports = `.red{ color:"#f40"; }` var style = module .exports;var styleElem = document .createElement("style" );styleElem.innerHTML = style; document .head.appendChild(styleElem);module .exports = {}
style-loader的处理结果也是简化过的,实际上处理远比比这个复杂
目前,css代码被css-loader转换后,交给的是style-loader进行处理。
style-loader使用的方式是用一段js代码,将样式加入到style元素中。
而实际的开发中,我们往往希望依赖的样式最终形成一个css文件。
这时候我们可以使用mini-css-extract-plugin这个库
该库提供了1个plugin和1个loader
plugin:负责生成css文件
loader:负责记录要生成的css文件的内容,同时导出开启css-module后的样式对象
安装mini-css-extract-plugin
1 npm install mini-css-extract-plugin -D
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const MiniCssExtractPlugin = require ("mini-css-extract-plugin" )module .exports = { module : { rules: [ { test: /\.css$/ , use: [MiniCssExtractPlugin.loader, "css-loader" ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: "css/[name].[contenthash:5].css" }) ] }
完整的webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 const path = require ("path" );const {CleanWebpackPlugin} = require ("clean-webpack-plugin" );const HtmlWebpackPlugin = require ('html-webpack-plugin' );const CopyWebpackPlugin = require ('copy-webpack-plugin' );const MiniCssExtractPlugin = require ("mini-css-extract-plugin" )const webpack = require ("webpack" );module .exports = { entry: { index: "./index.js" }, mode: "development" , devtool: "source-map" , output: { path: path.resolve(__dirname, "dist" ), filename: "js/[name].[chunkhash:5].js" , publicPath: "/" }, devServer: { contentBase: path.resolve(__dirname, 'dist' ), port: 9000 , open : true , }, context: path.resolve(__dirname, "src" ), module : { rules: [ { test: /\.css$/ , use: [MiniCssExtractPlugin.loader, "css-loader" ] }, { test: /\.(png)|(gif)|(jpg)$/ , use: [{ loader: "url-loader" , options: { limit: 10 * 1024 , name: "imgs/[name].[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html" , filename: "html/index.html" , chunks: ["index" ] }), new CopyWebpackPlugin({ patterns: [ { from : "./assets" , to: "./assets" }, ], }), new webpack.ProvidePlugin({ $ : 'jquery' }), new MiniCssExtractPlugin({ filename: "css/[name].[contenthash:5].css" }) ], resolve: { alias: { "@" : path.resolve(__dirname, 'src' ) }}, externals: { jquery : "$" } }
运行命令
打包成功
如果你出现了下面的问题
这是webpack版本的问题,安装mini-css-extract-plugin时我看到有这个东西
嘛,于是我Webpack更新到5.13.0问题就解决了
index.css的内容
1 2 3 4 5 6 :root, body { height: 100%; } body { background-image: url("./assets/bg.jpg"); }
打包结果
html/index.html
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <script src ="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js" > </script > <script defer src ="/js/index.473ec.js" > </script > <link href ="/css/index.8d0be.css" rel ="stylesheet" > </head > <body > <img src ="../assets/img.png" > </body > </html >
可以看到,打包后的html通过link标签引入css文件,而要引入哪个模块的css文件,可以在HtmlWebpackPlugin的chunks里进行配置
开启本地服务器
页面正常运行
命名约定 这里我们使用BEM来举例
BEM是一套针对css类样式的命名方法。
其他命名方法还有:OOCSS、AMCSS、SMACSS等等
BEM全称是:B lock E lement M odifier
三个部分的具体含义为:
Block :页面中的大区域,表示最顶级的划分,例如:轮播图(banner)、布局(layout)、文章(article)等等
element :区域中的组成部分,例如:轮播图中的横幅图片(banner__img)、轮播图中的容器(banner__container)、布局中的头部(layout__header)、文章中的标题(article_title)
modifier :可选。通常表示状态,例如:处于展开状态的布局左边栏(layout__left_expand)、处于选中状态的轮播图小圆点(banner__dot_selected)
例如:banner__dot_selected,可以表示:轮播图中,处于选中状态的小圆点
另外,在某些大型工程中,如果使用BEM命名法,还可能会增加一个前缀,来表示类名的用途,常见的前缀有:
l : layout,表示这个样式是用于布局的
c : component,表示这个样式是一个组件,即一个功能区域
u : util,表示这个样式是一个通用的、工具性质的样式
j : javascript,表示这个样式没有实际意义,是专门提供给js获取元素使用的
CSS in JS css in js简单来说就是:用一个JS对象来描述样式
比如下面的对象就描述了一组样式
1 2 3 4 5 6 7 const styles = { backgroundColor: "#f40" , color: "#fff" , width: "400px" , height: "500px" , margin: "0 auto" }
由于这种描述样式的方式根本就不存在类名 ,自然不会有类名冲突
至于如何把样式应用到界面上,不是它所关心的事情,你可以用任何技术、任何框架、任何方式将它应用到界面。
举个例子
修改index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <script src ="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js" > </script > </head > <body > <img src ="../assets/img.png" > <div id ="app" > app </div > </body > </html >
修改index.js
1 2 3 4 5 6 7 8 9 10 11 import css from "./style/index.css" ;import {applyStyles} from "@/util" ;const styles = { width: "400px" , height: "500px" , margin: "0 auto" , background : "#2083e5" } let app = document .querySelector("#app" );applyStyles(app, styles);
在util/index.js中添加下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 function applyStyles (dom, ...styles ) { let targetStyles = {}; for (const style of styles) { targetStyles = { ...targetStyles, ...style } } Object .keys(targetStyles).forEach((key ) => { dom.style[key] = targetStyles[key]; }); }
这样我们就可以把用对象描述的css应用到页面上了
你也可以使用jquery,代码会更简单
1 2 3 4 5 6 7 8 const styles = { width: "400px" , height: "500px" , margin: "0 auto" , background : "#2083e5" }; $("#app" ).css(styles);
css module css module 遵循以下思路解决类名冲突问题:
css的类名冲突往往发生在大型项目中
大型项目往往会使用构建工具(webpack等)搭建工程
构建工具允许将css样式切分为更加精细的模块
同JS的变量一样,每个css模块文件中难以出现冲突的类名,冲突的类名往往发生在不同的css模块文件中
只需要保证构建工具在合并样式代码后不会出现类名冲突即可
因此,css module引入了局部作用域 这个概念,让css样式只能在对应的模块中生效
我们来试一下,修改webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 module .exports = { module : { rules: [ { test: /\.css$/ , use: [MiniCssExtractPlugin.loader, { loader: "css-loader" , options: { modules : true } }] } ], } }
新建component1.css
1 2 3 4 5 .box { background : blueviolet; height : 30px ; }
新建component2.css
1 2 3 4 5 .box { background : brown; height : 50px ; }
修改index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <script src ="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js" > </script > </head > <body > <img src ="../assets/img.png" > <div class ="component1" > <div class ="box" > </div > </div > <div class ="component2" > <div class ="box" > </div > </div > </body > </html >
我们假设component1和component2是两个不同的组件,我们这两个组件的样式是隔离的
在index.js中导入
1 2 3 import "./style/index.css" ;import "./style/component1.css" import "./style/component2.css" ;
重新打包,查看打包结果
可以看到,最终的代码文件中,不再使用box这个类名,而是分别变成了两个值,这就是css-module的实现局部作用域的方法,css-loader会将样式中的类名进行转换,根据模块路径 和类名 生成一个hash,然后把这个类名进行重命名,因此,不同的css模块,哪怕具有相同的类名,转换后的hash值也不一样。
但是问题就来了,源代码的类名和最终生成的类名是不一样的,而开发者只知道自己写的源代码中的类名,并不知道最终的类名是什么,那我们必须要想办法把最终的类名应用到元素上
css-loader会导出一个包含了描述原类名和最终类名对应关系的对象(导出的对象.locals就是我们要的对象了)
在index.js中的打印一下导出结果(因为MiniCssExtractPlugin会把css-loader导出的对象.locals重新导出,所以这里可以打印)
1 2 3 4 5 6 import index from "./style/index.css" ;import style1 from "./style/component1.css" import style2 from "./style/component2.css" ;console .log(style1);console .log(style2);
我们修改一下index.js
1 2 3 4 5 6 7 8 import index from "./style/index.css" ;import style1 from "./style/component1.css" import style2 from "./style/component2.css" ;console .log(style1);console .log(style2);$(".component1 .box" ).addClass(style1.box); $(".component2 .box" ).addClass(style2.box);
这样就可以应用了
全局类名 某些类名是全局的、静态的,不需要进行转换,仅需要在类名位置使用一个特殊的语法即可:
使用了global的类名不会进行转换,相反的,没有使用global的类名,表示默认使用了local
使用了local的类名表示局部类名,是可能会造成冲突的类名,会被css module进行转换
配置类名 如果你想控制最后输出的类名,可以配置localIdentName这个选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 module .exports = { module : { rules: [ { test: /\.css$/ , use: [MiniCssExtractPlugin.loader, { loader: "css-loader" , options: { modules : { localIdentName: "[local]-[hash:5]" } } }] } ], } }
打包后代码就会变成这样
1 2 3 4 5 6 7 8 9 10 11 .box-36382 { background : blueviolet; height : 30px ; } .box-95cce { background : brown; height : 50px ; }
tips
css module往往配合构建工具使用
css module仅处理顶级类名(第一个类名),尽量不要书写嵌套的类名,也没有这个必要
css module仅处理类名,不处理其他选择器
css module还会处理id选择器
使用了css module后,只要能做到让类名望文知意即可,不需要遵守其他任何的命名规范
ps 事实上,如果你在一些框架里使用css-module,就会方便许多,比如React
预处理语言 编写css时,受限于css语言本身,常常难以处理一些问题:
重复的样式值:例如常用颜色、常用尺寸
重复的代码段:例如绝对定位居中、清除浮动
重复的嵌套书写
由此,预处理语言出现了,我们可以直接书写预处理语言来书写代码,虽然预处理语言的代码不能被浏览器直接识别,但通过一个编译器,就可以把预处理语言转化成css
这里我们介绍less和scss
less官网:http://lesscss.org/ less中文文档1(非官方):http://lesscss.cn/ less中文文档2(非官方):https://less.bootcss.com/ sass官网:https://sass-lang.com/ sass中文文档1(非官方):https://www.sass.hk/ sass中文文档2(非官方):https://sass.bootcss.com/
安装less和scss 首先安装less和scss
1 npm install sass less -D
然后新建两个文件less-style.less和scss-style.scss
less-style.less
1 2 3 4 @base-color : #c6538c ;.wrapper { border : 1px solid @base-color ; }
scss-style.scss
1 2 3 4 $base-color : #c6538c ;.wrapper { border : 1px solid $base-color ; }
转化less
1 lessc ./src/style/less-style.less ./src/style/less-style.css
转化scss
1 sass ./src/style/scss-style.scss ./src/style/scss-style.css
转化结果
最简单的用法就是这样了,但我们希望能在开发时直接书写预处理语言的文件,最后打包时webpack帮我们转化成css,我们就要进行相应的配置了
在webpack中使用 我们要先安装相应的loader
1 npm install less-loader sass-loader -D
在webpack.config.js中导入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 const cssLoaderConfig = { loader: "css-loader" , options: { modules : { localIdentName: "[local]-[hash:5]" } } }; module .exports = { module : { rules: [ { test: /\.css$/ , use: [MiniCssExtractPlugin.loader, cssLoaderConfig] }, { test: /\.s[ac]ss$/i , use: [ MiniCssExtractPlugin.loader, cssLoaderConfig, "sass-loader" , ] }, { test: /\.less$/i , use: [ MiniCssExtractPlugin.loader, cssLoaderConfig, "less-loader" , ] }, { test: /\.(png)|(gif)|(jpg)$/ , use: [{ loader: "url-loader" , options: { limit: 10 * 1024 , name: "imgs/[name].[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, }
打包结果
使用就是这么简单,具体的语法可以去看官方文档,还是挺详细的,而且不需要英文,康康代码就能看懂
PostCSS
官网地址:https://postcss.org/ github地址:https://github.com/postcss/postcss Api:http://api.postcss.org/
PostCSS是一个代码编译器,可以把你写的代码转化成最终的css代码
听起来是不是有点像sass这样的预处理语言,他们的功能其实有相同之处,不过PostCSS是一个编译器,只负责把源代码转化成AST语法树,然后调用插件处理AST树,最后再把AST语法树转化回代码,简单来说就是,PostCSS只负责代码转化,具体的功能需要单独的插件来使用,你可以自由选择插件,甚至可以自己写一个,sass如同定义,是一个语言,虽然最后要被编译成CSS才能执行
放个图吧
这就是PostCSS的工作流,Parser负责把CSS转化成AST,Stringifier负责把AST重新转化为CSS
安装 PostCss是基于node编写的,因此可以使用npm安装
安装postcss和cli工具
1 npm install postcss postcss-cli -D
postcss库提供了对应的Api用于转换代码,postcss-cli允许我们通过命令行调用postcss中的api来完成编译
比如
1 postcss ./src/style/postcss-style.pcss -o ./src/style/postcss-style.css
输出结果
配置文件 单纯使用postcss没有意义,因为postcss转化代码需要插件,在执行上面的命令时,postcss给了我们一个提示
1 2 You did not set any plugins, parser, or stringifier. Right now, PostCSS does nothing. Pick plugins for your case on https://www.postcss.parts / and use them in postcss.config.js.
所以我们要弄点插件进去,那么要弄插件自然要先有个配置文件,postcss的配置文件默认名称是postcss.config.js
我们新建一个postcss.config.js文件,加入一些配置
1 2 3 4 5 6 module .exports = { map: false , plugins: { } }
使用插件 postcss-preset-env 过去使用postcss的时候,往往会使用大量的插件,它们各自解决一些问题
这样导致的结果是安装插件、配置插件都特别的繁琐
于是出现了这么一个插件postcss-preset-env,它称之为postcss预设环境,大意就是它整合了很多的常用插件到一起,并帮你完成了基本的配置,你只需要安装它一个插件,就相当于安装了很多插件了。
安装插件
1 cnpm install postcss-preset-env -D
安装好该插件后,在postcss配置中加入下面的配置
1 2 3 4 5 6 7 module .exports = { map : false , plugins : { "postcss-preset-env" : {} } }
自动补全浏览器前缀 某些新的css样式需要在旧版本浏览器中使用厂商前缀方可实现
例如
1 2 3 ::placeholder { color : red; }
该功能在不同的旧版本浏览器中需要书写为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ::-webkit-input-placeholder { color : red; } ::-moz-placeholder { color : red; } :-ms-input-placeholder { color : red; } ::-ms-input-placeholder { color : red; } ::placeholder { color : red; }
要完成这件事情,需要使用autoprefixer库。
而postcss-preset-env内部包含了该库,自动有了该功能。
我们再次运行一次命令,可以看到输出的文件已经补全了前缀
如果需要调整兼容的浏览器 范围,可以通过下面的方式进行配置
方式1:在postcss-preset-env的配置中加入browsers
1 2 3 4 5 6 7 8 9 10 module.exports = { plugins: { "postcss-preset-env": { browsers: [ "last 2 version", "> 1%" ] } } }
方式2:添加 .browserslistrc 文件
这个方式比较推荐,因为这个文件,在以后需要做其他的浏览器兼容性处理时,其他插件也会读取这个文件,比如babel也会读取这个文件
创建文件.browserslistrc,填写配置内容
方式3:在package.json的配置中加入browserslist
这个方式也是比较推荐的,理由同上
1 2 3 4 "browserslist": [ "last 2 version", "> 1%" ]
browserslist是一个多行的(数组形式的)标准字符串。
它的书写规范多而繁琐,详情见:https://github.com/browserslist/browserslist
一般情况下,大部分网站都使用下面的格式进行书写
1 2 3 last 2 version > 1% in CN not ie <= 8
last 2 version: 浏览器的兼容最近期的两个版本
> 1% in CN: 匹配中国大于1%的人使用的浏览器, in CN可省略
not ie <= 8: 排除掉版本号小于等于8的IE浏览器
默认情况下,匹配的结果求的是并集
你可以通过网站:https://browserl.ist/ 对配置结果覆盖的浏览器进行查询,查询时,多行之间使用英文逗号分割
browserlist的数据来自于CanIUse 网站,由于数据并非实时的,所以不会特别准确
未来的CSS语法 CSS的某些前沿语法正在制定过程中,没有形成真正的标准,如果希望使用这部分语法,为了浏览器兼容性,需要进行编译。过去,完成该语法编译的是cssnext库,不过有了postcss-preset-env后,它自动包含了该功能。
你可以通过postcss-preset-env的stage配置,告知postcss-preset-env需要对哪个阶段的css语法进行兼容处理,它的默认值为2
1 2 3 "postcss-preset-env" : { stage: 0 }
一共有5个阶段可配置:
Stage 0: Aspirational - 只是一个早期草案,极其不稳定
Stage 1: Experimental - 仍然极其不稳定,但是提议已被W3C公认
Stage 2: Allowable - 虽然还是不稳定,但已经可以使用了
Stage 3: Embraced - 比较稳定,可能将来会发生一些小的变化,它即将成为最终的标准
Stage 4: Standardized - 所有主流浏览器都应该支持的W3C标准
下面我们来试一下
1 2 3 4 5 6 7 8 9 :root { --lightColor : #ddd ; --darkColor : #333 ; } a { color : var (--lightColor); background : var (--darkColor); }
编译后
1 2 3 4 5 6 7 8 9 10 11 :root { --lightColor : #ddd ; --darkColor : #333 ; } a { color : #ddd ; color : var (--lightColor); background : #333 ; background : var (--darkColor); }
编译后,仍然可以看到原语法,因为某些新语法的存在并不会影响浏览器的渲染,尽管浏览器可能不认识 如果不希望在结果中看到新语法,可以配置postcss-preset-env的preserve为false
postcss-apply 安装
1 npm install postcss-apply -D
该插件可以支持在css中书写属性集
类似于LESS中的混入,可以利用CSS的新语法定义一个CSS代码片段,然后在需要的时候应用它
1 2 3 4 5 6 7 8 9 10 11 12 :root { --center : { position : absolute; left : 50% ; top : 50% ; transform : translate(-50% , -50% ); }; } .item { @apply --center; }
编译后
1 2 3 4 5 6 7 .item { position : absolute; left : 50% ; top : 50% ; -webkit-transform : translate (-50% , -50% ); transform : translate (-50% , -50% ); }
postcss-color-function 安装
1 npm install postcss-color-function -D
该插件支持在源码中使用一些颜色函数
1 2 3 4 5 6 7 8 9 10 11 12 body { color : color(#aabbcc ); color : color(#aabbcc a(90% )); color : color(#aabbcc red(90% )); color : color(#aabbcc tint(50% )); color : color(#aabbcc shade(50% )); }
编译后
1 2 3 4 5 6 7 8 9 10 11 12 body { color : rgb (170 , 187 , 204 ); color : rgba (170 , 187 , 204 , 0.9 ); color : rgb (230 , 187 , 204 ); color : rgb (213 , 221 , 230 ); color : rgb (85 , 94 , 102 ); }
postcss-import 该插件可以让你在postcss文件中导入其他样式代码,通过该插件可以将它们合并
安装
1 npm install postcss-import -D
应用
1 2 3 4 5 6 7 8 9 10 11 12 module .exports = { map : false , plugins : { "postcss-preset-env" : { stage : 0 , preserve : false }, "postcss-apply" : {}, "postcss-color-function" : {}, "postcss-import" : {} } }
在Webpack中使用 安装postcss-loader
1 npm install postcss-loader -D
配置
1 2 3 4 5 6 7 8 9 10 11 12 module .exports = { module : { { test: /\.pcss$/i , use: [ MiniCssExtractPlugin.loader, cssLoaderConfig, "postcss-loader" , ] } } }
我们把postcss-loader加到css-loader前,postcss-loader会调用插件处理源代码,然后再把生成的css代码传给css-loader
用文档的原话:
After setting up your postcss.config.js, add postcss-loader to your webpack.config.js. You can use it standalone or in conjunction with css-loader (recommended).
Use it before css-loader and style-loader, but after other preprocessor loaders like e.g sass|less|stylus-loader, if you use any (since webpack loaders evaluate right to left/bottom to top ).
所以如果你要给在sass里用,把postcss-loader放到sass-loader前面就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 const path = require ("path" );const {CleanWebpackPlugin} = require ("clean-webpack-plugin" );const HtmlWebpackPlugin = require ('html-webpack-plugin' );const CopyWebpackPlugin = require ('copy-webpack-plugin' );const MiniCssExtractPlugin = require ("mini-css-extract-plugin" )const webpack = require ("webpack" );const cssLoaderConfig = { loader: "css-loader" , options: { modules : { localIdentName: "[local]-[hash:5]" } } }; module .exports = { entry: { index: "./index.js" }, mode: "development" , output: { path: path.resolve(__dirname, "dist" ), filename: "js/[name].[chunkhash:5].js" , publicPath: "/" }, devServer: { contentBase: path.resolve(__dirname, 'dist' ), port: 9000 , open : true , }, context: path.resolve(__dirname, "src" ), module : { rules: [ { test: /\.s[ac]ss$/i , use: [ MiniCssExtractPlugin.loader, cssLoaderConfig, "postcss-loader" , "sass-loader" , ] }, { test: /\.less$/i , use: [ MiniCssExtractPlugin.loader, cssLoaderConfig, "postcss-loader" , "less-loader" , ] }, { test: /\.p?css$/i , use: [ MiniCssExtractPlugin.loader, cssLoaderConfig, "postcss-loader" , ] }, { test: /\.(png)|(gif)|(jpg)$/ , use: [{ loader: "url-loader" , options: { limit: 10 * 1024 , name: "imgs/[name].[contenthash:5].[ext]" } }] }], noParse: /jquery/ }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ template: "./public/index.html" , filename: "html/index.html" , chunks: ["index" ] }), new CopyWebpackPlugin({ patterns: [ { from : "./assets" , to: "./assets" }, ], }), new webpack.ProvidePlugin({ $ : 'jquery' }), new MiniCssExtractPlugin({ filename: "css/[name].[contenthash:5].css" }) ], resolve: { alias: { "@" : path.resolve(__dirname, 'src' ) }}, externals: { jquery : "$" } }
后记 啊,CSS的部分就这样告一段落了,下一篇我们看看针对JS的基本配置,不咕的话,明天或者后天写完