簡単にvue-routerを分析して原理と2種類のモードを実現します。

10081 ワード

以前はVueで単一ページのアプリケーションを開発していましたが、ルートがどう変わってもブラウザのアドレスバーには必ず「〓」があります。

当時は自分のコードをチェックしていましたが、要請した住所帯の「菗」が見つかりませんでした。当時も不思議でしたが、ページのレンダリングやバックグラウンドへの要求に影響がなかったので、その時も気になりませんでした。最近vue-routerの実現原理を見て、ようやくこの謎が解けました。
vue-routerの2つの方式(ブラウザ環境下)
1.Hash(Hash History対応)
hash記号の本来の役割は、URLに加えてウェブページの位置を示すことである。
http://www.example.com/index.html#print
((zhi)记号自体とその后ろの文字をhashといいます。(つまり、私は以前どうして住所栏に「\25821;氥」というのがありますか?)window.location.hash属性で読むことができます。次のような特徴があります。
hashはURLに存在するが、HTTP要求には含まれない。ブラウザの動作を指導するために使用されています。サーバー側には全く役に立たないので、hashを変更してもページを再読み込みできません。
2.hashの変更のために傍受イベントを追加することができます。

window.addEventListener("hashchange", funcRef, false)
hashを変えるたびに、ブラウザのアクセス履歴に記録を追加します。
hashの以上の特徴を利用して、フロントエンドのルーティング「ビューを更新するが、ページを再要求しない」機能を実現することができます。
2.History(HTML 5 History対応)
ハイトーンインターフェースは、ブラウザ履歴スタックが提供するインターフェースであり、back()、forward()、go()などの方法により、ブラウザ履歴記録スタックの情報を読み取り、各種ジャンプ操作を行うことができます。
HTML 5から、History interfaceは2つの新しい方法を提供しました。pusState()、replacceState()はブラウザの歴史記録スタックを修正することができます。

window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
stateObject:ブラウザが新しい状態に遷移すると、このstateObjectパラメータのコピーtitleを携帯するpopStateイベントが起動されます。追加されたレコードのタイトルURL:追加されたレコードのURL
この2つの方法は共通の特徴があります。ブラウザの履歴スタックを変更した後、現在のURLが変更されましたが、ブラウザはページを更新しません。これは、1ページのアプリケーションのフロントエンドに「ビューを更新するが、ページを再要求しない」という基本を提供します。ブラウザの履歴は「スタック」とみなすことができます。スタックは後進先の構造であり、それを重ねた皿に想像できます。ユーザーは毎回新しいページを開いて、その上に新しい皿を追加して、「入スタック」と言います。ユーザーが「後退」ボタンを押すたびに一番上の皿を取ります。「出スタック」といいます。毎回ブラウザで表示されるのはもちろん一番上の皿の内容です。
vue-routerの役割
vue-routerの役割は、URLを変更することによって、ページを再要求しないで、ページビューを更新することです。簡単に言えば、アドレスバーの住所が変わったが、新しいページではなく、前のページの一部が修正されたということです。

export default new Router({
 // mode: 'history', //      
 routes: constantRouterMap
})
これはVueプロジェクトでよく見られているのですが、vue-routerを初期化するコードです。vue-routerを詳しく研究したことがありません。もう一つのモデルの属性があるかどうかは分かりません。その後、関連した文章を見て、mode属性はvue-routerを指定するためにどのモードを使うかが分かりました。modeの値が指定されていない場合は、hashモードを使用します。
ソース分析
まず、vue-routerの構造関数を見てください。

constructor (options: RouterOptions = {}) {
 this.app = null
 this.apps = []
 this.options = options
 this.beforeHooks = []
 this.resolveHooks = []
 this.afterHooks = []
 this.matcher = createMatcher(options.routes || [], this)

 let mode = options.mode || 'hash'
 this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
 if (this.fallback) {
 mode = 'hash'
 }
 if (!inBrowser) {
 mode = 'abstract'
 }
 this.mode = mode

 switch (mode) {
 case 'history':
 this.history = new HTML5History(this, options.base)
 break
 case 'hash':
 this.history = new HashHistory(this, options.base, this.fallback)
 break
 case 'abstract': //       
 this.history = new AbstractHistory(this, options.base) 
 break
 default:
 if (process.env.NODE_ENV !== 'production') {
  assert(false, `invalid mode: ${mode}`)
 }
 }
 }
まずmodeの値を取得します。modeの値がhistoryであってもブラウザがhistoryモードをサポートしていない場合は、強制的にmodeの値をhashに設定します。支持すればhistoryです。次に、modeの値によって、vue-routerはどのモードを使うかを選択します。

case 'history':
 this.history = new HTML5History(this, options.base)
 break
case 'hash':
 this.history = new HashHistory(this, options.base, this.fallback)
 break
このように二つのパターンがあります。vue-routerはどのモードを使うかを確認したら、initになります。まずrouterのinit方法を見てみます。src/index.jsの中です。

init (app: any /* Vue component instance */) {
// ....
 const history = this.history

 if (history instanceof HTML5History) {
 history.transitionTo(history.getCurrentLocation())
 } else if (history instanceof HashHistory) {
 const setupHashListener = () => {
 history.setupListeners()
 }
 history.transitionTo(
 history.getCurrentLocation(),
 setupHashListener,
 setupHashListener
 )
 }

 history.listen(route => {
 this.apps.forEach((app) => {
 app._route = route
 })
 })
 }
// ....
// VueRouter               history     
 push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
 this.history.push(location, onComplete, onAbort)
 }

 replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
 this.history.replace(location, onComplete, onAbort)
 }
}
HTML 5 Historyであれば、実行します。

