refer :
https://github.com/angular/angular/issues/10929
https://stackoverflow.com/questions/41280471/how-to-implement-routereusestrategy-shoulddetach-for-specific-routes-in-angular
一切从这个开始.
刚开始接触 SPA 的朋友们可能会很不习惯 scroll position 在游览器后退的时候并不会智能的滚动回之前页面的位置.
这时就会出现上面这张图片的需求了.
我个人认为, 做 SPA 交互设计就不要走回跳转页面的老套路线. 应该寻求创新而不是模拟从前的功能.
不过现实就是, 创新不出来..只能走老路...哈哈
下面来聊聊模拟的技术.
我们知道 SPA 是通过 history.push 来替换游览器地址的,然后通过路由器匹配出组件, 在渲染和替换页面.
即使是游览器后退也是通过监听 onpopstate, 然后匹配路由, 渲染组件...
所以整个过程都被 javascript l拦截处理了,游览器基本上什么也干不了. (从前游览器会在 url 替换时 scroll to top, 在后退时 scroll to 之前的位置)
那么我们就得替代游览器工作了.
首先建一个全局的滚动条.
在前进时要 scroll to top, 我们可以拦截 router change 事件,然后 scrollTop = 0
在后退时, 我们要恢复位置就必须做记入.
那么我们在 router change 时, 把当前的 scrollTop 记入起来.
这样就行了.
呃... 哪有这么容易...
游览器后退的时候, 其实是使用之前的缓存资料的, 所以你会感觉它渲染很快马上就回滚到位了.
但 SPA 每一次都会从新渲染页面, 组件初始化-> ajax -> 渲染 -> 这时候你才可以滚动到之前的位置..
来, 介绍 angular 的 RouteReuseStrategy !
这个东西可以让组件所有 state 和渲染好的 html 被存起来. 然后快速的调出来用.
no more 组件初始化-> ajax -> 渲染.
export declare abstract class RouteReuseStrategy { abstract shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean; abstract shouldAttach(route: ActivatedRouteSnapshot): boolean; abstract retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null; abstract shouldDetach(route: ActivatedRouteSnapshot): boolean; abstract store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle | null): void; }
这个是它的类,我们继承然后替换掉 provider 就可以被 angular 调用了.
angular 会在每一次 router change 的时候调用它.
angular 路由就是棵树, 当 url 从 a/b/c/d 切换到 a/e/f/g 时.
shouldReuseRoute 会被触发. angular 会让你去对比看是否要 reuse
a vs a
b vs e
c vs f
d vs g
一般情况下 a vs a 自然时 reuse
b vs e 就替换, 而一旦 parent 被替换, 那么所有 child 自然也是被替换的
替换一旦发生, 就会有某些组件要被丢弃 destroy, 这时 shouldDetech, store 就会被调用, 用于缓存这些已经渲染完成即将被丢弃的组件.
有组件被丢弃自然有组件需要进来替补, 而这时 shouldAttach,retrieve 就会被调用, 用来调出缓存的组件.
所以流程是这样 :
1. 是否替换 ?
2. 替换发生, 有组件离去, 有组件加入
3. 离去的组件, 我们可以缓存
4. 加入的组件, 我们可以使用缓存好的组件.
替换->缓存->重用 就是整体的核心了.