postcss是什么?
是一个用 JavaScript 工具和插件转换 CSS 代码的工具。譬如将css属性转换成符合多个浏览器css属性;再譬如将px转换成vh和vw。常用的插件有postcss-pxtorem,Autoprefixer等。
如何开发一款CSS插件
首先我们要了解,css插件整体流程:
先将 css 源码转换为 AST,插件基于转换后 AST 的信息进行个性化处理,最后 PostCSS 再将处理后的 AST 信息转换为 css 源码,完成 css 样式转换
css--->parser--->plugin1--->plugin2--->stringifier--->new css
-
Root: 根结点,整个处理过程基本上都在围绕着 Root,Commont,AtRule,Rule 都是它的子节点。
-
Commont: css 中的注释信息,注释的内容在 comment.text 下。
-
AtRule: 带@标识的部分,name 为标识名称,params 为标识参数。nodes 为内部包含的其他子节点,可以是 Commont,AtRule,Rule,这让我们可以自定义更多的规则。
-
Declaration:每个 css 属性以及属性值就代表一个 declaration
针对这些个类型, 文档中给我们提供了找到这些类型节点的方法如下, 具体可查看文档, 地址: postcss:
- walk
- walkAtRules
- walkComments
- walkDecls
- walkRules
通过postcss我要解决什么问题
对于ToC做移动端网页,想必最头疼的要属对各种机型的css适配问题。
- 0.5pxborder问题
- 不同机型字重问题,表现为字体粗细不一致
- 不同机型的行高问题、字体大小问题在不同机型上由于内核问题导致无法居中的问题
- 解决从蓝湖或者其他设计稿上复制的字体型号
那如何通过工程化的思想来解决很有必要。
- 通过伪元素+
transform来控制1px问题,代码如下
const { multiply } = require('./util')
/**
* 处理小于 1px 的边框,解决 android 不支持 0.5px 边框的问题
* @param {*} decl
* @param {*} options.remRootValue rem 基准值
* @param {*} options.minBorderWidth 处理的最小边框
* @returns
*/
function convert(decl, options) {
const arr = decl.value.split(' ')
const value = arr[0].toLowerCase()
const match = value.match(/([\d|.]+)/)
const minBorderWidth = options.minBorderWidth
if (match) {
const borderWidthNum = parseFloat(match[0])
const borderWidthUnit = value.replace(borderWidthNum, '').toLowerCase()
if (
(borderWidthUnit === 'rem' && borderWidthNum < minBorderWidth / options.remRootValue) ||
(borderWidthUnit === 'px' && borderWidthNum < minBorderWidth)
) {
const parent = decl.parent
let borderRadiusDecl = null
let hasPosition = false
for (const node of parent.nodes) {
if (node.type === 'decl') {
if (node.prop === 'border-radius') {
borderRadiusDecl = node
} else if (node.prop === 'position') {
hasPosition = true
}
}
}
if (!hasPosition) {
parent.append(decl.clone({ prop: 'position', value: 'relative' }))
}
arr[0] = '1px'
const borderDecl = decl.clone({ value: arr.join(' ') })
const cloneRule = parent.clone()
cloneRule.selector += ':after'
cloneRule.removeAll()
// 追加伪元素
cloneRule.append(decl.clone({ prop: 'content', value: '""' }))
cloneRule.append(decl.clone({ prop: 'position', value: 'absolute' }))
cloneRule.append(decl.clone({ prop: 'top', value: '0' }))
cloneRule.append(decl.clone({ prop: 'left', value: '0' }))
cloneRule.append(decl.clone({ prop: 'width', value: '200%' }))
cloneRule.append(decl.clone({ prop: 'height', value: '200%' }))
cloneRule.append(decl.clone({ prop: 'transform', value: 'scale(0.5)' }))
cloneRule.append(decl.clone({ prop: 'transform-origin', value: 'left top' }))
cloneRule.append(borderDecl)
if (borderRadiusDecl) {
cloneRule.append(borderRadiusDecl.clone({ value: multiply(borderRadiusDecl.value, 2) }))
}
cloneRule.raws.before = parent.raws.before || '\n'
decl.value = value + ' solid transparent'
parent.after(cloneRule)
}
}
}
module.exports = {
convert
}
- 解决字体样式问题
/**
* 处理字体样式,调整从蓝湖或 mastergo 上拷贝的字体样式,避免和全局样式冲突
* @param {*} decl
* @param {*} options.baseFontFamily 基础字体样式
* @returns
*/
function convert(decl, options) {
const ff = decl.value.replace(/'|"/gi, '').replace(', ', ',')
const normalFf = `PingFangSC-Regular`
const systemFf = 'system-ui'
if (ff.indexOf(normalFf) >= 0) {
decl.remove()
} else if (ff.indexOf(systemFf) < 0) {
const parent = decl.parent
if (parent.type === 'rule') {
// 如果是自定义字体,在后面追加全局字体样式,避免系统不支持自定义字体时显示异常
const clonedDecl = decl.clone({
value: ff.replace('PingFang SC', ` "PingFang SC"`) + ', ' + options.baseFontFamily
})
const cloneRule = parent.clone()
cloneRule.removeAll()
cloneRule.append(clonedDecl)
cloneRule.raws.before = parent.raws.before || '\n'
parent.after(cloneRule)
decl.remove()
}
}
}
module.exports = {
convert
}
其他的接种情况同理进行操作即可。
使用方法:
yarn add postcss-mobile || npm install postcss-mobile
针对多文件的postcss插件 很有用,手动狗头