封裝一個(gè)基礎(chǔ)的vue-router
前言
主要知識(shí)點(diǎn):
路由原理
Hash與History
實(shí)現(xiàn)路由
一、一個(gè)vue路由的工作原理
前端路由與后端路由的區(qū)別:
后端路由:
輸入url>請(qǐng)求發(fā)送到服務(wù)器>服務(wù)器解析請(qǐng)求的路徑>拿取對(duì)應(yīng)的頁(yè)面>返回回去
前端路由:
輸入url>js解析地址>找到對(duì)應(yīng)的地址的頁(yè)面>執(zhí)行頁(yè)面生成的js>看到頁(yè)面
vue-router工作流程
二、Hash與history的使用
hash:
#號(hào)后的就是hash的內(nèi)容
可以通過(guò)location.hash拿到
可以通過(guò)onhashchange監(jiān)聽(tīng)hash的改變
可以在#號(hào)后面加路徑不會(huì)向服務(wù)器請(qǐng)求
history:
history即正常的路徑
location.pathname
可以用onpopstate監(jiān)聽(tīng)history變化
三、Vue插件基礎(chǔ)知識(shí)
如:vue-router、vuex、element-ui都是插件
插件基礎(chǔ)點(diǎn):
Vue.use去使用一個(gè)插件,并且去執(zhí)行install方法
Vue.mixin往vue的全局混入自定義的操作
可以通過(guò)this.$options拿到new Vue時(shí)的參數(shù)
示例:
以下都是在main.js執(zhí)行
1、初始Vue.use()
Vue.use({
console.log('use') //會(huì)打印出use
})
2、install屬性
let a = function() {
console.log(a)
}
//或 let a ={}
a.install=function(){
console.log('install')
}
Vue.use(a) // 會(huì)打印install,而不會(huì)打印a。
// 如果你給他一個(gè)方法,他就執(zhí)行這個(gè)方法,
// 但是無(wú)論你給他的任何東西,只要給他一個(gè)install屬性,他就會(huì)執(zhí)行install。
3、Vue.mixin()
let a = function() {
console.log(a)
}
//或 let a ={}
a.install=function(vue){
// Vue.mixin 全局混入自定義操作。上面的vue是作為參數(shù)傳進(jìn)來(lái)的,而不是import Vue from 'vue'中的Vue
vue.mixin({
data () {
return {
c:123456 // 在其他頁(yè)面this.c
}
},
methods:{
globalMethods(){
console.log('我是全局方法') // 在其它頁(yè)面this.globalMethods()
}
},
created() {
console.log(this)
}
})
}
Vue.use(a)
4、Vue工具類(Vue.util)
vue 工具類: defineReactive、extend、mergeOptions、warn
let obj = {
key:'KEY'
}
setTimeout(function () {
obj.key='KEY2' // 3s改變{{this.obj1.key}}
},3000)
let a = function() {
console.log(a)
}
//或 let a ={}
a.install=function(vue){
console.log(vue.util) // vue 工具類: defineReactive、extend、mergeOptions、warn
vue.util.defineReactive(obj,'key') // 監(jiān)聽(tīng)。源碼使用的是Object.defineProperty()
vue.mixin({
beforeCreate(){
this.obj1=obj //在其他頁(yè)面{{this.obj1.key}},值為KEY。
}
})
}
vue.extend() 與 vue.util.extend() 區(qū)別:
vue.extend() // 單元測(cè)試
const home = Vue.extend(home)
// 新建這個(gè)組件的構(gòu)造函數(shù),也就是組件的this
const vm = new home().$mount()
vue.util.extend() // 淺拷貝對(duì)象
四、 封裝基礎(chǔ)vue-router
準(zhǔn)備完了前面幾個(gè)知識(shí)點(diǎn),我們下面來(lái)整代碼,需要注意的是,下面只是個(gè)基礎(chǔ)的vue-router,只可以頁(yè)面切換。本篇只是為了熟悉源碼,不為實(shí)際需要。
在src文件夾下新建一個(gè)文件夾myrouter,里面再新建一個(gè)index.js文件。
在原來(lái)的router文件夾里的index.js中把原來(lái)引入router的路徑換為import VueRouter from '../myrouter'
編輯myrouter文件夾下的index.js
// 記錄history對(duì)象
class HistoryRoute{
constructor(){
// 要監(jiān)聽(tīng)的路徑
this.current=null;
}
}
// vuerouter本身
class vueRouter{
constructor(options){
this.mode=options.mode||'hash'; //配置模式
this.routes=options.routes||[]; // routes路由表
this.routesMap=this.createMap(this.routes); // 調(diào)用 createMap,參數(shù)是路由表routes
this.history=new HistoryRoute;// 實(shí)例化history
this.init(); // 初始化
}
init(){
if(this.mode=='hash'){ // 判斷是否是hash模式
location.hash?'':location.hash='/'; // 自動(dòng)加#號(hào)。如果有hash返回空字符串。否則返回/
window.addEventListener('load',()=>{ // 監(jiān)聽(tīng)頁(yè)面加載完成
this.history.current=location.hash.slice(1); //拿到hash值,把#去掉。賦給實(shí)例化的history的current路徑。
})
window.addEventListener('hashchange',()=>{ // 監(jiān)聽(tīng)hash改變
this.history.current=location.hash.slice(1);
})
}
}
// 路徑對(duì)應(yīng)組件。新組合一個(gè)對(duì)象。如:{'/':Home},也就是映射。
createMap(routes){
return routes.reduce((memo,current)=>{ // current 就是路由表routes
memo[current.path]=current.component;
return memo
},{})
}
}
vueRouter.install=function(Vue){
// 寫插件要注意判斷插件是否注冊(cè)
if(vueRouter.install.installed)return
vueRouter.install.installed=true;
// 向vue里面混入操作
Vue.mixin({
beforeCreate(){
if(this.$options&&this.$options.router){ // 在App.vue文件里如果有router選項(xiàng)的話
this._root=this; // 這里的this指向當(dāng)前Vue實(shí)例,緩存下自身。
this._router=this.$options.router; // 掛載下選項(xiàng)里傳進(jìn)來(lái)的router.
Vue.util.defineReactive(this,'current',this._router.history); //監(jiān)聽(tīng)this.current,并且傳入第三個(gè)參數(shù)。相當(dāng)于children。
}else{
this._root=this.$parent._root; //如果沒(méi)有就向上查找一級(jí),直到查到App.vue里new Vue({router})為止。
}
// 這個(gè)方法只是說(shuō)明this.$router只讀,私有變量思想
Object.defineProperty(this,"$router",{ //this指當(dāng)前組件實(shí)例
get(){
return this._root._router; // 返回掛載后的router
}
})
}
})
// 定義組件
Vue.component('router-view',{
render(h){
let current=this._self._root._router.history.current;// 拿到當(dāng)前存進(jìn)去的router實(shí)例里的current
let routerMap=this._self._root._router.routesMap;//拿到當(dāng)前存進(jìn)去的router實(shí)例里的routesMap
return h(routerMap[current]); // 拿到routerMap,調(diào)用h方法。渲染routerMap對(duì)應(yīng)關(guān)系。放到router-view組件里面。
}
})
}
// 暴露vuerouter
export default vueRouter;
結(jié)語(yǔ)
前端的路上我們一起前行。
作者:Vam的金豆之路
主要領(lǐng)域:前端開(kāi)發(fā)
我的微信:maomin9761
微信公眾號(hào):前端歷劫之路