当前位置: 首页 > article >正文

vue原理分析(十一)研究new Vue()中的initRender

在Vue.prototype._init 中有一些init函数,今天我们来研究这些init函数

Vue.prototype._init = function (options) {
  ......
  {
    initProxy(vm);
  }
  ......
  initLifecycle(vm);
  initEvents(vm);
  initRender(vm);
  callHook$1(vm, 'beforeCreate', undefined, false /* setContext */);
  initInjections(vm); // resolve injections before data/props
  initState(vm);
  initProvide(vm); // resolve provide after data/props
  callHook$1(vm, 'created');
  ......
}

上一篇中已经研究了initEvents,今天我们往下研究

initRender(vm);
function initRender(vm) {
  vm._vnode = null; // the root of the child tree
  vm._staticTrees = null; // v-once cached trees
  const options = vm.$options;
  const parentVnode = (vm.$vnode = options._parentVnode); // the placeholder node in parent tree
  const renderContext = parentVnode && parentVnode.context;
  vm.$slots = resolveSlots(options._renderChildren, renderContext);
  vm.$scopedSlots = parentVnode
    ? normalizeScopedSlots(vm.$parent, parentVnode.data.scopedSlots, vm.$slots)
    : emptyObject;
  // bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates
  // @ts-expect-error
  vm._c = (a, b, c, d) => createElement$1(vm, a, b, c, d, false);
  // normalization is always applied for the public version, used in
  // user-written render functions.
  // @ts-expect-error
  vm.$createElement = (a, b, c, d) => createElement$1(vm, a, b, c, d, true);
  // $attrs & $listeners are exposed for easier HOC creation.
  // they need to be reactive so that HOCs using them are always updated
  const parentData = parentVnode && parentVnode.data;
  /* istanbul ignore else */
  {
    defineReactive(vm, '$attrs', (parentData && parentData.attrs) || emptyObject, () => {
      !isUpdatingChildComponent && warn$2(`$attrs is readonly.`, vm);
    }, true);
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
      !isUpdatingChildComponent && warn$2(`$listeners is readonly.`, vm);
    }, true);
  }
}
function initRender(vm) {
  vm._vnode = null; // the root of the child tree
  vm._staticTrees = null; // v-once cached trees
  const options = vm.$options;
  const parentVnode = (vm.$vnode = options._parentVnode); // the placeholder node in parent tree
  const renderContext = parentVnode && parentVnode.context;
  ......
}

清空 组件的 VNode 对象和静态dom节点树

并获取到该实例的 父组件虚拟dom树对象 parentVnode 与 父组件实例指向 renderContext

function initRender(vm) {
  ......
  vm.$slots = resolveSlots(options._renderChildren, renderContext);
  vm.$scopedSlots = parentVnode
    ? normalizeScopedSlots(vm.$parent, parentVnode.data.scopedSlots, vm.$slots)
    : emptyObject;
  ......
}

处理当前组件的 slots 插槽对象 ,以及标准化处理组件的数据域插槽

function resolveSlots(children, context) {
    // 1.判断是否有children,有没有插槽,如果没有直接返回空对象
    if (!children || !children.length) {
        return {};
    }
    const slots = {};
    // for循环遍历子节点
    for (let i = 0, l = children.length; i < l; i++) {
        const child = children[i];
        const data = child.data;
        // remove slot attribute if the node is resolved as a Vue slot node
        // 如果节点node已经处理了,移出slot,删除该节点attrs的slot
        if (data && data.attrs && data.attrs.slot) {
            delete data.attrs.slot;
        }
        // named slots should only be respected if the vnode was rendered in the
        // same context.
        // 判断是否是具名插槽,如果是具名插槽,还需要子组件 / 函数子组件 渲染上下文一致
        // 当需要向子组件的子组件传递具名插槽时,不会保持插槽的名字
        if ((child.context === context || child.fnContext === context) &&
            data &&
            data.slot != null) {
            const name = data.slot;
            const slot = slots[name] || (slots[name] = []);
            //处理父组件采用template形式的插槽
            if (child.tag === 'template') {
                slot.push.apply(slot, child.children || []);
            }
            else {
                slot.push(child);
            }
        }
        else {
            //返回匿名default插槽VNode数组
            (slots.default || (slots.default = [])).push(child);
        }
    }
    // ignore slots that contains only whitespace
    // 忽略仅仅包含whitespace的插槽
    for (const name in slots) {
        if (slots[name].every(isWhitespace)) {
            delete slots[name];
        }
    }
    return slots;
}

