CodingMeUp / AboutFE

知识归纳、内容都在issue里
74 stars 14 forks source link

10、PostCSS #11

Open CodingMeUp opened 7 years ago

CodingMeUp commented 7 years ago

1. 它本质上是一个什么东西?

直观的理解为: 平台 我们直接用它,感觉不能干什么事情,但是如果让一些插件在它上面跑,那么将会很强大。css -> css parser -> plugin system -> stringify -> css

组成

CodingMeUp commented 7 years ago

2. 它能解决我们什么问题?它是通过什么方式来解决我们的问题?

能够为 CSS 提供额外的功能;

通过在 PostCSS 这个平台上,我们能够开发一些插件,来处理我们的CSS,比如热门的:autoprefixer

CodingMeUp commented 7 years ago

3、优势及SASS LESS STYLUS比较

比如,我们用 SASS 来处理 box-shadow 的前缀,我们需要这样写:

/* CSS3 box-shadow */
@mixin box-shadow($top, $left, $blur, $size, $color, $inset: false) {
    @if $inset {
        -webkit-box-shadow: inset $top $left $blur $size $color;
        box-shadow: inset $top $left $blur $size $color;
    } @else {
        -webkit-box-shadow: $top $left $blur $size $color;
        box-shadow: $top $left $blur $size $color;
    }
}

使用 PostCSS 我们只需要按标准的 CSS 来写就行了,因为最后 autoprefixer 会帮我们做添加这个事情~

box-shadow: 0 0 3px 5px rgba(222, 222, 222, .3);

未来编码的问题。实际上,PostCSS 改变的是一种开发模式。

SASS等工具:源代码 -> 生产环境 CSS PostCSS:源代码 -> 标准 CSS -> 生产环境 CSS

这样能体会出优势吧,但是目前大家都是 SASS + PostCSS 这样的开发模式,其实我认为是不错的,取长补短嘛,当然,在 PostCSS 平台上都是可以做到的,只是目前这个过渡期

CodingMeUp commented 7 years ago

4、 富哥使用的postcss-flexible 及 其他的应用

'use strict';

var postcss = require('postcss')

var valueRegExp = /(dpr|rem|url)\((.+?)(px)?\)/
var dprRegExp = /dpr\((\d+(?:\.\d+)?)px\)/
var urlRegExp = /url\(['"]?\S+?@[1-3]x\S+?['"]?\)/

module.exports = postcss.plugin('postcss-flexible', function (options) {
  if (!options) {
    options = {}
  }

  return function (root, result) {
    var desktop = !!options.desktop
    var baseDpr = options.baseDpr || 2
    var remUnit = options.remUnit || 75
    var remPrecision = options.remPrecision || 6
    var addPrefixToSelector = options.addPrefixToSelector || function (selector, prefix) {
      if (/^html/.test(selector)) {
        return selector.replace(/^html/, 'html' + prefix)
      }
      return prefix + ' ' + selector
    }

    // get calculated value of px or rem
    function getCalcValue (value, dpr) {
      var valueGlobalRegExp = new RegExp(valueRegExp.source, 'g')

      function getValue(val, type) {
        val = parseFloat(val.toFixed(remPrecision)) // control decimal precision of the calculated value
        return val == 0 ? val : val + type
      }

      return value.replace(valueGlobalRegExp, function ($0, $1, $2) {
        if ($1 === 'url') {
          if (dpr) {
            return 'url(' + $2.replace(/@[1-3]x/g, '@' + dpr + 'x') + ')'
          }
        } else if ($1 === 'dpr') {
          if (dpr) {
            return getValue($2 * dpr / baseDpr, 'px')
          }
        } else if ($1 === 'rem') {
          return getValue($2 / remUnit, 'rem')
        }
        return $0
      })
    }

    function handleDesktop (rule) {
      rule.walkDecls(function (decl) {
        if (valueRegExp.test(decl.value)) {
          if (decl.value === '0px') {
            decl.value = '0'
          } else {
            if (dprRegExp.test(decl.value) || urlRegExp.test(decl.value)) {
              decl.value = getCalcValue(decl.value, 2)
            } else {
              // only has rem()
              decl.value = getCalcValue(decl.value)
            }
          }
        }
      })
    }

    function handleMobile (rule) {
      if (rule.selector.indexOf('[data-dpr="') !== -1) {
        return
      }

      var newRules = []
      var hasDecls = false

      for (var dpr = 3; dpr >= 1; dpr--) {
        var newRule = postcss.rule({
          selectors: rule.selectors.map(function (sel) {
            return addPrefixToSelector(sel, '[data-dpr="' + dpr + '"]')
          }),
          type: rule.type
        })
        newRules.push(newRule)
      }

      rule.walkDecls(function (decl) {
        if (valueRegExp.test(decl.value)) {
          if (decl.value === '0px') {
            decl.value = '0'
          } else {
            if (dprRegExp.test(decl.value) || urlRegExp.test(decl.value)) {
              // generate 3 new decls and put them in the new rules which has [data-dpr]
              newRules.forEach(function (newRule, index) {
                var newDecl = postcss.decl({
                  prop: decl.prop,
                  value: getCalcValue(decl.value, 3 - index)
                })
                newRule.append(newDecl)
              })
              hasDecls = true
              decl.remove() // delete this rule
            } else {
              // only has rem()
              decl.value = getCalcValue(decl.value)
            }
          }
        }
      })

      if (hasDecls) {
        newRules.forEach(function (newRule) {
          rule.parent.insertAfter(rule, newRule)
        })
      }

      // if the origin rule has no declarations, delete it
      if (!rule.nodes.length) {
        rule.remove()
      }
    }

    root.walkRules(function (rule) {
      desktop ? handleDesktop(rule) : handleMobile(rule)
    })
  }
})

rem px 转换

加入 processors 就可以用了,很方便:

var custom = function(css, opts){
    css.eachDecl(function(decl){
        decl.value = decl.value.replace(/\d+rem/, function(str){
            return 16 * parseFloat(str) + "px";
        });
    });
};

with webpack vue

const vueLoaderOptions = {
  postcss: pack => {
    // see: https://github.com/ai/browserslist#queries
    const browsers = 'Android >= 4, iOS >= 7, IE >= 10'

    return [
      require('postcss-import')({
        path: paths.src('application/styles')
      }),
      require('postcss-url')({
        basePath: paths.src('static')
      }),
      require('postcss-cssnext')({
        browsers,
        features: {
          customProperties: {
            variables: require(paths.src('application/styles/variables'))
          },
          // 禁用 autoprefixer,在 postcss-rtl 后单独引入
          // 否则会跟 postcss-rtl 冲突
          autoprefixer: false
        }
      }),
      // 如果不需要 flexible,请移除
      require('postcss-flexible')({
        remUnit: 75
      }),
      require('autoprefixer')({
        browsers
      }),
      require('postcss-browser-reporter')(),
      require('postcss-reporter')()
    ]
  },
  autoprefixer: false
}

   =====
  webpackConfig.plugins.push(
    new webpack.LoaderOptionsPlugin({
      minimize: true,
      options: {
        context: __dirname
      },
      vue: vueLoaderOptions
    }),