使用Vue开发项目,肯定对它的路由系统不会陌生。
学习router
直观感受
页面切换没有A标签那种明显的跳转以及新页面打开时的刷新等
概念
就是只有一个Web页面的应用,只加载单个HTML页面。在用户与应用程序交互时,动态更新该页面的Web应用程序。我们称之为SPA(单页应用程序)
区别
1、传统多页面程序:每次请求服务器返回的都是一个完整的页面
2、 单页应用程序:只有第一次会加载页面, 以后的每次请求, 仅仅是获取必要的数据.然后, 由页面中js解析获取的数据, 展示在页面中
优势
1 减少了请求体积,加快页面响应速度,降低了对服务器的压力
2 更好的用户体验,让用户在web app感受native app般的流畅
缺点
因为技术使用了ajax,导致页面不利于SEO,但是可以通过其他技术去规避
(SEO原则:搜索引擎的蜘蛛只识别href的一般超链接,而不识别JavaScript代码,遇到一般超链接就会爬进去,遇到JavaScript不会爬进去。即,搜索引擎抓不到AJAX动态加载的内容。)
History模式
HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;
新建vue-router-learn
自取的demo名称,具体demo地址
目录结构
vue-router-learn
├── README.md
├── .gitignore
├── node_modules
├── index.html
├── package.json
├── src
│ ├── main.js
│ └── pages
└── webpack.config.js
想知道这样的目录树如何自动生成,戳这里 安装tree
index.html
<body>
<div id="app">
{{ message }}
</div>
<!-- 注意这里的 script地址 -->
<script src="/dist/build.js"></script>
</body>
大家可能好奇,刚才的根目录上也没有 dist 文件啊?不要着急往下看
package.json
{
"name": "vue-router-learn",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --hot --open",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"predeploy": "npm run build"
},
"dependencies": {
"vue": "^2.3.3"
},
"devDependencies": {
"babel-core": "^6.0.0",
"babel-loader": "^7.0.0",
"babel-preset-env": "^1.6.1",
"cross-env": "^4.0.0",
"css-loader": "^0.28.1",
"file-loader": "^0.11.1",
"surge": "^0.19.0",
"vue-loader": "^12.0.3",
"vue-template-compiler": "^2.3.3",
"webpack": "^2.5.1",
"webpack-dev-server": "^2.4.5"
}
}
//下载所有依赖
cnpm i
//启动项目执行
npm run dev
这里会用到webpack-dev-server ,其中有这么一句解释:
It uses webpack-dev-middleware under the hood, which provides fast in-memory access to the webpack assets.
main.js
import Vue from 'vue'
const routes = {
'/': 'Home',
'/about': 'About'
}
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
currentRoute: window.location.pathname
},
computed: {
ViewComponent () {
const matchingView = routes[this.currentRoute];
return matchingView
? require('./pages/' + matchingView + '.vue')
: require('./pages/404.vue')
}
},
render (h) { return h(this.ViewComponent) }
})
其中如果不借助 webpack 直接使用import会报错:
Cannot use import statement outside a module
因为import 是es6的语法,而浏览器并不支持es6语法,当然你也可以在script 上添加type=“model”,或者频繁的使用script
<script src="./node_modules/vue/dist/vue.js" ></script>
<script src="./src/main.js" ></script>
但显然不是我们想要的。所以还是借助webpack比较好
pages 存放页面
目前有三个页面 404、home、about
//404页
<template>
<h1>这是404页</h1>
</template>
//home 页
<template>
<section>
<nav>
<a href="/">home页</a>
<a href="/about">about页</a>
</nav>
<h1>这是home页</h1>
</section>
</template>
//about 页
<template>
<section>
<nav>
<a href="/">home页</a>
<a href="/about">about页</a>
</nav>
<h1>这是about页</h1>
</section>
</template>
效果
这是我们已经完成了一个简易的页面切换,当点击home/about 按钮时,URL会发生改变,然后通过window.location.pathname获得对应的router,最后通过render展示对应的组件,不太了解main.js写法的可以戳这里–看template部分
继续改造
但是他照样是通过A标签跳转的,并且每次都会刷新页面,所以咱们还需要借助 history.pushState API
router.js
export default {
'/': 'Home',
'/about': 'About'
}
//main.js
//顶部加一行 import 引入 router文件
//保持不变
import routes from './router'
新建my-router-link
仿照router路由,新建一个my-router-link组件
<template>
<a
:href="to"
:class="[{ active: isActive },'a']"
@click="go"
>
<slot></slot>
</a>
</template>
<script>
import routes from '../router'
export default {
props: {
to: {
type:String,
default:''
}
},
computed: {
isActive () {
return this.to === this.$root.currentRoute
}
},
methods: {
go (event) {
event.preventDefault()
this.$root.currentRoute = this.to
window.history.pushState(
null,
routes[this.to],
this.to
)
}
}
}
</script>
<style scoped>
.a{
color: #333;
}
.active {
color: cornflowerblue;
}
</style>
引用my-router-link
// home页面
<template>
<section class="about-wrap">
<nav>
<my-router-link to="/">home页</my-router-link>
<my-router-link to="/about">about页</my-router-link>
</nav>
<h1>这是home页</h1>
</section>
</template>
<script>
import MyRouterLink from '../components/my-router-link.vue'
export default {
components:{
MyRouterLink
}
}
</script>
//about 页面同上
//只是把h1标签内容替换成 这是about页,方便切换页面时,有直观区别
效果
可惜csdn 没办法添加动效,所以看着和上一张效果图一样,其实是有区别大,我在这里先解释一下,感兴趣的小伙伴自己运行一下就知道了。
区别 这时点击tab按钮,URL改变,页面切换,但是不会像以前一样刷新了。
history.pushState
每执行一次都会增加一条历史记录,一共接收3个参数
history.pushState(data,title,url)
-
data:要设置的history.state的值,可以是任意类型的值,可根据此值进行判断执行想要的操作。
-
title:现在大多数浏览器不支持或者忽略这个参数,最好用null代替。
-
url:地址栏的值,若不需要可用空来代替。
history.replaceState(扩展知识)
replaceState()是用来修改当前的历史记录(history实体),而不是创建一个新的历史记录,所以当执行完history.replaceState()后,点击返回按钮照样会返回上一个一面。
当需要更新一个state对象或者当前history实体时,可以用replaceState()来实现。
popstate 监听
其实到这里添加路由已经差不多了,但是点击浏览器返回时,会发现页面没有反应,这里还需要对路由进行一下监听
改造后的main.js
//main.js
import Vue from 'vue'
import routes from './router'
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!',
currentRoute: window.location.pathname
},
computed: {
ViewComponent () {
const matchingView = routes[this.currentRoute];
return matchingView
? require('./pages/' + matchingView + '.vue')
: require('./pages/404.vue')
}
},
render (h) { return h(this.ViewComponent) }
})
window.addEventListener('popstate', () => {
//当点击浏览器返回按钮时,更新currentRoute,改变引用的pages页面
app.currentRoute = window.location.pathname;
})
history 刷新404问题
因为刷新是实实在在地去请求服务器的,history模式最终的路由都体现在url的pathname中,这部分是会传到服务器端的,因此需要服务端对每一个可能的path值都作相应的映射
// -e filename 如果 filename存在,则为真
location /{
root /**/**/文件目录;
index index.html index.htm;
if (!-e $filename) {
rewrite ^/(.*) /index.html last;
break;
}
}
hash模式
hash即URL中"#"字符后面的部分
1、使用浏览器访问网页时,如果网页URL中带有hash,页面就会定位到id(或name)与hash值一样的元素的位置;
2、hash还有另一个特点,它的改变不会导致页面重新加载;
3、hash值浏览器是不会随请求发送到服务器端的;
4、通过window.location.hash属性获取和设置hash值。
window.location.hash值的变化会直接反应到浏览器地址栏(#后面的部分会发生变化),同时,浏览器地址栏hash值的变化也会触发window.location.hash值的变化,从而触发onhashchange事件。
继续改造
hash和history 主要修改路由部分,其他文件都可以保持不变,因此咱们复用上边的项目,加以修改
修改my-router-link
//只需要修改 go 方法,将原有 History 部分注释掉
methods: {
go (event) {
event.preventDefault()
this.$root.currentRoute = this.to
//History 模式
// window.history.pushState(
// null,
// routes[this.to],
// this.to
// )
//Hash模式
window.location.hash = this.to
}
}
修改main.js
//注释掉History部分,添加hashchange 方法
//History 模式
// window.addEventListener('popstate', () => {
// app.currentRoute = window.location.pathname;
// })
//Hash模式
window.addEventListener('hashchange',(e) =>{
let pathname = e.newURL.split('/#')[1]
app.currentRoute = pathname;
},false);
1、当URL的片段标识符更改时,将触发hashchange事件(跟在#符号后面的URL部分,包括#符号)
2、hashchange事件触发时,事件对象会有hash改变前的URL(oldURL)和hash改变后的URL(newURL)两个属性
具体效果就不截图了,和上边实现的效果一样。