// 方法用于判断指定字符是否为空白字符,空白符包含:空格、tab键、换行符

function isWhitespace(node) {
    return (node.isComment && !node.asyncFactory) || node.text === ' ';
}
function initRender(vm) {
  ......
  // bind the createElement fn to this instance
  // so that we get proper render context inside it.
  // args order: tag, data, children, normalizationType, alwaysNormalize
  // internal version is used by render functions compiled from templates
  // @ts-expect-error
  vm._c = (a, b, c, d) => createElement$1(vm, a, b, c, d, false);
  // normalization is always applied for the public version, used in
  // user-written render functions.
  // @ts-expect-error
  vm.$createElement = (a, b, c, d) => createElement$1(vm, a, b, c, d, true);
  ......
}

给 组件 添加两个组件创建方法

_c 表示使用内部 render 函数,不需要额外的标准化处理

$createElement 则表示使用的是用户自己编写的 render 函数,需要内部重新进行一次标准化处理

这两个方法最终其实都是调用的 _createElement 方法,只是标准函数(即 _c)使用 simpleNormalizeChildren() 处理,而用户自定义 render (即 $createElement)使用 normalizeChildren() 处理

function initRender(vm) {
  ......
  const parentData = parentVnode && parentVnode.data;
  /* istanbul ignore else */
  {
    defineReactive(vm, '$attrs', (parentData && parentData.attrs) || emptyObject, () => {
      !isUpdatingChildComponent && warn$2(`$attrs is readonly.`, vm);
    }, true);
    defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
      !isUpdatingChildComponent && warn$2(`$listeners is readonly.`, vm);
    }, true);
  }
  ......
}

对 $attrs和$listeners  进行响应式处理。


 


http://www.kler.cn/news/302569.html

相关文章:

  • 基于深度学习的结构优化与生成
  • 深入理解Kotlin中的异步网络请求处理
  • JavaScript 将 json 美化输出
  • 前端速通面经八股系列(八)—— React篇(上)
  • 基于鸿蒙API10的RTSP播放器(八:音量和亮度调节功能的整合)
  • 数据结构之折半插入排序概念、折半插入排序的具体步骤、折半插入排序的具体代码示例
  • 摊牌了!一文教会你轻松上手豆包MarsCode 编程助手!
  • Android的内核
  • 【STM32】外部中断
  • 数据结构 - 栈
  • 多态(c++)
  • 怎样还原空白试卷?2024教你快速还原空白试卷的软件
  • Python 最小公倍数计算器:从基础到应用
  • 鸿蒙-沉浸式pc端失效
  • 深入理解全连接层:从线性代数到 PyTorch 中的 nn.Linear 和 nn.Parameter
  • Unity Shader实现简单的各向异性渲染(采用各向异性形式的GGX分布)
  • 优化销售流程:免费体验企元数智小程序合规分销系统!
  • Idea 2021.3 破解 window
  • vue3常见的bug 修复bug
  • 力扣每日一题:1372.二叉树中的最长交错路径
  • 腾讯云2024年数字生态大会开发者嘉年华(数据库动手实验)TDSQL-C初体验
  • 62. 不同路径
  • 户用光伏业务市场开发的步骤
  • 走进低代码报表开发(二):高效报表设计新利器
  • 基于SpringMVC的API灰度方案
  • SuperMap GIS基础产品FAQ集锦(20240911)
  • 使用AI大模型进行企业数据分析与决策支持
  • Redis 的标准使用规范之数据类型使用规范
  • MySQL总结(上)
  • 决策树(Decison Tree)—有监督学习方法、概率模型、生成模型、非线性模型、非参数化模型、批量学习