history.transitionTo(history.getCurrentLocation())
Hashモードであれば、実行します。

const setupHashListener = () => {
 history.setupListeners()
 }
 history.transitionTo(
 history.getCurrentLocation(),
 setupHashListener,
 setupHashListener
 )
両方のモードがtranition To関数を実行することが分かる。次に二つのモードがそれぞれどのように行われているかを見ます。まず、Hashモードを見てください。HashHistory.push()私たちはHashHistoryのpsh()の方法を見に来ました。

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
 this.transitionTo(location, route => {
 pushHash(route.fullPath)
 onComplete && onComplete(route)
 }, onAbort)
}

function pushHash (path) {
 window.location.hash = path
}
trantionTo()方法は父の種類の中で定義されているのはルートの変化の基礎的な論理を処理するためのもので、push()方法の一番主要なのはwindowのhashに直接的に値を付けたのです。window.location.hash = route.fullPath hashの変更は、ブラウザのアクセス履歴に自動的に追加されます。
では、ビューの更新はどうやって実現されますか?父の種類Historyのtranition Toの方法の一節を見てみます。

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
 //    match       route   
 const route = this.router.match(location, this.current)
 this.confirmTransition(route, () => {
 this.updateRoute(route)
 ...
 })
}
updateRoute (route: Route) {
 this.cb && this.cb(route)
}
listen (cb: Function) {
 this.cb = cb
}
ルーティングが変化すると、Historyのthis.cbメソッドが呼び出され、this.cb方法は、History.listen(cb)によって設定されていることがわかる。VueRouter類の定義に戻り、init()メソッドで設定されていることが分かりました。

init (app: any /* Vue component instance */) {
 
 this.apps.push(app)

 history.listen(route => {
 this.apps.forEach((app) => {
 app._route = route
 })
 })
}
コードのアプリとは、Vueの例を指します。routeは、自身のコンポーネントで定義されている内蔵属性ではなく、Vue.use(Router)にvue-routerプラグインをロードするとき、Vue.mixin()方法により、グローバルに一つの混合を登録し、登録後に作成されたすべてのVueのインスタンスに影響を与え、ベフォークリアーフックにおいてVue.util.defineReactive()により応答式の_を定義した。route応答式属性とは、_route値が変わると、自動的にVueインスタンスのrenderメソッドを呼び出して、ビューを更新します。vm.render()は現在の_によるものです。routeのpath、nameなどの属性は、ルーティングに対応するコンポーネントをレンダリングするためにまとめられたもので、ルートからビューに変更する更新プロセスは以下の通りである。

this.$router.push(path)
 --> 
HashHistory.push() 
--> 
History.transitionTo() 
--> 
const route = this.router.match(location, this.current)       ,           route(      )
-->
History.updateRoute(route) 
 -->
 app._route=route (Vue   _route  )   _route     vue     , _route     ,      render( )
-- >
vm.render()     <router-view></router-view>  render
 -->
window.location.hash = route.fullpath (             path)
HashHistory.replace()HashHistory.pushを言い終えました。HashHistory.replaceと言います。

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
 this.transitionTo(location, route => {
 replaceHash(route.fullPath)
 onComplete && onComplete(route)
 }, onAbort)
}
 
function replaceHash (path) {
 const i = window.location.href.indexOf('#')
 window.location.replace(
 window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
 )
}
HashHistory.replaceは、push()の実現構造とほぼ似ていますが、違いはwindow.location.hashを直接値付けするのではなく、window.locations.replace方法を呼び出してルートを置換することです。このように、ブラウザのアクセス履歴のスタックトップに新しいルートを追加するのではなく、現在のルートを置き換えます。
アドレスバーを傍受する
上記のプロセスはコードの内部でルーティングの変更を行うものであり、例えばプロジェクトでよく見られるthis.$router.push(), などの方法であることがわかる。ブラウザのアドレスバーを新しいhash値に設定します。アドレスバーに直接URLを入力してルートを変更します。

dashboadrを削除して、articale/hot Spotに置いて、車に戻って、vueはどうやって処理しますか?

setupListeners () {
 window.addEventListener('hashchange', () => {
 if (!ensureSlash()) {
 return
 }
 this.transitionTo(getHash(), route => {
 replaceHash(route.fullPath)
 })
 })
}
この方法は、ブラウザイベントのhashchangeを傍受し、呼び出した関数はreplacceHashであり、つまりブラウザのアドレスバーに直接入力したルートは、コードがreplace()メソッドを呼び出すのに相当する。後のステップはもちろん、HashHistory.replace()と同じで、ページレンダリングを実現する。
HTML 5 History
HTML 5 Historyモードのvue-routerコード構造及び更新ビューの論理はhashモードと基本的に似ています。hashHistoryのステップと基本的に一致しています。HashHistoryのpushとreplace()だけがHTML 5 History.pustate()とHTML 5 History.replace()になりました。
HTML 5 HistoryにブラウザのアドレスバーのURLを修正するモニターを追加して直接構造関数で実行します。HTML 5 Historyのpopstateイベントを傍受します。

constructor (router: Router, base: ?string) {
 
 window.addEventListener('popstate', e => {
 const current = this.current
 this.transitionTo(getLocation(this.base), route => {
 if (expectScroll) {
 handleScroll(router, route, current, true)
 }
 })
 })
}
以上がevue-router hashモードとhistoryモードが異なるモードでの処理ロジックの分析です。
締め括りをつける
以上述べたのは小编が皆さんに绍介したvue-routerの原理と二つのパターンの分析です。