背景
需求
工作中涉及到一个列表功能的实现(使用 vue), 功能相对简单, 一个滚动加载的文章列表显示文章标题, 点击标题可以查看文章详情.
第一版的路由设计为:/list 为列表页,/detail/:id 为详情页, 点击列表页文章标题时获取文章 id, 使用 vue 的 router.push 方法跳转到详情页.
体验
在第一版中查看详情页后返回列表页, 列表页会刷新, 随之产生用户体验问题, 如果查看的时列表底部的数据, 每点击一次详情, 返回列表页后要重新滚动到列表底部.
每产品提出了优化点, 要求在查看完详情后返回列表页依然能保持之前列表滚动的位置.
思考
首先列表保持当前滚动位置有两种方案, 第一种加载页面时记录上一次的滚动位置, 主动滚动到记录的位置点; 第二种保持当前列表页不刷新.
第一种方案很快被我排除, 因为列表数据是分页滚动加载出来的, 通过记录位置滚动列表会有一个过程, 且过程长度视网速而定, 明显在视觉上不友好.
根据第二种方案, 点击列表元素查看详情, 需要保持当前列表页不刷新, 同时使用遮罩的方式在列表层上方覆盖一个显示区域用来显示文章详情.
技术储备
考虑到当前页面使用遮罩, 我选择借助 https://youzan.GitHub.io/vant/#/zh-CN/intro (vue 组件库)的 popup 组件, 实现页面右侧弹出遮罩层.
在路由设计上, 将原来的 detail 页面改为 list 页面的子路由页面, 根据 vue router 官方文档关于嵌套路由的说明, 使用 router-view 作为子页面的加载出口.
特别说明, 使用嵌套路由的方式, 是为了在详情页弹出后可以结合手机上的返回键返回列表页, 下面在实现过程中详细讲解.
代码实现
路由设计
- export default new Router({
- routes: [
- {
- path: '/',
- redirect: '/list'
- },
- {
- path: '/list',
- name: 'List',
- component: List,
- children: [
- {
- path: 'detail/:id',
- name: 'Detail',
- props: true,
- component: Detail
- }
- ]
- }
- ]
- })
注意详情页的路径没有以 "/" 开头, 而是基于父级路径, 由此根据 vue router 官方文档介绍的组件实例复用原则, 可以实现 list 页面不被刷新.
提醒一下, 当使用路由参数时, 例如从 /user/foo 导航到 /user/bar, 原来的组件实例会被复用. 因为两个路由都渲染同个组件, 比起销毁再创建, 复用则显得更加高效. 不过, 这也意味着组件的生命周期钩子不会再被调用.
复用组件时, 想对路由参数的变化作出响应的话, 你可以简单地 watch (监测变化) $route 对象(引用自 vue router 官方文档)
弹出层设计
在 popup 层中添加 router-view 用于子路由的渲染出口
- <van-popup
- class="subpage"
- position="right"
- :overlay="false"
- v-model="show">
- <router-view />
- </van-popup>
点击列表元素跳转详情页
- this.$router.push({
- name: 'Detail',
- params: {
- id: id
- }
- })
监听路由, 判断路由改变时, 是否需要弹出遮罩层
- watch: {
- $route (to, from) {
- if (to.name === 'List') {
- this.show = false
- } else {
- this.show = true
- }
- }
- }
总结
进入列表页
进入详情页
从路由变化 (/list -> /list/detail/:id) 可以看出, 符合 vue router 官方文档中提到了组件实例复用情况, 所以路径在 / list 和 /list/detail/:id 之间切换不会重新加载列表数据, 实现了列表无刷新
同时遮罩层的显示与否完全依赖路由的地址, 所以当点击查看详情页时, 可以完全结合手机返回键实现路由地址由 / list/detail/:id 返回 / list, 进而控制详情页隐藏.
demo 源码地址
来源: http://www.qdfuns.com/article/38384/0303e02dc9e9634775f0f0726eee3905.html