大フロントエンド学習--手書きVue Router


手書きVue Router
文章内容出力源:勾引教育大先端高給訓練キャンプ
Vue-Router
コードウェアハウスアドレス:https://gitee.com/jiailing/lagou-fed/tree/master/fed-e-task-03-01/code/06-my-vue-router
vue-routerコアコード:
App.vue
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Homerouter-link> |
      <router-link to="/ablout">Aboutrouter-link>
    div>
    <router-view/>
  div>
template>

router/index.js
//     
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  }
]

const router = new VueRouter({
  routes
})

export default router


main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

const vm = new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
console.log(vm)


一、動的ルーティング
router/index.js
const routes = [
  {
    path: '/',
    name: 'Index',
    component: Index
  },
  {
    path: '/detail/:id',
    name: 'Detail',
    //   props,  URL         
    props: true,
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/Detail.vue')
  }
]


view/Detail.vue
<template>
  <div>
      Detail  
    
              :{{ $route.params.id }}

    <br>

    
        props  : {{ id }}
  div>
template>

<script>
export default {
  name: 'Detail',
  //         props 
  props: ['id']
}
script>


二、ネストルート
router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '../components/Layout.vue'
import Login from '../views/Login.vue'
import Index from '../views/Index.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/login',
    name: 'login',
    component: Login
  },
  //     
  {
    path: '/',
    component: Layout,
    children: [
      {
        path: '',
        name: 'index',
        component: Index
      },
      {
        path: 'detail/:id',
        name: 'detail',
        props: true,
        component: () => import('@/views/Detail.vue')
      }
    ]
  }
]

const router = new VueRouter({
  routes
})

export default router


components/Layout.vue
<template>
  <div>
    <div>
      <img width='80px' src='@/assets/logo.png'>
    div>
    <div>
      <router-view>router-view>
    div>
    <div>
      Footer
    div>
  div>
template>

三、プログラミングナビゲーション
View/Index.vue
<template>
  <div>
    <router-link to="/">  router-link>
    <button @click="replace"> replace button>
    <button @click="goDetail"> Detail button>
  div>
template>

<script>
export default {
  name: 'Index',
  methods: {
    replace () {
      this.$router.replace('/login')
    },
    goDetail () {
      this.$router.push({ name: 'Detail', params: { id: 1 } })
    }
  }
}
script>


View/Detail.vue
<template>
  <div>
      Detail  

        : {{ id }}

    <button @click="go"> go(-2) button>
  div>
template>

<script>
export default {
  name: 'Detail',
  //         props 
  props: ['id'],
  methods: {
    go () {
      this.$router.go(-2)
    }
  }
}
script>


四、HashモードとHistoryモード
1.表現形式の違い
  • Hashモード:http://localhost/#/detail?id=1234
  • Historyモード:http://localhost/detail/1234

  • 2.原理の違い
  • Hashモードは、アンカーポイント、およびonHashChangeイベントに基づいている.
  • HistoryモードはHTML 5のHistory APIに基づく
  • History.PushState()IE 10以降は
  • をサポート
  • History.replaceState()


  • 3.Historyモードの使用
  • Historyサーバのサポートが必要
  • 単一ページアプリケーションでは、サービス側は存在しません.http://www.test.com/loginこのようなアドレスは、ページ
  • が見つからないことを返す.
  • は、サービス側で静的リソース以外に、単一ページアプリケーションのindexを返すべきである.html
  • Node.jsサーバ構成
    const path = require('path')
    //      history      
    const history = require('connect-history-api-fallback')
    //    express
    const express = require('express')
    
    const app = express()
    //   :     history       
    app.use(history())
    //           ,      ../web
    app.use(express.static(path.join(__dirname, '../web')))
    
    //      ,    3000
    app.listen(3000, () => {
      console.log('     ,  :3000')
    })
    
  • Nginxサーバ構成
    #   
    start nginx
    #   
    nginx -s reload
    #   
    nginx -s stop
    
  • nginx.conf
    http: {
    	server: {
    		location / {
    			root html;
    			index index.html index.htm;
    			#     ,        
    			try_files $uri $uri/ /index.html;
    		}
    	}
    }
    

    4.Hashモード
  • URLの中の#の後ろの内容をパスアドレス
  • とする.
  • hashchangeイベント
  • を傍受する
  • 現在のルーティングアドレスに従って対応するコンポーネントを見つけて
  • を再レンダリングする.
    5.Historyモード
  • はHistoryを通過した.PushState()メソッド変更アドレスバー(サーバに要求は送信されませんが、今回のURLは履歴に記録されます)
  • popstateイベントの傍受
  • 現在のルーティングアドレスに従って対応するコンポーネントを見つけて
  • を再レンダリングする.
    五、自分のvue-routerを実現する
    Vueのビルドバージョン
  • ランタイム版:templateテンプレートはサポートされておらず、パッケージが必要な場合は事前にコンパイル
  • render関数
    Vue.component('router-link', {
          props: {
            to: String
          },
          render (h) {
            return h('a', {
              attrs: {
                href: this.to
              },
              
            }, [this.$slots.default])
          },
          // template: ''
        })
    
  • を使用
  • 完全版:実行時とコンパイラを含み、体積は実行時バージョンより10 k程度大きく、プログラム実行時にテンプレートをrender関数に変換する
  • プロジェクトのルートディレクトリにvueというファイルを追加します.config.js
    module.exports = {
      //      Vue(     )
      runtimeCompiler: true
    }
    


  • 最終コード:
    Vuerouter/index.js
    let _Vue = null
    
    export default class VueRouter {
      static install (Vue) {
        // 1.              
        if (VueRouter.install.installed) return
        VueRouter.install.installed = true
        // 2.  Vue           
        _Vue = Vue
        // 3.    Vue       router     Vue   
        //   
        _Vue.mixin({
          beforeCreate () {
            if (this.$options.router) {
              _Vue.prototype.$router = this.$options.router
              this.$options.router.init()
            }
          }
        })
      }
    
      constructor (options) {
        this.options = options
        this.routeMap = {}
        // _Vue.observable       
        this.data = _Vue.observable({
          current: '/'
        })
      }
    
      init () {
        this.createRoutMap()
        this.initComponents(_Vue)
        this.initEvent()
      }
    
      createRoutMap () {
        //          ,              ,   routeMap 
        this.options.routes.forEach(route => {
          this.routeMap[route.path] = route.component
        })
      }
    
      initComponents (Vue) {
        Vue.component('router-link', {
          props: {
            to: String
          },
          render (h) {
            return h('a', {
              attrs: {
                href: this.to
              },
              //   
              on: {
                click: this.clickHandler
              }
            }, [this.$slots.default])
          },
          methods: {
            clickHandler (e) {
              history.pushState({}, '', this.to)
              this.$router.data.current = this.to
              e.preventDefault()
            }
          }
          // template: ''
        })
        const self = this
        Vue.component('router-view', {
          render (h) {
            const component = self.routeMap[self.data.current]
            return h(component)
          }
        })
      }
    
      initEvent () {
        window.addEventListener('popstate', () => {
          this.data.current = window.location.pathname
        })
      }
    }
    
    

    Router/index.js
    // ....
    
    import VueRouter from '../vuerouter'
    
    // ....