предисловие
В предыдущей статье -Давайте поговорим о «яме», возникшей при реализации кэширования компонентов маршрутизации Vue.Проблема кэширования компонентов VUE, встречающаяся в проекте, возникающая в проекте, была решена, и проект был надлежащим образом оптимизирован и улучшен, но исходный код Vue-маршрутизатора не был исследован. Эта статья продолжает последнюю тему и глубоко анализирует исходный код Vue - Реализация навигационных ограждений, динамических сопоставлений параметров, эффектов переходов и асинхронных компонентов.
Исходный код, проанализированный в этой статье,vue-router@3.1.3,vue@2.6.11
Тысячи высоких зданий поднимаются из земли
обратитесь на официальный сайтначинаяОсновная идея vue-router состоит в том, чтобы сгенерировать экземпляр VueRouter в соответствии с записью маршрутизации и передать его в атрибут маршрутизатора экземпляра приложения Vue, а также использовать router-view для монтирования компонента маршрутизации, соответствующего маршруту, в определенную позицию. на странице.
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
const app = new Vue({
router
}).$mount('#app')
руководство
Философия дизайна vue-router здесь не такая, как у react-router V4.Первый основан на конфигурации маршрутизации для унифицированной настройки маршрутизации, а второй представляет собой концепцию маршрутизации как компонента (унифицированная конфигурация маршрутизации не требуется, но он инкапсулирован в конфигурацию маршрутизации сам по себе)
Основные особенности
Выше перечислены основные функции, предоставляемые vue-router. Полные инструкции по использованию см.официальная документация, далее будет постепенно анализироваться, как вышеуказанные основные функции реализованы в исходном коде vue-router.
Прочтите предварительные условия для исходного кода
Структура исходного каталога
Структура исходного кода соответствует четким характеристикам серии vue.Он в основном разделен на компоненты link и view, историю обслуживания маршрутов, метод регистрации плагинов vue install.js и файл экспорта модуля index.js.
Основная концепция — маршрутизатор экземпляра маршрутизации
Маршрутизатор экземпляра маршрутизации — это объект экземпляра, созданный путем передачи конфигурации, такой как записи маршрутизации, при использовании vue-router. Основное внимание уделяется реализации его класса VueRouter.
Реализация метода init
Метод init здесь больше всего связан с глобальным миксином, зарегистрированным в install.js.Это метод маршрутизации инициализации, выполняемый компонентом vue при его создании.Его необходимо проанализировать.
init (app: any /* Vue component instance */) {
this.apps.push(app)
app.$once('hook:destroyed', () => {
const index = this.apps.indexOf(app)
if (index > -1) this.apps.splice(index, 1)
if (this.app === app) this.app = this.apps[0] || null
})
if (this.app) {
return
}
this.app = app
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
})
})
}
Исходный код:L83
Приложение здесь является экземпляром компонента vue черезapp.$once('hook:destroyed', () => {}
Декларативно зарегистрируйте обработчик жизненного цикла уничтожения компонента, чтобы убедиться, что экземпляр приложения-компонента удаляется из соответствующего компонента при уничтожении соответствующего компонента.router.apps
Удалять.
Убедитесь, что маршрут инициализируется только один раз: поскольку init вызывается глобально зарегистрированным миксином, логика определения наличия здесь this.app гарантирует, что инициализация маршрутизации выполняется только в корневом компоненте.<App />
Инициализировать один раз,this.app
Последнее сохранение в соответствии с экземпляром компонента.
Инициировать изменение маршрута и начать мониторинг маршрута:использовать history.transitionTo
Чтобы инициировать изменения маршрутизации в режиме подмаршрутизации, используйтеhistory.listen
Прислушивайтесь к изменениям маршрута, чтобы обновить экземпляр корневого компонента.app._route
текущий маршрут прыжка.
Основная концепция — сопоставление маршрутов
Сопоставитель маршрутов — это объект, сгенерированный create-matcher, который внутренне преобразует записи маршрутизации, переданные в класс VueRouter, и предоставляет внешние методы сопоставления маршрутизации в соответствии с местоположением — match и зарегистрированные методы маршрутизации — addRoutes.
- matchМетод: сопоставление маршрута объекта маршрута с местоположением в соответствии с внутренней картой маршрутов.
- addRoutesМетод: добавить запись маршрута на карту маршрута экземпляра сопоставителя.
генерировать сопоставитель
// src/index.js
constructor (options: RouterOptions = {}) {
...
this.matcher = createMatcher(options.routes || [], this)
...
}
Исходный код:L42
options.routes
выполнятьnew VueRoute
Действия — это входящие записи маршрутизации
внутри createMatcher
createMatcher происходит отimport { createMatcher } from './create-matcher'
Внутреннее выполнение таких операций, как преобразование адреса маршрутизации в объект маршрутизации, сопоставление записей маршрутизации, обработка параметров маршрутизации и т. д.
// src/create-matcher.js
export function createMatcher (
routes: Array<RouteConfig>,
router: VueRouter
): Matcher {
...
function addRoutes (routes) {
createRouteMap(routes, pathList, pathMap, nameMap)
}
function match (
raw: RawLocation,
currentRoute?: Route,
redirectedFrom?: Location
): Route {
...
}
function _createRoute (
record: ?RouteRecord,
location: Location,
redirectedFrom?: Location
): Route {
...
}
return {
match,
addRoutes
}
}
Исходный код:L16
createRoute: преобразовать входящую запись маршрута извне в единый объект маршрута, передать$route для экземпляра компонентавозвращается сюда псевдоним: обрабатывать псевдонимы маршрутизации nameMap: обрабатывает именованные маршрутыРазбор параметров маршрутизации: Анализ маршрута location.params, параметров запроса, хэша и т. д.,Вот откуда берется динамическое сопоставление маршрутов
Динамический маршрут Соответствующие и вложенные маршруты
Динамическое сопоставление маршрутов
Динамическое сопоставление маршрутов означает, что в пути может быть задано несколько параметров, и параметры будут установлены на$route.params
на, например:
модель | путь совпадения | $route.params |
---|---|---|
/user/:username | /user/evan | { username: 'evan' } |
/user/:username/post/:post_id | /user/evan/post/123 | { username: 'evan', post_id: '123' } |
Ссылаться на:Пример официального сайта
Вложенные маршруты
Вложенная маршрутизация означает, что маршрутизация может иметь вложенные отношения, такие как компоненты, и запись маршрута может проходить черезchildren
Вложенность атрибутов представляет собой массив из нескольких дочерних записей маршрута, например:
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
Ссылаться на:Пример официального сайта
Пока в проекте используется vue-router, почти неизбежно использование динамического сопоставления маршрутов и вложенной маршрутизации.Видно, насколько важны две функции в vue-router.Изучая его исходный код, эти две функции должны Далее будет рассмотрено, как вышеуказанные функции реализованы в vue-router.
Основные идеи реализации
Для достижения динамического сопоставления маршрута главное — сопоставить атрибут пути записи маршрута с параметрами фактического пути маршрута, а для реализации вложенной маршрутизации необходимо разобрать запись маршрута по вложенным правилам, оба из которых находятся в create-route-Map, и идея реализации такова:
Основной код в create-route-map выглядит следующим образом:
export function createRouteMap (
routes: Array<RouteConfig>,
oldPathList?: Array<string>,
oldPathMap?: Dictionary<RouteRecord>,
oldNameMap?: Dictionary<RouteRecord>
): {
pathList: Array<string>,
pathMap: Dictionary<RouteRecord>,
nameMap: Dictionary<RouteRecord>
} {
...
routes.forEach(route => {
addRouteRecord(pathList, pathMap, nameMap, route)
})
...
/**
* TODO:
* 处理路由的优先级循序:将路由记录中的通配符*表示的路由按循序移动到路由记录末尾
* 采用的哪种排序算法?
*/
for (let i = 0, l = pathList.length; i < l; i++) {
if (pathList[i] === '*') {
pathList.push(pathList.splice(i, 1)[0])
l--
i--
}
}
...
/**
* TODO:
* 路由记录,将路由所有的路由记录映射到pathMap、nameMap中,pathMap:按路径映射,nameMap:按名称映射,pathList所有路由path组成的数组
* 处理嵌套路由:递归调用此方法,parent表示父级路由
* 处理路由别名:把路径别名看成是指向同一个组件的路由记录,由此方法处理一遍这个别名组成的路由
* 处理路由名称:若存在路由名称,则将该路由映射到nameMap中存储
*/
function addRouteRecord (
pathList: Array<string>,
pathMap: Dictionary<RouteRecord>,
nameMap: Dictionary<RouteRecord>,
route: RouteConfig,
parent?: RouteRecord,
matchAs?: string
) {
...
}
...
return {
pathList,
pathMap,
nameMap
}
}
Исходный код:L7
createRouteMap
Метод в основном состоит в том, чтобы пройти маршруты конфигурации маршрута, вызватьaddRouteRecord
способ обработки маршрута, после обработки маршрута получитьpathList
pathMap
nameMap
, и скомпонуйте его в объект и верните его.
Реализация динамического сопоставления маршрутов
существуетaddRouteRecord
Метод обрабатывает маршрутизацию в реализацииroute.path
Преобразование в регулярное выражение с использованием пути к регулярному выражению,record
хранится вpahtMap
nameMap
Значение на карте.
const record: RouteRecord = {
...
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
...
}
...
/**
* TODO:
* 调用path-to-regexp生成路由匹配用的正则
*/
function compileRouteRegex (
path: string,
pathToRegexpOptions: PathToRegexpOptions
): RouteRegExp {
const regex = Regexp(path, [], pathToRegexpOptions)
if (process.env.NODE_ENV !== 'production') {
const keys: any = Object.create(null)
regex.keys.forEach(key => {
warn(
!keys[key.name],
`Duplicate param keys in route with path: "${path}"`
)
keys[key.name] = true
})
}
return regex
}
Исходный код:L178
Затем в методе сопоставления, предоставленном create-matcher, в соответствии сroute.name
,route.path
для сопоставления маршрутов,При сопоставлении регулярное выражение, полученное выше, вызывается обратно для сопоставления маршрутов и разбора параметров., чтобы получить маршрут и динамические параметры, соответствующие пути или имени маршрута.
Реализация вложенной маршрутизации
addRouteRecord
Исходный код метода для реализации вложенной части маршрутизации выглядит следующим образом:
export function createRouteMap (
routes: Array<RouteConfig>,
oldPathList?: Array<string>,
oldPathMap?: Dictionary<RouteRecord>,
oldNameMap?: Dictionary<RouteRecord>
): {
pathList: Array<string>,
pathMap: Dictionary<RouteRecord>,
nameMap: Dictionary<RouteRecord>
} {
...
if (route.children) {
route.children.forEach(child => {
const childMatchAs = matchAs
? cleanPath(`${matchAs}/${child.path}`)
: undefined
addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)
})
}
...
}
Исходный код:L102
Дочерние записи маршрутизации представляют собой вложенные записи маршрутизации под текущую маршрутизацию.Когда она существует, маршрутизация обрабатывается рекурсивно, при обработке дочерней маршрутизации в нее будет вставлен полный путь маршрутизации.pathMap
nameMap
.
Таким образом, независимо от того, является ли маршрут вложенным или нет, конечное сопоставление маршрута заключается в выполнении рейтинга, а для сопоставления маршрутов используется унифицированный метод сопоставления маршрутов.
Механизм защиты навигации
Охранники навигации передают пользовательские методы ловушек для маршрутизации пользователей, чтобы управлять логикой перехода маршрутизации черезnext
Метод последовательно выполняет логику сопоставления следующего маршрута, который можно разделить на три категории в соответствии с положением определенной навигационной защиты:Global Navigation Guard, Routing Exclusive Guard, Component Internal Navigation Guard.
Регистрация навигационной охраны
Использование зарегистрированного метода навигационной защиты или конфигурация навигационной защиты будут зарегистрированы в очереди выполнения.При переходе маршрута обновление, замена и повторное использование экземпляра компонента рассчитываются в соответствии с картой конфигурации маршрутизации, а затем соответствующий компонент пройдено для выполнения очереди защиты навигации.
Зарегистрировать глобальный навигационный сторож
Глобальные навигационные защиты делятся на: глобальные преднавигационные защиты, глобальные защиты синтаксического анализа и глобальные пост-защиты.router.beforeEach
router.beforeResolve
router.afterEach
зарегистрироваться.
// src/index.js
beforeEach (fn: Function): Function {
return registerHook(this.beforeHooks, fn)
}
beforeResolve (fn: Function): Function {
return registerHook(this.resolveHooks, fn)
}
afterEach (fn: Function): Function {
return registerHook(this.afterHooks, fn)
}
Исходный код:L133
Регистрация глобального навигационного сторожа называетсяregisterHook
Метод помещает функцию ловушки в очередь функций ловушки и возвращает метод, который удаляет функцию ловушки.
Это обычное использование очереди push и pop, очень распространенное использование в исходном коде vue.
Исходный код метода registerHook выглядит следующим образом:
// src/index.js
function registerHook (list: Array<any>, fn: Function): Function {
list.push(fn)
return () => {
const i = list.indexOf(fn)
if (i > -1) list.splice(i, 1)
}
}
Зарегистрировать эксклюзивный охранник маршрута
Эксклюзивные охранники маршрутизации регистрируются в виде конфигурации маршрутизации, например:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
Зарегистрировать внутреннюю защиту компонента
Внутренние средства защиты компонента регистрируются путем настройки свойств средства защиты навигации компонента, например:
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
Процесс разбора навигационного караула
Проанализируйте вышеуказанные средства навигации, зарегистрированные в глобальной регистрации, регистрации конфигурации маршрутизации и внутренней регистрации компонента, в соответствии сПорядок разрешения навигационного хукапродвижение в очереди
const queue: Array<?NavigationGuard> = [].concat(
// in-component leave guards
extractLeaveGuards(deactivated), // 失效组件的beforeRouterLeave
// global before hooks
this.router.beforeHooks, // 全局的前置钩子beforeEach
// in-component update hooks
extractUpdateHooks(updated), // 重用的组件beforeRouteUpdate
// in-config enter guards
activated.map(m => m.beforeEnter),// 路由配置的beforeRouteEnter
// async components
resolveAsyncComponents(activated) // 路由配置中异步组件的加载解析
)
Исходный код:L133
Процесс разбора навигационного хука
Хук навигации анализирует соответствующий исходный код
// 执行前置守卫
runQueue(queue, iterator, () => {
const postEnterCbs = []
const isValid = () => this.current === route
const enterGuards = extractEnterGuards(activated, postEnterCbs, isValid)
const queue = enterGuards.concat(this.router.resolveHooks)
// 执行解析守卫
runQueue(queue, iterator, () => {
if (this.pending !== route) {
return abort()
}
this.pending = null
onComplete(route)
if (this.router.app) {
this.router.app.$nextTick(() => {
// 执行后置守卫
postEnterCbs.forEach(cb => {
cb()
})
})
}
})
})
Исходный код:L179
Отложенная загрузка маршрута
Ленивая загрузка маршрутизации заключается в подпакете кода страницы на основе модуля маршрутизации. Когда соответствующий маршрут соответствует, код соответствующего компонента маршрутизации загружается асинхронно. Недавно созданный проект с vue-cli можно использовать напрямую.Функция разделения кода в webpack, комбинированныйvue асинхронный компонентПример ленивой загрузки маршрутов с новым синтаксисом +ES выглядит следующим образом:
vue VueRouter({
routes: [
{
path: '/foot',
component: () => import('./my-async-component')
}
]
})
При загрузке асинхронных компонентов необходимо обращать внимание на их статус загрузки.Статус загрузки асинхронных компонентов в vue-router — это только загрузка, ошибка и т. д., в то время как в vue-router ленивая загрузка повторно реализованных компонентов реализует более подробную загрузку компонентов. контроль состояния и маршрутизация.
Асинхронные компоненты vue-router разрешаются вПроцесс разбора очереди навигационной защитыВнутри ключевой исходный код для разбора асинхронных компонентов выглядит следующим образом:
// util/resolve-components.js
/**
* TODO:
* 异步路由解析:重写异步组件的resolve、reject方法,添加了组件加载状态控制、路由解析控制;对异步组件的传统写法及promise写进行兼容
*/
export function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
return (to, from, next) => {
let hasAsync = false
let pending = 0
let error = null
flatMapComponents(matched, (def, _, match, key) => {
if (typeof def === 'function' && def.cid === undefined) {
hasAsync = true
pending++
// 重写vue异步组件的resolve和reject方法
const resolve = once(resolvedDef => {
if (isESModule(resolvedDef)) {
resolvedDef = resolvedDef.default
}
// save resolved on async factory in case it's used elsewhere
def.resolved = typeof resolvedDef === 'function'
? resolvedDef
: _Vue.extend(resolvedDef)
match.components[key] = resolvedDef
pending--
if (pending <= 0) {
next()
}
})
const reject = once(reason => {
const msg = `Failed to resolve async component ${key}: ${reason}`
process.env.NODE_ENV !== 'production' && warn(false, msg)
if (!error) {
error = isError(reason)
? reason
: new Error(msg)
next(error)
}
})
let res
try {
res = def(resolve, reject)
} catch (e) {
reject(e)
}
// 兼容异步组件的promise写法
if (res) {
if (typeof res.then === 'function') {
res.then(resolve, reject)
} else {
// new syntax in Vue 2.3
const comp = res.component
if (comp && typeof comp.then === 'function') {
comp.then(resolve, reject)
}
}
}
}
})
}
}
Исходный код:L6
Методы разрешения и отклонения, загруженные асинхронными компонентами Vue, переписаны, чтобы контролировать, входит ли синтаксический анализ маршрута в следующее совпадение, добавлена обработка исключений для сбоя синтаксического анализа компонентов сопоставления маршрутов, а также совместима запись обещаний асинхронных компонентов.
компонент просмотра маршрутизатора
router-view — один из двух основных компонентов, предоставляемых vue-router.Это функциональный компонент, который не имеет собственного экземпляра компонента и отвечает только за вызов данных, хранящихся в родительском компоненте.keepAlive
$route.match
и другие связанные свойства/методы для управления визуализацией компонента, соответствующего маршруту.
Компонент router-view может быть вложенным для реализации вложенной маршрутизации, а его собственная позиция на странице в конечном итоге является позицией, в которой смонтирован соответствующий компонент маршрутизации.
Основной исходный код его части рендеринга исходного кода выглядит следующим образом:
render (_, { props, children, parent, data }) {
// 标识当前组件是router-view
data.routerView = true
const h = parent.$createElement
const name = props.name
const route = parent.$route
const cache = parent._routerViewCache || (parent._routerViewCache = {})
let depth = 0
let inactive = false
// 由router-view组件向上遍历直到跟组件,遇到其他的router-view组件则路由深度+1
// vnodeData.keepAlivepj
while (parent && parent._routerRoot !== parent) {
const vnodeData = parent.$vnode ? parent.$vnode.data : {}
if (vnodeData.routerView) {
depth++
}
if (vnodeData.keepAlive && parent._directInactive && parent._inactive) {
inactive = true
}
parent = parent.$parent
}
data.routerViewDepth = depth
// 启用缓存时
if (inactive) {
const cachedData = cache[name]
const cachedComponent = cachedData && cachedData.component
if (cachedComponent) {
if (cachedData.configProps) {
fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
}
return h(cachedComponent, data, children)
} else {
return h()
}
}
const matched = route.matched[depth]
const component = matched && matched.components[name]
if (!matched || !component) {
cache[name] = null
return h()
}
cache[name] = { component }
// 往父组件注册registerRouteInstance方法
data.registerRouteInstance = (vm, val) => {
// val could be undefined for unregistration
const current = matched.instances[name]
if (
(val && current !== vm) ||
(!val && current === vm)
) {
matched.instances[name] = val
}
}
...
return h(component, data, children)
}
Исходный код:L13
Оценка кеша маршрута
parent представляет прямой экземпляр родительского компонента компонента router-view. Он проходит от router-view к внешним компонентам. Когда встречается router-view, это означает, что существует вложенный маршрут, а глубина маршрута равна +1 .;
То есть следующая структура будет использовать кеш маршрута
<keep-alive>
<router-view></router-view>
</keep-alive>
Экземпляр кэшированного компонента маршрутизации существует в экземпляре родительского компонента. Если кэш маршрутизации включен, соответствующий компонент маршрутизации в родительском кэше будет использоваться для рендеринга, в противном случае он будет использоваться.$route.match
соответствоватьmatcher
Соответствующий маршрут отображается.
**parent._inactive
** по основному модулю vueobserver/scheduler
Обновление планировщика**parent._directInactive**
основным модулем vueinstance/lifecycle
Обновить, оба используются для определения того, находится ли текущий компонент в активном состоянии. Конкретные различия см. в этой проблеме.#1212.
компонент маршрутизатора
router-link — один из двух основных компонентов, предоставляемых vue-router, это общий компонент, внутренне отмененныйa标签
поведение прыжка по умолчанию и управляет компонентом с помощьюcontrol
,meta
Проблемы совместимости, такие как кнопки, существуют в то же время, предоставьте класс стиля, когда активировано текущее сопоставление маршрута;
пройти черезto
для определения целевого маршрута перехода по событию клика черезappend
replace
и другие свойства изменяют поведение перехода по маршруту по умолчанию.
Распространяйте контент через слоты
const scopedSlot =
!this.$scopedSlots.$hasNormal &&
this.$scopedSlots.default &&
this.$scopedSlots.default({
href,
route,
navigate: handler,
isActive: classes[activeClass],
isExactActive: classes[exactActiveClass]
})
if (scopedSlot) {
if (scopedSlot.length === 1) {
return scopedSlot[0]
} else if (scopedSlot.length > 1 || !scopedSlot.length) {
if (process.env.NODE_ENV !== 'production') {
warn(
false,
`RouterLink with to="${
this.to
}" is trying to use a scoped slot but it didn't provide exactly one child. Wrapping the content with a span element.`
)
}
return scopedSlot.length === 0 ? h() : h('span', {}, scopedSlot)
}
}
Исходный код:L91
Унифицированная обработка совместимости событий щелчка
function guardEvent (e) {
// don't redirect with control keys
if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) return
// don't redirect when preventDefault called
if (e.defaultPrevented) return
// don't redirect on right click
if (e.button !== undefined && e.button !== 0) return
// don't redirect if `target="_blank"`
if (e.currentTarget && e.currentTarget.getAttribute) {
const target = e.currentTarget.getAttribute('target')
if (/\b_blank\b/i.test(target)) return
}
// this may be a Weex event which doesn't have this method
if (e.preventDefault) {
e.preventDefault()
}
return true
}
Исходный код:L158
Найти отображаемый тег
Рекурсивно найдите тег a в дочерних элементах в качестве содержимого замены по умолчанию для слота компонента по умолчанию.
function findAnchor (children) {
if (children) {
let child
for (let i = 0; i < children.length; i++) {
child = children[i]
if (child.tag === 'a') {
return child
}
if (child.children && (child = findAnchor(child.children))) {
return child
}
}
}
}
Исходный код:L177
Суммировать
После приведенного выше анализа реализация основных функций vue-router была в основном проанализирована. Из-за ограниченного уровня автора в настоящее время анализ некоторых исходных кодов недостаточно тщательный, например, в исходном коде router-view задействованы соответствующие части ядра vue, и даже есть упущения или ошибки в в некоторых местах Пожалуйста, поправьте меня.
На написание этой статьи у меня ушло почти неделю.Объем статьи полностью превысил первоначальную оценку.Если вы можете настоять на том, чтобы прочитать это, по крайней мере, вы должны быть очень хорошими, и вы должны поставить себе лайк.
Если эта статья вам хоть немного поможет, пожалуйста, поставьте лайк, чтобы поощрить автора, ведь оригинальность не из легких :)
Первая говорящая сама по себе птица:ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууноуэтомушт.ком/Джон IE-рубашка/положить в…
Адрес авторского блога:blog.lessing.online/
Автор гитхаб:github.com/johniexu