我們在使用vue構建單頁應用的時候,能給我們的效率帶來很大的提升,但是緊隨而來的也有一些新框架的問題。
1,比如我們公司在移動端微信上面的項目,如果所有項目都合並成一個,首頁加載速度就會非常慢,所以我就去改webpack配置,把它變成多頁應用。點擊微信菜單后,只加載當前菜單項的所有內容,也就是以菜單項分為分頁的標准,在當前菜單項里面通過vue的路由控制。
2,單頁應用路由切換,微信的title不會改變,經過查閱資料,發現網上有一個解決方案,就是利用vue-wechat-title這個插件可以解決;
3,還有就是Vue使用動態組件或者router時,當多個視圖使用/指向同一個組件時,如何能夠生成多個新實例?比如文章頁里面有個相關文章閱讀,點擊之后會一直復用文章頁的組件,這個問題困擾我很久,網上查閱資料也沒有具體的方法,在作者官網上市這么說的:
響應路由參數的變化
提醒一下,當使用路由參數時,例如從
/user/foo
導航到user/bar
,原來的組件實例會被復用。因為兩個路由都渲染同個組件,比起銷毀再創建,復用則顯得更加高效。不過,這也意味着組件的生命周期鈎子不會再被調用。復用組件時,想對路由參數的變化作出響應的話,你可以簡單地 watch(監測變化)
$route
對象:const User = { template: '...', watch: { '$route' (to, from) { // 對路由變化作出響應... } } }
道理說的很明白了,只要是同一個組件,不管你切換多少次,它都會共用一個數據模型,雖然通過路由監聽的方式可以檢測到同組件的路由切換,但是並沒有什么卵用,因為它並沒有解決創建多個數據實例的問題,每當切換頁面的時候,頁面都會去拉取頁面,造成了請求和帶寬的浪費,用戶體驗十分不好。那到底有沒有解決辦法呢?答案是肯定的。經過我不斷地嘗試,找到了一個解決辦法,而且可行。具體思路就是我們通過自己去創建管理刪除組件的實例,然后用vuex狀態管理區存儲它(這個也不是必選項,因為相同組件之間切換,組件的數據實例並不會銷毀,切換頁面后之前頁面的數據依然可以使用)。我們通過在路由上面傳遞不同的參數來區分在不同路由的情況下來顯示哪個數據實例。
這里面還有一些細節:
a:創建新路由頁面的時候,要創建新實例;
b:返回路由后需要把當前的實例刪除掉,不然會一直占用內存;
c:文章組件返回到列表組件,然后再進入文章組件后,文章組件實例不會再創建
解決辦法:貼出我的具體代碼
index.js
import Vue from 'vue'; import router from './router'; import store from './store'; import index from './components/index'; import vueWechatTitle from 'vue-wechat-title'; Vue.use(vueWechatTitle); Vue.config.productionTip = false; //綁定vue new Vue({ el: '#contract', router, store, template: '<index/>', components: {index} });
router.js
import Vue from 'vue'; import Router from 'vue-router'; import contractList from './components/contract-list'; import contractDetail from './components/contract-detail'; Vue.use(Router); const router = new Router({ routes: [ { path: '/', name: 'contractList', meta: { title: '合同列表', keepAlive:true }, component: contractList }, { path: '/contractDetail/:timestamp', name: 'contractDetail', meta: { title: '合同詳情', keepAlive:true }, component: contractDetail } ] }); export default router;
store.js
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); const state = {}; const actions = {}; const getters = {}; const mutations = { addData: (state, res) => { if (!state.hasOwnProperty(res.key)) { Vue.set(state, res.key, res.value); } console.log('set', state); },//增 deleteData: (state, key) => { console.log(key); delete state[key]; console.log('del', state); },//刪 updateData: (state, res) => { if (state.hasOwnProperty(res.key)) { for (let item in res.value) { state[res.key][item] = res.value[item]; } } console.log('updateData', state); },//改 getData: (state, key) => { if (state.hasOwnProperty(key)) { return state[key]; } else { return null; } console.log('get', state); }//查 }; export default new Vuex.Store({ state, actions, getters, mutations });
下面是模板頁面
index.vue
<template> <div id="contract"> <keep-alive> <router-view v-if="$route.meta.keepAlive" v-wechat-title="$route.meta.title"></router-view> </keep-alive> <router-view v-if="!$route.meta.keepAlive" v-wechat-title="$route.meta.title"></router-view> </div> </template> <script> export default { data() { return {} }, computed: {}, created() { }, mounted() { }, methods: {}, watch: {} } </script>
contract-list.vue
<template> <div id="contract-list"> <input type="text" v-model="msg"> <router-link :to="'/contractDetail/'+new Date().getTime()">點擊進詳情頁</router-link> </div> </template> <script> export default { data() { return { msg: '' } }, computed: {}, created() { }, mounted() { }, methods: {}, watch: {} } </script> <style scoped> #contract-list { color: red; } </style>
contract-detail.vue
<template> <div id="contract-detail"> <input type="text" v-if="currentData" v-model="currentData.msg"> 我是合同詳情頁 <router-link :to="'/contractDetail/'+new Date().getTime()">進如下頁詳情</router-link> {{currentData}} </div> </template> <script> import {mapState} from 'vuex'; export default { data() { return {} }, computed: { currentKey() { return this.$route.name + this.$route.params.timestamp; }, ...mapState({ currentData(state) { return state[this.currentKey]; } }) }, created() { console.log('created'); this.initModel(); }, mounted() { console.log('mounted'); }, methods: { createModel() { class model { constructor() { this.msg = ''; } } return new model(); }, initModel() { this.$nextTick(function () { if (!this.$store.state.hasOwnProperty(this.currentKey)) { console.log('initModel-set'); this.$store.commit('addData', { key: this.currentKey, value: this.createModel() }); } }); } }, watch: { currentData: { handler: function (value) { console.log('watch-set', this.currentKey); this.$nextTick(function () { this.$store.commit('updateData', { key: this.currentKey, value: value }); }); }, deep: true } }, beforeRouteUpdate(to, from, next) { console.log('beforeRouteUpdate1', to, from); //清除老對象 if (to.params.timestamp < from.params.timestamp) { this.$store.commit('deleteData', from.name + from.params.timestamp); } //創建新對象 console.log('beforeRouteUpdate2', to, from); if (to.name == from.name) { console.log(to.name, from.name, to.name == from.name); this.initModel(); } next(); }, beforeRouteLeave(to, from, next) { console.log('beforeRouteLeave', to, from); this.$store.commit('deleteData', from.name + from.params.timestamp); this.$destroy(); next(); } } </script> <style scoped> #contract-detail { color: green; } </style>