首先思考一个问题:我们在与界面交互时,路由发生了变化,这明明是多个页面,为什么要叫单页应用呢?
答:单页应用指的是所有的代码(HTML、JavaScript、CSS)通过单个页面都加载进来,当页面路由发生变化时,不再去请求服务端。划重点:不再请求服务端。
· history模式
hash模式是一种旧版本浏览器就支持的模式(IE8及IE8以上都支持),通过修改url的hash值(#后面的称为hash)和监听hashchange事件来完成对逻辑的处理。因为修改hash值是不会触发页面刷新导致页面重新向服务端发送请求的。
history模式是H5新推出的一种实现url发生变化(从IE10开始支持),而不向服务端发送请求的模式。其原理是通过history的pushState方法(会在url的历史记录中增加当前的url)和history的replaceState方法(替换当前的url,所以当前的url不会被记录到历史中),以及window监听popstate事件来实现。
Tip:可以通过访问 https://caniuse.com 网站来查看某个特性在浏览器中的支持情况。
答:其实要想使用history模式,需要服务端配合或者在nginx进行一些配置,我们平时在本地启动项目是因为启动项目时,本地server已经默认兼容了history模式。
如果你不相信,下面我们来分别试一下在node服务端和在nginx进行配置的效果:
1. 启动一个简单的Vue项目,配置好路由,有简单的路由跳转功能。
2. 执行 yarn build 命令进行打包,默认生成dist文件夹
3. 在本地新建一个node服务。
const path = require('path')
const express = require('express')
const history = require('connect-history-api-fallback')
const app = express()
app.use(history()) // 增加这句话可以兼容history模式,反之注释掉此行,不支持history模式
app.use(express.static(path.join(__dirname, './dist'))) // 把静态资源指向我们打包好的dist目录
app.listen(3003, () => {
console.log('server open')
})
1. 运行 node server.js 启动服务
2. 访问http://localhost:3003 (具体看自己配置的路由),进入index页面。
3. 通过交互改变url(例如点击一个按钮,路由跳转到localhost:3003/about)
4. 刷新当前页面,发现页面可以正常访问
5. 把server.js中app.use(history())这行代码删除掉。重新启动服务
6. 刷新页面,发现localhost:3003/about不能再访问。
结论:使用history需要服务端配合兼容。
1. 在电脑中找到nginx的目录(mac为例:/usr/local/etc/nginx)
2. 把打包好的dist文件夹放到nginx文件夹下
3. 配置一下nginx:
server {
server_name localhost;
listen 3000;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location / {
root /usr/local/etc/nginx/dist;
index index.html index.htm;
# try_files $uri $uri/ /index.html;
}
}
4. 启动 nginx
5. 访问 http://localhost:3000
6. 通过交互改变url(例如点击一个按钮,路由跳转到 localhost:3000/about)
7. 刷新当前页面,发现页面不可以正常访问
8. 在nginx配置文件中,把注释掉的那行 try_files $uri $uri/ /index.html; 打开。重启nginx -s reload
9. 刷新页面,发现页面已经可以正常访问
$uri表示当前地址栏中的url,try_files $uri $uri/ /index.html; 此行的意思是,当访问当前这个url时,如果找不到对应的文件,就访问这个路径下的index文件,如果还是找不到对应文件,就访问域名下的index.html页面。
结论:使用history需要nginx配置兼容。
经过上面两次的验证可以发现,我们使用history模式时,需要在服务端或者nginx进行一些兼容。这些是我们前端开发平时可能会忽略掉的。
import Vue from 'vue'
import App from './App.vue'
import router from 'vue-router'
import index from './index.vue'
Vue.use(router)
const routes = [
{
path: '/',
name: 'index',
component: index
},{
path: '/about',
name: 'about',
component: () => import('./about.vue') // 懒加载
},{
path: '/hello/:id', // 动态路由
props: true, // 开启通过props传值
name: 'hello',
component: () => import('./hello.vue')
}
]
new Vue ({
router: new router({routes, mode: 'history'}),
render: h => h(App),
}) .$mount('#app')