Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端路由模式 hash、 history #7

Open
gauseen opened this issue Dec 20, 2018 · 0 comments
Open

前端路由模式 hash、 history #7

gauseen opened this issue Dec 20, 2018 · 0 comments
Assignees
Labels
javascript javascript node nodejs vuejs vuejs

Comments

@gauseen
Copy link
Owner

gauseen commented Dec 20, 2018

一、概念

当前单页面应用路由模式有2种,hashhistory 模式

  • hash 模式

    • 样式比较丑,不太符合人的 “审美”
    • 浏览器地址栏 URL# (如:http://localhost:3000/#/a)
    • # 后面的内容不会传给服务端
    • 改变浏览器地址栏 URL # 后面的值,不会网页重载
  • history 模式

    • HTML5 新特性,样式比较优雅
    • 浏览器地址没有 # (如:http://localhost:3000/a)
    • 没有 # ,所有 domain 后面的内容都会传给服务端
    • 改变浏览器地址栏 URL 网页重载,再次请求服务器,并向 history 栈中插入一条记录

二、事件监听

  • hash 模式

    • 当浏览器 hashURL# 后面的部分)改变时就会触发 hashchange 事件
    window.addEventListener('hashchange', (e) => {
      // hash 改变会调用此回调
    }, false)
    • hash 模式下,改变浏览器 URL 方式
    window.location.hash = '#/b'
    window.location = 'http://localhost:3000/#/b'
    window.location.href = 'http://localhost:3000/#/b'
  • history 模式

    • 每当历史记录发生变化时,popstate 事件就会被触发
      调用 history.pushState() 或者 history.replaceState() 不会触发popstate 事件. popstate 事件只会在浏览器某些行为下触发, 比如点击后退、前进按钮(或者在 JavaScript 中调用 history.back()、history.forward()、history.go()方法).
    window.addEventListener('popstate', (e) => {
      // 历史记录发生改变会调用此回调
    }, false)
    
    // 添加历史记录栈
    let stateObj = { foo: "bar" }
    window.history.pushState(stateObj, 'title', 'url')
    
    window.history.replaceState() 的使用与 window.history.pushState() 非常相似,
    区别在于 replaceState() 是修改了当前的历史记录项而不是新建一个

三、论据

  • node koa 创建静态服务 index.js,并启动该服务 node index.js
// index.js

const Koa = require('koa')
const koaStatic = require('koa-static')
const app = new Koa()
const path = require('path')

// 项目根路径
const root = path.join(__dirname, './')

// 记录客户端访问该服务的次数
let count = 0

// 中间件,记录请求
app.use(async (ctx, next) => {
  console.log('ctx: ', ctx.href, ++count)
  await next()
})

// 在文件夹根目录,创建静态服务
app.use(koaStatic(root))

app.listen(3000)

console.log('listening on port 3000')
  • 新建静态 index.html 文件,内容如下
<!-- index.html -->

<!-- hash 模式 -->
<a href="/#/a">a</a>
<a href="/#/b">b</a>
<a href="/#/c/d">c/d</a>

<br />

<!-- history 模式 -->
<a href="/a">a</a>
<a href="/b">b</a>
<a href="/c/d">c/d</a>
  • 浏览器访问 http://localhost:3000

此时终端有一条访问记录

ctx:  http://localhost:3000/ 1

情景一:分别点击 hash 模式下的 a, b, c/d 文字,可以看到浏览器地址栏改变,但是终端访问记录依然不变。

说明:浏览器地址栏 hash 改变,并不会向后端发起请求


情景二:点击 history 模式下的 a 文字,可以看到浏览器地址栏改变,同时终端访问记录也会增加 1
如下所示:

ctx:  http://localhost:3000/ 1
ctx:  http://localhost:3000/a 2

但是浏览器报 404 错误,这是因为浏览器地址改变时,它会向服务器发送一条 GET 请求,默认寻找 http://localhost:3000 服务下 a 文件夹下的 index.html 、 index.htm、index.jsp 文件

说明history 模式下,浏览器地址栏改变,向后端发起请求

四、理论结合实践 🌰

我们以 Vue 2.x 框架为例,其他框架也是同样的道理,详细代码请看这里git clone 下来之后

# 安装依赖

yarn install
# OR
npm run install

# 启动本地服务
yarn dev

# 产出代码(可放在生产环境)
yarn build

这里默认 vue router 路由模式为 historyhash 模式无此问题,不再赘述

// src/router/index.js

export default new Router({
  mode: 'history', // 路由模式设置
  base: process.env.BASE_URL,
  routes,
})

yarn build 操作后,将 dist 文件夹拷贝到上面建的 index.js 同级目录下。

启动服务: node index.js

浏览器访问:http://localhost:3000/admin-vb/
/admin-vb/vue.config.js 文件下的 baseUrl 字段对应的值为准

此时一切显示正常,也可以尝试切换路由。但是刷新浏览器会 404 错误。
这是因为,在刷新浏览器之前,访问的路由受前端 vue router 代码控制。当我们刷新浏览器后,浏览器就会尝试访问当前路径下的 index.html、index.htm、index.jsp 文件,很遗憾,当前路径下没有该文件,所以就会报错 404 找不到该文件。

解决 history 模式,刷新浏览器 404 问题

  • ❤️ 官宣方案 ❤️

  • 我们刚刚自己创建的 node 服务如何解决呢?
    具体思路,当前端向后端请求页面时,找不到页面则返回 index.html 页面。直接上代码,如下:

const Koa = require('koa')
const koaStatic = require('koa-static')
const app = new Koa()
// 解决刷新 404 问题
const historyApiFallback = require('koa2-connect-history-api-fallback')
const path = require('path')

const root = path.join(__dirname, './')

// 记录访问服务次数
let count = 0

// 中间件,记录请求
app.use(async (ctx, next) => {
  console.log('ctx: ', ctx.href, ++count)
  await next()
})

// 解决刷新 404 问题
app.use(historyApiFallback({
  index: '/admin-vb/index.html'
}))

// 在文件夹根目录,创建静态服务
app.use(koaStatic(root))

app.listen(3000)

console.log('listening on port 3000')

广告植入::loudspeaker: :loudspeaker: :loudspeaker: 如果喜欢,欢迎 :star:、watch

@gauseen gauseen changed the title 前端路由模式 hashhistory 前端路由模式 hash、 history Dec 20, 2018
@gauseen gauseen added vuejs vuejs javascript javascript node nodejs labels Dec 20, 2018
@gauseen gauseen self-assigned this Oct 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
javascript javascript node nodejs vuejs vuejs
Projects
None yet
Development

No branches or pull requests

1 participant