vue/react單頁(yè)應(yīng)用后退不刷新方案
引言
前進(jìn)刷新,后退不刷新,是一個(gè)類似app頁(yè)面的特點(diǎn),要在單頁(yè)web應(yīng)用中做后退不刷新,卻并非一件易事。
為什么麻煩
spa的渲染原理(以vue為例):url的更改觸發(fā)onHashChange/pushState/popState/replaceState,通過(guò)url中的pathName去匹配路由中定義的組件,加載進(jìn)來(lái)并實(shí)例化渲染在項(xiàng)目的出口router-view中。
換言之,一個(gè)實(shí)例的解析渲染意味著另外一個(gè)實(shí)例的銷毀,因?yàn)殇秩境隹谥挥幸粋€(gè)。
keep-alive為什么不行?因?yàn)閗eep-alive的原理是將實(shí)例化后的組件存儲(chǔ)起來(lái),當(dāng)下次url匹配到了改組件時(shí),優(yōu)先從存儲(chǔ)里面取。
但是vue只提供了入存儲(chǔ)的方式,沒(méi)提供刪存儲(chǔ)的方式,所以沒(méi)法實(shí)現(xiàn)“前進(jìn)刷新”。
有一種方案是手動(dòng)根據(jù)to和from去做前進(jìn)后退判斷,這種判斷不能應(yīng)對(duì)復(fù)雜的跳轉(zhuǎn)邏輯,可維護(hù)性也很差。
有坑的社區(qū)方案(以vue為例)
vue-page-stack
,vue-navigation
。
這兩個(gè)方案都有明顯缺點(diǎn):前者不支持嵌套路由,在一些場(chǎng)景下會(huì)出現(xiàn)url變化,頁(yè)面完全無(wú)反應(yīng)的情況,后者存在類似的bug。并且這兩種方案侵入性都很強(qiáng),因?yàn)樗麄兌际腔趘ue-router的魔改。并且會(huì)在url中增加無(wú)意義的多余字段(stackID)
目前不錯(cuò)的方案
現(xiàn)在有一個(gè)可行且簡(jiǎn)單的方案:嵌套子路由 + 疊頁(yè)面。
疊頁(yè)面的靈感:原生應(yīng)用中的webview in webview,多頁(yè)應(yīng)用中的window in window。
要在spa中實(shí)現(xiàn)后退不刷新,本質(zhì)是要實(shí)現(xiàn)多實(shí)例共存。
這個(gè)方案的核心在于:通過(guò)嵌套子路由實(shí)現(xiàn)多實(shí)例共存,通過(guò)css的absolute實(shí)現(xiàn)視覺(jué)上的頁(yè)面堆疊。
上效果圖
vue中的實(shí)現(xiàn)
在routes配置文件中:
import Home from "../views/Home.vue"; const routes = [ { path: "/home", name: "Home", component: Home, children: [ { path: "sub", component: () => import(/* webpackChunkName: "sub" */ "../views/Sub.vue"), }, ], }, ]; export default routes;
主頁(yè):
<template> <div class="home"> <input v-model="inputValue" /> <h3>{{ inputValue }}</h3> <button @click="handleToSub">to sub</button> <router-view @reload="handleReload" /> </div> </template> <script> export default { name: "Home", data() { return { inputValue: "", }; }, methods: { handleToSub() { // 注意路由格式,是基于上一個(gè)路由/home下面的sub,不是獨(dú)立的/sub this.$router.push("/home/sub"); }, handleReload(val) { // 這里可以做一些重新獲取數(shù)據(jù)的操作,比如在詳情頁(yè)修改數(shù)據(jù),返回后重新拉取列表 console.log("reload", val); }, }, mounted() { // 子頁(yè)面返回,不會(huì)重新跑生命周期 console.log("mounted"); }, }; </script> <style scoped> .home { position: relative; } </style>
子頁(yè)面:
<template> <div class="sub"> <h1>This is Sub page</h1> </div> </template> <script> export default { beforeDestroy() { // 可以傳自定義參數(shù),如果沒(méi)需要,也可以不做 this.$emit("reload", 123); }, }; </script> <style scoped> .sub { position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #fff; } </style>
react中的實(shí)現(xiàn)
在routes中:
import { Route } from "react-router-dom"; const Routes = () => { return ( <> {/* 這里不能加exact,因?yàn)橐绕ヅ涓疙?yè)面再匹配子頁(yè)面 */} <Route path="/home" component={lazy(() => import("../views/Home"))} /> </> ); }; export default Routes;
主頁(yè):
import React, { useEffect, useState } from "react"; import { Route, useHistory } from "react-router-dom"; import styled from "styled-components"; import Sub from "./Sub"; const HomeContainer = styled.div` position: relative; const Home: React.FC = () => { const [inputValue, setInputValue] = useState(""); const history = useHistory(); const handleToSub = () => { history.push("/home/sub"); }; const handleReload = (val: number) => { console.log("reload", val); }; useEffect(() => { console.log("mounted"); }, []); return ( <HomeContainer> <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} /> <h3>{inputValue}</h3> <button onClick={handleToSub}>to sub</button> <Route path="/home/sub" component={() => <Sub handleReload={handleReload} />} /> </HomeContainer> ); }; export default Home;
子頁(yè)面:
import React from "react"; import styled from "styled-components"; const SubContainer = styled.div` position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: #fff; type SubProps = { handleReload: (val: number) => void; }; const Sub: React.FC<SubProps> = ({ handleReload }) => { useEffect(() => { return () => handleReload(123); }, []); return ( <SubContainer> <h1>This is Sub page</h1> </SubContainer> ); }; export default Sub;
題外
在前司的核心項(xiàng)目“平安好車主”中,我就在部分h5新項(xiàng)目用了該方案,在線上經(jīng)受住了170w+訪問(wèn)量的考驗(yàn)。目前在Shopee也在推行這種h5方案,由于邏輯簡(jiǎn)單,得到了不少同事的認(rèn)可和使用。比如常見(jiàn)的:列表頁(yè)存在搜索條件,進(jìn)入詳情頁(yè)再返回。 大家可以試用一下,會(huì)有驚喜的。
該方案的優(yōu)點(diǎn)
- 實(shí)現(xiàn)簡(jiǎn)單,無(wú)侵入式修改,幾乎0邏輯;
- 子頁(yè)面可以單獨(dú)提供出去,供三方接入;
- 完全的多實(shí)例共存,后退不刷新;
- 可以像父子組件一樣通信,監(jiān)聽(tīng)子頁(yè)面離開;
缺點(diǎn)
路由格式需要做改造,必須做成嵌套關(guān)系,對(duì)url有一定要求。
github地址
https://github.com/zhangnan24/no-refresh-back-vue
到此這篇關(guān)于vue/react單頁(yè)應(yīng)用后退不刷新方案的文章就介紹到這了,更多相關(guān)vue/react后退不刷新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一步一步實(shí)現(xiàn)Vue的響應(yīng)式(對(duì)象觀測(cè))
這篇文章主要介紹了一步一步實(shí)現(xiàn)Vue的響應(yīng)式(對(duì)象觀測(cè)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Vue.js 2.0 和 React、Augular等其他前端框架大比拼
這篇文章主要介紹了Vue.js 2.0 和 React、Augular等其他前端框架大比拼的相關(guān)資料,React 和 Vue 有許多相似之處,本文給大家提到,需要的朋友可以參考下2016-10-10vue element 中的table動(dòng)態(tài)渲染實(shí)現(xiàn)(動(dòng)態(tài)表頭)
這篇文章主要介紹了vue element 中的table動(dòng)態(tài)渲染實(shí)現(xiàn)(動(dòng)態(tài)表頭),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11vue中設(shè)置echarts寬度自適應(yīng)的代碼步驟
這篇文章主要介紹了vue中設(shè)置echarts寬度自適應(yīng)的問(wèn)題及解決方案,常常需要做到echarts圖表的自適應(yīng),一般是根據(jù)頁(yè)面的寬度做對(duì)應(yīng)的適應(yīng),本文記錄一下設(shè)置echarts圖表的自適應(yīng)的步驟,需要的朋友可以參考下2022-09-09Vue+EleMentUI實(shí)現(xiàn)el-table-colum表格select下拉框可編輯功能實(shí)例
這篇文章主要給大家介紹了關(guān)于Vue+EleMentUI實(shí)現(xiàn)el-table-colum表格select下拉框可編輯功能的相關(guān)資料,element-UI表格的使用相信大家都不陌生,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下2023-07-07Vue項(xiàng)目中ESLint配置超全指南(VScode)
ESLint是一個(gè)代碼檢查工具,用來(lái)檢查你的代碼是否符合指定的規(guī)范,下面這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目中ESLint配置(VScode)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04