个人将前端开发分为三个阶段:
Web1.0前端主要工作:
伴随着AJAX的诞生,前端的工作模式也发生了很大变化,前端不仅仅是展示界面,而且还可以管理数据以及和用户进行数据的交互。在这样的阶段过程中,诞生了像jQuery这样的优秀前端工具库。
在这个阶段中,前端的工作变得更加多样化和复杂化,例如现在前端不仅仅需要开发PC端Web界面,还有移动端的Web界面,以及小程序和公众号,甚至有些时候还需要做App和桌面客户端。
伴随着需要做的事情越来越多,流程也越来越复杂,因此就会出现一些问题。比如说:
现代Web开发“问题”
采用模块化开发
不同浏览器对模块化的支持不同,而且模块化本身存在多种实现规范,这些给最终产出带来了影响
使用新特性提高效率保证安全性
编码过程中,为了提高开发效率,还会使用ES6+、TypeScript、Saas、Less,这些条件浏览器在默认情况下不能正常处理
实时监听开发过程使用热更新
项目结果打包压缩优化
需要有一个工具站出来解决问题,可以让开发者在入口的地方随心所欲,用个人喜欢的技术栈完成开发,从而不需要关系过程,但是最终的结果可以在浏览器上正常展示,因此这里就会用到打包工具。当前Vue、React、Angular本身集成Webpack。
Webpack定义:为现代JavaScript应用提供静态模块打包
Webpack功能:
构件如图目录结构,并编码
在web server中进行预览,发现虽然在index.html中使用了type="module"
,但是依然无法同时识别ES Module和CommonJS。
此时,提前安装好的Webpack就起了作用,在命令行终端输入:Webpack
,这时发现目录中输出了dist目录。这里需要注意,Webpack打包会默认找到项目目录下的src目录,并且找到index.js作为入口文件,对依赖进行打包处理,并输出到dist目录中,输出结果默认为main.js
。如下图:
观察main.js,发现当前Webpack并未解决ES6+的语法兼容问题
此时将index.html中引入的js文件变更为dist/main.js
。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上手Webpacktitle>
head>
<body>
<script type="module" src="./src/index.js">script>
body>
html>
展示结果如下:
目录结构:
自定义打包入口文件和打包输出目录及输出文件名。
通过命令行参数进行打包
yarn Webpack --entry ./src/main.js --output-path ./build
,其中--entry
指定入口文件,--output-path
指定输入路径
通过package.json配置简短命令
...
"scripts": {
"build": "Webpack --entry ./src/main.js --output-path ./build"
}
...
通过命令行运行yarn build
进行打包
通过Webpack.config.js配置文件进行配置
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist') // 必须使用绝对路径,不然会Webpack抛出错误
}
}
目录结构:
在index.js中引入lg.js,随后在index.html中引入dist目录下的main.js。Webpack在打包过程中会自动寻找依赖关系并引入,最终打包为main.js。
上文提到可以使用命令行参数对入口文件、输出目录及输出文件名做配置,这里使用–config参数可以对Webpack配置文件进行自定义。例如:
... { "scripts" : { "build": "Webpack --config my-Webpack-config.js" } } ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
此时Webpack就会从my-Webpack-config.js读取配置进行打包处理。
在项目中,模拟创建dom元素,并给dom元素赋css样式,如下代码:
index.html中script引入打包后的main.js
index.js入口文件中引入login.js
// login.js
function createDom() {
const h2 = document.createElement('h2')
h2.innerHTML = '拉勾教育'
h2.className = 'title'
return h2
}
document.body.appendChild(createDom())
此时,需要.title添加css样式
/*
login.css
*/
.title {
color: red
}
在login.js中引入login.css,并进行Webpack打包,此时会抛出异常,css文件并不是一个模块。
import '../css/login.css'
function login() {
const oH2 = document.createElement('h2')
oH2.innerHTML = '拉勾教育前端'
oH2.className = 'title'
return oH2
}
document.body.appendChild(login())
loader是一个模块,内部使用js实现具体逻辑,比如现在需要一个loader让login.css代码转换为Webpack能识别的模块。
安装css-loader
yarn add css-loader
Webpack4中对loader的使用一般分为三种:
Webpack5中对cli中使用loader不建议使用,已废弃
import 'css-loader!../css/login.css'
function login() {
const oH2 = document.createElement('h2')
oH2.innerHTML = '拉勾教育前端'
oH2.className = 'title'
return oH2
}
document.body.appendChild(login())
重新执行yarn Webpack
,虽然没有语法报错,但是样式并未生效。还需要使用一个style-loader
。
配置文件中使用css-loader
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
// {
// test: /\.css$/, // 一般就是一个正则表达式,用于匹配我们需要处理的文件类型
// use: [
// {
// loader: 'css-loader'
// }
// ]
// },
// {
// test: /\.css$/,
// loader: 'css-loader'
// },
{
test: /\.css$/,
use: ['css-loader']
}
]
}
}
然后回到Webpack.config.js下,将入口文件的路径指向新创建的css文件。随后配置loader组件,test值为正则表达式/.css$/,use配置一个数组,分别为style-loader以及style-loader
const path = require('path')
module.exports = {
mode: 'none',
entry: './src/main.css',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
},
module: {
// rules数组针对于其它资源模块的加载规则,每个规则对象的都需要设置两个属性。
rules: [
{
test: /.css$/, // test用来去匹配在打包过程当中所遇到的文件路径
// use用来去指定我们匹配到的文件,需要去使用的loader
use: [
'style-loader',
'css-loader'
]
}
]
}
}
命令行启动,yarn Webpack,通过serve . 运行,在浏览器中访问就可以看到我们的css生效了。
ps:use中,如果配置了多个loader,其执行顺序是从数组最后一个元素往前执行。所以这里一定要把css-loader放到最后,因为我们必须要先通过css-loader把css代码转换为模块才可以正常打包。
style-loader工作代码在bundle.js中,部分代码如下:
loader是Webpack实现整个前端模块化的核心,通过不同的loader就可以实现加载任何类型的资源。
在项目中使用less编写css代码,首先在login.js中正常引入login.less,正常使用Webpack进行编译,发现报错基本与未使用css-loader相同。Webpack默认不支持less文件的编译,所以按照思路,需要先将less文件编译为css文件,然后使用css-loader与style-loader搭配使用,将css样式引入到index.html。下面进行尝试:
首先安装less,尝试把login.less编译为index.css
npm i less -D # 安装less
npx less ./src/css/login.less index.css # 使用less将login.less编译为index.css
其次在login.js中将其引入:
此时运行Webpack进行打包,发现会抛出错误,错误类型与上面提到的Webpack无法编译css文件时相同。
回到初始思路上,我们需要less-loader将less文件编译为css文件,其次使用css-loader搭配style-loader,将css样式编译至html文件中,所以需要进行配置,配置思路与css相同。如下代码:
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
}
ps:记住loader加载使用顺序是:从右到左,从下到上
caniuse.com
.browserslistrc 是在不同的前端工具之间共用目标浏览器和 node 版本的配置文件。它主要被以下工具使用:
Autoprefixer
Babel
post-preset-env
eslint-plugin-compat
stylelint-unsupported-browser-features
postcss-normalize
Webpack默认会安装browserlistrc
前端工程需要在package.json中配置
{
"browserslist": [
"last 1 version",
"> 1%",
"maintained node versions",
"not dead"
]
}
也可在.browserslistrc中配置
# 注释是这样写的,以#号开头
last 1 version #最后的一个版本
> 1% #代表全球超过1%使用的浏览器
maintained node versions #所有还被 node 基金会维护的 node 版本
not dead
不配置默认为:> 0.5%, last 2 versions, Firefox ESR, not dead
在当前目录下查询目标浏览器 npx browserslist
查询条件列表
你可以用如下查询条件来限定浏览器和 node 的版本范围(大小写不敏感):
> 5%: 基于全球使用率统计而选择的浏览器版本范围。>=,<,<=同样适用。
> 5% in US : 同上,只是使用地区变为美国。支持两个字母的国家码来指定地区。
> 5% in alt-AS : 同上,只是使用地区变为亚洲所有国家。这里列举了所有的地区码。
> 5% in my stats : 使用定制的浏览器统计数据。
cover 99.5% : 使用率总和为99.5%的浏览器版本,前提是浏览器提供了使用覆盖率。
cover 99.5% in US : 同上,只是限制了地域,支持两个字母的国家码。
cover 99.5% in my stats :使用定制的浏览器统计数据。
maintained node versions :所有还被 node 基金会维护的 node 版本。
node 10 and node 10.4 : 最新的 node 10.x.x 或者10.4.x 版本。
current node :当前被 browserslist 使用的 node 版本。
extends browserslist-config-mycompany :来自browserslist-config-mycompany包的查询设置
ie 6-8 : 选择一个浏览器的版本范围。
Firefox > 20 : 版本高于20的所有火狐浏览器版本。>=,<,<=同样适用。
ios 7 :ios 7自带的浏览器。
Firefox ESR :最新的火狐 ESR(长期支持版) 版本的浏览器。
unreleased versions or unreleased Chrome versions : alpha 和 beta 版本。
last 2 major versions or last 2 ios major versions :最近的两个发行版,包括所有的次版本号和补丁版本号变更的浏览器版本。
since 2015 or last 2 years :自某个时间以来更新的版本(也可以写的更具体since 2015-03或者since 2015-03-10)
dead :通过last 2 versions筛选的浏览器版本中,全球使用率低于0.5%并且官方声明不在维护或者事实上已经两年没有再更新的版本。目前符合条件的有 IE10,IE_Mob 10,BlackBerry 10,BlackBerry 7,OperaMobile 12.1。
last 2 versions :每个浏览器最近的两个版本。
last 2 Chrome versions :chrome 浏览器最近的两个版本。
defaults :默认配置> 0.5%, last 2 versions, Firefox ESR, not dead。
not ie <= 8 : 浏览器范围的取反。
可以添加not在任和查询条件前面,表示取反
注意:
1.可以使用如下写法,从另外一个输出 browserslist 配置的包导入配置数据:
"browserslist": [
"extends browserslist-config-mycompany"
]
为了安全起见,额外的配置包只支持前缀 browserslist-config- 的包命名. npm包作用域也同样支持 @scope/browserslist-config,例如:
@scope/browserslist-config or @scope/browserslist-config-mycompany.
#When writing a shared Browserslist package, just export an array.
#browserslist-config-mycompany/index.js:
module.exports = [
'last 1 version',
'> 1%',
'ie 10'
]
2.环境的差异化配置
你可以为不同的环境配置不同的浏览器查询条件。Browserslist 将依赖BROWSERSLIST_ENV 或者 NODE_ENV查询浏览器版本范围。如果两个环境变量都没有配置正确的查询条件,那么优先从 production 对应的配置项加载查询条件,如果再不行就应用默认配置。
在 package.json:
"browserslist": {
"production": [
"> 1%",
"ie 10"
],
"development": [
"last 1 chrome version",
"last 1 firefox version"
]
}
在配置文件.broswerslistrc
中
[production staging]
> 1%
ie 10
[development]
last 1 chrome version
last 1 firefox version
官网说:“PostCSS,一个使用 JavaScript 来处理CSS的框架”。这句话高度概括了 PostCSS 的作用,但是太抽象了。按我理解,PostCSS 主要做了三件事:
parse
:把 CSS 文件的字符串解析成抽象语法树(Abstract Syntax Tree)的框架,解析过程中会检查 CSS 语法是否正确,不正确会给出错误提示。runPlugin
: 执行插件函数。PostCSS 本身不处理任何具体任务,它提供了以特定属性或者规则命名的事件。有特定功能的插件(如 autoprefixer、CSS Modules)会注册事件监听器。PostCSS 会在这个阶段,重新扫描 AST,执行注册的监听器函数。generate
: 插件对 AST 处理后,PostCSS 把处理过的 AST 对象转成 CSS string。「如果没有插件」,那么初始传入的 CSS string 和 generate 生成的 CSS string 是一样的。由此可见,PostCSS 本身并不处理任何具体的任务,只有当我们为其附加各种插件之后,它才具有实用性。
CSS 语法简述
CSS 规则集(rule-set)由选择器和声明块组成:
;
五类对象
AST 用五类对象描述 CSS 语法。这里举个具体的例子,再打印出对应的 AST 结果,对照了解 AST 五类对象和 CSS 语法的对应关系。
app.css
文件中写如下内容:
@import url('./app-02.css');
.container {
color: red;
}
Declaration(声明) 对象
Declaration 对象用来描述 CSS 中的每一条声明语句。
上边 CSS 文件中的color: red;
会被描述成如下对象:
{
parent: Rule, // 外层的选择器被转译成 Rule 对象,是当前声明对象的 parent
prop: "color", // prop 字段记录声明的属性
raws: { // raws 字段记录声明前、后的字符串,声明属性和值之间的字符串,以及前边语句是否分号结束。
before: '\n ', // raws.before 字段记录声明前的字符串
between: ': ', // raws.between 字段记录声明属性和值之间的字符串
},
source: { // source 字段记录声明语句的开始、结束位置,以及当前文件的信息
start: { offset: 45, column: 3, line: 4 },
end: { offset: 55, column: 13, line: 4 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype 属性
type: 'decl', // type 记录对象类型,是个枚举值,声明语句的 type 固定是 decl
value: "red" // value 字段记录声明的值
}
每个字段的含义和功能已经以注释的形式进行了解释。
Rule 对象
Rule 对象是描述选择器的。
上边 app.css 文件中.container
经过 postcss 转译后的对象是(每个字段的含义和功能已经以注释的形式进行了解释):
{
nodes: [Declaration], // nodes 记录包含关系,Rule 对象包含 Declaration 对象
parent: Root, // 根对象是 Root 对象,是当前声明对象的 parent
raws: { // raws 字段记录如下
before: '\n\n', // raws.before 字段记录选择器前的字符串
between: ' ', // raws.between 字段记录选择器和大括号之间的字符串
semicolon: true, // raws.semicolon 字段记录前置声明语句是正常分号结束
after: '\n' // raws.after 字段记录最后一个声明和结束大括号之间的字符串
},
selector:'.container', // selector 记录 selector
source: { // source 字段记录选择器语句的开始、结束位置,以及当前文件的信息
start: { offset: 30, column: 1, line: 3 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
},
end: { offset: 57, column: 1, line: 5 }
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
type: 'rule' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 rule
}
Root 对象
Root 对象是 AST 对象的根对象。
上边 app.css 文件中 root 对象是(每个字段的含义和功能已经以注释的形式进行了解释):
{
nodes: [AtRule, Rule], // nodes 记录子对象(选择器和 @开头的对象),AtRule 对象会在后边提到
raws: { // raws 字段记录如下
semicolon: false, // raws.semicolon 最后是否是分号结束
after: '' // raws.after 最后的空字符串
},
source: { // source 字段记录根目录语句的开始,以及当前文件的信息
start: { offset: 0, column: 1, line: 1 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
type: 'root' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 root
}
AtRule 对象
CSS 中除了选择器,还有一类语法是 @
开头的,例如 @import
、@keyframes
、@font-face
,PostCSS 把这类语法解析成 AtRule 对象。
@
紧跟着的单词例如 @import url("./app-02.css");
将被解析成如下对象:
{
name: "import", // name 记录 @ 紧跟着的单词
params: "url('./app-02.css')", // params 记录 name 值
parent: Root, // parent 记录父对象
raws: { // raws 字段记录如下
before: '', // raws.before 记录 @语句前的空字符串
between: '', // raws.between 记录 name 和 { 之间的空字符串
afterName: '', // raws.afterName 记录 name 和 @ 语句之间的空字符串
after: '', // raws.after 记录大括号和上一个 rule 之间的空字符串
semicolon: false // raws.semicolon 上一个规则是否是分号结束
},
source: { // source 字段记录@语句的开始,以及当前文件的信息
start: { offset: 0, column: 1, line: 1 },
end: { offset: 27, column: 28, line: 1 },
input: Input {
css: '@import url('./app-02.css');\n\n.container {\n color: red;\n}',
file: '/Users/admin/temp/postcss/app.css',
hasBOM: false,
Symbol(fromOffsetCache): [0, 29, 30, 43, 57]
}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
type: 'atrule' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 atrule
}
Comment 对象
css 文件中的注释被解析成 Comment 对象。text 字段记录注释内容。/* 你好 */
被解析成:
{
parent: Root, // parent 记录父对象
raws: { // raws 字段记录如下
before: '', // raws.before 记录注释语句前的空字符串
left: ' ', // raws.left 记录注释语句左侧的空字符串
right: ' ' // raws.right 记录注释语句右侧的空字符串
},
source: { // source 字段记录注释语句的开始、结束位置,以及当前文件的信息
start: {…}, input: Input, end: {…}
},
Symbol('isClean'): false, // Symbol(isClean) 字段默认值都是 false,用于记录当前对象关联的 plugin 是否执行。plugin 会在后续解释
Symbol('my'): true, // Symbol(my) 字段默认值都是 true,用于记录当前对象是否是对应对象的实例,如果不是,可以根据类型把对象的属性设置为普通对象的 prototype
text: '你好', // text 记录注释内容
type: 'comment' // type 记录对象类型,是个枚举值,声明语句的 type 固定是 comment
}
图解五类对象之间的继承关系
从上一段可以知道,CSS 被解析成 Declaration、Rule、Root、AtRule、Comment 对象。这些对象有很多公共方法,PostCSS 用了面向对象的继承思想,把公共方法和公共属性提取到了父类中。
Root、Rule、AtRule 都是可以有子节点的,都有 nodes 属性,他们三个继承自 Container 类,对 nodes 的操作方法都写在 Container 类中。Container、Declaration、Comment 继承自 Node 类,所有对象都有 Symbol(‘isClean’)、Symbol(‘my’)、raws、source、type 属性,都有toString()、error()等方法,这些属性和方法都定义在 Node 类中。
Container、Node 是用来提取公共属性和方法,不会生成他们的实例。
五个类之间的继承关系如下图所示:
;
图中没有穷举类的方法,好奇的同学可以看直接看源码文件: https://github.com/postcss/postcss/tree/main/lib 。
把 CSS 语法解析成 AST 对象的具体算法
算法对应源码中位置是:postcss/lib/parser.js
中的parse
方法,代码量不大,可自行查看。
PostCSS 本身并不处理任何具体的任务,只有当我们为其附加各种插件之后,它才具有实用性。
PostCSS 在把 CSS string 解析成 AST 对象后,会扫描一边 AST 对象,每一种 AST 的对象都可以有对应的监听器。在遍历到某类型的对象时,如果有对象的监听器,就会执行其监听器。
第一类监听器
PostCSS 提供的**「以特定属性或者规则命名」**的事件监听器,如下:
CHILDREAN 代表子节点的事件监听器。
// root
['Root', CHILDREN, 'RootExit']
// AtRule
['AtRule', 'AtRule-import', CHILDREN, 'AtRuleExit', 'AtRuleExit-import']
// Rule
['Rule', CHILDREN, 'RuleExit']
// Declaration
['Declaration', 'Declaration-color', 'DeclarationExit', 'DeclarationExit-color']
// Comment
['Comment', 'CommentExit']
PostCSS 以深度优先的方式遍历 AST 树。
{
Root: (rootNode, helps) => {},
RootExit: (rootNode, helps) => {}
}
{
Rule: (ruleNode, helps) => {},
RuleExit: (ruleNode, helps) => {}
}
['AtRule', 'AtRule-import', CHILDREN, 'AtRuleExit', 'AtRuleExit-import']
。CHILDREAN 代表子节点的事件。```// 函数 { AtRule: (atRuleNode, helps) => {} }// 对象
{
AtRule: {
import: (atRuleNode, helps) => {},
keyframes: (atRuleNode, helps) => {}
}
}
['Declaration', 'Declaration-color', 'DeclarationExit', 'DeclarationExit-color']
。Declaration 没有子对象,只需要执行当前对象的事件,不需要深度执行子对象的事件。// 函数
{
Declaration: (declarationNode, helps) => {}
}
// 对象
{
Declaration: {
color: (declarationNode, helps) => {},
border: (declarationNode, helps) => {}
}
}
第二类监听器
除以特定属性或者规则命名的事件监听器,PostCSS 还有以下四个:
{
postcssPlugin: string,
prepare: (result) => {},
Once: (root, helps) => {},
OnceExit: (root, helps) => {},
}
PostCSS 插件事件的整体执行是:[prepare, Once, ...一类事件,OnceExit]
,postcssPlugin 是插件名称,不是事件监听器。
{
postcssPlugin: "PLUGIN NAME",
prepare(result) {
const variables = {};
return {
Declaration(node) {
if (node.variable) {
variables[node.prop] = node.value;
}
},
OnceExit() {
console.log(variables);
},
};
},
};
{
Once: (root, helps) => {}
}
插件源码截图
此时再看市面上流行的基于 postcss 的工具,有没有醍醐灌顶?
autoprefixer | postcss-import-parser | postcss-modules | postcss-modules |
---|---|---|---|
插件有哪些?
基于 postcss 的插件有很多,可查阅:https://github.com/postcss/postcss/blob/main/docs/plugins.md。
generate 的过程依旧是以深度优先的方式遍历 AST 对象,针对不同的实例对象进行字符串的拼接。算法对应源码中位置是:postcss/lib/stringifier.js
中的stringify
方法,代码量不大,可自行查看。
css3自动加前缀 -webkit
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader:'postcss-loader',
options:{ // Webpack选项
postcssOptions:{ // loader配置选项
plugins:[
require('autoprefixer')
]
}
}
}
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
处理颜色的8进制
color: #12345678后两位用于指定透明度
postcss-preset-env
预设就是插件的集合,postcss-preset-env
已经包含了autoprefixer
,所以可以只使用postcss-preset-env
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader:'postcss-loader',
options:{ // Webpack选项
postcssOptions:{ // loader配置选项
plugins:[
require('postcss-preset-env')
]
}
}
}
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
单独使用配置文件配置postcss插件
postcss.config.js
module.exports = {
plugins: [
require('postcss-preset-env')
]
}