单页面应用(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
}
}
推荐阅读
- 前端面试总结|vue核心面试题(vue中模板编译原理)
- Vue|Vue2的核心原理剖析
- vue.js|Vue练习题--不定时分享
- 前端|2022前端应该掌握的10个 JS 小技巧
- js小工具|手机调试打开控制台方法vconsole
- 前端|js vue base64 byte 转 为文件格式 (以excel为例)
- VueJs|【Vue 响应式原理】发布订阅模式、观察者模式
- vue|Vue.js响应式原理(三)——发布订阅模式和观察者模式
- vue|快速理解Vue 使用 vm.$set 解决对象新增属性不能响应的问题