当你陪着家人嗑着瓜子, 和家人一起看着春晚, 顺便拿着手淘参与春晚抽奖互动的时候, 杭州还有两百多程序员还奋战在一线当中现在年也过完了, 奖也抽了, 红包也拿了也该好好回来工作的时候了这次很荣幸, 自己能参与手淘过年项目 (红包开光和春晚互动项目) 的项目中, 虽然仅仅参与其中的部分工作, 但事后感觉有些东西还是应该总结总结的, 为之后的项目做准备那么简单的来总结一下, 我自己在参与项目中用到的一些前端技术
这些技术其实也并不是什么鲜为人知的技术栈, 因为这些技术点已经出现很久了, 只不过大家习惯了自己的开发模式, 加上项目时间紧, 怕尝试新的东西事实上我自己也是如此, 害怕使用这些技术点, 给项目带来其他的风险(本来项目时间就很紧), 庆幸的是, 接下来了到的一些东西, 经住了项目的考验, 虽然当中踩过一些坑, 但总算是无惊无险
过年项目
手淘过年项目, 事实上分为两个, 其中一个叫红包开光, 另一个是春晚抽奖的互动项目
上面两张图分别是红包开光和春晚互动的主界面视觉图如果你参与过手淘过年互动的活动中, 这两个界面应该对您来说并不会太陌生
经过团队同学一起讨论, 这次两个项目都基于 vue 来开发, Vue 只是一个 JavaScript 库而以, 选择他并不没有太多的主要原因, 而是想让团队在今后的项目开发的时候, JavaScript 库能趋于统一, 从而慢慢在项目中有所沉淀与积累基于这个原因, 我在其中主要做的事情, 在这个脚手架中 (也就是 Vue-cli 的基础) 添加了以下三个部分:
PostCSS 插件
vw 适配方案
iPhone X 适配
PostCSS 插件
在互动脚手架中, 目前已配置的 PostCSS 插件主要有:
- postcss-import
- postcss-url
- postcss-aspect-ratio-mini
- postcss-cssnext
- autoprefixer
- postcss-px-to-viewport
- postcss-write-svg
- cssnano
- postcss-viewport-units
PostCSS 插件的配置
webpack 项目的. postcssrc.js 最终的 PostCSS 插件的配置:
- module.exports = {
- "plugins": {
- "postcss-import": {},
- "postcss-url": {},
- "postcss-aspect-ratio-mini": {},
- "postcss-write-svg": {
- utf8: false
- },
- "postcss-cssnext": {},
- "postcss-px-to-viewport": {
- viewportWidth: 750,
- viewportHeight: 1334,
- unitPrecision: 3,
- viewportUnit: 'vw',
- selectorBlackList: ['.ignore', '.hairlines'],
- minPixelValue: 1,
- mediaQuery: false
- },
- "postcss-viewport-units":{},
- "cssnano": {
- preset: "advanced",
- autoprefixer: false,
- "postcss-zindex": false
- }
- }
- }
对于这些 PostCSS 插件所起的作用和怎么配置, 在其对应的 GitHub 上都有详细的描述这里简要的描述一下, 为什么在我们的项目中会采用这些 PostCSS 的插件:
postcss-import 和 postcss-url 两个主要是用于处理引入的文件和资源路径的处理以及工作模式如果你的项目也使用的是 Vue, 并且配置了 vue-loader, 并且配置了相关的参数, 那就就具有类似的功能
autoprefixer 主要用来处理浏览器的私有前缀, 这个已经是大家经常使用的一个 PostCSS 插件了这里需要提出的是, 如果你的项目中使用了 postcss-next 和 cssnano, 那么 autoprefixer 插件可以不引入, 而且在 postcss-next 和 cssnano 两者中选择其一关闭 autoprefixer, 因为这两个插件都集成了 autoprefixer 插件的特性
postcss-cssnext 其实就是 cssnext 该插件可以让我们使用 CSS 未来的特性, 其会对这些特性做相关的兼容性处理其包含的特性主要有:
有关于 cssnext 的每个特性的操作文档, 可以点击这里浏览
cssnano 主要用来压缩和清理 CSS 代码在 Webpack 中, cssnano 和 css-loader 捆绑在一起, 所以不需要自己加载它不过你也可以使用 postcss-loader 显式的使用 cssnano 有关于 cssnano 的详细文档, 可以点击这里获取
注: 由于 cssnano 的 preset 配置使用的是 advanced, 所以需要安装
npm install cssnano-preset-advanced --save-dev
另外 cssnext 和 cssnano 都具有 autoprefixer 的插件, 因此在 cssnano 中将 autoprefixer 设置为 false
postcss-write-svg 插件主要用来处理移动端 1px 的解决方案该插件主要使用的是 border-image 和 background 配合 SVG 绘制的矢量图来做 1px 的相关处理后续将会专门花一节的内容来介绍 postcss-write-svg 或者说怎么能更好的使用 SVG 来处理移动端 1px
postcss-aspect-ratio-mini
主要用来处理元素容器宽高比在项目当中很多地方会使用 imgobject 或者 video, 那么这个插件能更好的帮助我们完美处理宽高比的缩放在实际使用的时候, 具有一个默认的结构:
- <div aspectratio>
- <div aspectratio-content></div>
- </div>
在实际使用的时候, 你可以把自定义属性 aspectratio 和
aspectratio-content
换成相应的类名, 比如:
- <div class="aspectratio">
- <div class="aspectratio-content"></div>
- </div>
我个人比较喜欢用自定义属性, 它和类名所起的作用是同等的结构定义之后, 需要在你的样式文件中添加一个统一的宽度比默认属性:
- [aspectratio] {
- position: relative;
- }
- [aspectratio]::before {
- content: '';
- display: block;
- width: 1px;
- margin-left: -1px;
- height: 0;
- }
- [aspectratio-content] {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- width: 100%;
- height: 100%;
- }
如果我们想要做一个 188:246(
188
是容器宽度,
246
是容器高度)这样的比例容器, 只需要这样使用:
- [w-188-246] {
- aspect-ratio: '188:246';
- }
有一点需要特别注意: aspect-ratio 属性不能和其他属性写在一起, 否则编译出来的属性只会留下 aspect-ratio 的值, 比如:
<div aspectratio w-188-246 class="color"></div>
编译前的 CSS 如下:
- [w-188-246] {
- width: 188px;
- background-color: red;
- aspect-ratio: '188:246';
- }
编译之后:
- [w-188-246]:before {
- padding-top: 130.85106382978725%;
- }
主要是因为在插件中做了相应的处理, 不在每次调用 aspect-ratio 时, 生成前面指定的默认样式代码, 这样代码没那么冗余所以在使用的时候, 需要把 width 和 background-color 分开来写:
- [w-188-246] {
- width: 188px;
- background-color: red;
- }
- [w-188-246] {
- aspect-ratio: '188:246';
- }
这个时候, 编译出来的 CSS 就正常了:
- [w-188-246] {
- width: 25.067vw;
- background-color: red;
- }
- [w-188-246]:before {
- padding-top: 130.85106382978725%;
- }
这个现象也算是一个天坑吧而这个坑是该插件自己带来的, 上面的处理方式只是治标而不能治本所以在使用该插件的时候, 需要特别注意这个细节
目前采用 PostCSS 插件只是一个过渡阶段, 在将来我们可以直接在 CSS 中使用 aspect-ratio 属性来实现长宽比当然, 如果你对 cssnext 熟悉的话, 可以给其添加这样的一个 PR, 将 CSS 原生的 aspect-ratio 属性添加到 cssnext 特性当中, 这样只要你使用 postcss-next 就可以忽略这个插件了
剩下的
postcss-px-to-viewport
和
postcss-viewport-units
两个 PostCSS 插件主要是用于 vw 适配方案, 算是这次项目中必不可少的 PostCSS 插件其中,
postcss-px-to-viewport
插件主要用来把 px 单位转换为 vwvhvmin 或者 vmax 这样的视窗单位, 也是 vw 适配方案的核心插件之一
在配置中需要配置相关的几个关键参数:
- "postcss-px-to-viewport": {
- viewportWidth: 750, // 视窗的宽度, 对应的是我们设计稿的宽度, 一般是 750
- viewportHeight: 1334, // 视窗的高度, 根据 750 设备的宽度来指定, 一般指定 1334, 也可以不配置
- unitPrecision: 3, // 指定 `px` 转换为视窗单位值的小数位数(很多时候无法整除)
- viewportUnit: 'vw', // 指定需要转换成的视窗单位, 建议使用 vw
- selectorBlackList: ['.ignore', '.hairlines'], // 指定不转换为视窗单位的类, 可以自定义, 可以无限添加, 建议定义一至两个通用的类名
- minPixelValue: 1, // 小于或等于 `1px` 不转换为视窗单位, 你也可以设置为你想要的值
- mediaQuery: false // 允许在媒体查询中转换 `px`
- }
目前出视觉设计稿, 我们都是使用 750px 宽度的, 那么 100vw = 750px, 即 1vw = 7.5px 那么我们可以根据设计图上的 px 值直接转换成对应的 vw 值在实际撸码过程, 不需要进行任何的计算, 直接在代码中写 px, 比如:
- .test {
- border: .5px solid black;
- border-bottom-width: 4px;
- font-size: 14px;
- line-height: 20px;
- position: relative;
- }
- [w-188-246] {
- width: 188px;
- }
编译出来的 CSS:
- .test {
- border: .5px solid #000;
- border-bottom-width: .533vw;
- font-size: 1.867vw;
- line-height: 2.667vw;
- position: relative;
- }
- [w-188-246] {
- width: 25.067vw;
- }
在不想要把 px 转换为 vw 的时候, 首先在对应的元素 (html) 中添加配置中指定的类名 ignore 或 hairlines(hairlines 一般用于设置 border-width:0.5px 的元素中):
<div class="box haspx"></div>
写 CSS 的时候:
- .ignore {
- margin: 10px;
- background-color: red;
- }
- .box {
- width: 180px;
- height: 300px;
- }
编译出来的 CSS:
- .box {
- width: 24vw;
- height: 40vw;
- }
- .ignore {
- margin: 10px; /*.box 元素中带有. ignore, 在这个类名写的 `px` 不会被转换 */
- background-color: red;
- }
上面解决了 px 到 vw 的转换计算
由于浏览器对 vw 还具有一定的兼容性, 其在 Android 4.4 之下和 iOS8 以下的版本都存有一定的问题为了让 vwvhvmin 和 vmax 这些 viewport 单位能更好的使用其兼容方案就是使用 viewport 的 polyfill:Viewport Units Buggyfill
而在采用 Viewport Units Buggyfill 的时候, 需要手动给使用 viewport 单位的样式中添加其对应的 Hack 代码, 比如:
- .box {
- top: 2vw;
- left: 1vw;
- content: 'viewport-units-buggyfill;top: 2vw;left: 1vw;';
- }
如果每一个都这样来做, 那么将是灾难性的幸运的是, 可以使用
postcss-viewport-units
其主要是给 CSS 的属性添加 content 的属性, 配合
viewport-units-buggyfill
库给 vwvhvmin 和 vmax 做适配的操作
另一个坑, 使用
postcss-viewport-units
将会给具有 content 属性的元素造成一定的影响, 比如你的项目中使用伪元素::before::after 或者伪类: before:after 之类那么使用该插件, 会自动替换你原来的 content 内容, 为了避免该现象, 需要在 content 的属性值末尾添加! important
上面这些 PostCSS 插件是在这次项目中使用的, 也将会在后面的项目中继续使用, 使用其主要原因是帮助我们解放双手能更好的撸或许你对其中一些插件有更好的使用心得, 欢迎和我们一起分享, 如果你有更好的插件, 能帮助我们解放双手, 也欢迎分享给我们
vw 适配方案
vw 适配方案, 主要是用于解决移动端布局的问题事实上, 在手淘, 甚至到目前为止都还在使用 Flexible 的布局方案, 用于适配移动端的各种终端在 15 年双 11 之后, 写了一篇使用 Flexible 实现手淘 H5 页面的终端适配博文, 将此方案分享给业内, 而且该方案在业内快速的被复用和修改(原理是一样的)
Flexible 的适合方案, 在那个时期是非常强大的, 想出这个方案的大神让我膜拜已久当然, 事物是两极的, 他非常强大, 但他也有自己的不足之处, 特别是在 vw 得到更多的支持的时候, 我觉得 Flexible 应该退出其历史的使用 (这是我自己 YY 的) 所以在 17 年年初我开始在探讨 vw 在移动端中的使用, 经过一段时间的探讨和尝试, 我写下了再聊移动端页面的适配一文
使用 vw 可以看到测试用例得到了众多设备的支持:
大家看到众多, 或许会生疑, 那么还有不支持的将会是怎么? 特别是老板跟我说, 这次过年项目咱们使用 vw 来做适配布局吧其实听到这个消息, 我自己是非常高兴的, 毕竟学习过的技术方案有较大的项目来验证心里是美的, 但也略感压力, 就害怕又会折腾出新的妖蛾子想想都怕怕(^_^)
众所周知, 浏览器对 vw 还具有一定的兼容性, 其在 Android 4.4 之下和 iOS8 以下的版本都存有一定的问题为了让 vwvhvmin 和 vmax 这些 viewport 单位能更好的使用需要考虑 viewport 单位在不支持的浏览器 (或设备) 做相应的处理
为了能让项目更安全, 在决定过年项目中采用 vw 布局方案的时候, 我就又再一次做了一个技术验证, 这次是基于 Vue 的 Vue-cli 脚手架的上来做的, 毕竟我们的项目也要有 Vue 嘛在这个脚手加上, 我将上面介绍的 PostCSS 插件配置进去, 特别是
postcss-px-to-viewport
和
postcss-viewport-units
两个 PostCSS 插件和 Viewport Units Buggyfill 让我完美的解决了 vw 兼容问题而且让开发者无感知他们不需要考虑怎么处理兼容, 只需要按着设计稿前行
当然, 完成这个技术方案的验证, 其中还是碰到一些坑的, 幸好能像打老怪一样, 一个一个 Fix 这里就不阐述整个过程, 如果你感兴趣可以阅读如何在 Vue 项目中使用 vw 实现移动端适配一文接下来简单的介绍一下 vw 兼容方案处理方式
vw 兼容方案
移动端使用 vw 布局, 其兼容方案就是使用 viewport 的 polyfill:Viewport Units Buggyfill 使用
viewport-units-buggyfill
主要分以下几步走:
引入 JavaScript 文件
在你的 HTML 文件, 比如 index.html 中的</head > 或</body > 引入下面的 JavaScript 文件:
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
调用 viewport-units-buggyfill 的方法
同样在 HTML 文件中调用
viewport-units-buggyfill
的方法, 比如:
- <script>
- window.onload = function () {
- window.viewportUnitsBuggyfill.init({
- hacks: window.viewportUnitsBuggyfillHacks
- });
- }
- </script>
有关于 Viewport Units Buggyfill 更多的方法, 可以阅读其官网文档
Flexible 项目无缝过渡到 vw
在前面发布的博客当中, 有同学提到一个问题, 如何能快速的将使用 Flexible 布局的项目无缝过渡到 vw 布局, 刚好借这次项目的机会做了一个小测试实现这个也并不复杂简单的描述一下其过程:
第一步
将 Flexible 项目中的 flexible.js 和 flexible.css 删除, 并使用下面的 vw 的兼容脚本:
<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
第二步
- @media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) {
- /* iPhone X 独有样式写在这里 */
- }
来源: https://juejin.im/entry/5a9550e66fb9a0633f0e488a