vue如何實現(xiàn)pc和移動端布局詳細代碼
1.核心點
- 本方案未采用同一套代碼去響應(yīng)各端的方式去開發(fā),個人覺得兼容太麻煩、樣式難以精確控制,遂采用 pc 和 移動端 兩套代碼、一套路由規(guī)則的方式去開發(fā)
- 本方案適用于官網(wǎng)類、展示類的項目
- 本方案在移動端中使用 vw 作為基本單位,使用 postcss-px-to-viewport 實現(xiàn)移動端 px 單位自動轉(zhuǎn)為 vw
2.開擼
2.1 獲取當前設(shè)備類型,共享給全局使用
- vuex 中定義名為 device 的 state,用于全局共享當前設(shè)備(pc || 移動)
export default new Vuex.Store({ state: { device: 'pc' }, mutations: { setDevice (state, data) { state.device = data } } })
- App.vue 中使用像素來檢測當前所處設(shè)備
<template> <div id="app"> <router-view /> </div> </template> <script> import { mapMutations } from 'vuex' import { throttle } from 'lodash-es' export default { created () { // resize節(jié)流 this.resizeChange = throttle(this.resizeChange, 200) this.resizeChange() window.addEventListener('resize', this.resizeChange, false) }, methods: { ...mapMutations(['setDevice']), resizeChange () { // 判斷是否是 pc 或者 移動端,也可以用機型之類的條件來判斷,個人覺得用像素更簡單快捷 // 默認設(shè)置當屏幕寬度 > 1000 時,為PC端 if (document.documentElement.clientWidth > 1000) { this.setDevice('pc') } else { // 默認設(shè)置當屏幕寬度 <= 1000 時,為移動端 this.setDevice('m') } } }, destroyed () { window.removeEventListener('resize', this.resizeChange, false) } } </script>
到目前為止,我們可以通過 vuex 中的 device 拿到當前的設(shè)備類型,pc 對應(yīng)的是 pc 端,m 對應(yīng)的是移動端
2.2 頁面文件結(jié)構(gòu)及關(guān)鍵代碼
- 頁面文件結(jié)構(gòu)
- 代碼
// 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 沒有本質(zhì)差異,僅修改 類名 或 組件名稱后綴為 -pc </p> </div> </template> <script> export default { name: 'index-m' } </script> <style lang="scss" scoped> .index-m { // 如果是 m.vue,這里正常使用 px 作為基本單位 } </style>
在首頁 index.vue 中,將其所對應(yīng)的 pc 端代碼定義為 pc.vue,對應(yīng)的 移動端 代碼定義為 m.vue,并導(dǎo)入到 index.vue 中,分別命名為 pc 和 m,而后使用動態(tài)組件,并使用$store.state.device來控制當前所展示的頁面為 pc端 還是 移動端。
2.3 使用 postcss-px-to-viewport 將項目中的 px 單位轉(zhuǎn)換為 vw
截止到目前,我們已經(jīng)實現(xiàn)了不同設(shè)備下(pc端 或 移動端)代碼的按需展示,但是有一個問題亟待解決。
眾所周知,移動端設(shè)備的屏幕大小不一、“千奇百怪”,如果繼續(xù)使用 px 作為基本單位,最后呈現(xiàn)的效果必然不理想,經(jīng)過對比,決定使用 vw 來作為移動端的基本單位。
但是我們都知道,一般情況下設(shè)計稿的尺寸是固定的 px 單位,在開發(fā)時需要將 px 單位轉(zhuǎn)為 vw 來進行開發(fā),但是這一過程略顯繁瑣,哪怕安裝了 vs code 的單位轉(zhuǎn)換插件依舊差強人意,這時,我們便可以請出 postcss-px-to-viewport 來幫我們解決這一問題。
作用
該插件可以讓我們在寫 css 代碼的時候正常的使用 px,其會將 px 單位自動轉(zhuǎn)換為視口單位 (vw, vh, vmin, vmax)安裝
npm install postcss-px-to-viewport --save-dev yarn add -D postcss-px-to-viewport
- 配置
// .postcssrc.js module.exports = { plugins: { // 用來給不同的瀏覽器自動添加相應(yīng)前綴,如-webkit-,-moz-等等 autoprefixer: {}, "postcss-px-to-viewport": { // 需要轉(zhuǎn)換的單位,默認為"px" unitToConvert: 'px', viewportWidth: 375, // 單位轉(zhuǎn)換后保留的精度 unitPrecision: 3, // 能轉(zhuǎn)化為vw的屬性列表 propList: [ '*' ], // 希望使用的視口單位 viewportUnit: 'vw', // 字體使用的視口單位 fontViewportUnit: 'vw', // 設(shè)置最小的轉(zhuǎn)換數(shù)值,如果為1的話,只有大于1的值會被轉(zhuǎn)換 minPixelValue: 1, // 僅轉(zhuǎn)換的文件,這里不要使用 \/ 去匹配文件,不生效?? // 這里還有一個坑,就是如果使用了 include 規(guī)則的話,正常的版本不生效,可以安裝這個 // "postcss-px-to-viewport": "github:evrone/postcss-px-to-viewport" include: [/device\\m.vue/] } } };
配置include: [/device\\m.vue/]
使我們移動端代碼中的 px 單位自動轉(zhuǎn)換為 vw,而 pc 端的代碼不受影響。
2.4 使用 node 自動創(chuàng)建符合需求的頁面
2.4.1 創(chuàng)建腳本文件
- 根目錄下創(chuàng)建 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> ` }, // 移動端 頁面基本結(jié)構(gòu) 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端 頁面基本結(jié)構(gòu) 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) } }) }) } // 創(chuàng)建頁面目錄 const dotExistDirectoryCreate = (directory) => { return new Promise((resolve) => { mkdirs(directory, function () { resolve(true) }) }) } // 遞歸創(chuàng)建目錄 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 節(jié)點下添加如下代碼
"new:view": "node ./scripts/generateView"
2.4.2 使用腳本生成符合需求的頁面
- 打開終端,輸入
npm run new:view
- 根據(jù)提示,輸入要生成的文件名稱(支持深層目錄解析)
- 下圖中我們創(chuàng)建了一個名為 about 的頁面,可以看到,只需要一個指令及文件名,我們便得到了想要的頁面結(jié)構(gòu),極大的提高了開發(fā)效率
3.持續(xù)優(yōu)化
經(jīng)過實踐發(fā)現(xiàn),pc端 和 移動端 的差別更多體現(xiàn)在頁面元素的大小、位置、顯隱上,而實際的業(yè)務(wù)邏輯變化并不大,上述方案中并未抽離頁面的 js 代碼,導(dǎo)致代碼存在冗余,這里我們可以使用 mixins 來優(yōu)化。
舉例來講,我們可以在首頁 index 頁面目錄下添加一個 mixin.js 文件,將 device/pc.vue 和 device/m.vue 的公共業(yè)務(wù)邏輯抽離到該文件中,從而實現(xiàn)復(fù)用。
因項目較小,我也是后知后覺的才想到可以進一步優(yōu)化,現(xiàn)如今項目已部署,因此就沒有再調(diào)整,可以按照上述方案自行優(yōu)化調(diào)整。
到此這篇關(guān)于vue如何實現(xiàn)pc和移動端布局的文章就介紹到這了,更多相關(guān)vue pc和移動端布局內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue列表數(shù)據(jù)刪除后主動刷新頁面及刷新方法詳解
這篇文章主要給大家介紹了關(guān)于vue列表數(shù)據(jù)刪除后主動刷新頁面及刷新方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05vue之el-menu-item如何更改導(dǎo)航菜單欄選中的背景顏色
這篇文章主要介紹了vue之el-menu-item如何更改導(dǎo)航菜單欄選中的背景顏色問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05vue3實戰(zhàn)-axios請求封裝問題(get、post、put、delete)
這篇文章主要介紹了vue3實戰(zhàn)-axios請求封裝問題(get、post、put、delete),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03