vuejs|vue-router原理与源码分析

单页面应用(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 Vueclass 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: 'aaa' } } 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
Document ="https://unpkg.com/vue/dist/vue.js">="./myVue-router.js">
Hello App!Go to Foo Go to Bar
> // 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)// 1. 定义 (路由) 组件。 // 可以从其他文件 import 进来 const Foo = { template: 'foo' } const Bar = { template: 'bar' }// 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')// 现在,应用已经启动了!

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()源码 【vuejs|vue-router原理与源码分析】比较简单
就是将传进来的插件挂到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 } }

    推荐阅读