端適配的配置骨架與驗(yàn)證清單)
1. 移動(dòng)端適配為什么總在真機(jī)上翻車(chē)做 Vue 移動(dòng)端項(xiàng)目最容易踩的坑不是業(yè)務(wù)邏輯而是適配。你在 Chrome 的 iPhone 模擬器里調(diào)得好好的換到真機(jī)上一看字體忽大忽小、按鈕被擠到屏幕外、1px 邊框在某些機(jī)型上粗得像 3px。這類(lèi)問(wèn)題的根源通常只有一個(gè)——你用的是固定 px而移動(dòng)端設(shè)備的邏輯像素寬度從 320 到 428 不等甚至折疊屏展開(kāi)后更寬。lib-flexible 解決的就是這件事。它的核心思路是根據(jù)設(shè)備寬度動(dòng)態(tài)設(shè)置根元素html的font-size然后所有尺寸用 rem 表示。設(shè)計(jì)稿通常是 750px 寬對(duì)應(yīng) iPhone 6/7/8 的 2 倍圖lib-flexible 會(huì)把屏幕分成 10 份每份就是 1rem。750 的設(shè)計(jì)稿上1rem 75px到了 375 邏輯寬度的手機(jī)上1rem 37.5px。元素寫(xiě)width: 2rem在 750 稿上就是 150px在 375 屏上自動(dòng)變成 75px比例完全一致。這套方案適合誰(shuí)適合用 Vue CLI 搭建的移動(dòng)端 H5 項(xiàng)目尤其是需要一套代碼適配多種屏幕寬度的場(chǎng)景。它不依賴(lài)任何后端服務(wù)純前端計(jì)算接入成本低。下面我把從安裝到真機(jī)驗(yàn)證的完整路徑拆開(kāi)講每一步都給可復(fù)制的配置。2. 接入前的環(huán)境確認(rèn)與 TaoToken 前置準(zhǔn)備在動(dòng)手改配置之前先確認(rèn)你的項(xiàng)目環(huán)境。lib-flexible 對(duì) Vue 2 和 Vue 3 都兼容但配置方式略有差異。Vue CLI 項(xiàng)目看vue.config.js或build/utils.jsVite 項(xiàng)目則走postcss.config.js。我下面以 Vue CLI 為主Vite 的差異會(huì)單獨(dú)標(biāo)注。另外如果你在開(kāi)發(fā)過(guò)程中需要調(diào)用大模型接口做輔助比如讓模型幫你檢查 rem 換算、生成適配代碼片段可以先把 API Key 準(zhǔn)備好。TaoToken 的接入文檔在 https://taotoken.net/apiKey 在控制臺(tái)的 API Keys 頁(yè)面生成https://taotoken.net/console/api-keys。拿到 Key 之后模型對(duì)話入口在 https://taotoken.net/models編碼場(chǎng)景可以用 Coding Planhttps://taotoken.net/coding-plan。這部分不是適配的必需步驟但如果你想讓模型幫你批量轉(zhuǎn)換設(shè)計(jì)稿尺寸提前配好會(huì)省事?;氐竭m配本身。你需要確認(rèn)三件事項(xiàng)目用的是 Vue CLI 還是 Vite、有沒(méi)有裝 PostCSS、設(shè)計(jì)稿寬度是多少。設(shè)計(jì)稿寬度決定了 rem 基準(zhǔn)值750 稿對(duì)應(yīng) 75375 稿對(duì)應(yīng) 37.5。這個(gè)值后面配置 postcss-pxtorem 時(shí)要用。3. 可復(fù)制的 main.js 與 postcss 配置3.1 安裝依賴(lài)打開(kāi)終端在項(xiàng)目根目錄執(zhí)行npm install lib-flexible --save npm install postcss-pxtorem --save-dev注意這里用的是postcss-pxtorem不是老教程里的px2rem-loader。px2rem-loader是 webpack loader 方案在 Vue CLI 4 和 Vite 里已經(jīng)不太適用了。postcss-pxtorem是 PostCSS 插件兼容性更好配置也更干凈。3.2 在 main.js 中引入 lib-flexible// main.js import Vue from vue import App from ./App.vue import lib-flexible/flexible.js Vue.config.productionTip false new Vue({ render: h h(App), }).$mount(#app)引入順序有講究lib-flexible/flexible.js要放在所有樣式和組件之前因?yàn)樗枰陧?yè)面渲染前就設(shè)置好根元素的 font-size。如果放在后面首屏可能會(huì)出現(xiàn)一瞬間的尺寸跳變。3.3 刪除 index.html 里的 viewport meta打開(kāi)public/index.html找到這一行并刪掉!-- 刪除這行 -- meta nameviewport contentwidthdevice-width, initial-scale1.0lib-flexible 會(huì)自己根據(jù)設(shè)備 DPR 動(dòng)態(tài)生成 viewport meta 標(biāo)簽。如果你手動(dòng)保留了一個(gè)兩個(gè) meta 會(huì)沖突導(dǎo)致縮放行為異常。這是很多人接入后真機(jī)顯示不對(duì)的首要原因。3.4 配置 postcss-pxtoremVue CLI 項(xiàng)目在根目錄創(chuàng)建或修改postcss.config.js// postcss.config.js module.exports { plugins: { postcss-pxtorem: { rootValue: 75, propList: [*], selectorBlackList: [.norem], minPixelValue: 2 } } }參數(shù)逐個(gè)說(shuō)明參數(shù)作用建議值rootValue設(shè)計(jì)稿寬度 / 10750 稿填 75375 稿填 37.5propList需要轉(zhuǎn)換的屬性[*]表示全部轉(zhuǎn)換selectorBlackList不轉(zhuǎn)換的選擇器加.norem類(lèi)可跳過(guò)minPixelValue小于該值不轉(zhuǎn)換填 2避免 1px 邊框被轉(zhuǎn)成 rem如果你用的是 Vite配置寫(xiě)在vite.config.js里// vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue import postcssPxtorem from postcss-pxtorem export default defineConfig({ plugins: [vue()], css: { postcss: { plugins: [ postcssPxtorem({ rootValue: 75, propList: [*], minPixelValue: 2 }) ] } } })3.5 特殊場(chǎng)景的寫(xiě)法有些尺寸你不想被轉(zhuǎn)換。比如 1px 邊框轉(zhuǎn)成 rem 后在部分機(jī)型上會(huì)消失或變粗。這時(shí)候用PX大寫(xiě)postcss-pxtorem 會(huì)跳過(guò).card { width: 300px; /* 會(huì)被轉(zhuǎn)成 4rem */ border: 1PX solid #ddd; /* 保持 1px 不變 */ font-size: 28px; /* 會(huì)被轉(zhuǎn)成 0.3733rem */ }字體大小是否用 rem 取決于你的需求。用 rem 的好處是隨屏幕縮放壞處是在大屏手機(jī)上字會(huì)偏大。我的做法是正文用 rem標(biāo)題用PX固定避免大屏上標(biāo)題過(guò)于夸張。4. 驗(yàn)證請(qǐng)求與真機(jī)確認(rèn)適配生效配置改完重啟開(kāi)發(fā)服務(wù)器npm run serve4.1 瀏覽器端快速驗(yàn)證打開(kāi) Chrome DevTools切到 iPhone 6/7/8375×667模式。在 Console 里輸入document.documentElement.style.fontSize如果返回37.5px說(shuō)明 lib-flexible 生效了。再切到 iPhone 5320 寬應(yīng)該返回32px。切到 iPhone 14 Pro Max430 寬應(yīng)該返回43px。這個(gè)值始終等于屏幕邏輯寬度除以 10。再檢查一個(gè)元素的 computed style。找一個(gè)你寫(xiě)了width: 150px的元素看它實(shí)際渲染的寬度是不是2rem對(duì)應(yīng)的像素值。在 375 屏上2rem 75px元素應(yīng)該占屏幕寬度的五分之一。4.2 真機(jī)驗(yàn)證步驟模擬器只能驗(yàn)證邏輯真機(jī)才能暴露 DPR 和 viewport 的問(wèn)題。用手機(jī)連上同一局域網(wǎng)訪問(wèn)開(kāi)發(fā)服務(wù)器的 IP 地址。重點(diǎn)看三件事第一頁(yè)面有沒(méi)有出現(xiàn)橫向滾動(dòng)條。如果有說(shuō)明某個(gè)元素寬度超過(guò)了 100%通常是 padding 或 margin 沒(méi)算進(jìn)去。第二1px 邊框在真機(jī)上是否清晰。如果模糊或消失檢查是不是用了小寫(xiě)px被轉(zhuǎn)成了 rem。第三旋轉(zhuǎn)屏幕后布局是否正常。lib-flexible 會(huì)監(jiān)聽(tīng) resize 事件重新計(jì)算但有些組件庫(kù)的彈窗可能不會(huì)跟著變需要手動(dòng)觸發(fā)。4.3 用模型輔助檢查換算如果你有一批設(shè)計(jì)稿尺寸要批量轉(zhuǎn) rem可以把換算規(guī)則發(fā)給模型讓它幫你算。接入方式參考 https://taotoken.net/api對(duì)話入口在 https://taotoken.net/models。比如你告訴它「rootValue 是 75把 320px、48px、120px 轉(zhuǎn)成 rem」它會(huì)直接給出結(jié)果比手算快。5. 本篇常見(jiàn)錯(cuò)誤排查5.1 根元素 font-size 始終是 0 或不變最常見(jiàn)的原因是flexible.js沒(méi)有正確引入。檢查 main.js 里的 import 路徑lib-flexible/flexible.js和lib-flexible是兩個(gè)不同的入口前者才會(huì)執(zhí)行初始化邏輯。另外確認(rèn)node_modules/lib-flexible目錄存在有時(shí)候 npm 裝包失敗但沒(méi)報(bào)錯(cuò)。5.2 樣式里的 px 沒(méi)有被轉(zhuǎn)成 rem先確認(rèn)postcss.config.js的位置。Vue CLI 項(xiàng)目放在根目錄Vite 項(xiàng)目可以放在根目錄也可以在vite.config.js里內(nèi)聯(lián)。如果兩個(gè)地方都配了會(huì)沖突。其次檢查propList是否包含了你要轉(zhuǎn)的屬性默認(rèn)[*]是全轉(zhuǎn)但如果你改成了[width, height]那font-size就不會(huì)轉(zhuǎn)。還有一個(gè)隱蔽的坑node_modules里的第三方組件庫(kù)樣式默認(rèn)不會(huì)被 PostCSS 處理。如果你用了 Vant 或 Element UI 的移動(dòng)端組件它們的 px 不會(huì)轉(zhuǎn)。解決辦法是在vue.config.js里配置transpileDependencies或者用selectorBlackList排除組件庫(kù)的類(lèi)名。5.3 真機(jī)上字體過(guò)大或過(guò)小這是 rootValue 設(shè)錯(cuò)了。750 的設(shè)計(jì)稿必須配 75如果你填了 37.5所有元素會(huì)縮小一半。反過(guò)來(lái)375 的稿填了 75元素會(huì)放大一倍。確認(rèn)你的設(shè)計(jì)稿寬度然后除以 10。5.4 橫屏?xí)r布局錯(cuò)亂lib-flexible 默認(rèn)以豎屏寬度為基準(zhǔn)。橫屏?xí)r屏幕寬度變大rem 基準(zhǔn)值也跟著變大元素會(huì)被拉寬。如果你的頁(yè)面需要支持橫屏可以在 flexible.js 引入后手動(dòng)覆蓋// 橫屏?xí)r以高度為基準(zhǔn) window.addEventListener(resize, () { if (window.orientation 90 || window.orientation -90) { document.documentElement.style.fontSize (window.innerHeight / 10) px } })5.5 首屏閃爍頁(yè)面加載時(shí)先按默認(rèn) font-size 渲染然后 flexible.js 執(zhí)行后重新計(jì)算導(dǎo)致一瞬間的尺寸跳變。解決辦法是在 index.html 的head里內(nèi)聯(lián)一段腳本在 CSS 加載前就設(shè)置好 font-sizescript (function() { var width document.documentElement.clientWidth || window.innerWidth document.documentElement.style.fontSize (width / 10) px })() /script這段腳本要放在所有 CSS 之前這樣首屏渲染時(shí)根元素尺寸就是對(duì)的。6. 后續(xù)開(kāi)發(fā)與工具鏈銜接適配配置跑通之后日常開(kāi)發(fā)還有幾件事可以提前做。第一在 VS Code 里裝cssrem插件設(shè)置cssrem.rootFontSize為 75寫(xiě) px 時(shí)它會(huì)實(shí)時(shí)提示對(duì)應(yīng)的 rem 值不用手算。第二把常用的間距、字號(hào)抽成 SCSS 變量統(tǒng)一用 rem 單位避免散落在各個(gè)組件里。第三如果項(xiàng)目要長(zhǎng)期維護(hù)考慮把適配邏輯封裝成一個(gè)獨(dú)立的 composable 或 mixin方便在多個(gè)項(xiàng)目間復(fù)用。如果你在接入過(guò)程中遇到報(bào)錯(cuò)或者想讓模型幫你審查 postcss 配置可以走 API Keys 頁(yè)面拿 Key 后接入https://taotoken.net/console/api-keys接入文檔在 https://taotoken.net/api。長(zhǎng)期做移動(dòng)端編碼的話Coding Plan 的額度更劃算https://taotoken.net/coding-plan。Claude Code 的接入方式參考https://taotoken.net/claude-code。最后提醒一句lib-flexible 已經(jīng)停止維護(hù)但它的方案足夠穩(wěn)定在存量項(xiàng)目里仍然是主流選擇。新項(xiàng)目可以考慮postcss-px-to-viewport走 vw 方案原理類(lèi)似但不需要 JS 運(yùn)行時(shí)。選哪個(gè)取決于你的團(tuán)隊(duì)習(xí)慣和瀏覽器兼容要求。