前提
你已经了解vue cli来构建项目。无论使用vue cli 2.x 还是3.x 版本,如果你了解webpack配置,那么过程都是大同小异。首先我们初始化一个项目:
vue init webpack vue-vw
进入项目:
cd vue-vw
启动项目:
npm run dev
1. 安装一些PostCSS插件
在项目的根目录下有一个.postcssrc.js,默认情况下已经安装了以下几个插件:
- postcss-import
- postcss-url
- autoprefixer
postcss-import
postcss-import主要功有是解决@import引入路径问题。使用这个插件,可以让你很轻易的使用本地文件、node_modules文件
postcss-url
该插件主要用来处理文件,比如图片文件、字体文件等引用路径的处理。在Vue项目中, vue-loader 已具有类似的功能
autoprefixer
autoprefixer插件是用来自动处理浏览器前缀的一个插件
为了完成vw的布局兼容方案并简化我们的工作,还需要安装配置下面的几个PostCSS插件:
- postcss-px-to-viewport
- postcss-cssnext
- cssnano
- postcss-aspect-ratio-mini
- postcss-write-svg
- postcss-viewport-units
npm i postcss-px-to-viewport postcss-cssnext cssnano postcss-aspect-ratio-mini postcss-write-svg postcss-viewport-units --save-dev
接下来在.postcssrc.js文件对新安装的PostCSS插件进行配置:
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// "autoprefixer": {},
"postcss-aspect-ratio-mini": {},
"postcss-write-svg": {
utf8: false
},
"postcss-cssnext": {},
"postcss-px-to-viewport": {
viewportWidth: 750, // (Number) The width of the viewport.
unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
propList:['*','!font','!font-size'],
selectorBlackList: ['.ignore', '.hairlines'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false // (Boolean) Allow px to be converted in media queries.
},
"postcss-viewport-units":{},
"cssnano": {
preset: "advanced",
autoprefixer: false,
"postcss-zindex": false
}
}
}
注意:由于cssnext和cssnano都具有autoprefixer。所以需要把默认的autoprefixer删除掉,然后把cssnano中的autoprefixer设置为false,我们使用cssnext的autoprefixer。
将viewportWidth设为我们视觉稿的尺寸,这里使用750px的视觉稿
postcss-px-to-viewport
postcss-px-to-viewport插件主要用来把px单位转换为vw、vh、vmin或者vmax这样的视窗单位,也是vw适配方案的核心插件之一。配置我们参考官网
postcss-cssnext
postcss-cssnext其实就是cssnext。该插件可以让我们使用CSS未来的特性,其会对这些特性做相关的兼容性处理。
cssnano
cssnano主要用来压缩和清理CSS代码。在Webpack中,cssnano和 css-loader捆绑在一起,所以不需要自己加载它。不过你也可以使postcss-loader显式的使用cssnano。
在cssnano的配置中,使用了preset: “advanced”,所以我们需要另外安装cssnano-preset-advanced:
npm i cssnano-preset-advanced --save-dev
cssnano集成了一些其他的PostCSS插件,如果你想禁用cssnano中的某个插件的时候,可以像下面这样操作:
"cssnano": {
autoprefixer: false,
"postcss-zindex": false
}
上面的代码把autoprefixer和postcss-zindex禁掉了。前者是有重复调用,后者只要启用了,z-index的值就会重置为1,我们需要禁用。
postcss-aspect-ratio-mini
postcss-aspect-ratio-mini主要用来处理元素容器宽高比。在实际使用的时候,具有一个默认的结构,用法可以去github查看。
postcss-write-svg
postcss-write-svg插件主要用来处理移动端1px的解决方案。该插件主要使用的是border-image和background来做1px的相关处理。
postcss-viewport-units
postcss-viewport-units插件主要是给CSS的属性添加content的属性,配合viewport-units-buggyfill库给vw、vh、vmin和vmax做适配的操作。
这是实现vw布局必不可少的一个插件,因为少了这个插件,这将是一件痛苦的事情。
2. 兼容vw
让浏览器兼容视口单位的最终的解决方案就是使用viewport-units-buggyfill
viewport-units-buggyfill主要有两个JavaScript文件:viewport-units-buggyfill.js和viewport-units-buggyfill.hacks.js。
(1)在vue项目中的index.html引入它们:
<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>
当然你也可以使用其他CDN,或者使用npm安装
(2)调用viewport-units-buggyfill:
<script>
window.onload = function () {
window.viewportUnitsBuggyfill.init({
hacks: window.viewportUnitsBuggyfillHacks
});
}
</script>
(3)在使用了视口单位地方,添加content
在你的CSS中,只要使用到了viewport的单位(vw、vh、vmin或vmax )地方,需要在样式中添加content:
ul {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
font-size: 18px;
list-style-type: none;
padding:20px;
/* hack to engage viewport-units-buggyfill */
content: "viewport-units-buggyfill; padding: 2.667vw";
}
如果每次都需要手动书写,会极大地增加我们的工作量。这个时候就需要前面提到的postcss-viewport-units 插件。这个插件将让你无需关注content的内容,插件会自动帮你处理。
viewport-units-buggyfill还提供了其他的功能,详细的这里不阐述了。
3.常见问题
(1)遇到不想px转换为vw的地方,可以添加指定的类名像.ignore。然后在 "postcss-px-to-viewport"插件配置中的selectorBlackList属性添加这个类名即可
(2)使用了ui框架的,需要避免px转vw。可以在 "postcss-px-to-viewport"插件配置中的exclude属性添加ui框架的目录,将其排除在外
(3)Viewport Units Buggyfill添加的content也会引起一定的副作用。比如
The content hack may not work well on and other replaced elements, even though it should compute to content: normal; on regular elements. If you find yourself in such a situation, this may be a way out:
img {
content: normal !important;
}