vue实现pc和移动端布局
但是我们都知道,一般情况下设计稿的尺寸是固定的 px 单位,在开发时需要将 px 单位转为 vw 来进行开发,但是这一过程略显繁琐,哪怕安装了 vs code 的单位转换插件依旧差强人意,这时,我们便可以请出 postcss-px-to-viewport 来帮我们解决这一问题。经过实践发现,pc端 和 移动端 的差别更多体现在页面元素的大小、位置、显隐上,而实际的业务逻辑变化并不大,上述方案中并未
文章目录
1.核心点
- 本方案未采用同一套代码去响应各端的方式去开发,个人觉得兼容太麻烦、样式难以精确控制,遂采用 pc 和 移动端 两套代码、一套路由规则的方式去开发
- 本方案适用于官网类、展示类的项目
- 本方案在移动端中使用 vw 作为基本单位,使用 postcss-px-to-viewport 实现移动端 px 单位自动转为 vw
2.开撸
2.1 获取当前设备类型,共享给全局使用
- vuex 中定义名为 device 的 state,用于全局共享当前设备(pc || 移动)
export default new Vuex.Store({
state: {
device: 'pc'
},
mutations: {
setDevice (state, data) {
state.device = data
}
}
})
- App.vue 中使用像素来检测当前所处设备
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import { throttle } from 'lodash-es'
export default {
created () {
// resize节流
this.resizeChange = throttle(this.resizeChange, 200)
this.resizeChange()
window.addEventListener('resize', this.resizeChange, false)
},
methods: {
...mapMutations(['setDevice']),
resizeChange () {
// 判断是否是 pc 或者 移动端,也可以用机型之类的条件来判断,个人觉得用像素更简单快捷
// 默认设置当屏幕宽度 > 1000 时,为PC端
if (document.documentElement.clientWidth > 1000) {
this.setDevice('pc')
} else {
// 默认设置当屏幕宽度 <= 1000 时,为移动端
this.setDevice('m')
}
}
},
destroyed () {
window.removeEventListener('resize', this.resizeChange, false)
}
}
</script>
到目前为止,我们可以通过 vuex 中的 device 拿到当前的设备类型,pc 对应的是 pc 端,m 对应的是移动端
2.2 页面文件结构及关键代码
- 页面文件结构
- 代码
// index.vue
<template>
<div class="index">
<component :is="$store.state.device"></component>
</div>
</template>
<script>
import m from './device/m.vue'
import pc from './device/pc.vue'
export default {
name: 'index',
components: {
pc,
m
}
}
</script>
// device 目录下的 m.vue 或 pc.vue
// m.vue
<template>
<div class="index-m">
<p> 这里没有其他特殊之处,正常写 移动端 和 pc 端代码即可 </p>
<p> m.vue 和 pc.vue 没有本质差异,仅修改 类名 或 组件名称后缀为 -pc </p>
</div>
</template>
<script>
export default {
name: 'index-m'
}
</script>
<style lang="scss" scoped>
.index-m {
// 如果是 m.vue,这里正常使用 px 作为基本单位
}
</style>
在首页 index.vue 中,将其所对应的 pc 端代码定义为 pc.vue,对应的 移动端 代码定义为 m.vue,并导入到 index.vue 中,分别命名为 pc 和 m,而后使用动态组件,并使用$store.state.device
来控制当前所展示的页面为 pc端 还是 移动端。
2.3 使用 postcss-px-to-viewport 将项目中的 px 单位转换为 vw
截止到目前,我们已经实现了不同设备下(pc端 或 移动端)代码的按需展示,但是有一个问题亟待解决。
众所周知,移动端设备的屏幕大小不一、“千奇百怪”,如果继续使用 px 作为基本单位,最后呈现的效果必然不理想,经过对比,决定使用 vw 来作为移动端的基本单位。
但是我们都知道,一般情况下设计稿的尺寸是固定的 px 单位,在开发时需要将 px 单位转为 vw 来进行开发,但是这一过程略显繁琐,哪怕安装了 vs code 的单位转换插件依旧差强人意,这时,我们便可以请出 postcss-px-to-viewport 来帮我们解决这一问题。
-
作用
该插件可以让我们在写 css 代码的时候正常的使用 px,其会将 px 单位自动转换为视口单位 (vw, vh, vmin, vmax) -
安装
npm install postcss-px-to-viewport --save-dev
yarn add -D postcss-px-to-viewport
- 配置
// .postcssrc.js
module.exports = {
plugins: {
// 用来给不同的浏览器自动添加相应前缀,如-webkit-,-moz-等等
autoprefixer: {},
"postcss-px-to-viewport": {
// 需要转换的单位,默认为"px"
unitToConvert: 'px',
viewportWidth: 375,
// 单位转换后保留的精度
unitPrecision: 3,
// 能转化为vw的属性列表
propList: [
'*'
],
// 希望使用的视口单位
viewportUnit: 'vw',
// 字体使用的视口单位
fontViewportUnit: 'vw',
// 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
minPixelValue: 1,
// 仅转换的文件,这里不要使用 \/ 去匹配文件,不生效😓
// 这里还有一个坑,就是如果使用了 include 规则的话,正常的版本不生效,可以安装这个
// "postcss-px-to-viewport": "github:evrone/postcss-px-to-viewport"
include: [/device\\m.vue/]
}
}
};
配置include: [/device\\m.vue/]
使我们移动端代码中的 px 单位自动转换为 vw,而 pc 端的代码不受影响。
2.4 使用 node 自动创建符合需求的页面
2.4.1 创建脚本文件
- 根目录下创建 scripts 及其子文件
- template.js
module.exports = {
viewTemplate: viewName => {
return `
<template>
<div class="${viewName}">
<component :is="$store.state.device"></component>
</div>
</template>
<script>
import m from './device/m.vue'
import pc from './device/pc.vue'
export default {
name: '${viewName}',
components: {
pc,
m
}
}
</script>
`
},
// 移动端 页面基本结构
mTemplate: viewName => {
return `
<template>
<div class="${viewName}-m">
</div>
</template>
<script>
export default {
name: '${viewName}-m'
}
</script>
<style lang="scss" scoped>
.${viewName}-m {
}
</style>
`
},
// pc端 页面基本结构
pcTemplate: viewName => {
return `
<template>
<div class="${viewName}-pc">
</div>
</template>
<script>
export default {
name: '${viewName}-pc'
}
</script>
<style lang="scss" scoped>
.${viewName}-pc {
}
</style>
`
}
}
- generateView.js
// generateView.js
const chalk = require('chalk')
const path = require('path')
const fs = require('fs')
const log = message => console.log(chalk.green(`${message}`))
const successLog = message => console.log(chalk.blue(`${message}`))
const errorLog = error => console.log(chalk.red(`${error}`))
const { viewTemplate, mTemplate, pcTemplate } = require('./template')
const generateFile = (path, data) => {
if (fs.existsSync(path)) {
errorLog(`${path}文件已存在`)
return
}
return new Promise((resolve, reject) => {
fs.writeFile(path, data, 'utf8', err => {
if (err) {
errorLog(err.message)
reject(err)
} else {
resolve(true)
}
})
})
}
// 创建页面目录
const dotExistDirectoryCreate = (directory) => {
return new Promise((resolve) => {
mkdirs(directory, function () {
resolve(true)
})
})
}
// 递归创建目录
const mkdirs = (directory, callback) => {
var exists = fs.existsSync(directory)
if (exists) {
callback()
} else {
mkdirs(path.dirname(directory), function () {
fs.mkdirSync(directory)
callback()
})
}
}
// 获取页面名称
const getViewName = (viewPath) => {
const arr = viewPath.split('\\')
return arr[arr.length - 1]
}
log('请输入要生成的页面组件名称(无需添加.vue后缀)、支持深层目录解析(dirName/viewName)、页面将生成在 views/目录下')
let viewName = ''
process.stdin.on('data', async chunk => {
const inputName = String(chunk).trim().toString()
// Vue页面组件路径
let viewPath = path.resolve(__dirname, '../src/views', inputName)
viewName = getViewName(inputName)
viewPath = path.resolve(viewPath, viewName + '.vue')
const viewDirectory = path.dirname(viewPath)
// 检查界面是否存在
const hasViewExists = fs.existsSync(viewPath)
if (hasViewExists) {
errorLog(`${inputName}页面已存在,请重新输入`)
return
}
try {
// 1.生成页面目录
log(`正在生成页面目录 ${viewDirectory}`)
await dotExistDirectoryCreate(viewDirectory)
// 2.生成页面子目录
const sonViewDirectory = path.resolve(viewDirectory, './device')
log(`正在生成页面子目录 ${sonViewDirectory}`)
await dotExistDirectoryCreate(sonViewDirectory)
// 3.生成 m.vue 页面
const mViewPath = path.resolve(sonViewDirectory, './m.vue')
log(`正在生成子目录子页面文件 ${mViewPath}`)
await generateFile(mViewPath, mTemplate(viewName))
// 4.生成 pc.vue 页面
const pcViewPath = path.resolve(sonViewDirectory, './pc.vue')
log(`正在生成子目录子页面文件 ${pcViewPath}`)
await generateFile(pcViewPath, pcTemplate(viewName))
// 5.生成页面
log(`正在生成页面文件 ${viewPath}`)
await generateFile(viewPath, viewTemplate(viewName))
successLog('生成成功')
} catch (e) {
errorLog(e.message)
}
process.stdin.emit('end')
})
process.stdin.on('end', () => {
log('exit')
process.exit()
})
- package.json 中 scripts 节点下添加如下代码
"new:view": "node ./scripts/generateView"
2.4.2 使用脚本生成符合需求的页面
- 打开终端,输入
npm run new:view
- 根据提示,输入要生成的文件名称(支持深层目录解析)
- 下图中我们创建了一个名为 about 的页面,可以看到,只需要一个指令及文件名,我们便得到了想要的页面结构,极大的提高了开发效率
3.持续优化
经过实践发现,pc端 和 移动端 的差别更多体现在页面元素的大小、位置、显隐上,而实际的业务逻辑变化并不大,上述方案中并未抽离页面的 js 代码,导致代码存在冗余,这里我们可以使用 mixins 来优化。
举例来讲,我们可以在首页 index 页面目录下添加一个 mixin.js 文件,将 device/pc.vue 和 device/m.vue 的公共业务逻辑抽离到该文件中,从而实现复用。
因项目较小,我也是后知后觉的才想到可以进一步优化,现如今项目已部署,因此就没有再调整,可以按照上述方案自行优化调整。
更多推荐
所有评论(0)