最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何手寫簡(jiǎn)易的 Vue Router

 更新時(shí)間:2020年10月10日 11:05:14   作者:Jacky-summer  
這篇文章主要介紹了如何手寫簡(jiǎn)易的 Vue Router,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下

前言

還是那樣,懂得如何使用一個(gè)常用庫(kù),還得了解其原理或者怎么模擬實(shí)現(xiàn),今天實(shí)現(xiàn)一下 vue-router 。

有一些知識(shí)我這篇文章提到了,這里就不詳細(xì)一步步寫,請(qǐng)看我 手寫一個(gè)簡(jiǎn)易的 Vuex

基本骨架

  • Vue 里面使用插件的方式是 Vue.use(plugin) ,這里貼出它的用法:

安裝 Vue.js 插件。如果插件是一個(gè)對(duì)象,必須提供 install 方法。如果插件是一個(gè)函數(shù),它會(huì)被作為 install 方法。install 方法調(diào)用時(shí),會(huì)將 Vue 作為參數(shù)傳入。這個(gè)方法的第一個(gè)參數(shù)是 Vue 構(gòu)造器,第二個(gè)參數(shù)是一個(gè)可選的選項(xiàng)對(duì)象。

  • 全局混入

使用 Vue.mixin(mixin)

全局注冊(cè)一個(gè)混入,影響注冊(cè)之后所有創(chuàng)建的每個(gè) Vue 實(shí)例??梢允褂没烊胂蚪M件注入自定義的行為,它將影響每一個(gè)之后創(chuàng)建的 Vue 實(shí)例。

  • 路由用法

比如簡(jiǎn)單的:

// 路由數(shù)組
const routes = [
 {
  path: '/',
  name: 'Page1',
  component: Page1,
 },
 {
  path: '/page2',
  name: 'Page2',
  component: Page2,
 },
]

const router = new VueRouter({
 mode: 'history', // 模式
 routes,
})

它是傳入了moderoutes,我們實(shí)現(xiàn)的時(shí)候需要在VueRouter構(gòu)造函數(shù)中接收。

在使用路由標(biāo)題的時(shí)候是這樣:

<p>
 <!-- 使用 router-link 組件來(lái)導(dǎo)航. -->
 <!-- 通過(guò)傳入 `to` 屬性指定鏈接. -->
 <!-- <router-link> 默認(rèn)會(huì)被渲染成一個(gè) `<a>` 標(biāo)簽 -->
 <router-link to="/page1">Go to Foo</router-link>
 <router-link to="/page2">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>

故我們需要使用Vue.component( id, [definition] )注冊(cè)一個(gè)全局組件。

了解了大概,我們就可以寫出一個(gè)基本骨架

let Vue = null

class VueRouter {
 constructor(options) {
  this.mode = options.mode || 'hash'
  this.routes = options.routes || []
 }
}

VueRouter.install = function (_Vue) {
 Vue = _Vue

 Vue.mixin({
  beforeCreate() {
   // 根組件
   if (this.$options && this.$options.router) {
    this._root = this // 把當(dāng)前vue實(shí)例保存到_root上
    this._router = this.$options.router // 把router的實(shí)例掛載在_router上
   } else if (this.$parent && this.$parent._root) {
    // 子組件的話就去繼承父組件的實(shí)例,讓所有組件共享一個(gè)router實(shí)例
    this._root = this.$parent && this.$parent._root
   }
  },
 })

 Vue.component('router-link', {
  props: {
   to: {
    type: [String, Object],
    required: true,
   },
   tag: {
    type: String,
    default: 'a', // router-link 默認(rèn)渲染成 a 標(biāo)簽
   },
  },
  render(h) {
   let tag = this.tag || 'a'
   return <tag href={this.to}>{this.$slots.default}</tag>
  },
 })

 Vue.component('router-view', {
  render(h) {
   return h('h1', {}, '視圖顯示的地方') // 暫時(shí)置為h1標(biāo)簽,下面會(huì)改
  },
 })
}

export default VueRouter

mode

vue-router有兩種模式,默認(rèn)為 hash 模式。

history 模式

通過(guò)window.history.pushStateAPI 來(lái)添加瀏覽器歷史記錄,然后通過(guò)監(jiān)聽popState事件,也就是監(jiān)聽歷史記錄的改變,來(lái)加載相應(yīng)的內(nèi)容。

  • popstate 事件

