blog

Vue -Routerの概要

Vue-Routerのコアを簡単に分析 公式サイト Vue-plugin機構はroutesmap{path:rule}hash-change$router mixinsを解決します。...

Oct 1, 2020 · 5 min. read
シェア

Vue -Routerのコアを簡単に分析

プラグインメカニズム-$ルーターミックスイン

VueRouter.install = function(_Vue) {
 Vue = _Vue;
 Vue.mixin({u
 beforeCreate() {
 if (this.$options.router) { // ルートコンポーネントだけがルーター属性を持つ
 Vue.prototype.$router = this.$options.router;
 }
 }
 })
}
// install ==> use(CM)  
// mixin ==> ミックスイン:オブジェクトのマージ--- コンポーネントのインスタンスを作成するとき

routesオプションの解析

  1. イベントリスナー
  2. ルートマップの作成
  3. コンポーネントの作成

イベントをバインド

window.addEventListener('hashchange', this.onHashChange.bind(this), false)
window.addEventListener('load', this.onHashChange.bind(this), false)
onHashChange = () => { this.currentPath = window.location.hash.slice(1) || '/' }

ルートマップの作成

this.$options.routes.forEach(item => {
 this.routerMap[item.path] = item.component;
})

グローバルコンポーネント

// グローバル・コンポーネントを作成する
function renderComponent() {
 Vue.component('router-link', {
 props: { to: {type: String, required: true} },
 render = h => {
 return h('a', { attrs: {href: this.to}}, [this.$options.default])
 // return <a href={this.to}>{this.$slots.default}</a> jsx 
 }
 })
 Vue.component('router-view', {
 render = h => {
 const component = this.routerMap[this.currentPath].component;
 return h(component);
 }
 })
}

ソースコードセクション

// install  
import View from './components/view'
import Link from './components/link'
export let _Vue
export function install (Vue) {
 if (install.installed && _Vue === Vue) return
 install.installed = true
 _Vue = Vue
 const isDef = v => v !== undefined
 const registerInstance = (vm, callVal) => {
 let i = vm.$options._parentVnode
 if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
 i(vm, callVal)
 }
 }
 //  
 Vue.mixin({
 beforeCreate () {
 if (isDef(this.$options.router)) {
 this._routerRoot = this
 this._router = this.$options.router
 this._router.init(this)
 Vue.util.defineReactive(this, '_route', this._router.history.current)
 } else {
 this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
 }
 registerInstance(this, this)
 },
 destroyed () {
 registerInstance(this)
 }
 })
 // インターセプトを登録する
 Object.defineProperty(Vue.prototype, '$router', {
 get () { return this._routerRoot._router }
 })
 Object.defineProperty(Vue.prototype, '$route', {
 get () { return this._routerRoot._route }
 })
 // グローバルコンポーネントをマウントする
 Vue.component('RouterView', View) 
 Vue.component('RouterLink', Link)
 const strats = Vue.config.optionMergeStrategies
 // use the same hook merging strategy for route hooks
 // ルーターのフックを登録する ルートを入力する ルートを離脱する ルート更新など
 strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}
// history api
import { inBrowser } from './dom'
import { saveScrollPosition } from './scroll'
import { genStateKey, setStateKey, getStateKey } from './state-key'
import { extend } from './misc'
export const supportsPushState =
 inBrowser &&
 (function () {
 const ua = window.navigator.userAgent
 /* ブラウザサポート
 Chrome Safari Firefox Opera IE Android iOS
 + + + 7.1+ */
 if (
 (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
 ua.indexOf('Mobile Safari') !== -1 &&
 ua.indexOf('Chrome') === -1 &&
 ua.indexOf('Windows Phone') === -1
 ) {
 return false
 }
 return window.history && typeof window.history.pushState === 'function'
 })()
export function pushState (url?: string, replace?: boolean) {
 saveScrollPosition()
 // try...catch the pushState call to get around Safari
 // DOM Exception 18 where it limits to 100 pushState calls
 const history = window.history
 try {
 if (replace) {
 // preserve existing history state as it could be overriden by the user
 const stateCopy = extend({}, history.state)
 stateCopy.key = getStateKey()
 history.replaceState(stateCopy, '', url)
 } else {
 history.pushState({ key: setStateKey(genStateKey()) }, '', url)
 }
 } catch (e) {
 window.location[replace ? 'replace' : 'assign'](url)
 }
}
export function replaceState (url?: string) {
 pushState(url, true)
}
/* @RouterLink */
import { createRoute, isSameRoute, isIncludedRoute } from '../util/route'
import { extend } from '../util/misc'
import { normalizeLocation } from '../util/location'
import { warn } from '../util/warn'
// work around weird flow bug
const toTypes: Array<Function> = [String, Object]
const eventTypes: Array<Function> = [String, Array]
const noop = () => {}
export default {
 name: 'RouterLink',
 props: {
 to: {
 type: toTypes,
 required: true
 },
 tag: {
 type: String,
 default: 'a'
 },
 exact: Boolean,
 append: Boolean,
 replace: Boolean,
 activeClass: String,
 exactActiveClass: String,
 ariaCurrentValue: {
 type: String,
 default: 'page'
 },
 event: {
 type: eventTypes,
 default: 'click'
 }
 },
 render (h: Function) {
 // .... プロパティのマウントやイベントを省略し、classNameの起動判定などを残す。
 return h(this.tag, data, this.$slots.default)
 }
}
function guardEvent (e) { ... }
function findAnchor (children) {...}
/* RouterView */
import { warn } from '../util/warn'
import { extend } from '../util/misc'
export default {
 name: 'RouterView',
 functional: true,
 props: {
 name: {
 type: String,
 default: 'default'
 }
 },
 render (_, { props, children, parent, data }) {
 // used by devtools to display a router-view badge
 data.routerView = true
 // directly use parent context's createElement() function
 // so that components rendered by router-view can resolve named slots
 const h = parent.$createElement // hファンクションタイムのソースコード VDOMコア
 const name = props.name
 const route = parent.$route
 const cache = parent._routerViewCache || (parent._routerViewCache = {}) //  
 
 // ... 省略
 return h(component, data, children)
 }
}
function fillPropsinData (component, data, route, configProps) {...}
function resolveProps (route, config) {...}

Read next

プロジェクト・デモ playを学ぶシリーズ2 - vuetifyダッシュボード

ダッシュボードは、この時間はそのままレコードの単純なバージョンを行うために、数回行っている書き込みと再生シリーズ2、、、将来の引き出しは、その後、更新の機能を追加します。 Vuetifyは、Vueのためのセマンティックコンポーネントフレームワークであり、アプリケーションの構築を容易にするクリーン、セマンティック、再利用可能なコンポーネントを提供するように設計されています。 v-app-barコンポーネントは、アプリケーション全体のアクションや文字...

Sep 30, 2020 · 2 min read