写点什么

【VueRouter 源码学习】第九篇 - router-view 组件的实现

用户头像
Brave
关注
发布于: 1 小时前
【VueRouter 源码学习】第九篇 - router-view 组件的实现

一,前言


上篇,介绍了$route$routerrouter-link 组件的实现,主要涉及以下内容:


  • 定义原型方法 $route$router

  • <router-link>组件的功能和实现;


本篇,介绍<router-view>组件的实现;


二,前文回顾


  • 在 VueRouter 实例化时,通过路由匹配器 createMatcher 对路由配置进行扁平化处理;

  • 路由匹配器 createMatcher 中的 match 方法,匹配路由记录并创建路由对象;

  • 将当前匹配路由对象 current,创建为响应式数据;

  • 点击 router-link 组件进行路径切换,调用 transitionTo 匹配新路由记录并更新响应式数据 current,触发视图更新;


点击 router-link 组件进行路径切换时,需要根据最新路由地址匹配到全部路由规则,并递归进行页面的更新渲染操作;


三,函数式组件


router-view 是一个函数式组件,函数式组件有以下特点:

  • 性能高,渲染时没有 this;

  • functional: true 表示函数式组件;

export default {  name: 'routerView',  functional: true,// 函数式组件  render() {      // this 为 null      return <div></div>  }}
复制代码


普通组件,使用组件需要先进行实例化再挂载:new Ctor().$mount();

函数式组件,无需创建实例即可直接使用(相当于 react 中的函数组件);

他们之间唯一的区别就是 render 函数中没有 this,即没有组件状态(没有 data,props 等)


Vue官网:函数式组件的介绍


四,<router-view>组件的实现

1,获取渲染记录数据


在当前组件的父亲 parent 上,存在 $route 属性;

parent.$router 就是 _route,即当前匹配到的路由对象 current 属性;

/** * router-view 函数式组件,多层级渲染 * 函数式组件特点:性能高,无需创建实例,没有 this */export default {    name: 'routerView',    functional: true,// 函数式组件    render(h, { parent }) {        // 获取当前需要渲染的相关路由记录,即:this.current        let route = parent.$route;    }}
复制代码

2,记录递归深度 depth


获取到 $route 属性后,route.matched 即为全部路由匹配记录;


处理逻辑:

  • app.vue 中包含 router-view 组件,渲染 Home 首页组件;

  • 当点击“跳转至我的”进行路由切换时,app.vue 中的 router-view 渲染 Mine 组件;

  • 当点击“我的-个人信息”进行路由切换时,mine.vue 中的 router-view 渲染 /mine/user 路由对应的组件;


所以,当访问/mine/user 时,将会匹配到三条路由规则: /、/mine、/mine/user;


此时,需要按照上面的描述,依次进行渲染操作;

所以,需要通过记录深度 depth 对应到每一层 router-view 要渲染的内容;


3,处理第一层渲染


当 router-view 组件渲染时,将调用 routerView 组件的 render 方法;

此时,在当前层级(第一层)的 data 属性中,添加自定义属性 data.routerView = true;

export default {    name: 'routerView',    functional: true,     render(h, { parent, data }) {        let route = parent.$route;        let depth = 0;// 记录等级深度        data.routerView = true; // 自定义属性    }}
复制代码


第一次渲染时,根据匹配结果与层级深度 depth,需要渲染 route.matched 中的第 0 项;

通过 let record = route.matched[0] 获取对应层级渲染所需的记录;

export default {    name: 'routerView',    functional: true,    render(h, { parent, data }) {        let route = parent.$route;        let depth = 0;        data.routerView = true;
// 第一层router-view 渲染第一个record 第二个router-view渲染第二个 let record = route.matched[depth]; // 获取对应层级的记录 }}
复制代码


  • router-view 有可能只有一层,直接取出来;

  • 若未匹配到记录,record 不存在,直接返回一个空的虚拟节点 empty-vnode 叫注释节点h()

  • 若匹配到记录,record 有值,进行组件渲染h(record.component),通过 record.component 获取组件;

  • h(record.component):渲染当前组件,当组件渲染时,传入 data 数据,其中 data 包含了之前标识的 routerView 属性;

export default {    name: 'routerView',    functional: true,    render(h, { parent, data }) {      let route = parent.$route;      let depth = 0;      data.routerView = true;
// 根据当前层级深度获取该层级对应的路由记录,用于视图渲染 let record = route.matched[depth]; // 未匹配到路由记录,渲染空虚拟节点(empty-vnode),也叫作注释节点 if (!record) { return h(); } return h(record.component, data); }}
复制代码


这样,就完成了第一层 router-view 标签的渲染;


4,处理多层 router-view 渲染


  • 每次 router-view 渲染时,都会在当前 data 中设置 routerView 属性作为标记;

  • render 函数中的 parent 为当前 router-view 的父标签;

  • 通过循环 parent,判断parent.$vnode.data.routerView标识,计算当前渲染的层级深度 depth;

  • 获取对应层级的路由记录,进行视图渲染;


相关说明:

  • parent.$vnode:代表占位符的 vnode;即:组件标签名的虚拟节点;

  • parent._vnode 指组件的内容;即:实际要渲染的虚拟节点;

/** * router-view 函数式组件,多层级渲染 * 函数式组件特点:性能高,无需创建实例,没有 this */export default {  name: 'routerView',  functional: true, // 函数式组件  render(h, { parent, data }) {    // 获取当前需要渲染的相关路由记录,即:this.current    let route = parent.$route;    let depth = 0;// 记录等级深度    data.routerView = true; // 自定义属性
// App.vue渲染组件时,调用render函数,此时的父亲中没有 data.routerView 属性 // 在渲染第一层时,添加routerView=true标识 while (parent) { // parent 为 router-view 的父标签 // parent.$vnode:代表占位符的vnode;即:组件标签名的虚拟节点; // parent._vnode 指组件的内容;即:实际要渲染的虚拟节点; if (parent.$vnode && parent.$vnode.data.routerView) { depth++; } parent = parent.$parent; // 更新父组件,用于循环的下一次处理 }
// 根据当前层级深度获取该层级对应的路由记录,用于视图渲染 let record = route.matched[depth]; // 未匹配到路由记录,渲染空虚拟节点(empty-vnode),也叫作注释节点 if (!record) { return h(); } return h(record.component, data); }}
复制代码


以上就实现了 router-view 组件的视图更新操作;


五,结尾


本篇,介绍了 router-view 组件的实现,主要涉及以下内容:


  • 函数式组件的介绍;

  • router-view 组件的实现:

  • 获取渲染记录;

  • 标记 router-view 层级深度;

  • 根据深度进行 router-view 渲染;


下一篇,介绍 vue-router 的钩子函数;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【VueRouter 源码学习】第九篇 - router-view 组件的实现