Appearance
Hash VS History
两种模式的异同点
相同点
不管哪种模式都是客户端路由的实现方式 —— 当路径发生变化之后,不会向服务器发送请求。使用JS监视不同的路径变化,根据不同的地址渲染不同的内容,如果需要从服务端获取内容的话,使用Ajax获取。
表现形式的区别
- Hash 模式
- 例如:
https://music.163.com/#/playlist?id=3102961863
#
后面的内容作为我们的路由地址- 通过
?
携带参数URL参数 - 官方文档说这种格式很丑,路径上带有和数据无关的符号
- 例如:
- History 模式
- 例如:
https://music.163.com/playlist/3102961863
- 要用好history模式,需要服务端支持
- 例如:
原理区别
- Hash 模式
Hash模式基于锚点,以及onhashchange事件 —— 通过锚点的值作为路由地址,当地址变化后触发onhashchange事件,这里根据路径决定页面上呈现的内容
- History 模式
History 模式基于HTML5中的History API,即
history.pushState()
(IE10以后才支持)和history.replaceState()
两种方法
history.pushState()
和history.push()
方法的区别是,调用history.push()
方法的时候路径会发生变化,这时要向服务器发送请求。不会向服务器发送请求,history.pushState()
方法只会改变浏览器中地址栏中的地址,并且把地址记录到历史记录中。所以history.pushState()
可以实现客户端路由,但是history.pushState()
只有IE10以后才支持,使用有兼容性问题,如果要兼容before IE9版本,只能使用Hash模式。
History模式
- History 需要服务器的支持。
- 原因是单页应用中,只存在一个页面
index.html
,服务端不存在login.html页面,如果是单页应用不会有任何问题,如果当前地址栏http://www.testurl.com/login
,我们刷新浏览器会向服务器发送请求/login页面,服务器不存在这样的地址会返回404,所以在服务端配置应该除了静态页面以外的所有路径都返回单页应用的 index.html。
前端示例演示
- 下载模板
- router/index.js中配置路由地址并把模式改为history模式
js
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
// 如果找不到路由地址,就会加载404.vue
{
path: '*',
name: '404',
component: () => import(/* webpackChunkName: "404" */ '../views/404.vue')
}
]
const router = new VueRouter({
// mode默认情况是hash模式,这里要弄成history
mode: 'history',
routes
})
- App.vue中前两个链接的地址是存在的,第三个video的地址是不存在的
html
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<!--因为路由没有配置,所以会输出404.vue的内容-->
<router-link to="/video">Video</router-link>
</div>
<router-view/>
</div>
</template>
- 命令行执行
npm run serve
,点击video链接的时候可以看到查看页面不存在。这个时候点击刷新,页面并没有跳系统404页面是因为vue-cli自带服务器已经配置好了我们之前所说的内容。
Node.js服务器配置示例演示
- 下载模板
- frontend: 是上一个演示的前端项目
- backend: 是基于node.js开发的外服务器
- web:对front文件进行npm run build 打包存放的文件夹
- backend/app.js 中的代码是基于node-express开发的
js
// node的核心模块,用来处理路径,可以合并两个路径
const path = require('path')
// 导入处理 vue-router中的history 模式的模块(第三方模块)
const history = require('connect-history-api-fallback')
// 导入 express,基于node的外部开发框架(第三方模块)
const express = require('express')
// 通过express返回一个app对象
const app = express()
// 处理静态资源的中间件,网页引用的图片、js、css将来在访问的时候就是通过这个中间件处理的
// 网站根目录 ../web
app.use(express.static(path.join(__dirname, '../web')))
// 开启服务器,监听端口是 3000
app.listen(3000, () => {
console.log('服务器开启,端口:3000')
})
打开终端运行
node app.js
并打开浏览器http://127.0.0.1:3000/
可看到开发项目。(服务器所管理的静态文件都在web文件夹下。我们在访问外部服务器时,他输出的就是web文件夹下存储的网站资源。)打开之后点击home、about、video超链接,会调用history.pushState()方法,这个方法会改变浏览器地址栏中的地址,但是并不会向服务器发送请求,它还会把地址保存到历史记录中来。
如果点击到about的时候刷新浏览器, 此时浏览器要向服务器发送请求,地址就是
http://127.0.0.1:3000/about
,而我们在服务器中并没有请求这个地址,所以这里node服务器输出一个默认的404页面。
- 在app.js中添加注册处理 history 模式的中间件
js
...
const app = express()
// 注册处理 history 模式的中间件
app.use(history())
// 处理静态资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, '../web')))
...
- 再次重启服务器
node app.js
刷新浏览器发现可以看到页面,不会报找不到。
原因是我们在配置了之后刷新浏览器,服务器接收到这个请求,因为开启了对history模式的支持,服务器找不到对应页面,所以它会将单页应用默认的index.html返回给浏览器,浏览器接收到页面之后会再判断路由地址,/about会加载对应的about.vue组件
Nginx 服务器配置
- 从官网下载 nginx 的压缩包,版本1.18.0
- 把压缩包解压到 c 盘根目录(存放目录不能有中文),c:\nginx-1.18.0 文件夹
- 文件目录
主要文件说明
- nginx 主程序
- conf:配置文件
- html:用来存储我们的网站,nginx默认在里面放了一个index.html
- 打开命令行,切换到安装目录,在命令行中启动 nginx,如果80端口被占用,则无法启动,但是命令行也不会报错。
bash
# 启动(会在后台启动nginx,不会阻塞命令行的运行)
start nginx
# 重启(如果修改了nginx文件,需要重启服务器)
nginx -s reload
# 停止
nginx -s stop
- 打开浏览器看nginx服务器是否开启
127.0.0.1
不用输入端口号,默认是80,显示下面的页面表示nginx服务启动成功。(如果没有启动成功,可以找到占用80端口的应用关掉,或者修改nginx的配置文件)
- 将打包好的项目放到html文件中,刷新浏览器就可以找到我们的项目。同样点击超链接about之后,刷新页面,发现项目404。
5.如果要修复这个问题,需要修改 conf\nginx.conf 文件,http/server/location/try_files
bash
http {
...
server {
# 端口号,默认80,如果80端口被占用,可以修改
listen 80;
# 域名
server_name localhost;
...
# 指定了当前网站所在的根目录,
location / {
# root就是根目录,html文件夹
root html;
# 默认首页,localhost按回车的时候就会访问这个文件
index index.html index.htm;
# 处理history模式
# 尝试访问当前浏览器请求路径对应的文件
# $uri变量是当前请求的路径,它会去找这个路径对应的文件,如果找到了就把路径直接返回,没有找到就继续往下找$uri/,把$uri当成一个目录,再去找这个目录下的首页index.html,如果找到了就范湖给浏览器,没有找到就返回单页应用的首页index.html
try_files $uri $uri/ /index.html;
}
...
}
...
}
- 重启服务器nginx -s reload,刷新浏览器页面显示正常。
页面刷新之后,浏览器会向服务器请求地址,服务器接收到这次请求之后,会去找路径在服务器上对应的文件,服务器没有这个文件,try_files会返回网站根目录下的index.html,浏览器接收到这个页面再去匹配路由地址,客户端解析路由地址对应的组件并加载出来。