单页面应用(SPA)
一次性从服务器下载,切换页面不用再发送页面请求,只发送对应页面的图片等请求
Vue-router
核心
$router.go(-1) === history.go(-1) === 浏览器后退操作
路由需要实现响应式
1.vue-router 实现响应式与vuex一样都是借助vue的响应式原理 不过vue-router是借助util下的defineReactive(需要看过vue响应式原理), 而vuex 是通过借助一个vue实例 将store变成vue实例的属性来实现
简易源码
hash模式
myVue-Router.js
let Vue
class VueRouter {
constructor({routers}){
let routerMap = {}
routers.forEach(router => {
let path = router.path
if(!routerMap[path]){
routerMap[path] = router
}
});
// path /foo
this.routerMap = routerMap
//当前路由对象
this.current = {
path: '/',
component: {
template: '<div>aaa</div>'
}
}
this.listener()
}
//增加监听器
listener(){
//刚加载时 默认传/
window.addEventListener('load', () => {
let hash = window.location.hash
if(!hash){
window.location.hash = '/'
}
let router = this.search(hash.slice(1))
if(router){
this.current.path = router.path
this.current.component = router.component
}
})
//hash值改变时 做操作
window.addEventListener('hashchange', () => {
let hash = window.location.hash
let router = this.search(hash.slice(1))
if(router){
this.current.path = router.path
this.current.component = router.component
}
})
}
search(path){
if(this.routerMap[path]){
return this.routerMap[path]
}
return null
}
}
VueRouter.install = function(vue, options){
Vue = vue
Vue.mixin({
beforeCreate(){
let vm = this
if(vm.$options.router){
//那个实例挂载了router
vm._routerRoot = this
vm._router = vm.$options.router
//实现响应式 通过vue实现响应式的api
Vue.util.defineReactive(vm, 'router', )
}else{
//往上找,
vm._routerRoot = vm.$parent && vm.$parent._routerRoot
}
}
})
//生成router-link组件
Vue.component('router-link', {
props:{
to: String
},
render(h){
//h > creatElement 虚拟dom转为真实dom
//相当于生成一个a标签
return h('a', {attrs: {href: '#'+this.to}}, this.$slots.default)
}
})
//生成router-view组件
Vue.component('router-view', {
render(h){
let component = this._routerRoot.component
return h(component)
}
})
}
if(typeof Vue !== 'undefined'){
Vue.use(VueRouter)
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> -->
<script src="./myVue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<!-- <router-view></router-view> -->
</div>
</body>
<script>
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app')
// 现在,应用已经启动了!
</script>
</html>
hash模式与history模式的对比
最直观的感受就是,
1.hash会比history多个#
2.在vue的路由配置中有mode选项 :
mode:“hash”;
mode:“history”;
细节对比:
概念区别:
1.hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
2.history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)
通过这两个api完成URL跳转不会重新加载页面,history模式解决了hash模式存在的问题. hash的传参是基于URL的, 如果要传递复杂的数据, 会有体积限制, 而history模式不仅可以在URL里传参, 也可以将数据存放到一个特定的对象中
在请求方式区别:
1.hash :只会向服务器发送#之前的。如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
2.history:前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误
Vue.use()源码
比较简单
就是将传进来的插件挂到vue对象上,只是需要判断插件是对象还是函数,如果是对象 ,需要将插件的install属性改变this指向该插件
export function initUse(Vue) {
Vue.use = function (plugin) {
let installPlugins = this._installPlugins || this._installPlugins = []
if(installPlugins.indexOf(plugin)>-1){
return this
}
let args = toArry(arguments, 1)
args.push(this)
if(plugin instanceof 'object'){
plugin.install.apply(plugin, args)
}else if(plugin instanceof 'function'){
plugin.apply(null, args)
}
return this
}
}