少妇无码太爽了在线播放_久久久综合香蕉尹人综合网_日日碰狠狠添天天爽五月婷_国产欧美精品一区二区三区四区

人參的功(gong)效

vue路由的傳參方式,詳解Vue3中 router 帶來了哪些變化?



作者: Leiy

轉發鏈接:
//segmentfault.com/a/1190000022582928

前言

Vue Router 是 Vue.js 官(guan)方的(de)路(lu)由管理器。它和 Vue.js 的(de)核心深(shen)度(du)集成,讓(rang)構建單頁面應用變(bian)得易如(ru)反掌。

本(ben)文(wen)(wen)基于的源(yuan)碼版本(ben)是 vue-next-router alpha.10,為(wei)了(le)與(yu) Vue 2.0 中的 Vue Router 區分,下文(wen)(wen)將 vue-router v3.1.6 稱為(wei) vue2-router。

本文旨(zhi)在(zai)幫助更多人對新(xin)版本 Router 有一個初(chu)步的(de)了解,如果文中有誤導大家的(de)地方,歡迎留言指正。

重大改進

此次 Vue 的(de)重大改進隨之而(er)來(lai)帶來(lai)了 Vue Router 的(de)一系列改進,現(xian)階段(alpha.10)相比 vue2-router 的(de)主要變化,總(zong)結如下:

1. 構建選項 mode

由(you)原來(lai)的(de) mode: "history" 更改為 history: createWebHistory()。(設置其他 mode 也是同(tong)樣的(de)方式)。

// vue2-routerconst router = new VueRouter({  mode: 'history',
  ...
})// vue-next-routerimport { createRouter, createWebHistory } from 'vue-next-router'const router = createRouter({  history: createWebHistory(),
  ...
})

2. 構建選項 base

傳給 createWebHistory()(和其他模(mo)式(shi)) 的第一個參(can)數作為 base。

//vue2-routerconst router = new VueRouter({  base: __dirname,
})// vue-next-routerimport { createRouter, createWebHistory } from 'vue-next-router'const router = createRouter({
  history: createWebHistory('/'),
  ...
})

