因项目的需求, 自己动手写了一个 PostCSS https://github.com/postcss/postcss 插件 https://github.com/moohng/postcss-px2vw , 主要用于将 px 转成 vw 和 rem,rem 作为回退模式. 也刚好借此机会总结一下 npm 包的发布流程, 文章还会介绍到七牛云图片的使用与上传相关的技巧, 以及期间遇到的一些问题.
为什么需要它
转换 px 单位的插件有很多, 知名的有 https://github.com/evrone/postcss-px-to-viewport 和 https://github.com/cuth/postcss-pxtorem , 前者是将 px 转成 vw, 后者是将 px 转成 rem.
起初是看了大漠的一篇文章如何在 vue 项目中使用 vw 实现移动端适配 http://www.w3cplus.com/mobile/vw-layout-in-vue.html , 于是怀着激动的心情, 就在项目中也使用 vw 来做移动端的适配. 该文章大力推行用 vw 代替 rem 做适配, 在 amfe-flexible https://github.com/amfe/lib-flexible 项目文档中也推荐 vw 的替代方案. 但是考虑到移动端对 vw 的支持情况不如 rem, 所以仍有很多项目都选择使用 rem 来布局. 于是就想到将 rem 作为一种回退机制, 或许觉得没必要, 直接放弃 vw 使用 rem 不就完了? 确实, 不过既然是折腾, 也就不需要那么多理由了, 其实饿了么平台 https://h5.ele.me/ 就用了此方案.
关于移动端适配方案, 也有一些个人的亲身体会, 有时间另启一篇文章详细总结一下.
实现方案
首先, 得提一下 CSS 样式的回退原理: 当 CSS 遇到无法识别的一些样式时, 不会报错, 而是忽略它. 并且后面的样式声明比前面的样式声明权重要高 (也就是会覆盖前面的样式). 所以, 我们要达到的效果是这样的:
- .class {
- margin-top: 2rem;
- margin-top: 20vw;
- }
复制代码
浏览器会优先使用第二个 margin-top: 20vw;, 如果不支持该声明就会直接忽略它, 并使用第一个 margin-top: 2rem;.
要实现这样的效果, 一开始想到的是同时使用上面介绍到的两款插件, 设置 postcss-pxtorem 的参数 replace: false, 最后的结果是这样的:
- .class {
- margin-top: 20vw;
- margin-top: 2rem;
- }
复制代码
虽然最后结果同时保留了两种单位, 但是优先使用的是 rem, 这并不能满足我们的需求. 原因也很简单, 看 postcss-pxtorem 源码就会知道, 设置 replace: false 时, 它会在当前样式下面插入转换后的结果. 而
postcss-px-to-viewport
是不能设置 replace 的, 所以, 无论如何配置, rem 单位始终会在 vw 的下面.
基于这个点, 就可以编写我们自己的代码了.
代码实现
打开 https://github.com/evrone/postcss-px-to-viewport 源码, 其实也就短短的几十行代码. 就算没写过 PostCSS 插件, 看到源码, 依葫芦画瓢, 也能写一个适合自己的插件来. 这是修改过后的源码:
- 'use strict';
- var postcss = require('postcss');
- var objectAssign = require('object-assign');
- module.exports = postcss.plugin('postcss-px2vw', function (options) {
- var opts = objectAssign({
- viewportWidth: 750,
- unitPrecision: 5,
- rootValue: 75,
- minPixelValue: 1
- }, options);
- var pxRegex = /"[^"]+"|'[^']+'|url\([^\)]+\)|(\d*\.?\d+)px/ig;
- return function (css) {
- css.walkDecls(function (decl, i) {
- if (decl.value.indexOf('px') === -1) return;
- var value = decl.value;
- if (opts.viewportWidth) {
- var pxReplaceForVw = createPxReplace(opts.viewportWidth / 100, opts.minPixelValue, opts.unitPrecision, 'vw');
- decl.value = value.replace(pxRegex, pxReplaceForVw);
- }
- if (opts.rootValue) {
- var pxReplaceForRem = createPxReplace(opts.rootValue, opts.minPixelValue, opts.unitPrecision, 'rem');
- if (opts.viewportWidth) {
- decl.parent.insertBefore(i, decl.clone({
- value: value.replace(pxRegex, pxReplaceForRem)
- }));
- } else {
- decl.value = value.replace(pxRegex, pxReplaceForRem);
- }
- }
- });
- };
- });
- function createPxReplace(perRatio, minPixelValue, unitPrecision, unit) {
- return function (m, $1) {
- if (!$1) return m;
- var pixels = parseFloat($1);
- if (pixels <= minPixelValue) return m;
- return toFixed((pixels / perRatio), unitPrecision) + unit;
- };
- }
- function toFixed(number, precision) {
- var multiplier = Math.pow(10, precision + 1);
- var wholeNumber = Math.floor(number * multiplier);
- return Math.round(wholeNumber / 10) * 10 / multiplier;
- }
复制代码
合并了两款插件的核心转换功能, 并去掉了一些不常用的功能和配置项, 实现最初的目的就行, 尽量保持简单易用的原则. 该插件只保留了 4 个可配置参数 (viewportWidth,rootValue,unitPrecision,minPixelValue), 具体使用说明可查看 项目的 README.md https://github.com/moohng/postcss-px2vw 文档.
发布到 npm 仓库
本来只是为了项目的需要, 没打算从项目中独立出现来的. 一方面为了熟悉一下 npm 的发布流程, 另一方面也是为了在自己的 github 仓库中保留一份记录.
整个流程很简单, 也就几步:
要发布到 npm 仓库, 首先得新建一个项目. 运行 npm init, 填写相关参数说明;
登录 npm 账号: npm login, 输入用户名密码以及邮箱. 如果没有账号就去 npm 官网 https://www.npmjs.com/ 注册一个;
发布: npm publish, 大功告成.
不过现实往往没有理论那么容易, 总会遇到一些问题.
如果你使用的是淘宝源, 需要先切回官方源, 使用 nrm 的话, 只需 nrm use npm 即可;
package.json 中的 name 不能与仓库中已有的项目重名. 如果难以命名, 可添加用户名前缀, 如:
@moohng/postcss-px2vw
. 添加用户前缀是不能直接 publish 到公有仓库中的, 需要使用
- npm publish --access=public
- ;
每次更新发布必须增加 package.json 中的版本号.
七牛云的使用
为什么会使用到七牛云? 这本是八竿子也打不着的东西. 其实, 也就是为了学别人在项目的 README.md 文档最后贴一张个人的收款二维码. 先别想其他, 就贴一张图本身而言, 其实很简单, 就是在 markdown 语法中插入一张图片链接地址.
为了一张图片的链接地址, 就把七牛云也整一遍? 的确, 就是出于这个目的于是就研究了一遍七牛云这东西. 简单的上传无外乎就是登陆到七牛云平台, 然后拽一张图片上去, 链接地址就有了. 可是, 作为一个高大上的程序猿, 怎能如此将就, 当然得有一套不同寻常的方式了.
最终要达到的效果就是得到一张上传图片的链接地址: http://static.moohng.com/FrEihC8JSWMtsxtnDUpQiuaL9ZbE,自定义域名 + 图片 hash 值.
域名配置
要配置自定义域名, 首先得拥有一个已经备案过的域名. 然后在七牛云的融合 CDN https://portal.qiniu.com/cdn/domain 菜单下可以添加域名:
因掘金对图片强制需要使用 https 链接, 所以就不配图了. 其中要注意的一点就是源站配置一定要选择七牛云存储和对应的存储空间名称.
接下来在域名管理平台配置 CNAME,CNAME 值可在刚添加的域名下查看. 如此一来, 以后访问七牛云上的资源就可以直接通过自定义域名来访问了.
图片上传
获取上传 token
实现上传最关键的就是要有一个 token,token 的生成在官方文档 https://developer.qiniu.com/kodo/manual/1208/upload-token 中都有说明. 不过我们不用自己写算法, 官网已经用多种语言实现了封装. 前端可参考 Node.js SDK https://developer.qiniu.com/kodo/sdk/1289/nodejs , 简单实现:
新建一个项目 demo, 初始化 npm init, 然后安装七牛云 SDK npm install qiniu.
- // index.js
- var qiniu = require('qiniu');
- // 可在七牛云查看
- var accessKey = 'accessKey';
- var secretKey = 'secretKey';
- var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
- var options = {
- scope: 'moohng', // 存储空间名称
- };
- var putPolicy = new qiniu.rs.PutPolicy(options);
- var uploadToken = putPolicy.uploadToken(mac);
- console.log(uploadToken);
复制代码
其中, accessKey 和 secretKey 可在七牛云个人中心 https://portal.qiniu.com/user/key 查看.
在 Nodejs 环境下运行代码, node index.js, 得到 token.
上传
上传地址可在七牛云开发者中心 https://developer.qiniu.com/kodo/manual/1671/region-endpoint 查看, 不同地区的地址不一样. 例如, 华南地区: http(s)://upload-z2.qiniup.com.
本来是想写一个图形化界面, 用来上传图片. 无赖自己比较懒, 既然拿到了最关键的 token, 也有了上传地址, 至于图形化界面有时间再说吧.
作为程序员, 逼格最高的莫过于命令行了. 于是, 就想到了 Linux 下常用的 curl 命令.
$ curl http://upload-z2.qiniup.com -F "file=@./img_example.png" -F "token = 这里是生成的 token"
复制代码
然后一个优雅的回车, 一切就结束了! 不出意外会返回一个 key, 这个 key 拼接上之前自定义的域名, 就可以为所欲为了.
关于 curl 命令,-F 表示上传文件, 文件参数与其他参数必须分开写, 文件参数使用 @+ 文件路径.
更多的使用说明及相关参数请参考七牛云开发文档 https://developer.qiniu.com/kodo/manual/1272/form-upload .
图片压缩
七牛云提供了多个图片处理工具 http://developer.qiniu.com/dora/manual/3683/img-directions-for-use , 裁剪, 缩放, 压缩等. 如果放到 github 上的图片太大, 可能会加载不出来, 所以, 对图片进行缩放和压缩是很有必要的.
处理之前: http://static.moohng.com/FrEihC8JSWMtsxtnDUpQiuaL9ZbE (85.1k)
处理之后: http://static.moohng.com/FrEihC8JSWMtsxtnDUpQiuaL9ZbE?imageView2/1/w/320/h/320/interlace/1/q/20|imageslim (17.1k)
其效果还是很明显的.
最后
关注我, 不定期更新前端技术型文章.
超轻量代码实现字段校验工具库 (移动端) https://www.jianshu.com/p/29535b7e9377
js 中异步处理的正确姿势: 你想知道的都在这了 https://www.jianshu.com/p/3e1b0349c9bc
全栈实用技能, pm2 部署 node 应用到服务器 https://www.jianshu.com/p/9680c87a3696
来源: https://juejin.im/post/5b82009ce51d4538cf53dc4c