記 vue 移動端開發 中的經驗
專案背景
手上的 vue移動端 專案已經開發了大幾個月了,遇到了一些很有意思的坑,也讓自己學習了很多;寫此文主要目的是記下一些我遇到的坑,以及自己的解決方案,分享的同時也方便以後複習。
專案的底層是上司通過 Cordova 等常用的 hybird app工具打包出來的。然後通過 webview 開啟我的vue專案。所以嚴格意義上說,我還是在做單頁面應用。 hybird app 的底層會提供一些api 給我呼叫,方便我關閉開啟webview,或者跳轉到不同子頁面。hybird app會整合不同的業務。這些業務有hybird app本事的服務,也有像我這種,完全來自其服務的頁面。這些就是專案大概的背景。
上中下三部分的定位問題。
這個問題我在文章 中已經詳細說過。
rem 的使用;
我直接在 app.vue 中新增以下方法,執行後,你會在html 標籤中看到 fontsize 設定為了50px; 表示 1rem = 50px;
created() { this.resize(document, window); }, methods:{ /*設定rem參照單位。width:1rem = 50px 所以設計稿寬 375px == 375/50 = 7.5rem * 由於頁面中有些元素用了絕對定位。特別是top,bottom。由於裝置不同,計算出的rem不同, * 導致定位覆蓋。所以,建議涉及高度的 統一用 px 做單位,包括padding-top,bottom等。 * 因為高度存在滾動條,不存在適配問題。主要針對寬度做適配。 * * */ resize(doc, win) { var docE1 = doc.documentElement, resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize', recalc = function () { var clientWidth = docE1.clientWidth; if (!clientWidth) return; //docE1.style.fontSize = clientWidth / 375+ 'px'; 這裡希望設定 1rem = 1px,實驗證明,這樣做 會導致 html 的 fontsize小於 12px docE1.style.fontSize = (clientWidth / (375*2)) * 100 + 'px'; //乘以100的意義是,1為了不受fontsize小於12的影響,2為了計算方便; }; if (!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); doc.addEventListener('DOMContentLoaded', recalc, false); }, }
使用建議:
1,少量大小的定義儘量使用px,因為對自適應效果影響不大。例如某個div的padding,設定為5px 10px,影響是不大的。
2,寬度上的定義儘量使用rem 作為單位,因為移動裝置對寬度敏感,可謂寸金寸土。設定了以上程式碼後,可以通過設計稿尺寸/50 得到rem單位的數值。 例如 padding:10px; 可以寫成 padding: 10px 0.2rem; 或者 padding:0.2rem;
3,高度上的定義,儘量使用px;因為本專案可以滾動內容頁,所以高度是不敏感的。設定為px 的原因是,後面定位 loadermore 元件會有幫助。當然,如果你對計算很有把握,或者頁面內容不允許滾動,也可以使用 rem;
重新整理某個子頁面
遇到一個填寫表單點儲存形成草稿模式的需求。要求在url中加入引數 id;重新整理本頁面,重新通過id獲取資料回填。 vue 是單頁面應用,肯定不能全域性重新整理。
同事的解決方案
呼叫儲存介面,獲取到id後, 通過
this.router.push(this.$route.path + "&id=" + id);//加引數本頁並不會重新整理
改變url ,然後重新申請 呼叫介面,拿到最新的資料,回填回去。
這樣做,理論上是行得通的。當時很危險,因為使用者操作頁面,會改變很多變數。如果回填資料後,由於沒有經歷完整的created等生命週期,這些變數還是原來狀態,容易出bug;
其次,如果像本專案那樣,需要支援 hybird app 通過url+id 的方式直接去到草稿的話,程式碼不好維護。所以,最理想的做法,就是真實的重新
load 一次這個子頁面。
正確做法
利用vue 的provide / inject api
* app.vue 中定義 <router-view v-if="isRouterAlive"/> data() { return { isRouterAlive: true, } }, provide() { return { reload: this.reload, } }, methods: { reload() { this.isRouterAlive = false this.$nextTick(() => (this.isRouterAlive = true)) }, } * 需要重新整理的子頁面 inject: ['reload'], //需要呼叫的地方 let path =this.$route.path+"?id="+id this.$router.replace(path); this.reload();
keep-alive 頁面怎麼重新整理
這個需求很常見,有個列表頁面,點選某一條去到詳情頁面,點選返回,列表頁面保持狀態不變,滾動條保持原來位置。如果,詳情對資料做了改變,點選返回,列表頁面才重新整理。
* app.vue 中 <div id="app"> <keep-alive> <router-view v-if="isRouterAlive&&$route.meta.keepAlive"/> </keep-alive> <router-view v-if="isRouterAlive&&!$route.meta.keepAlive"/> </div> * route.js 中 { path: 'a',//我的草稿 name: 'myDraft', meta:{ keepAlive:true, }, component: resolve => require(['page/myDraft'],resolve) },
這樣,定義了meta keepAlive 為true 的頁面就會被 快取。資料不變的情況下,點選返回, 只要把滾動條位置設定到原來離開哪裡就好了。
但是問題來了,1,從首頁進入 keepAlive 頁面,每次都要重新整理,二,詳情頁如果改變了資料,返回後也要重新整理 頁面。
這裡我主要通過 eventBus 來解決了元件通知 頁面 重新整理的問題。
細節可以看我的筆記,最好的實踐應該是最後提到大神的連結文章。
不同頁面跳轉到同一個頁面,topBar 點選返回,回到各個出發位置。
* topBar.vue 元件的封裝並不難,就是預留自定cancel函式,不然就呼叫 app.vue 中的 backHome 函式 對返回做統一處理 inject:['backHome'], cancel(){ if(this.popup){ this.$emit('cancel') }else{ this.backHome(); } }, * app.vue provide() { return { backHome:this.backHome } }, backHome(){ //返回或退出webview letisOutsidePage = this.$route.params.inside; letfrom = this.$route.params.from; if(isOutsidePage=='in'){ //內頁跳轉 if(from=="CC"){ //回到a中心 this.$router.replace('/controlCenter') }else if(from=="SF"){ //回到b中心 this.$router.replace('/controlCenter2') }else { //回到原來的子頁面(從a頁到b頁前,必須要先儲存lastFullPath) this.$router.replace(this.$store.getters.lastFullPath) this.$store.commit('setLastFullPath',"")//置空舊路徑 } }else{//關閉webView closeWebView(); } } * router.js { path: '/myDraft/:from/:inside', name: 'myDraft', component: resolve => require(['page/myDraft'],resolve) }, { path: '/myDraft', redirect: 'myDraft/ll/out', },
通過上面的定義 //hybrid app 只需要呼叫 ip:xxxx/myDraft 就能開啟這個頁面,並且返回鍵自動關閉webview;
通過 CC CF 等標誌字元 可以判斷來自哪個 中心的。
最後來到重點的 子頁跳子頁返回 操作,主要就是需要藉助vuex 儲存舊 路徑
a.vue 子頁 //跳轉前先把當前路徑儲存到全域性vuex變數lastFullPath this.$store.commit('setLastFullPath',route.fullPath)//儲存路由用於返回本頁 this.$router.replace('/ ');//清空路由,不重置會導致url 混亂。 this.$router.replace(`b/`+route.name+`/in?id=`+id);