react实现浏览器自动刷新的示例代码

在单页应用如此流行的今天,曾经令人惊叹的前端路由已经成为各大框架的基础标配,每个框架都提供了强大的路由功能,导致路由实现变的复杂。想要搞懂路由内部实现还是有些困难的,但是如果只想了解路由实现基本原理还是比较简单的。本文针对前端路由主流的实现方式 hash 和 history,提供了原生JS/React/Vue 共计六个版本供参考,每个版本的实现代码约 25~40 行左右(含空行)。

什么是前端路由?

路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系。

在 Web 前端单页应用 SPA(Single Page Application)中,路由描述的是 URL 与 UI 之间的映射关系,这种映射是单向的,即 URL 变化引起 UI 更新(无需刷新页面)。

如何实现前端路由?

要实现前端路由,需要解决两个核心问题:

如何改变 URL 却不引起页面刷新?如何检测 URL 变化了?

下面分别使用 hash 和 history 两种实现方式回答上面的两个核心问题。

hash 实现

  • hash 是 URL 中 hash (#) 及后面的那部分,常用作锚点在页面内进行导航,改变 URL 中的 hash 部分不会引起页面刷新
  • 通过 hashchange 事件监听 URL 的变化,改变 URL 的方式只有这几种:通过浏览器前进后退改变 URL、通过标签改变 URL、通过window.location改变URL,这几种情况改变 URL 都会触发 hashchange 事件

history 实现

  • history 提供了 pushState 和 replaceState 两个方法,这两个方法改变 URL 的 path 部分不会引起页面刷新
  • history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便。

原生JS版前端路由实现

基于上节讨论的两种实现方式,分别实现 hash 版本和 history 版本的路由,示例使用原生 HTML/JS 实现,不依赖任何框架。

基于 hash 实现

运行效果:

HTML 部分:

<body>
  <ul>
ref="">    <!-- 定义路由 -->
    <li><a href="#/home" rel="external nofollow" >home</a></li>
    <li><a href="#/about" rel="external nofollow" >about</a></li>

ref="">    <!-- 渲染路由对应的 UI -->
    <div id="routeView"></div>
  </ul>
</body>

JavaScript 部分:

// 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件
window.addEventListener('DOMContentLoaded', onLoad)
// 监听路由变化
window.addEventListener('hashchange', onHashChange)

// 路由视图
var routerView = null

function onLoad () {
  routerView = document.querySelector('#routeView')
  onHashChange()
}

// 路由变化时,根据路由渲染对应 UI
function onHashChange () {
  switch (location.hash) {
    case '#/home':
      routerView.innerHTML = 'Home'
      return
    case '#/about':
      routerView.innerHTML = 'About'
      return
    default:
      return
  }
}

基于 history 实现

运行效果:

HTML 部分:

<body>
  <ul>
    <li><a href='/home'>home</a></li>
    <li><a href='/about'>about</a></li>

    <div id="routeView"></div>
  </ul>
</body>

JavaScript 部分:

// 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件
window.addEventListener('DOMContentLoaded', onLoad)
// 监听路由变化
window.addEventListener('popstate', onPopState)

// 路由视图
var routerView = null

function onLoad () {
  routerView = document.querySelector('#routeView')
  onPopState()

 href="">  // 拦截 <a> 标签点击事件默认行为, 点击时使用 pushState 修改 URL并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。
  var linkList = document.querySelectorAll('a[href]')
  linkList.forEach(el => el.addEventListener('click', function (e) {
    e.preventDefault()
    history.pushState(null, '', el.getAttribute('href'))
    onPopState()
  }))
}

// 路由变化时,根据路由渲染对应 UI
function onPopState () {
  switch (location.pathname) {
    case '/home':
      routerView.innerHTML = 'Home'
      return
    case '/about':
      routerView.innerHTML = 'About'
      return
    default:
      return
  }
}

React 版前端路由实现

基于 hash 实现

运行效果:

使用方式和 react-router 类似:

  <BrowserRouter>
    <ul>
      <li>
        <Link to="/home">home</Link>
      </li>
      <li>
        <Link to="/about">about</Link>
      </li>
    </ul>

    <Route path="/home" render={() => <h2>Home</h2>} />
    <Route path="/about" render={() => <h2>About</h2>} />
  </BrowserRouter>

BrowserRouter 实现

export default class BrowserRouter extends React.Component {
  state = {
    currentPath: utils.extractHashPath(window.location.href)
  };

  onHashChange = e => {
    const currentPath = utils.extractHashPath(e.newURL);
    console.log("onHashChange:", currentPath);
    this.setState({ currentPath });
  };

  componentDidMount() {
    window.addEventListener("hashchange", this.onHashChange);
  }

  componentWillUnmount() {
    window.removeEventListener("hashchange", this.onHashChange);
  }

  render() {
    return (
      <RouteContext.Provider value={{currentPath: this.state.currentPath}}>
        {this.props.children}
      </RouteContext.Provider>
    );
  }
}

Route 实现

export default ({ path, render }) => (
  <RouteContext.Consumer>
    {({currentPath}) => currentPath === path && render()}
  </RouteContext.Consumer>
);

Link 实现

export default ({ to, ...props }) => <a {...props} href={"#" + to} />;

基于 history 实现

运行效果:

使用方式和 react-router 类似:

  <HistoryRouter>
    <ul>
      <li>
        <Link to="/home">home</Link>
      </li>
      <li>
        <Link to="/about">about</Link>
      </li>
    </ul>

    <Route path="/home" render={() => <h2>Home</h2>} />
    <Route path="/about" render={() => <h2>About</h2>} />
  </HistoryRouter>

HistoryRouter 实现

export default class HistoryRouter extends React.Component {
  state = {
    currentPath: utils.extractUrlPath(window.location.href)
  };

  onPopState = e => {
    const currentPath = utils.extractUrlPath(window.location.href);
    console.log("onPopState:", currentPath);
    this.setState({ currentPath });
  };

  componentDidMount() {
    window.addEventListener("popstate", this.onPopState);
  }

  componentWillUnmount() {
    window.removeEventListener("popstate", this.onPopState);
  }

  render() {
    return (
      <RouteContext.Provider value={{currentPath: this.state.currentPath, onPopState: this.onPopState}}>
        {this.props.children}
      </RouteContext.Provider>
    );
  }
}

Route 实现

export default ({ path, render }) => (
  <RouteContext.Consumer>
    {({currentPath}) => currentPath === path && render()}
  </RouteContext.Consumer>
);

Link 实现

export default ({ to, ...props }) => (
  <RouteContext.Consumer>
    {({ onPopState }) => (
      <a
        href=""
        {...props}
        onClick={e => {
          e.preventDefault();
          window.history.pushState(null, "", to);
          onPopState();
        }}
      />
    )}
  </RouteContext.Consumer>
);

Vue 版本前端路由实现

基于 hash 实现

运行效果:

使用方式和 vue-router 类似(vue-router 通过插件机制注入路由,但是这样隐藏了实现细节,为了保持代码直观,这里没有使用 Vue 插件封装):

    <div>
      <ul>
        <li><router-link to="/home">home</router-link></li>
        <li><router-link to="/about">about</router-link></li>
      </ul>
      <router-view></router-view>
    </div>

const routes = {
  '/home': {
    template: '<h2>Home</h2>'
  },
  '/about': {
    template: '<h2>About</h2>'
  }
}

const app = new Vue({
  el: '.vue.hash',
  components: {
    'router-view': RouterView,
    'router-link': RouterLink
  },
  beforeCreate () {
    this.$routes = routes
  }
})

router-view 实现:

<template>
  <component :is="routeView" />
</template>

<script>
import utils from '~/utils.js'
export default {
  data () {
    return {
      routeView: null
    }
  },
  created () {
    this.boundHashChange = this.onHashChange.bind(this)
  },
  beforeMount () {
    window.addEventListener('hashchange', this.boundHashChange)
  },
  mounted () {
    this.onHashChange()
  },
  beforeDestroy() {
    window.removeEventListener('hashchange', this.boundHashChange)
  },
  methods: {
    onHashChange () {
      const path = utils.extractHashPath(window.location.href)
      this.routeView = this.$root.$routes[path] || null
      console.log('vue:hashchange:', path)
    }
  }
}
</script>

router-link 实现:

<template>
  <a @click.prevent="onClick" href=''><slot></slot></a>
</template>

<script>
export default {
  props: {
    to: String
  },
  methods: {
    onClick () {
      window.location.hash = '#' + this.to
    }
  }
}
</script>

基于 history 实现

运行效果:

使用方式和 vue-router 类似:

    <div>
      <ul>
        <li><router-link to="/home">home</router-link></li>
        <li><router-link to="/about">about</router-link></li>
      </ul>
      <router-view></router-view>
    </div>

const routes = {
  '/home': {
    template: '<h2>Home</h2>'
  },
  '/about': {
    template: '<h2>About</h2>'
  }
}

const app = new Vue({
  el: '.vue.history',
  components: {
    'router-view': RouterView,
    'router-link': RouterLink
  },
  created () {
    this.$routes = routes
    this.boundPopState = this.onPopState.bind(this)
  },
  beforeMount () {
    window.addEventListener('popstate', this.boundPopState)
  },
  beforeDestroy () {
    window.removeEventListener('popstate', this.boundPopState)
  },
  methods: {
    onPopState (...args) {
      this.$emit('popstate', ...args)
    }
  }
})

router-view 实现:

<template>
  <component :is="routeView" />
</template>

<script>
import utils from '~/utils.js'
export default {
  data () {
    return {
      routeView: null
    }
  },
  created () {
    this.boundPopState = this.onPopState.bind(this)
  },
  beforeMount () {
    this.$root.$on('popstate', this.boundPopState)
  },
  beforeDestroy() {
    this.$root.$off('popstate', this.boundPopState)
  },
  methods: {
    onPopState (e) {
      const path = utils.extractUrlPath(window.location.href)
      this.routeView = this.$root.$routes[path] || null
      console.log('[Vue] popstate:', path)
    }
  }
}
</script>

router-link 实现:

<template>
  <a @click.prevent="onClick" href=''><slot></slot></a>
</template>

<script>
export default {
  props: {
    to: String
  },
  methods: {
    onClick () {
      history.pushState(null, '', this.to)
      this.$root.$emit('popstate')
    }
  }
}
</script>

小结

前端路由的核心实现原理很简单,但是结合具体框架后,框架增加了很多特性,如动态路由、路由参数、路由动画等等,这些导致路由实现变的复杂。本文去粗取精只针对前端路由最核心部分的实现进行分析,并基于 hash 和 history 两种模式,分别提供原生JS/React/Vue 三种实现,共计六个实现版本供参考,希望对你有所帮助。

所有的示例的代码放在 Github 仓库:https://github.com/whinc/web-router-principle

参考

详解单页面路由的几种实现原理

单页面应用路由实现原理:以 React-Router 为例

到此这篇关于react实现浏览器自动刷新的示例代码的文章就介绍到这了,更多相关react 浏览器自动刷新内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解vuex持久化插件解决浏览器刷新数据消失问题

    众所周知,vuex的一个全局状态管理的插件,但是在浏览器刷新的时候,内存中的state会释放,通常的解决办法就是用本地存储的方式保存数据,然后再vuex初始化的时候再赋值给state,手动存再手动取会觉得很麻烦,这个时候就可以使用vuex的插件vuex-solidification 插件地址: vuex-solidification, 欢迎star 插件原理 vuex有一个hook方法:store.subscribe((mutation, state) => {}) 每次在mutation方法执

  • 解决微信浏览器Javascript无法使用window.location.reload()刷新页面

    场景是这样子的,页面在初始化时有一个ajax请求,在页面上有一个按钮,点击的时候执行window.location.reload(),正常情况reload()后页面依然会向后台发出请求,但在安卓的微信浏览器中reoad后请求的一直是第一次打开页面时请求的数据.可以理解为请求被缓存了,但没有实测,也不知道是否是缓存. 解决方法是,使用window.location.href="window.location.href+随机数" 代替 window.location.reload().切记

  • 深入理解浏览器的各种刷新规则

    PC浏览器的刷新方式我们常用的有大概四种: 1. url+enter或者a标签的超链接点击 2. F5刷新/ctrl+R刷新/按钮刷新 3. ctrl+F5刷新/ctrl+shift+R刷新 4. 后退刷新 WISE浏览器的刷新方式则一般是三种: 1. url+enter或者a标签的超链接点击 2. 按钮刷新 3. 后退刷新 他们各有不同,不同浏览器下的效果也不一样,以一个带iframe的测试页面为例,我们测试的前提是允许页面缓存,下边我们一个一个讲: PC浏览器 1. url+enter或者a

  • Js智能判断浏览器是关闭还是刷新的代码

    Js智能判断浏览器是关闭还是刷新 关闭或刷新浏览器试试! window.onbeforeunload=function(){ var n=window.event.screenX-window.screenLeft; var b=n>document.documentElement.scrollWidth-20; if(b&&window.event.clientY [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]

  • 基于JavaScript判断浏览器到底是关闭还是刷新(超准确)

    本文是小编总结的一些核心内容,个人感觉对大家有所帮助,具体内容请看下文: 页面加载时只执行onload 页面关闭时只执行onunload 页面刷新时先执行onbeforeunload,然后onunload,最后onload. 经过验证我得出的结论是: //对于ie,谷歌,360: //页面加载时只执行onload //页面刷新时,刷新之前执行onbeforeunload事件,在新页面即将替换旧页面时onunload事件,最后onload事件. //页面关闭时,先onbeforeunload事件,

  • JavaScript不刷新实现浏览器的前进后退功能

    最近在学习backbone,学习理解backbone就要先理解spa,理解spa就要先了解单页面应用是如何做到页面不刷新改变url的. 相较于不同页面的跳转,AJAX可以说大大提高了用户的浏览体验,不用看到页面切换之间的白屏是件很惬意的事情.但是很多早先的AJAX应用是不支持浏览器的前进后退的,这导致了用户不管在网站里浏览到何处,一旦刷新就会立刻回到起初的位置,并且用户也无法通过浏览器的前进后退按钮来实现浏览历史的切换. 对于第一个问题,解决还算容易,只要用cookie或者localStorag

  • react实现浏览器自动刷新的示例代码

    在单页应用如此流行的今天,曾经令人惊叹的前端路由已经成为各大框架的基础标配,每个框架都提供了强大的路由功能,导致路由实现变的复杂.想要搞懂路由内部实现还是有些困难的,但是如果只想了解路由实现基本原理还是比较简单的.本文针对前端路由主流的实现方式 hash 和 history,提供了原生JS/React/Vue 共计六个版本供参考,每个版本的实现代码约 25~40 行左右(含空行). 什么是前端路由? 路由的概念来源于服务端,在服务端中路由描述的是 URL 与处理函数之间的映射关系. 在 Web

  • python+selenium实现简历自动刷新的示例代码

    本文用到的文件的下载地址 百度网盘链接: https://pan.baidu.com/s/1tmpdEfAZKff5TOMAitUXqQ 提取码: e6at 1 安装Python 和 selenium (1)下载Python 官网地址: https://www.python.org/ftp/python/3.6.8/python-3.6.8-amd64.exe 百度网盘链接如上所示 (2)安装Python 等待安装完毕 (3) 安装 selenium pip install selenium –

  • 用python-webdriver实现自动填表的示例代码

    在日常工作中常常需要重复填写某些表单,如果人工完成,费时费力,而且网络延迟令人十分崩溃.如果能够用程序实现自动填表,效率可以提高一倍以上,并且能够移植到多台计算机,进一步提高工作效率.webdriver是python的selenium库中的一个自动化测试工具,它能完全模拟浏览器的操作,无需处理复杂的request.post,对爬虫初学者十分友好. 一.环境配置 python3.6+selenium库+xlrd库+xlwt库 其中xlrd和xlwt库用于读写excel表中的数据. 还要下载一个浏览

  • python+selenium小米商城红米K40手机自动抢购的示例代码

    使用环境 1.python3 2.selenium selenium使用简述 1.安装selenium pip install selenium 2.安装ChromeDriver 下载地址:http://chromedriver.storage.googleapis.com/index.html 注意:下载的ChromeDriver需要与Chrome版本一致. 1)Chrome版本查看: 2)ChromeDriver对应版本下载: 3)ChromeDriver下载后解压到任意文件夹,建议可以放到

  • 利用Selenium添加cookie实现自动登录的示例代码(fofa)

    介绍 Selenium可以模拟浏览器进行自动化操作,但一些网站需要进行登录才能进行一些操作,比起输入账号密码,cookie是更加方便的.而且fofa首先登录邮箱账号时获得的cookie并不是fofa的cookie,因此我们直接选择利用fofa的cookie进行自动登录.但是selenium需要先打开一个网站才会加载进去cookies,因此我们需要将cookies写在代码中,加载进去 扩展 get_cookies(): 获得所有cookie信息. get_cookie(name): 返回字典的ke

  • SpringBoot实现JWT token自动续期的示例代码

    为什么要 token自动续期 token中一般会包含用户的基本信息,为了保证token的安全性,一般会将token的过期时间设置的比较短,但是这样会导致用户因为token过期需要频繁登录,因此需要token自动续期. //创建token String token = JwtUtil.createToken(sysUser.getId(), user.getUserName()); //将token放入redis中,key为用户的手机号+"token" redisUtil.set(sys

  • React 实现拖拽功能的示例代码

    本文介绍了React 实现拖拽功能的示例代码,分享给大家,具体如下: 实现效果: 因为工作中会用到 JIRA 所以想实现一下相似的功能,顺便学习一下 H5 的拖拽.不支持拖拽改变顺序,感觉有点麻烦,而且没必要.感觉相关的博文好少的,大部分都是直接上代码,没有解释. 图片默认可以拖动,其他元素的拖动效果同图片.正常的 div 是不能被拖动的,鼠标点击选择后移动没有效果,需要加  draggable="true" 使得元素可以被拖动. 拖拽相关的几个事件,有被拖动元素的事件,也有拖动进入的

  • django js实现部分页面刷新的示例代码

    例子中,我用的是显示机器上的进程信息的表格,获取不同的机器的进程信息时,更新这个展示信息的表格,如下: 当我在输入框中输入ip时,我希望只是更新这个表格,页面其他部分不变,实现方式如下: 1.在原页面中设置这个表格的id为pstable <table class="table table-striped" id="pstable"> <thead> <tr> <th>user</th> <th>

  • OpenCV 表盘指针自动读数的示例代码

    前段时间参加了一个表盘指针读数的比赛,今天来总结一下 数据集一共有一千张图片: 方法一:径向灰度求和 基本原理: 将图像以表盘圆心转换成极坐标,然后通过矩阵按行求和找到二值图最大值即为指针尖端 导入需要用到的包 import cv2 as cv import numpy as np import math from matplotlib import pyplot as plt import os 图像预处理 去除背景:利用提取红色实现 def extract_red(image): "&quo

  • js实现浏览器打印功能的示例代码

    最近接触到一个新需求,实现打印机打印小票的功能.打的一桌子小票(惭愧),不过也基本满足了业务上的需求,现在分享一下如何实现(好记性不如烂笔头) 先上代码 // 布局代码 <div id="print">     <div id="print_content"></div> </div> //js 部分代码var f = document.getElementById('printf'); if (f) { docume

随机推荐