对于web开发和移动端开发,两者在路由上的处理是不同的。对于移动端来说,页面的路由是相当于栈的结构的。vue-router与keep-alive提供的路由体验与移动端是有一定差别的,因此常常开发微信公众号的我想通过一些尝试来将两者的体验拉近一些。
目标
问题
首先一个问题是keep-alive的行为。我们可以通过keep-alive来保存页面状态,但这样的行为对于类似于APP的体验是有些奇怪的。例如我们的应用有首页、列表页、详情页3个页面,当我们从列表页进入详情页再返回,此时列表页应当是keep-alive的。而当我们从列表页返回首页,再次进入列表页,此时的列表页应当在退出时销毁,并在重新进入时再生成才比较符合习惯。
第二个问题是滚动位置。vue-router提供了 scrollBehavior 来帮助维护滚动位置,但这一工具只能将页面作为滚动载体来处理。但我在实际开发中,喜欢使用flex来布局页面,滚动列表的载体常常是某个元素而非页面本身。
使用环境
对于代码能正确运行的环境,这里严格假定为微信(或是APP中内嵌的web页面),而非通过普通浏览器访问,即:用户无法通过直接输入url来跳转路由。在这样的前提下,路由的跳转是代码可控的,即对应于vue-router的push、replace等方法,而唯一无法干预的是浏览器的回退行为。在这样的前提下,我们可以假定,任何没有通过vue-router触发的路由跳转,是 回退1个记录 的回退行为。
改造前
这里我列出改造前的代码,是一个非常简单的demo,就不详细说了(这里列表页有两个列表,是为了展示改造后的滚动位置维护):
// css* { margin: 0; padding: 0; box-sizing: border-box;}html, body { height: 100%;}#app { height: 100%;}
// html<div id="app"> <keep-alive> <router-view></router-view> </keep-alive></div>
// jsconst Index = { name: 'Index', template: `<div> 首页 <div> <router-link :to="{ name: 'List' }">Go to List</router-link> </div> </div>`, mounted() { console.warn('Main', 'mounted'); },};const List = { name: 'List', template: `<div style="display: flex;flex-direction: column;height: 100%;"> <div>列表页</div> <div style="flex: 1;overflow: scroll;"> <div v-for="item in list" :key="item.id"> <router-link style="line-height: 100px;display:block;" :to="{ name: 'Detail', params: { id: item.id } }"> {{item.name}} </router-link> </div> </div> <div style="flex: 1;overflow: scroll;"> <div v-for="item in list" :key="item.id"> <router-link style="line-height: 100px;display:block;" :to="{ name: 'Detail', params: { id: item.id } }"> {{item.name}} </router-link> </div> </div> </div>`, data() { return { list: new Array(10).fill(1).map((_,index) => { return {id: index + 1, name: `item${index + 1}`}; }), }; }, mounted() { console.warn('List', 'mounted'); }, activated() { console.warn('List', 'activated'); }, deactivated() { console.warn('List', 'deactivated'); },};const Detail = { name: 'Detail', template: `<div> 详情页 <div> {{$route.params.id}} </div> </div>`, mounted() { console.warn('Detail', 'mounted'); },};const routes = [ { path: '', name: 'Main', component: Index }, { path: '/list', name: 'List', component: List }, { path: '/detail/:id', name: 'Detail', component: Detail },];const router = new VueRouter({ routes,});const app = new Vue({ router,}).$mount('#app');
新闻热点
疑难解答