单页应用后退不刷新方案(vue & react)

时间:?2021-09-27阅读:?67标签:?刷新

引言

前进刷新,后退不刷新,是一个类似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;

题外

在前司的核心项目“平安好车主”中,我就在部分h5新项目用了该方案,在线上经受住了170w+访问量的考验。目前在Shopee也在推行这种h5方案,由于逻辑简单,得到了不少同事的认可和使用。比如常见的:列表页存在搜索条件,进入详情页再返回。 大家可以试用一下,会有惊喜的。

该方案的优点

  • 实现简单,无侵入式修改,几乎0逻辑;
  • 子页面可以单独提供出去,供三方接入;
  • 完全的多实例共存,后退不刷新;
  • 可以像父子组件一样通信,监听子页面离开;

缺点

路由格式需要做改造,必须做成嵌套关系,对url有一定要求。

github地址:https://github.com/zhangnan24/no-refresh-back-vue
原文来自:https://www.cnblogs.com/zhangnan35/archive/2021/09/27/15344231.html


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: http://www.pannellisolari.net/article/detial/10681

vue实现局部刷新

使用2.2.0 新增的provide / inject控制<router-view>的显示隐藏,在App.vue中使用provide,在使用局部刷新的组件中使用inject

Vue刷新当前路由

开发项目的时候突然接到了这个需求,实验过后,解决方法也蛮多种,下面就讲下常规的几种方案:1.改变router-view中的key值通过改变router-view中的key值,来达到刷新组件的目的

Vuex持久化----解决刷新页面,数据丢失

利用localStorage或者是sessionStorage在vuex操作store的时候同时对存储操作,利用插件 vuex-persistedstate (Persist Vuex state with localStorage ,默认存储到localStorage)

nodejs自动刷新如何实现的?

nodejs自动刷新的实现方法:采用gulp,browser-sync及gulp-nodemon可以实现自动刷新,使用npm install命令进行安装,在项目根目录中新建js文件添加代理端口,使用命令gulp server启动代理端口即可。

$this.router.push到当前页面,只是传入参数不同,页面不刷新的问题解决

在vue中使用$this.router.push()方法,如果只是传入的参数不同,会出现url地址变化了,但是页面没有重新请求数据,需要刷新一下页面才有新的数据

移动端下拉刷新头实现原理及代码实现

实现下拉刷新主要分为三步:监听原生touchstart事件,记录其初始位置的值,e.touches[0].pageY;监听原生touchmove事件,记录并计算当前滑动的位置值与初始位置值的差值,大于某个临界值时,显示下拉刷新头

js刷新当前页面的5种方式

当调用 location.reload() 方法时, aspx页面此时在服务端内存里已经存在, 因此必定是 IsPostback 的。如果有这种应用: 需要重新加载该页面,也就是说期望页面能够在服务端重新被创建,期望是 Not IsPostback 的

HTML5中History.back()页面后退刷新页面

在开发中经常会遇到在一个页面操作完返回上一个页面,此时要求上个页面展示最新数据的需求。比如 A1为上一个页面,A2为当前页面

js实现返回上一页(页面刷新与不刷新)

history.go(-1), 返回两个页面: history.go(-2);window.history.forward() 返回下一页;window.history.go(返回第几页,也可以使用访问过的URL) ;如果要强行刷新的话就是:window.history.back();

Vue刷新当前页面的3种实现

缺点:同 this.$router.go(0) 一样,会白屏。通过 $nextTick(),协助实现。先把 <router-view /> 移除,移除后再重新添加,达到刷新当前页面的功能。是目前最合适的实现方式。

点击更多...

内容以共享,参考,研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!

Baidu