The previous article described in detail the implementation idea and code of v1 version: h5 page route switching animation - left and right sideslip effect (v1)
Later, many problems in v1 were found in the process of practice and have been reconstructed. Therefore, let's share a new implementation method, which is called v2 version. The core idea is the same.
1, Review and reflection
The v1 version stores the page history and page direction through vuex, obtains the route information to jump through the beforeEach global route navigation guard, and compares and judges the page direction.
Then I thought:
- The reason why we use vuex storage instead of ordinary js module storage is that the change of page direction data in vuex can trigger the response, so that the switching animation can change immediately according to the page direction;
- The data in vuex is only used by the page switching module. Can it be moved into the encapsulated page switching component PageSlide;
If the handlePageDirection method in the component is moved in, it cannot be called in the route navigation guard beforeEach, and the purpose of the call is to pass the route information of the next page; - Is there any other way to get the route of the next page? Yes, just watch and listen to $route, because the route exit is to be wrapped in this component.
Then, after the transformation, a component implements all the code logic.
2, Other optimization points
1. Reduced routing stack data
In v1, the routeStack data of the routing stack stores the complete data of all routing routes. In fact, the current function only uses the data of path, so only path is stored, and the deep cloning operation of JSON.parse(JSON.stringify(route)) is subtracted.
pushRouteStack (route) { const { path } = route this.routeStack.push({ path }) },
2. Support page refresh
vuex stored in v1 will be initialized and emptied after the page is refreshed, so the routeStack data will be synchronously stored in sessionStorage this time, and read from sessionStorage after the page is refreshed.
created () { this.routeStack = this.getSessionRouteStack() }, pushRouteStack (route) { const { path } = route this.routeStack.push({ path }) this.setSessionRouteStack() }, popRouteStack () { this.routeStack.pop() this.setSessionRouteStack() },
3. Compatible with vue3.0
At a glance, the only incompatible part of vue3.0 should be the transition class name:
Modify class enter to class enter from;
Modify class leave to class leave from.
Then write it all:
.slide-left-enter, .slide-left-enter-from, .slide-right-leave-active { opacity: 0; transform: translate(@distanceX, 0); } .slide-left-leave-active, .slide-right-enter, .slide-right-enter-from { opacity: 0; transform: translate(-@distanceX, 0); }
For more vue3.0 upgrades, please refer to: Project upgrade vue3.0 experience summary
4. Reconstruct page direction judgment logic
This version reconstructs the judgment logic of page forward and backward, considers the processing of more detailed scenarios, and stores the home page route by default at the beginning of the route stack:
handleRouteChange (to) { const len = this.routeStack.length const currentRoute = this.routeStack[len - 1] // Determine whether to refresh the route if (currentRoute.path !== to.path) { if (len === 1) { // Open the non home page route for the first time and move forward this.setPageForward(to) } else { // len > 1 const lastRoute = this.routeStack[len - 2] if (lastRoute.path === to.path) { // Open the previous route and go back this.setPageBack(currentRoute) } else { if (to.path === this.indexPagePath) { // Open the home page halfway, go back and reset the routing stack this.setDirectionName('slide-right') this.resetRouteStack() } else { // Routine, forward this.setPageForward(to) } } } } },
3, Complete code encapsulation
1. Encapsulated component PageSlide
<template> <transition :name="directionName"> <slot></slot> </transition> </template> <script> export default { name: 'PageSlide', props: { // Home routing path indexPagePath: { type: String, default: '/', }, }, data () { return { directionName: '', // Page switching direction: slide left slide forward, slide right slide backward, routeStack: [], // Store routing stack } }, watch: { '$route' (val) { this.handleRouteChange(val) } }, created () { this.routeStack = this.getSessionRouteStack() || [{ path: this.indexPagePath }] }, methods: { handleRouteChange (to) { const len = this.routeStack.length const currentRoute = this.routeStack[len - 1] // Determine whether to refresh the route if (currentRoute.path !== to.path) { if (len === 1) { // Open the non home page route for the first time and move forward this.setPageForward(to) } else { // len > 1 const lastRoute = this.routeStack[len - 2] if (lastRoute.path === to.path) { // Open the previous route and go back this.setPageBack(currentRoute) } else { if (to.path === this.indexPagePath) { // Open the home page halfway, go back and reset the routing stack this.setDirectionName('slide-right') this.resetRouteStack() } else { // Routine, forward this.setPageForward(to) } } } } }, // forward setPageForward (route) { this.setDirectionName('slide-left') this.pushRouteStack(route) }, // back off setPageBack () { this.setDirectionName('slide-right') this.popRouteStack() }, // Set page orientation setDirectionName (name) { this.directionName = name }, // Reset resetRouteStack () { this.routeStack = [{ path: this.indexPagePath }] this.setSessionRouteStack() }, pushRouteStack (route) { const { path } = route this.routeStack.push({ path }) this.setSessionRouteStack() }, popRouteStack () { this.routeStack.pop() this.setSessionRouteStack() }, setSessionRouteStack () { sessionStorage.setItem('routeStack', JSON.stringify(this.routeStack)) }, getSessionRouteStack () { const str = sessionStorage.getItem('routeStack') if (!str) { return null } let val = [] try { val = JSON.parse(str) } catch (error) { console.warn('parse routeStack wrong') } return val }, } } </script> <style lang="less" scoped> @distanceX: 100%; .slide-left-enter-active, .slide-left-leave-active, .slide-right-enter-active, .slide-right-leave-active { position: absolute; width: 100%; transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1); } .slide-left-enter, .slide-right-leave-active { opacity: 0; transform: translate(@distanceX, 0); } .slide-left-leave-active, .slide-right-enter { opacity: 0; transform: translate(-@distanceX, 0); } </style>
2. App.vue reference
<template> <div id="app"> <PageSlide indexPagePath="/home"> <router-view /> </PageSlide> </div> </template> <script> import PageSlidefrom '@/components/PageSlide' export default { name: 'App', components: { PageSlide, }, } </script>
- The indexPagePath parameter is the path of the project home page route. Replace it with the path of your own project.
4, react implementation guidelines
No matter v1 or v2, vue or react, the core idea remains unchanged, that is:
Store the route access history data, obtain the route data of the next page, compare and judge the page direction.
The implementation of react mainly needs to solve the following problems:
- 1. Transition Animation: you can react-transition-group realization.
- 2. Get the routing data of the next page: you can use the withRouter To achieve.
- 3. Listen for route changes:
- With the above withRouter, when the props.location changes, the componentWillReceiveProps hook will be executed and can be used as a listening function;
import { withRouter } from 'react-router-dom' class Main extends Component { constructor (props) { super(props) this.state = {} } componentWillReceiveProps (nextProps) { console.log(nextProps.location) } render() { return ( <div>demo</div> ) } } export default withRouter(Main)
- hooks syntax is simpler. useEffect can directly respond to routing changes.
const Demo = function (props) { useEffect(() => { console.log(props.location) }, [props.location]) }
The rest of the logic is almost the same as vue. Implement it yourself.