當(dāng)活動(dòng)歷史記錄條目更改時(shí),將觸發(fā) popstate 事件。如果被激活的歷史記錄條目是通過(guò)對(duì) history.pushState()的調(diào)用創(chuàng)建的,或者受到對(duì) history.replaceState()的調(diào)用的影響,popstate 事件的 state 屬性包含歷史條目的狀態(tài)對(duì)象的副本。

  • History.pushState()方法

window.history.pushState(state, title, url)

該方法用于在歷史中添加一條記錄,接收三個(gè)參數(shù),依次為:

  • state:一個(gè)與添加的記錄相關(guān)聯(lián)的狀態(tài)對(duì)象,主要用于popstate事件。該事件觸發(fā)時(shí),該對(duì)象會(huì)傳入回調(diào)函數(shù)。也就是說(shuō),瀏覽器會(huì)將這個(gè)對(duì)象序列化以后保留在本地,重新載入這個(gè)頁(yè)面的時(shí)候,可以拿到這個(gè)對(duì)象。如果不需要這個(gè)對(duì)象,此處可以填null。
  • title:新頁(yè)面的標(biāo)題。但是,現(xiàn)在所有瀏覽器都忽視這個(gè)參數(shù),所以這里可以填空字符串。
  • url:新的網(wǎng)址,必須與當(dāng)前頁(yè)面處在同一個(gè)域。瀏覽器的地址欄將顯示這個(gè)網(wǎng)址。

hash 模式