4. 捕獲所有路由 ( /* ) 時,現在必須使用帶有自定義正則表達式的參數進行定義:/:catchAll(.*)。

// vue2-routerconst router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '/user/:a*' },
  ],
})// vue-next-routerconst router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/user/:a:catchAll(.*)', component: component },
  ],
})

當路由(you)為(wei) /user/a/b 時,捕獲到的 params 為(wei) {"a": "a", "catchAll": "/b"}。

5. router.match 與 router.resolve 合并在一起為router.resolve,但簽名略有不同。

// vue2-router...resolve ( to: RawLocation, current?: Route, append?: boolean) {  ...  return {    location,    route,    href,    normalizedTo: location,    resolved: route  }}// vue-next-routerfunction resolve(    rawLocation: Readonly<RouteLocationRaw>,    currentLocation?: Readonly<RouteLocationNormalizedLoaded>  ): RouteLocation & { href: string } {  ...  let matchedRoute = matcher.resolve(matcherLocation, currentLocation)  ...  return {    fullPath,    hash,    query: normalizeQuery(rawLocation.query),    ...matchedRoute,    redirectedFrom: undefined,    href: routerHistory.base + fullPath,  }}

6. 刪除 router.getMatchedComponents,可以從router.currentRoute.value.matched 中獲取。


router.getMatchedComponents 返回目標位置或(huo)是(shi)當前路由匹配的(de)組(zu)件數組(zu) (是(shi)數組(zu)的(de)定義/構造類,不(bu)是(shi)實例(li))。通(tong)常在(zai)服務端(duan)渲染的(de)數據(ju)預加(jia)載時使用。

[{  aliasOf: undefined  beforeEnter: undefined  children: []  components: {default: {…}, other: {…}}  instances: {default: null, other: Proxy}  leaveGuards: []  meta: {}  name: undefined  path: "/"  props: ? (to)  updateGuards: []}]

7. 如果使用 <transition>,則可能需要等待 router 準備就緒才能掛載應用程序。

app.use(router)// Note: on Server Side, you need to manually push the initial locationrouter.isReady().then(() => app.mount('#app'))

一(yi)般情況下,正常掛載也(ye)是可以(yi)使用 <transition> 的,但是現在導航(hang)都是異步的,如果在路由初始(shi)化時有路由守衛,則在 resolve 之前會(hui)出現一(yi)個(ge)初始(shi)渲(xuan)染的過渡,就像給 <transiton> 提(ti)供一(yi)個(ge) appear 一(yi)樣(yang)。

8. 在服務端渲染 (SSR) 中,需要使用一個三目運算符手動傳遞合適的 mode。

let history = isServer ? createMemoryHistory() : createWebHistory()let router = createRouter({ routes, history })// on server onlyrouter.push(req.url) // request urlrouter.isReady().then(() => {  // resolve the request})

9. push 或者 resolve 一個不存在的命名路由時,將會引發錯誤,而不是導航到根路由 "/" 并且不顯示任何內容。

在 vue2-router 中,當 push 一個(ge)不(bu)存在的(de)命名路由(you)時,路由(you)會導航到(dao)根路由(you)"/" 下,并且不(bu)會渲(xuan)染任何內(nei)容。

const router = new VueRouter({
  mode: 'history',
  routes: [{ path: '/', name: 'foo', component: Foo }]
}this.$router.push({name: 'baz'})

瀏覽器控制臺(tai)只會提(ti)示(shi)如下警告(gao),并且 url 會跳轉到根(gen)路由 / 下。


在 vue-next-router 中,同樣做法會(hui)引發錯誤(wu)。

const router = createRouter({
  history: routerHistory(),
  routes: [{ path: '/', name: 'foo', component: Foo }]
})
...import { useRouter } from 'vue-next-router'...const router = userRouter()
router.push({name: 'baz'})) // 這段代碼會報錯

Active-RFCS

以下內容的(de)改進來(lai)自 active-rfcs(active 就是已(yi)經討論通(tong)過并(bing)且正在實(shi)施的(de)特性)。

  • 0021-router-link-scoped-slot

  • 0022-router-merge-meta-routelocation

  • 0028-router-active-link

  • 0029-router-dynamic-routing

  • 0033-router-navigation-failures - 本文略

router-link-scoped-slot

這個 rfc 主(zhu)要提(ti)議(yi)及(ji)改(gai)進如下:

  • 刪除(chu) tag prop - 使(shi)用作用域插槽代(dai)替

  • 刪除 event prop - 使用作用域插槽代替

  • 增加 scoped-slot API

  • 停止自動將(jiang) click 事件分配給內部錨(mao)點(dian)

  • 添加 custom prop 以完全支持自定義的 router-link 渲染

在 vue2-router 中,想要將 <roter-link&gt; 渲染成某種標簽,例(li)如(ru) <button>,需要這么做:

<router-link to="/" tag="button">按鈕</router-link>!-- 渲染結果 --><button>按鈕</button>

根據此次(ci) rfc,以后可能需(xu)要這樣做:

<router-link to="/" custom v-slot="{ navigate, isActive, isExactActive }">  <button role="link" @click="navigate" :class="{ active: isActive, 'exact-active': isExactActive }">
    按鈕  </button><router-link>!-- 渲染結果 --><button role="link">按鈕</button>

更(geng)多(duo)詳(xiang)細的介(jie)紹請看這個 rfc 。

router-active-link

這個(ge) rfc 改(gai)進(jin)的緣由是(shi) gayhub 上名為 zamakkat 的大哥提出(chu)來(lai)的,他的 issues 主要內(nei)容是(shi),有一個(ge)嵌套(tao)組(zu)件,像這樣:

Foo (links to /pages/foo)
|-- Bar (links to /pages/foo/bar)

需求:需要突出顯示當前選中的頁面(并且只能突出顯示一項)。

  • 如果用(yong)戶打開 /pages/foo,則僅 Foo 高亮顯示。

  • 如果用戶打(da)開 /pages/foo/bar,則(ze)僅(jin) Bar 應高亮顯示。

但是,Bar 頁面也(ye)有(you)分(fen)頁,選擇第二頁時,會導航到 /pages/foo/bar?page=2。vue2-router 默(mo)認情(qing)況下(xia),路由匹配規則是「包含匹配」。也(ye)就(jiu)是說,當(dang)前的路徑是/pages 開頭的,那么 <router-link to="/pages/*"> 都會被(bei)設置 CSS 類名(ming)。

在(zai)這個示(shi)例(li)中,如果使用「精(jing)確(que)匹配(pei)(pei)模(mo)式(shi)」(exact: true),則精(jing)確(que)匹配(pei)(pei)將匹配(pei)(pei) /pages/foo/bar,不(bu)會(hui)匹配(pei)(pei) /pages/foo/bar?page=2 因為(wei)它在(zai)比較(jiao)中包括查(cha)詢(xun)參(can)數 ?page=2,所以當選擇第二頁面時,Bar 就不(bu)高亮顯(xian)示(shi)了(le)。

所以(yi)無(wu)論是「精確匹配」還是「包含匹配」都不能滿(man)足此(ci)需求。

為了解決上述問(wen)題和其他邊界情況,此次改進使(shi)得 router-link-active 應用方(fang)式更(geng)嚴(yan)謹(jin),處(chu)理(li)此問(wen)題的核心(xin):

// 確認路由 isActive 的行為function includesParams(
  outer: RouteLocation['params'],
  inner: RouteLocation['params']): boolean {  for (let key in inner) {    let innerValue = inner[key]    let outerValue = outer[key]    if (typeof innerValue === 'string') {      if (innerValue !== outerValue) return false
    } else {      if (
        !Array.isArray(outerValue) ||
        outerValue.length !== innerValue.length ||
        innerValue.some((value, i) => value !== outerValue[i])
      )        return false
    }
  }  return true}

詳情(qing)請(qing)參見這個 rfc。

router-merge-meta-routelocation

在 vue2-router中,在處理嵌套路由時,meta 僅包含匹配位置的 route meta信息。 看個栗子:

{  path: '/parent',  meta: { nested: true },  children: [    { path: 'foo', meta: { nested: true } },    { path: 'bar' }  ]}

在導航到 /parent/bar 時,只會顯(xian)示當(dang)前路由對(dui)應的 meta 信(xin)(xin)息為 {},不會顯(xian)示父級的 meta 信(xin)(xin)息。

meta: {}

所(suo)以在(zai)這種情況下,需要通過 to.matched.some() 檢查 meta 字段是否(fou)存在(zai),而(er)進行下一步邏輯。

router.beforeEach((to, from, next) => {  if (to.matched.some(record => record.meta.nested))
    next('/login')  else next()
})

因此為了(le)避免使用額(e)外(wai)的 to.matched.some, 這個 rfc 提議,將父(fu)子路(lu)由中的 meta 進行第一(yi)層合并(同(tong) Object.assing())。如果再遇到上述嵌套路(lu)由時,將可以直接通過(guo) to.meta 獲取信息。

router.beforeEach((to, from, next) => {  if (to.meta.nested) next('/login')  else next()
})

更多(duo)詳細介(jie)紹請看這個 rfc。

router-dynamic-routing

這(zhe)個 rfc 的(de)主要內容是(shi),允許給 Router 添加和刪除(chu)(單個)路(lu)由規則。

  • router.addRoute(route: RouteRecord) - 添加路由規(gui)則

  • router.removeRoute(name: string | symbol) - 刪除路由規則(ze)

  • router.hasRoute(name: string | symbol): boolean - 檢查路(lu)由(you)是否存在

  • router.getRoutes(): RouteRecord[] - 獲取當前路由規則(ze)的(de)列表

相(xiang)比(bi) vue2-router 刪除(chu)了動(dong)態(tai)添加多個路(lu)由(you)規(gui)則的(de) router.addRoutes API。

在 Vue 2.0 中,給路由(you)動(dong)態添(tian)加多個路由(you)規則時,需(xu)要這么做:

router.addRoutes(
 [
   { path: '/d', component: Home },
   { path: '/b', component: Home }
 ]
)

而在 Vue 3.0 中(zhong),需要(yao)使用 router.addRoute() 單個添加記錄,并且(qie)還可(ke)以使用更豐富的 API:

router.addRoute({
 path: '/new-route',
 name: 'NewRoute',
 component: NewRoute
})  
// 給現有路由添加子路由router.addRoute('ParentRoute', {
 path: 'new-route',
 name: 'NewRoute',
 component: NewRoute
})// 根據路由名稱刪除路由router.removeRoute('NewRoute')  
// 獲得路由的所有記錄const routeRecords \= router.getRoutes()

關于 RfCS 上提出(chu)的(de)改進,這(zhe)里就介紹這(zhe)么(me)多,想了解更多的(de)話,請移步(bu)到(dao) active-rfcs。

走進源碼

相比 vue2-router 的(de) ES6-class 的(de)寫(xie)法 vue-next-router 的(de) function-to-function 的(de)編寫(xie)更易讀也(ye)更容易維護。

Router 的 install

暴(bao)露的 Vue 組件解析入(ru)口相(xiang)對(dui)來(lai)說(shuo)更清(qing)晰,開發插件時定義(yi)的 install 也(ye)簡化(hua)了(le)許多。

我們先(xian)看下(xia) vue2-router 源(yuan)碼(ma)中 install 方法的定義:

import View from './components/view'import Link from './components/link'export let _Vue
export function install (Vue) {  // 當 install 方法被同一個插件多次調用,插件將只會被安裝一次。  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)
    }
  }  // 將 router 全局注冊混入,影響注冊之后所有創建的每個 Vue 實例
  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
      }      // 注冊實例,將 this 傳入
      registerInstance(this, this)
    },
    destroyed () {
      registerInstance(this)
    }
  })  // 將 $router 綁定的 vue 原型對象上
  Object.defineProperty(Vue.prototype, '$router', {    get () { return this._routerRoot._router }
  })  // 將 $route 手動綁定到 vue 原型對象上
  Object.defineProperty(Vue.prototype, '$route', {    get () { return this._routerRoot._route }
  })  // 注冊全局組件 RouterView、RouterLink
  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
}

我們可以看到(dao),在 2.0 中(zhong),Router 提供的(de) install() 方法(fa)中(zhong)更觸碰底層,需要(yao)用到(dao)選(xuan)項的(de)私有方法(fa) _parentVnode(),還會(hui)用的(de) Vue.mixin() 進(jin)行全(quan)局(ju)混入,之后會(hui)手(shou)動將 $router、$route 綁定到(dao) Vue 的(de)原型(xing)對(dui)象上。

VueRouter.install = install
VueRouter.version = '__VERSION__'// 以 src 方法導入if (inBrowser && window.Vue) {  window.Vue.use(VueRouter)
}

做了這么多事情之后,然后會在定(ding)義(yi) VueRouter 類的文件中,將 install() 方(fang)法(fa)綁(bang)定(ding)到(dao) VueRouter 的靜態屬(shu)性 install 上,以符(fu)合插件的標準。

安(an)裝(zhuang) Vue.js 插件(jian)。如果插件(jian)是一(yi)個(ge)(ge)對象,必須提供 install 方法。如果插件(jian)是一(yi)個(ge)(ge)函數,它會被作為 install 方法。install 方法調(diao)用時(shi),會將 Vue 作為參數傳入(ru)。

我們可(ke)以看(kan)到(dao),在 2.0 中開(kai)發一個(ge)插件(jian)需要做的事情很(hen)多(duo),install 要處(chu)理很(hen)多(duo)事情,這對不了解 Vue 的童鞋,會變得很(hen)困(kun)難。

說了這么(me)多,那么(me) vue-next-router 中(zhong)暴露的 install 是什(shen)么(me)樣(yang)的呢?applyRouterPlugin() 方法就是處理 install() 全部(bu)邏輯的地方,請看(kan)源碼:

import { App, ComputedRef, reactive, computed } from 'vue'import { Router } from './router'import { RouterLink } from './RouterLink'import { RouterView } from './RouterView'export function applyRouterPlugin(app: App, router: Router) {  // 全局注冊組件 RouterLink、RouterView
  app.component('RouterLink', RouterLink)
  app.component('RouterView', RouterView)  //省略部分代碼  // 注入 Router 實例,源碼其他地方會用到
  app.provide(routerKey, router)
  app.provide(routeLocationKey, reactive(reactiveRoute))
}

基于 3.0 使用(yong) composition API 時,沒有 this 也沒有混(hun)入,插件將充分利用(yong) provide 和(he) inject 對外暴露一(yi)個(ge)組合函(han)數即可,當然,沒了 this 之后也有不好(hao)的地方,看這(zhe)里(li)。

provide 和(he) inject 這(zhe)對(dui)選項需要一起使用(yong),以允許一個(ge)祖先組(zu)件向(xiang)其所有(you)子孫(sun)后代注入(ru)一個(ge)依賴,不論組(zu)件層次有(you)多深,并在其上(shang)下游關系成立的時間里始終(zhong)生(sheng)效。

再來看下 vue-next-router 中 install() 是什么樣的:

export function createRouter(options: RouterOptions): Router {  // 省略大部分代碼  const router: Router = {
    currentRoute,
    addRoute,
    removeRoute,
    hasRoute,    history: routerHistory,
    ...    // install
    install(app: App) {
      applyRouterPlugin(app, this)
    },
  }  return router
}

很簡單,在 vue-next-router 提供的 install() 方法中(zhong)調(diao)用applyRouterPlugin 將 Vue 和 Router 作為參數(shu)傳(chuan)入。

最后(hou)在應用(yong)程序中使用(yong) Router 時,只需要導入(ru) createRouter 然后(hou)顯示調用(yong) use() 方法(fa),傳入(ru) Vue,就可以在程序中正常使用(yong)了。

import { createRouter, createWebHistory } from 'vue-next-router'const router = createRouter({  history: createWebHistory(),  strict: true,  routes: [
    { path: '/home', redirect: '/' }
})const app = createApp(App)
app.use(router)

沒有全局 $router、$route

我們知(zhi)道在(zai)(zai) vue2-router 中(zhong),通過在(zai)(zai) Vue 根實例的(de) router 配(pei)置傳(chuan)入 router 實例,下面這些屬性成(cheng)員會被注(zhu)入到每個(ge)子組(zu)件。

  • this.$router - router 實例。

  • this.$route - 當前激活的路由信(xin)息對象。

但是(shi) 3.0 中(zhong),沒有 this,也就不存在在 this.$router | $route 這樣(yang)的(de)屬性,那么(me)在 3.0 中(zhong)應該如何(he)使(shi)用這些屬性呢(ni)?

我(wo)們首先(xian)看下(xia)源碼暴露的(de) api 的(de)地方(fang):

// useApi.tsimport { inject } from 'vue'import { routerKey, routeLocationKey } from './injectionSymbols'import { Router } from './router'import { RouteLocationNormalizedLoaded } from './types'// 導出 useRouterexport function useRouter(): Router {  // 注入 router Router (key 與 上文的 provide 對應)  return inject(routerKey)!
}// 導入 useRouteexport function useRoute(): RouteLocationNormalizedLoaded {  // 注入 路由對象信息 (key 與 上文的 provide 對應)  return inject(routeLocationKey)!
}

源(yuan)碼中,useRouter 、 useRoute 通過 inject 注入對象實(shi)例,并以(yi)單個函數的方式暴露出(chu)去。

在應(ying)用(yong)程序中(zhong)只需要通過(guo)命名導入的方式導入即可使(shi)用(yong)。

import { useRoute, useRouter } from 'vue-next-router'...
setup() {  const route = useRoute()  const router = useRouter()
  ...  // router -> this.$router  // route > this.$route
  router.push('/foo')  console.log(route) // 路由對象信息}

除了(le)可以(yi)(yi)命名(ming)導入 useRouter 、 useRoute 之外,還可暴露出很多函(han)數,以(yi)(yi)更好的支持(chi) tree-shaking(期待新(xin)版本(ben)的發布(bu)吧)。

NavigationFailureTypeRouterLink
RouterView
createMemoryHistory
createRouter
createWebHashHistory
createWebHistoryonBeforeRouteLeaveonBeforeRouteUpdate
parseQuery
stringifyQuery
useLink
useRoute
useRouter
...

最后

我(wo)想(xiang),就(jiu)介(jie)紹這么(me)多(duo)吧,上文介(jie)紹到的只(zhi)是改進(jin)的一部分,感覺還有很(hen)多(duo)很(hen)多(duo)東西需要我(wo)們(men)去了解和(he)掌握,新版本(ben)給(gei)我(wo)們(men)帶(dai)來了更靈活的編程,讓我(wo)們(men)共同期待 vue 3.0 到到來吧。

推薦Vue學習資料文章:

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

《》

作者: Leiy

轉發鏈接:
//segmentfault.com/a/1190000022582928

聯系我們

聯系我們

在線咨詢:

郵件:@QQ.COM

工作時(shi)間(jian):周(zhou)一至周(zhou)五(wu),8:30-21:30,節假日不休(xiu)

關注微信
關注微信
返(fan)回頂部(bu)