1、核心概念
1-1、物理像素
- 一个物理概念,显示器显示的最小物理单位
- iphone分辨率750*1334
- px是一个相对单位,相对的是设备像素
1-2、设备独立像素
1-3、设备像素比
- DPR设备像素比 = 设备像素/CSS像素
1-4、移动端适配
- 一般设计师按照设备像素为单位设计
- 前端工程师根据设备像素比进行换算
1-4-1、rem
- 参照跟元素的字体大小
- 适配就是让跟元素的字体大小根据分辨率进行动态改变
- px2rem-loader
1-4-2、vw和vh
- 参照的是viewport视口
- vw参照的是视口的宽度(1vw = 视口宽度/100)
- vh参照的是视口的宽度(1vh = 视口宽度/100)
- iPhone6 1vw = 3.75px
- postcss-px-to-viewport
2、px2rem-loader实战
2-1、安装
npm i webpack webpack-cli style-loader css-loader px2rem-loader html-webpack-plugin css -D
- amfe-flexible
npm i amfe-flexible -D 自动转化font-size: 37.5px; 核心是设置根元素的大小
// 导入import 'amfe-flexible'
src/index.js
// import 'px2rem-loader!./index.css' // 使用内联loader 单独设置这import './index.css'// import 'amfe-flexible'import '../amfe.flexible.js'import React from 'react'import ReactDOM from 'react-dom'import 'antd/dist/antd.css'import { Button } from 'antd'// antd已经做了转换,再次转换会有问题ReactDOM.render(<Button type='primary'>按钮</Button>,document.getElementById('root'))
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>Document</title></head><body><div id="root">root</div></body><script src="./main.js"></script></html>
webpack.config.js
const path = require("path");const HtmlWebPackPlugin = require("html-webpack-plugin");const px2rem2LoaderPath = path.resolve(__dirname, "loaders/px2rem2-loader.js");module.exports = {entry: "./src/index.js",mode: "development",devtool: false,output: {path: path.resolve(__dirname, "dist"),filename: "main.js",},resolveLoader: {// alias:{// 'px2rem2-loader': px2rem2LoaderPath// },modules: ["loaders", "node_modules"], // 找loaders文件夹 没有在找node_module 三种方式引用},module: {rules: [{test: /\.js$/,use: [{// babel-core @babel/preset-env @babel/preset-reactloader: "babel-loader",options: {presets: ["@babel/preset-env", //=> ES6 转成 ES5"@babel/preset-react", //=> 支持 React],},},],include: path.resolve(__dirname, "src"),exclude: /node_modules/,},{test: /\.css$/,use: ["style-loader","css-loader",{// loader: "px2rem-loader", // 这种找不到 要设置 resolveLoader 的别名loader: px2rem2LoaderPath, // 可以直接放一个绝对路径options: {// 1rem 设计为设计稿 / 10remUnit: 75,remPrecision: 8, // 8位小数exclude: /antd.css/},},],},],},plugins: [new HtmlWebPackPlugin({template: "./src/index.html",}),],};// 750px =》 375// 图片宽75px =》 37.5
3、实现px2rem-loader
px2rem2-loader
/*** parser转化成AST语法树* 1. 分词* 2. 语法分析、**/// let Px2rem = require("px2rem");let Px2rem = require("./pm2rem");function loader(source) {// 安装了webpack后 就会有一个这个工具类 通过它可以获取loader,里面配置的参数对象// 通过getOptions方法可以获得webpack.config.js中设置的optionslet loaderUtils = require("loader-utils");// console.log(source);let options = loaderUtils.getOptions(this);console.log(options,'options...')// let options = loaderUtils.getOptions({ remUnit: 75, remPrecision: 8 });// let options = { remUnit: 75, remPrecision: 8 }let px2rem = new Px2rem(options);let targetSource = px2rem.generateRem(source); // 生成remreturn targetSource;}module.exports = loader;// let source = `// #root{// width: 750px;// height: 750px;// }// `;// loader(source);
pm2rem.js
/*** 核心就是将css转换成rem* 靠的就是CSS抽象语法树*/const css = require("css");const pxRegExp = /\b(\d+(\.\d+)?)px\b/;class Px2rem {constructor(config) {this.config = config;}generateRem(cssText) {let self = this; // 缓存thisfunction processRules(rules) {console.log(rules,'ssssssss')for (let i = 0; i < rules.length; i++) {let rule = rules[i];let declarations = rule.declarations;for (let j = 0; j < declarations.length; j++) {let declaration = declarations[j];if (declaration.type === "declaration" &&pxRegExp.test(declaration.value)) {declaration.value = self._getCalcValue("rem", declaration.value);}}}}var astObj = css.parse(cssText); // 解析css AST// console.log(JSON.stringify(astObj, {}, 2));console.log(astObj.stylesheet,'///')processRules(astObj.stylesheet.rules);let newCSSText = css.stringify(astObj); // 处理完后 在转换回来// console.log(newCSSText, "newCSSText");return newCSSText;}_getCalcValue(type, value) {let { remUnit, remPrecision } = this.config;return value.replace(pxRegExp, (_, $1) => {// console.log($1);// console.log(this.config);let val = parseFloat($1) / remUnit.toFixed(remPrecision);return val + type;});}}module.exports = Px2rem;
AST工作流
- Parse解析,江原道吗转换成抽象语法树,时尚很多estree节点
- Transform对臭显语法书进行转换
- Generate(代码生成)将上一步的结果转换成新的代码
在线语法转换: astexpoler
lib-flexible
let docEl = document.documentElement;let dpr = window.devicePixelRatio || 1// https://github.com/amfe/lib-flexible/blob/2.0/index.jsfunction setRemUnit(){// 750情况下 750px// Iphone 375pxif(document.body){document.body.style.fontSize = (12 * dpr) + 'px'}let rem = docEl.clientWidth / 10;docEl.style.fontSize = rem + 'px'}setRemUnit()window.addEventListener('resize', setRemUnit)
- 第三方库以及做了适配 在使用原声的px2rem-loader会造成样式很小,原声库无法改变这个问题,只能自己实现一个
4、第三方框架产生的样式问题
原因是第三方做了适配移动端,又被处理了一次导致了问题
解决方案
方案一:
手写px2rem2LoaderPath , 增加一个exclude: /antd.css/
webpack.config.js
{test: /\.css$/,use: ["style-loader","css-loader",{// loader: "px2rem-loader",// loader: "px2rem-loader", // 这种找不到 要设置 resolveLoader 的别名loader: px2rem2LoaderPath, // 可以直接放一个绝对路径options: {// 1rem 设计为设计稿 / 10remUnit: 75,remPrecision: 8, // 8位小数exclude: /antd.css/},},],},
// px2rem2-loader.js/*** parser转化成AST语法树* 1. 分词* 2. 语法分析、**/// let Px2rem = require("px2rem");let Px2rem = require("./pm2rem");function loader(source) {// 安装了webpack后 就会有一个这个工具类 通过它可以获取loader,里面配置的参数对象// 通过getOptions方法可以获得webpack.config.js中设置的optionslet loaderUtils = require("loader-utils");let options = loaderUtils.getOptions(this);console.log(this.resource,'resource+++');if(options.exclude&&options.exclude.test(this.resource)){return source; // 不转换,直接返回}let px2rem = new Px2rem(options);let targetSource = px2rem.generateRem(source); // 生成remconsole.log(targetSource);return targetSource;}module.exports = loader;// let source = `// #root{// width: 750px;// height: 750px;// }// `;// loader(source);
方案二、css和antd.css分别去处理
{test: /\.css$/,exclude:/antd\.css$/,use: ["style-loader","css-loader",{loader: "px2rem-loader",options: {// 1rem 设计为设计稿 / 10remUnit: 75,remPrecision: 8},},],},{test: /antd\.css$/,use: ["style-loader","css-loader"],},
方案三 内联loader
index.js
// import 'px2rem-loader!./index.css' // 使用内联loader 单独设置这
