Vue 中使用 pxtorem 实现自适应
一、前言
在前端,自适应是不得不考虑的问题。
但是实现自适应有方案也有很多种。今天介绍的是通过在 Webpack 中配置 loader 实现。
还有一个原因,在查资料的时候,网上的一些文章对 postcss-pxtorem、px2rem-loader 用法有些混乱。
特别是 postcss-pxtorem 是 postcss 的 plugin ,px2rem-loader 是一个独立的 loader 这一点。
所以下面也对这一点做了对比分析。
二、原理、选工具
以前有项目中使用的是 px2rem-loader 这个工具库。
其工作原理是:配置一个 remUnit ,在打包过程中把所有的 px 单位通过换算:
font-size: 16px; // 转换 16/remUnit ,remUnit =16 font-size: 1rem;
这样全局的 px 全部转换为了 rem。
再配置 html 下的 font-size ,进而设置 rem 的值,达到自适应当前尺寸。
基本原理清除了。继续搜索了一些这方面的工具库:
postcss-pxtorem 最近一次更新 9个月前 周下载5w+
postcss-px2rem 最近一次更新5年前 周下载2k+
px2rem-loader 最近一次更新3年前 周下载2k+
通过对比后打算在项目使用第一个:postcss-pxtorem。
下面对 postcss-pxtorem 和 px2rem-loader 做对比。
三、Postcss-pxtorem
首先下载安装工具包:
npm install postcss-pxtorem --save-dev
配置:
使用不同的脚手架,其配置方式有所不同。
Vue-cli2:
直接在 Webpack 配置项中的 module 中添加 loader配置项:
module: { rules: [ { test: /\.(scss|css)$/, loaders: [ 'style-loader', 'css-loader', //**************************适配配置开始 { loader: 'postcss-pxtorem', //放在这'style-loader','css-loader'后面,sass-loader前面 options: { ident: 'postcss', //当引入外部的依赖包作为组件配置项时需要定义一个唯一的标识符,推荐这样写 plugins: [ pxtorem({ rootValue: 16, //表示根元素html的fontSize值,也可以是100,获取任意其他值 propList: ['*'], //设置px转换成rem的属性值,*表示所有属性的px转换为rem }), ], }, }, //**************************适配部分结束 'sass-loader', ], } ] }
因为这个是依赖 postcss 的,所以是放在 postcss-loader 配置项中的 plugins 中。
Vue-cli3:
Vue-cli3 中没有 webpack 单独的配置问题件,需要在 vue.config.js 中配置。
module.exports = { // css 配置 css: { loaderOptions: { postcss: { plugins: [require('tailwindcss'), require('autoprefixer'), require('postcss-pxtorem')({ rootValue: 16, propList: ['*'] })] } } } }
postcss 配置文件中设置(.postcssrc.js 或者 postcss.config.js):
注意:这个适用于不同的脚手架,只是这个配置文件的名称会不一样
module.exports = { plugins: { 'autoprefixer': { browsers: ['Android >= 4.0', 'iOS >= 7'] }, 'postcss-pxtorem': { rootValue: 16, //结果为:设计稿元素尺寸/16,比如元素宽320px,最终页面会换算成 20rem propList: ['*'], selectorBlackList: ['.norem'] // 过滤掉 .norem 开头的 class,不进行转换 } } }
注意:
在最新的项目中使用该插件,报错:[object Object] is not a PostCSS plugin。
查了一番资料后,原来是版本的问题,由最新的 6.0.0 更新为 5.1.1 (2021年3月25日 更新该问题)。
后续如果使用新的 postcss 的话应该不会出现这个问题。
四、px2rem-loader
因为这个不是依赖于 postcss,所以配置上会不一样:
这个是直接在 css 文件的 loader 里配置,和其他 loader 同级别
Vue-cli2:
module.exports = { // ... module: { rules: [{ test: /\.css$/, use: [{ loader: 'style-loader' }, { loader: 'css-loader' }, { loader: 'px2rem-loader', // options 配置 options: { remUni: 75, remPrecision: 8 } }] }] } }
Vue-cli3:
module.exports = { chainWebpack: config => { config.module .rule('scss') .test(/\.scss$/) .oneOf('vue') .use('px2rem-loader') .loader('px2rem-loader') .before('postcss-loader') .options({ remUnit: 75, remPrecision: 8 }) .end() } }
五、动态修改 rem 对应值
使用上面的 loader,是把代码中的 px 全部转换为 rem。要实现自适应,需要动态的改变 html 的 font-size 才可以。
所以要绑定对 window 绑定 onresize 事件。
注意:为了避免其他地方也有需要根据窗口大小变化而变化的,给 onresize 绑定一个回调数组。
/** * 根据屏幕变化,自动更改 html 的 font-size */ function setRem() { const htmlWidth = document.body && document.body.offsetWidth const htmlEle = document.getElementsByTagName('html')[0] // 默认是在 1920 下 16px,那么对应比例缩放 htmlEle.style.fontSize = htmlWidth ? (htmlWidth / 1920 * 16 + 'px') : '16px' } setRem() // onresize 回调数组 window.resizeCallbackList = [setRem] window.onresize = () => { window.resizeCallbackList.map(callback => { callback() }) }
其他地方也需要绑定的时候,直接:
window.resizeCallbackList.push(callback)