1.核心概念 #

1.1 设备物理像素 #

1.2 设备独立像素 #

1.3 设备像素比 #

8335aff90708c99fe6dd75a92e0cd787

1.4 移动端适配 #

1.4.1 rem #

1.4.2 vw和vh #

型号 宽度 1vw
iPhone6 375 3.75px

750px 75px

1vw=7.5px 10vw=75px

75/10

2.px2rem-loader实战 #

2.1 安装 #

npm install webpack webpack-cli html-webpack-plugin style-loader css-loader amfe-flexible px2rem-loader --save-dev

2.2 src\index.js #

import './base.css'

2.3 src\base.css #

#root{
    width:750px;
    height:750px;
}

2.4 src\index.html #

src\index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>webpack</title>
</head>
<body>
    <div id="root"></div>
</body>
</html>

2.5 webpack.config.js #

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode: 'development',
    devtool: false,
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [{
                loader: 'style-loader'
            }, {
                loader: 'css-loader'
            }, {
                loader: 'px2rem-loader',
                options: {
                    remUni: 75,
                    remPrecision: 8
                }
            }]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
    ]
};

2.6 package.json #

package.json

{
   "scripts": {
    "build": "webpack"
   }
}

3. loader #

loaders\px2rem-loader.js

function loader(source){
    console.log('px2rem-loader');
    return source;
}
module.exports = loader;

4. 使用自定义loader #

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    mode: 'development',
    devtool: false,
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
+   resolveLoader: {
+       alias: {
+           "px2rem-loader": path.resolve('./loaders/px2rem-loader.js')
+       },
+       modules: [path.resolve('./loaders'), 'node_modules']
+   },
    module: {
        rules: [{
            test: /\.css$/,
            use: [
                { loader: 'style-loader' },
                { loader: 'css-loader' },
                {
+                   loader: path.resolve(__dirname, 'loaders/px2rem-loader.js'),
                    options: {
                        remUni: 75,
                        remPrecision: 8
                    }
                }]
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' })
    ]
};

5 css #

5.1 AST #

ast

5.2 AST工作流 #

ast-compiler-flow

5.3 px2rem.js #

px2rem.js

var css = require('css');
var pxRegExp = /\b(\d+(\.\d+)?)px\b/;
var pxGlobalRegExp = new RegExp(pxRegExp.source, 'g');
class Px2rem {
    constructor(config) {
        this.config = config;
    }
    generateRem(cssText) {
        let self = this;
        function processRules(rules) {
            for (var i = 0; i < rules.length; i++) {
                var rule = rules[i];
                var declarations = rule.declarations;
                for (var j = 0; j < declarations.length; j++) {
                    var declaration = declarations[j];
                    if (declaration.type === 'declaration' && pxRegExp.test(declaration.value)) {
                        declaration.value = self._getCalcValue('rem', declaration.value);
                    }
                }
            }
        }
        var astObj = css.parse(cssText);
        //console.log(JSON.stringify(astObj.stylesheet.rules, null, 2));
        processRules(astObj.stylesheet.rules);
        return css.stringify(astObj);
    }
    _getCalcValue(type, value) {
        var { remUnit, remPrecision } = this.config;
        return value.replace(pxGlobalRegExp, (_, $1) => {
            let val = parseFloat($1) / remUnit.toFixed(remPrecision);
            return val + type;
        });
    }
}
module.exports = Px2rem;

5.4 usePx2rem.js #

usePx2rem.js

let Px2rem = require('./px2rem');
let px2rem = new Px2rem({
    remUnit: 75,
    remPrecision: 8
});
let cssText = `
#root{
    width:750px;
    height:750px;
}
`;
let newCSS = px2rem.generateRem(cssText);
console.log(newCSS);

/**
[
  {
    "type": "rule",
    "selectors": ["#root"],
    "declarations": [
      {
        "type": "declaration",
        "property": "width",
        "value": "750px"
      },
      {
        "type": "declaration",
        "property": "height",
        "value": "750px"
      }
    ]
  }
]
*/

6. px2rem-loader.js #

loaders\px2rem-loader.js

var loaderUtils = require('loader-utils');
var Px2rem = require('./px2rem');
function loader(source) {
  var options = loaderUtils.getOptions(this);
  var px2remIns = new Px2rem(options);
  let targetSource = px2remIns.generateRem(source);
  return targetSource;
}
module.exports = loader;

7. lib-flexible #

src\index.js

import './base.css';
import 'amfe-flexible/index.js';
<script>
     (function flexible (window, document) {
         var docEl = document.documentElement;
         // set 1rem = viewWidth / 10
         function setRemUnit () {
             var rem = docEl.clientWidth / 10;
             docEl.style.fontSize = rem + 'px';
         }
         setRemUnit();
         window.addEventListener('resize', setRemUnit);
         }(window, document))
 </script>

7. 第三方框架样式问题 #

7.1 index.js #

import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import {Button} from 'antd';
ReactDOM.render(<div>
    <Button type="primary">按钮</Button>
</div>, document.getElementById('root'));

7.2 webpack.config.js #

webpack.config.js

 {
            test: /\.css$/,
            use: [
                { loader: 'style-loader' },
                { loader: 'css-loader' },
                {
                    loader: path.resolve(__dirname, 'loaders/px2rem-loader.js'),
                    options: {
                        remUnit: 75,
                        remPrecision: 8,
+                       exclude:/antd\.css/
                    }
                }],

7.3 px2rem-loader.js #

loaders\px2rem-loader.js

var loaderUtils = require('loader-utils');
var Px2rem = require('./px2rem');

module.exports = function (source) {
  var options = loaderUtils.getOptions(this);
+ if(options.exclude && options.exclude.test(this.resource)){
+   return source;
+ }
  var px2remIns = new Px2rem(options);
  let targetSource = px2remIns.generateRem(source);
  return targetSource;
}