使用 URL 的 hash 來(lái)模擬一個(gè)完整的 URL。,通過(guò)監(jiān)聽hashchange事件,然后根據(jù)hash值(可通過(guò) window.location.hash 屬性讀?。┤ゼ虞d對(duì)應(yīng)的內(nèi)容的。

繼續(xù)增加代碼,

let Vue = null

class HistoryRoute {
 constructor() {
  this.current = null // 當(dāng)前路徑
 }
}

class VueRouter {
 constructor(options) {
  this.mode = options.mode || 'hash'
  this.routes = options.routes || []
  this.routesMap = this.createMap(this.routes)
  this.history = new HistoryRoute() // 當(dāng)前路由
  this.initRoute() // 初始化路由函數(shù)
 }

 createMap(routes) {
  return routes.reduce((pre, current) => {
   pre[current.path] = current.component
   return pre
  }, {})
 }

 initRoute() {
  if (this.mode === 'hash') {
   // 先判斷用戶打開時(shí)有沒有hash值,沒有的話跳轉(zhuǎn)到 #/
   location.hash ? '' : (location.hash = '/')
   window.addEventListener('load', () => {
    this.history.current = location.hash.slice(1)
   })
   window.addEventListener('hashchange', () => {
    this.history.current = location.hash.slice(1)
   })
  } else {
   // history模式
   location.pathname ? '' : (location.pathname = '/')
   window.addEventListener('load', () => {
    this.history.current = location.pathname
   })
   window.addEventListener('popstate', () => {
    this.history.current = location.pathname
   })
  }
 }
}

VueRouter.install = function (_Vue) {
 Vue = _Vue

 Vue.mixin({
  beforeCreate() {
   if (this.$options && this.$options.router) {
    this._root = this
    this._router = this.$options.router
    Vue.util.defineReactive(this, '_route', this._router.history) // 監(jiān)聽history路徑變化
   } else if (this.$parent && this.$parent._root) {
    this._root = this.$parent && this.$parent._root
   }
   // 當(dāng)訪問(wèn)this.$router時(shí)即返回router實(shí)例
   Object.defineProperty(this, '$router', {
    get() {
     return this._root._router
    },
   })
   // 當(dāng)訪問(wèn)this.$route時(shí)即返回當(dāng)前頁(yè)面路由信息
   Object.defineProperty(this, '$route', {
    get() {
     return this._root._router.history.current
    },
   })
  },
 })
}

export default VueRouter

router-link 和 router-view 組件

VueRouter.install = function (_Vue) {
 Vue = _Vue

 Vue.component('router-link', {
  props: {
   to: {
    type: [String, Object],
    required: true,
   },
   tag: {
    type: String,
    default: 'a',
   },
  },
  methods: {
   handleClick(event) {
    // 阻止a標(biāo)簽?zāi)J(rèn)跳轉(zhuǎn)
    event && event.preventDefault && event.preventDefault()
    let mode = this._self._root._router.mode
    let path = this.to
    this._self._root._router.history.current = path
    if (mode === 'hash') {
     window.history.pushState(null, '', '#/' + path.slice(1))
    } else {
     window.history.pushState(null, '', path.slice(1))
    }
   },
  },
  render(h) {
   let mode = this._self._root._router.mode
   let tag = this.tag || 'a'
   let to = mode === 'hash' ? '#' + this.to : this.to
   console.log('render', this.to)
   return (
    <tag on-click={this.handleClick} href={to}>
     {this.$slots.default}
    </tag>
   )
   // return h(tag, { attrs: { href: to }, on: { click: this.handleClick } }, this.$slots.default)
  },
 })

 Vue.component('router-view', {
  render(h) {
   let current = this._self._root._router.history.current // current已經(jīng)是動(dòng)態(tài)響應(yīng)
   let routesMap = this._self._root._router.routesMap
   return h(routesMap[current]) // 動(dòng)態(tài)渲染對(duì)應(yīng)組件
  },
 })
}

至此,一個(gè)簡(jiǎn)易的vue-router就實(shí)現(xiàn)完了,案例完整代碼附上:

let Vue = null

class HistoryRoute {
 constructor() {
  this.current = null
 }
}

class VueRouter {
 constructor(options) {
  this.mode = options.mode || 'hash'
  this.routes = options.routes || []
  this.routesMap = this.createMap(this.routes)
  this.history = new HistoryRoute() // 當(dāng)前路由
  // 初始化路由函數(shù)
  this.initRoute()
 }

 createMap(routes) {
  return routes.reduce((pre, current) => {
   pre[current.path] = current.component
   return pre
  }, {})
 }

 initRoute() {
  if (this.mode === 'hash') {
   // 先判斷用戶打開時(shí)有沒有hash值,沒有的話跳轉(zhuǎn)到 #/
   location.hash ? '' : (location.hash = '/')
   window.addEventListener('load', () => {
    this.history.current = location.hash.slice(1)
   })
   window.addEventListener('hashchange', () => {
    this.history.current = location.hash.slice(1)
   })
  } else {
   // history模式
   location.pathname ? '' : (location.pathname = '/')
   window.addEventListener('load', () => {
    this.history.current = location.pathname
   })
   window.addEventListener('popstate', () => {
    this.history.current = location.pathname
   })
  }
 }
}

VueRouter.install = function(_Vue) {
 Vue = _Vue

 Vue.mixin({
  beforeCreate() {
   // 根組件
   if (this.$options && this.$options.router) {
    this._root = this // 把當(dāng)前vue實(shí)例保存到_root上
    this._router = this.$options.router // 把router的實(shí)例掛載在_router上
    Vue.util.defineReactive(this, '_route', this._router.history) // 監(jiān)聽history路徑變化
   } else if (this.$parent && this.$parent._root) {
    // 子組件的話就去繼承父組件的實(shí)例,讓所有組件共享一個(gè)router實(shí)例
    this._root = this.$parent && this.$parent._root
   }
   // 當(dāng)訪問(wèn)this.$router時(shí)即返回router實(shí)例
   Object.defineProperty(this, '$router', {
    get() {
     return this._root._router
    },
   })
   // 當(dāng)訪問(wèn)this.$route時(shí)即返回當(dāng)前頁(yè)面路由信息
   Object.defineProperty(this, '$route', {
    get() {
     return this._root._router.history.current
    },
   })
  },
 })

 Vue.component('router-link', {
  props: {
   to: {
    type: [String, Object],
    required: true,
   },
   tag: {
    type: String,
    default: 'a',
   },
  },
  methods: {
   handleClick(event) {
    // 阻止a標(biāo)簽?zāi)J(rèn)跳轉(zhuǎn)
    event && event.preventDefault && event.preventDefault() // 阻止a標(biāo)簽?zāi)J(rèn)跳轉(zhuǎn)
    let mode = this._self._root._router.mode
    let path = this.to
    this._self._root._router.history.current = path
    if (mode === 'hash') {
     window.history.pushState(null, '', '#/' + path.slice(1))
    } else {
     window.history.pushState(null, '', path.slice(0))
    }
   },
  },
  render(h) {
   let mode = this._self._root._router.mode
   let tag = this.tag || 'a'
   let to = mode === 'hash' ? '#' + this.to : this.to
   return (
    <tag on-click={this.handleClick} href={to}>
     {this.$slots.default}
    </tag>
   )
   // return h(tag, { attrs: { href: to }, on: { click: this.handleClick } }, this.$slots.default)
  },
 })

 Vue.component('router-view', {
  render(h) {
   let current = this._self._root._router.history.current // current已經(jīng)是動(dòng)態(tài)
   let routesMap = this._self._root._router.routesMap
   return h(routesMap[current]) // 動(dòng)態(tài)渲染對(duì)應(yīng)組件
  },
 })
}

export default VueRouter

ps: 個(gè)人技術(shù)博文 Github 倉(cāng)庫(kù),覺得不錯(cuò)的話歡迎 star,給我一點(diǎn)鼓勵(lì)繼續(xù)寫作吧~

以上就是如何手寫簡(jiǎn)易的 Vue Router的詳細(xì)內(nèi)容,更多關(guān)于手寫簡(jiǎn)易的 Vue Router的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺談Vue知識(shí)系列-axios

    淺談Vue知識(shí)系列-axios

    這篇文章主要介紹了淺談Vue知識(shí)系列-axios,本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,需要的朋友可以參考下<BR>
    2023-01-01
  • 多個(gè)vue項(xiàng)目復(fù)用一個(gè)node_modules的問(wèn)題

    多個(gè)vue項(xiàng)目復(fù)用一個(gè)node_modules的問(wèn)題

    這篇文章主要介紹了多個(gè)vue項(xiàng)目復(fù)用一個(gè)node_modules的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue中el-table兩個(gè)表尾合計(jì)行聯(lián)動(dòng)同步滾動(dòng)條實(shí)例代碼

    vue中el-table兩個(gè)表尾合計(jì)行聯(lián)動(dòng)同步滾動(dòng)條實(shí)例代碼

    項(xiàng)目開發(fā)中遇到一個(gè)比較兩個(gè)form差異的需求,但當(dāng)item過(guò)多就需要滾動(dòng)條,下面這篇文章主要給大家介紹了關(guān)于vue中el-table兩個(gè)表尾合計(jì)行聯(lián)動(dòng)同步滾動(dòng)條的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • vue 實(shí)現(xiàn)全選全不選的示例代碼

    vue 實(shí)現(xiàn)全選全不選的示例代碼

    本篇文章主要介紹了vue 實(shí)現(xiàn)全選全不選的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • Vue 3集成海康Web插件實(shí)現(xiàn)視頻監(jiān)控功能

    Vue 3集成??礧eb插件實(shí)現(xiàn)視頻監(jiān)控功能

    本文詳細(xì)介紹了如何使用 Vue 3 框架集成海康Web插件實(shí)現(xiàn)視頻監(jiān)控功能,通過(guò)定義屬性、事件、變量,以及編寫初始化、播放視頻、處理節(jié)點(diǎn)點(diǎn)擊事件等方法,我們成功實(shí)現(xiàn)了視頻監(jiān)控系統(tǒng)的前端部分,感興趣的朋友一起看看吧
    2024-11-11
  • Vue express鑒權(quán)零基礎(chǔ)入門

    Vue express鑒權(quán)零基礎(chǔ)入門

    這篇文章主要介紹了詳解express鑒權(quán),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Vue-router 報(bào)錯(cuò)NavigationDuplicated的解決方法

    Vue-router 報(bào)錯(cuò)NavigationDuplicated的解決方法

    這篇文章主要介紹了Vue-router 報(bào)錯(cuò)NavigationDuplicated的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 如何在vue里添加好看的lottie動(dòng)畫

    如何在vue里添加好看的lottie動(dòng)畫

    這篇文章主要介紹了在vue里添加好看的lottie動(dòng)畫效果的方法,在vue中引入lottie非常簡(jiǎn)單,需要的朋友可以參考下
    2018-08-08
  • vue中關(guān)于el-popover的使用

    vue中關(guān)于el-popover的使用

    這篇文章主要介紹了vue中關(guān)于el-popover的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • vue3+Echarts頁(yè)面加載不渲染顯示空白頁(yè)面的解決

    vue3+Echarts頁(yè)面加載不渲染顯示空白頁(yè)面的解決

    這篇文章主要介紹了vue3+Echarts頁(yè)面加載不渲染顯示空白頁(yè)面的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10

最新評(píng)論

中超| 广水市| 堆龙德庆县| 遂昌县| 新河县| 高安市| 郴州市| 平凉市| 垣曲县| 张家口市| 开鲁县| 抚松县| 溧阳市| 渑池县| 依兰县| 呼图壁县| 利川市| 高州市| 资中县| 曲麻莱县| 南和县| 吉隆县| 凤山县| 自治县| 石阡县| 澳门| 上饶县| 玉龙| 页游| 黄骅市| 潜山县| 泗水县| 宜川县| 县级市| 内乡县| 商洛市| 招远市| 福海县| 娱乐| 台前县| 永平县|