单页应用后退不刷新方案(vue & react)
引言
前进刷新,后退不刷新,是一个类似app页面的特点,要在单页web应用中做后退不刷新,却并非一件易事。
为什么麻烦
spa的渲染原理(以vue为例):url的更改触发onHashChange
/pushState
/popState
/replaceState
,通过url中的pathName去匹配路由中定义的组件,加载进来并实例化渲染在项目的出口router-view中。
换言之,一个实例的解析渲染意味着另外一个实例的销毁,因为渲染出口只有一个。
keep-alive为什么不行?因为keep-alive的原理是将实例化后的组件存储起来,当下次url匹配到了改组件时,优先从存储里面取。
但是vue只提供了入存储的方式,没提供删存储的方式,所以没法实现“前进刷新”。
有一种方案是手动根据to和from去做前进后退判断,这种判断不能应对复杂的跳转逻辑,可维护性也很差。
有坑的社区方案(以vue为例)
vue-page-stack
,vue-navigation
。
这两个方案都有明显缺点:前者不支持嵌套路由,在一些场景下会出现url变化,页面完全无反应的情况,后者存在类似的bug。并且这两种方案侵入性都很强,因为他们都是基于vue-router的魔改。并且会在url中增加无意义的多余字段(stackID)
目前不错的方案
现在有一个可行且简单的方案:嵌套子路由 + 叠页面。
叠页面的灵感:原生应用中的webview in webview
,多页应用中的window in window
。
要在spa中实现后退不刷新,本质是要实现多实例共存。
这个方案的核心在于:通过嵌套子路由实现多实例共存,通过css的absolute实现视觉上的页面堆叠。
vue中的实现
在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;
主页:
<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() {
// 注意路由格式,是基于上一个路由/home下面的sub,不是独立的/sub
this.$router.push("/home/sub");
},
handleReload(val) {
// 这里可以做一些重新获取数据的操作,比如在详情页修改数据,返回后重新拉取列表
console.log("reload", val);
},
},
mounted() {
// 子页面返回,不会重新跑生命周期
console.log("mounted");
},
};
</script>
<style scoped>
.home {
position: relative;
}
</style>
子页面:
<template>
<div class="sub">
<h1>This is Sub page</h1>
</div>
</template>
<script>
export default {
beforeDestroy() {
// 可以传自定义参数,如果没需要,也可以不做
this.$emit("reload", 123);
},
};
</script>
<style scoped>
.sub {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: #fff;
}
</style>
react中的实现
在routes中:
import { Route } from "react-router-dom";
const Routes = () => {
return (
<>
{/* 这里不能加exact,因为要先匹配父页面再匹配子页面 */}
<Route path="/home" component={lazy(() => import("../views/Home"))} />
</>
);
};
export default Routes;
主页:
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;
子页面:
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;
该方案的优点
- 实现简单,无侵入式修改,几乎0逻辑;
- 子页面可以单独提供出去,供三方接入;
- 完全的多实例共存,后退不刷新;
- 可以像父子组件一样通信,监听子页面离开;
缺点
路由格式需要做改造,必须做成嵌套关系,对url有一定要求。
单页应用后退不刷新方案(vue & react)的更多相关文章
- vue单页应用前进刷新后退不刷新方案探讨
引言 前端webapp应用为了追求类似于native模式的细致体验,总是在不断的在向native的体验靠拢:比如本文即将要说到的功能,native由于是多页应用,新页面可以启用一个的新的webview ...
- vue-router+webpack线上部署时单页项目路由,刷新页面出现404问题
使用vue项目,线上部署的时候,访问首页以及通过路由打开二级页面没有问题,但是一刷新就出现404现象 因为刷新页面时访问的资源在服务端找不到,因为vue-router设置的路由不是真实存在的路径. 解 ...
- 单页应用SPA做SEO的一种清奇的方案
单页应用SPA做SEO的一种清奇的方案 网上有好几种单页应用转seo的方案,有服务端渲染ssr.有预渲染prerender.google抓AJAX.静态化...这些方案都各有优劣,开发者可以根据不同的 ...
- 关于单页应用(SPA)的经验之谈
时下SPA单页应用如火如荼,对前端乃至后端开发都带来不小的冲击和变革.笔者整理了下笔记,决定写一下以前基于iframe做单页博客的一些经验方法. 对于单页应用,笔者没有找到最官方的定义.在笔者看来,在 ...
- 前端 JS 原生 javascript 和 location.hash 实现一个单页应用的路由 router
开篇日常立个flag-- 前言 最近在做一些应用,类似于单页应用,想实现类似于 Vue 路由的效果. 但是个人 Vue 基础四舍五入约等于无,而且看着 Vue-router 吃力+用不起来(因为我的项 ...
- React + Node 单页应用「二」OAuth 2.0 授权认证 & GitHub 授权实践
关于项目 项目地址 预览地址 记录最近做的一个 demo,前端使用 React,用 React Router 实现前端路由,Koa 2 搭建 API Server, 最后通过 Nginx 做请求转发. ...
- vue/react/angular开发的css架构思考
前端开发现在已经从传统的后端web多页面开发模式转向前端单页SPA开发模式,而vuejs/react/angular则是开发SPA非常优秀的前端框架.组件化开发由react最早提出,vuejs后发优势 ...
- [转] 2017-11-20 发布 另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
目的:vue-cli构建的vue单页面应用,某些特定的页面,实现前进刷新,后退不刷新,类似app般的用户体验.注: 此处的刷新特指当进入此页面时,触发ajax请求,向服务器获取数据.不刷新特指当进入此 ...
- 另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
目的:vue-cli构建的vue单页面应用,某些特定的页面,实现前进刷新,后退不刷新,类似app般的用户体验.注: 此处的刷新特指当进入此页面时,触发ajax请求,向服务器获取数据.不刷新特指当进入此 ...
随机推荐
- C#中IEumerable的简单了解
参考网址:https://blog.csdn.net/qq_39806817/article/details/115024666 一.IEnumerable简单介绍IEnumerable是可枚举类型, ...
- SpringCloud之Hystrix集群监控turbine仪表盘
1.引入 在前一节中我们演示了单机模式下Hystrix服务监控Dashboard仪表盘,但是在实际生产中微服务都是集群模式, 为了更接近世界生产,我们在这里也给大家讲一下如何监控集群模式 2.准备工作 ...
- 三 MongoDB进阶
1 Limit方法 概念:查询指定数量的数据,该方法接受一个数字参数作为查询记录数的数量 举个例子:查询集合col中最多2条记录数 2 Skip方法 概念:查询到的结果集中,跳过指定数量的数据,该方法 ...
- jquery validate 如何校验多个相同name
在表单页中有如下代码 <form> <input name="zhai"/><!-- 三个相同name的input --> <input ...
- Mybatis一对一、一对多、多对多查询。+MYSQL
场景:使用三张数据表:student学生表.teacher教师表.position职位表 一个学生可以有多为老师.一位老师可以有多个学生.但是一个老师只能有一个职位:教授.副教授.讲师:但是一个职位可 ...
- pgsql基本介绍
join on 与数学原理 pgsql切换数据库 直接输入 \C youdatabasename 即可 \d 表名 -- 得到表结构 select * from tablename查看表的数据 相信有 ...
- for循环操作(for...in、forEach)
1.for...in语句用于对数组或者对象的属性进行循环操作,是for循环的一种. 注意:该方法可用于数组或对象. 语法: for(变量 in 对象/数组){} 如: var obj = { nam ...
- 手机端rem简单配置相关
手机端rem简单配置相关 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 ...
- Qt之文件操作
虽然文件操作是一项很常用的功能,但是总记不住,今天就干脆记了一下笔记,以后好查阅. 在Qt中,主要使用的是QFile类进行文件操作,因此要包括#include <QFile>头文件.下面就 ...
- Jenkins 构建JOB失败
问题描述 同事在使用Jenkins打包项目的时候报错 error:index-pack died of signal 15 fatal: index-pack failed 出现这个问题,初步怀疑是拉 ...