如何理解vue中的vnode虚拟dom?

return _c('div',{ attrs: { id: 'app' } },[_c('span', { on: { click: onClick } }, [_v('我是data数据' + _s(message))]),_v(' '), // 没整懂 为啥每个中间都夹杂个空VNode(在编译阶段时 遇到同级兄弟节点 会默认生成一个空本文节点)_l(dsArr, function (item, index) {return _c('p', { key: index }, [_v('我是v-for' + _s(index))]);}),_v(' '), // 没整懂 为啥每个中间都夹杂个空VNode(在编译阶段时 遇到同级兄弟节点 会默认生成一个空本文节点)!!message ? _c('span', [_v('我是v-if')]) : _e(),_v(' '), // 没整懂 为啥每个中间都夹杂个空VNode(在编译阶段时 遇到同级兄弟节点 会默认生成一个空文节点)_c('base-show'), //  with(this){return _c('h3',[_v(_s(myMessage))])}],2);
}
// 1.1 定义_c 源码 3485行
function initRender(vm) {vm._vnode = null; // the root of the child treevm._staticTrees = null; // v-once cached treesvar options = vm.$options;var parentVnode = (vm.$vnode = options._parentVnode); // the placeholder node in parent treevar renderContext = parentVnode && parentVnode.context;vm.$slots = resolveSlots(options._renderChildren, renderContext);vm.$scopedSlots = 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// TODO:这里在实例上面挂载_c和$createElement两个方法 均访问的是createElement函数vm._c = function (a, b, c, d) {// 但是有点不同的地方是_c这里第6个参数'alwaysNormalize'给的falsereturn createElement(vm, a, b, c, d, false);};// normalization is always applied for the public version, used in// user-written render functions.vm.$createElement = function (a, b, c, d) {// 但是有点不同的地方是_c这里第6个参数'alwaysNormalize'给的truereturn createElement(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 updatedvar parentData = parentVnode && parentVnode.data;/* istanbul ignore else */// TODO: 在之前分析data响应式时老是有两个属性先设置 就是在这个地方挂载的// $attrs和$listeners均只能读取 不能写入{defineReactive$$1(vm,'$attrs',(parentData && parentData.attrs) || emptyObject,function () {!isUpdatingChildComponent && warn('$attrs is readonly.', vm);},true);defineReactive$$1(vm,'$listeners',options._parentListeners || emptyObject,function () {!isUpdatingChildComponent && warn('$listeners is readonly.', vm);},true);}
}// 开始下面前 先执行renderMixin(Vue) -> installRenderHelpers(Vue.prototype)
// 给Vue原型上添加各种方法 所以上面能访问_c\_s等等操作 是在这里绑定的
// 源码3519行
function renderMixin(Vue) {installRenderHelpers(Vue.prototype);// .....
}
// 源码 2940行
function installRenderHelpers(target) {target._o = markOnce;target._n = toNumber;target._s = toString;target._l = renderList;target._t = renderSlot;target._q = looseEqual;target._i = looseIndexOf;target._m = renderStatic;target._f = resolveFilter;target._k = checkKeyCodes;target._b = bindObjectProps;target._v = createTextVNode;target._e = createEmptyVNode;target._u = resolveScopedSlots;target._g = bindObjectListeners;target._d = bindDynamicKeys;target._p = prependModifier;
}// 1.1.1(源码 96行) 上面函数首先执行的是内层(基本操作)即:
// _s(message)
// 这个地方还涉及到一个问题就是 当执行_s这个函数时message->this.message(with作用)->触发getter(Object.definedProperty)
// 拿到真实的值
function toString(val) {// TODO: 这里值得关注的是JSON.stringify用法// JSON.stringify({obj:1,age:2}) => "{"obj":1,"age":2}"// JSON.stringify({obj:1,age:2},null,2) => "{//                                           "obj": 1,//                                           "age": 2//                                         }"// 表示解析所有属性且解析出来属性前面空格2格return val == null ? '' : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) ? JSON.stringify(val, null, 2) : String(val);
}// 1.1.2(源码 821行)再执行 即:
// _v('我是data数据test')
function createTextVNode(val) {return new VNode(undefined, undefined, undefined, String(val));
}
// VNode终于出来了 (源码 767行)
function VNode(tag, data, children, text, elm, context, componentOptions, asyncFactory) {this.tag = tag;this.data = data;this.children = children;this.text = text;this.elm = elm;this.ns = undefined;this.context = context;this.fnContext = undefined;this.fnOptions = undefined;this.fnScopeId = undefined;this.key = data && data.key;this.componentOptions = componentOptions;this.componentInstance = undefined;this.parent = undefined;this.raw = false;this.isStatic = false;this.isRootInsert = true;this.isComment = false;this.isCloned = false;this.isOnce = false;this.asyncFactory = asyncFactory;this.asyncMeta = undefined;this.isAsyncPlaceholder = false;
}// 1.1.3 createElement函数 源码 3344行
/**** @param {*} context 在initRender里面这个参数就是传的vm实例* @param {*} tag 标签 比如div span* @param {*} data 这个在ast->element时对应每个标签作了处理 比如上面的就是{on:{click:onClick}}* @param {*} children 当前标签下的child* @param {*} normalizationType 0:不需要规则化 1:需要简单规划 2:完全需要规划* @param {*} alwaysNormalize true&false*/
function createElement(context, tag, data, children, normalizationType, alwaysNormalize) {// TODO: 这里判断data是否为数组结构或者原始数据 如string\number\symbol\booleanif (Array.isArray(data) || isPrimitive(data)) {normalizationType = children;children = data;data = undefined;}if (isTrue(alwaysNormalize)) {normalizationType = ALWAYS_NORMALIZE;}return _createElement(context, tag, data, children, normalizationType);
}// 1.1.3.1
function _createElement(context, tag, data, children, normalizationType) {if (isDef(data) && isDef(data.__ob__)) {warn('Avoid using observed data object as vnode data: ' + JSON.stringify(data) + '\n' + 'Always create fresh vnode data objects in each render!', context);return createEmptyVNode();}// object syntax in v-bindif (isDef(data) && isDef(data.is)) {tag = data.is;}if (!tag) {// in case of component :is set to falsy valuereturn createEmptyVNode();}// warn against non-primitive keyif (isDef(data) && isDef(data.key) && !isPrimitive(data.key)) {{warn('Avoid using non-primitive value as key, ' + 'use string/number value instead.', context);}}// support single function children as default scoped slotif (Array.isArray(children) && typeof children[0] === 'function') {data = data || {};data.scopedSlots = { default: children[0] };children.length = 0;}if (normalizationType === ALWAYS_NORMALIZE) {children = normalizeChildren(children);} else if (normalizationType === SIMPLE_NORMALIZE) {children = simpleNormalizeChildren(children);}var vnode, ns;if (typeof tag === 'string') {var Ctor;ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);if (config.isReservedTag(tag)) {// platform built-in elementsif (isDef(data) && isDef(data.nativeOn)) {warn('The .native modifier for v-on is only valid on components but it was used on <' + tag + '>.', context);}vnode = new VNode(config.parsePlatformTagName(tag), data, children, undefined, undefined, context);} else if ((!data || !data.pre) && isDef((Ctor = resolveAsset(context.$options, 'components', tag)))) {// componentvnode = createComponent(Ctor, data, context, children, tag);} else {// unknown or unlisted namespaced elements// check at runtime because it may get assigned a namespace when its// parent normalizes childrenvnode = new VNode(tag, data, children, undefined, undefined, context);}} else {// direct component options / constructorvnode = createComponent(tag, data, context, children);}if (Array.isArray(vnode)) {return vnode;} else if (isDef(vnode)) {if (isDef(ns)) {applyNS(vnode, ns);}if (isDef(data)) {registerDeepBindings(data);}return vnode;} else {return createEmptyVNode();}
}// 1.1.3.1 type为2时 拍平树状为一维数组 比如里面嵌套有v-for时需要将v-for生成的Vnode集合拍平
// 2. When the children contains constructs that always generated nested Arrays,
// e.g. <template>, <slot>, v-for, or when the children is provided by user
// with hand-written render functions / JSX. In such cases a full normalization
// is needed to cater to all possible types of children values.
function normalizeChildren(children) {return isPrimitive(children) ? [createTextVNode(children)] : Array.isArray(children) ? normalizeArrayChildren(children) : undefined;
}// 1.1.3.2 type为1时 简易拍平树状为一维数组
// 1. When the children contains components - because a functional component
// may return an Array instead of a single root. In this case, just a simple
// normalization is needed - if any child is an Array, we flatten the whole
// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep
// because functional components already normalize their own children.
function simpleNormalizeChildren(children) {for (var i = 0; i < children.length; i++) {if (Array.isArray(children[i])) {return Array.prototype.concat.apply([], children);}}return children;
}// 1.1.3.3
function normalizeArrayChildren(children, nestedIndex) {var res = [];var i, c, lastIndex, last;for (i = 0; i < children.length; i++) {c = children[i];// TODO: 1. 判断当前vnode是否存在或者是一个布尔值 即不再执行if (isUndef(c) || typeof c === 'boolean') {continue;}lastIndex = res.length - 1;last = res[lastIndex];//  nested// TODO: 2. 判断当前元素是否是一个数组 比如v-for生成的数据if (Array.isArray(c)) {if (c.length > 0) {c = normalizeArrayChildren(c, (nestedIndex || '') + '_' + i);// merge adjacent text nodesif (isTextNode(c[0]) && isTextNode(last)) {res[lastIndex] = createTextVNode(last.text + c[0].text);c.shift();}// TODO: 2.1 这里就将比如v-for生成的数组的节点vnode 压入了一维数组后面 将二维数组变成一维数组res.push.apply(res, c);}} // TODO:3. 判断当前元素是否一个原始值else if (isPrimitive(c)) {if (isTextNode(last)) {// merge adjacent text nodes// this is necessary for SSR hydration because text nodes are// essentially merged when rendered to HTML stringsres[lastIndex] = createTextVNode(last.text + c);} else if (c !== '') {// convert primitive to vnoderes.push(createTextVNode(c));}} // TODO:4. 排除前面的情况else {// TODO: 4.1 是否当前节点是一个文本节点并且res的最后一个也是文本节点if (isTextNode(c) && isTextNode(last)) {// merge adjacent text nodesres[lastIndex] = createTextVNode(last.text + c.text);} // TODO: 4.2 则为正常节点 直接push进reselse {// default key for nested array children (likely generated by v-for)if (isTrue(children._isVList) && isDef(c.tag) && isUndef(c.key) && isDef(nestedIndex)) {// 这个地方 我觉得是兼容了当我们写v-for的时候 没有绑定key值 这里默认生成了一个keyc.key = '__vlist' + nestedIndex + '_' + i + '__';}res.push(c);}}}return res;
}// 1.1.4 (源码 2629行)再执行 即: v-for原理
// _l(dsArr, function (item, index) {return _c('p', { key: index }, [_v('我是v-for' + _s(index))]);})
function renderList(val, render) {var ret, i, l, keys, key;// TODO:1. 若in&of 后面跟的是数组或者字符串 则对应创建数组或字符串个数的对应个数的空数组if (Array.isArray(val) || typeof val === 'string') {ret = new Array(val.length);for (i = 0, l = val.length; i < l; i++) {// 这里将数组每一项传入匿名函数 所以我们经常使用item.xxx访问每一项的值ret[i] = render(val[i], i);}} else if (typeof val === 'number') {// TODO:2. 若in&of 后面跟的是数字 则创建对应数字的对应个数的空数组ret = new Array(val);for (i = 0; i < val; i++) {ret[i] = render(i + 1, i);}} else if (isObject(val)) {// TODO:3. 若in&of 后面跟的是对象 这种模式我们一般使用的很少// TODO: 3.1判断是否数据结构是symbol格式if (hasSymbol && val[Symbol.iterator]) {ret = [];var iterator = val[Symbol.iterator]();var result = iterator.next();while (!result.done) {ret.push(render(result.value, ret.length));result = iterator.next();}} else {// TODO:3.2若一般的格式 就获取key及key对应的valuekeys = Object.keys(val);ret = new Array(keys.length);for (i = 0, l = keys.length; i < l; i++) {key = keys[i];ret[i] = render(val[key], key, i);}}}// 若前面都不是 则作一层容错处理 置ret为空数组if (!isDef(ret)) {ret = [];}// 标识当前数组最后一项属性为_isVList为trueret._isVList = true;return ret;
}// 1.1.5 (源码 812行)这里先执行this.message -> getter拿到真实值 -> 执行三目 -> 执行_c
// 这里分析下_e 当false时执行_e()
// !!message ? _c('span', [_v('我是v-if')]) : _e(),
function createEmptyVNode(text) {// TODO:等于说就创建个空的VNodeif (text === void 0) text = '';var node = new VNode();node.text = text; // text等于空字符串node.isComment = true; // 并且这里改成了truereturn node;
}// 1.1.6 (源码 3176行)再执行 即:
// _c('base-show') 这里遇到需要创建组件 组件名字为base-show
function createComponent(Ctor, data, context, children, tag) {if (isUndef(Ctor)) {return;}var baseCtor = context.$options._base;// plain options object: turn it into a constructorif (isObject(Ctor)) {Ctor = baseCtor.extend(Ctor);}// if at this stage it's not a constructor or an async component factory,// reject.if (typeof Ctor !== 'function') {{warn('Invalid Component definition: ' + String(Ctor), context);}return;}// TODO: 1. 处理异步组件// async componentvar asyncFactory;if (isUndef(Ctor.cid)) {asyncFactory = Ctor;Ctor = resolveAsyncComponent(asyncFactory, baseCtor);if (Ctor === undefined) {// return a placeholder node for async component, which is rendered// as a comment node but preserves all the raw information for the node.// the information will be used for async server-rendering and hydration.return createAsyncPlaceholder(asyncFactory, data, context, children, tag);}}data = data || {};// resolve constructor options in case global mixins are applied after// component constructor creationresolveConstructorOptions(Ctor);// TODO:2. 转化在组件上得v-model绑定// transform component v-model data into props & eventsif (isDef(data.model)) {transformModel(Ctor.options, data);}// TODO:3. 处理组件上的props绑定// extract propsvar propsData = extractPropsFromVNodeData(data, Ctor, tag);// TODO: 4. 处理函数式组件// functional componentif (isTrue(Ctor.options.functional)) {return createFunctionalComponent(Ctor, propsData, data, context, children);}// extract listeners, since these needs to be treated as// child component listeners instead of DOM listenersvar listeners = data.on;// replace with listeners with .native modifier// so it gets processed during parent component patch.data.on = data.nativeOn;if (isTrue(Ctor.options.abstract)) {// abstract components do not keep anything// other than props & listeners & slot// work around flowvar slot = data.slot;data = {};if (slot) {data.slot = slot;}}// install component management hooks onto the placeholder nodeinstallComponentHooks(data);// return a placeholder vnodevar name = Ctor.options.name || tag;var vnode = new VNode('vue-component-' + Ctor.cid + (name ? '-' + name : ''),data,undefined,undefined,undefined,context,{ Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },asyncFactory);return vnode;
}

vue2.x(虚拟domvnode)相关推荐

  1. 在 Vue3 成为默认版本后,盘点了 Vue3 与 Vue2 的区别

    目录 前言 正文 一.Vue3 与 Vue2 区别概览 二.Vue3 与 Vue2 区别详述 生命周期 多根节点 Composition API 异步组件(Suspense) Teleport 响应式 ...

  2. vue2+vue3——107+

    vue2+vue3--107+ vue2 Vuex工作原理图[23:54] vue2 搭建Vuex环境[26:40] 插入 话题 npm i vue@3 store / index.js 修改 vue ...

  3. 有关vue源码的简单实现 实现一个属于自己的min-vue

    vue-study 自己实现的mini-vue仓库:https://github.com/maolovecoding/mini-vue2-stage[1]建议克隆代码观看,效果更佳. 实现了Vue 实 ...

  4. 【面试题】面试不面试,你都必须得掌握的vue知识

    前言    因为是自己的理解,所以难免会出现错误.如果大家发现了错误,或者有些问题需要交流,欢迎在评论区下留言.由于最近项目加急,还有很多事情需要处理,剩下的渲染函数.路由守卫.vue2.x底层原理会 ...

  5. Vue2源码解析 虚拟dom简介

    目录 1  什么是虚拟dom 2  为什么要引入虚拟dom 3  vue中的虚拟dom 4  总结 1  什么是虚拟dom 通过描述状态和dom之间的映射关系是怎样的,就可以将状态渲染成视图. 状态可 ...

  6. (转)深入Vue2.x的虚拟DOM diff原理

    深入Vue2.x的虚拟DOM diff原理 2017年09月22日 00:00:00 小时光茶社 阅读数 28335 作者简介:汪玉林,高级工程师,增值产品部前端Leader,目前团队负责手Q游戏中心 ...

  7. 深入浅出虚拟 DOM 和 Diff 算法,及 Vue2 与 Vue3 中的区别

    因为 Diff 算法,计算的就是虚拟 DOM 的差异,所以先铺垫一点点虚拟 DOM,了解一下其结构,再来一层层揭开 Diff 算法的面纱,深入浅出,助你彻底弄懂 Diff 算法原理 认识虚拟 DOM ...

  8. 深入Vue2.x的虚拟DOM diff原理

    一.前言 Vue的核心是双向绑定和虚拟DOM(下文我们简称为vdom),关于双向绑定可以参阅木琴的文章<剖析Vue原理&实现双向绑定MVVM>,vdom是树状结构,其节点为vnod ...

  9. Vue2源码解析 虚拟节点VNode

    目录 1  什么是VNode 2  VNode的作用 3  VNode的类型 3.1  注释节点 3.2  文本节点 3.3  克隆节点 3.4  元素节点 3.5  组件节点 3.6  函数式组件 ...

最新文章

  1. 如何修改hosts文件?
  2. Linux下sed命令替换配置文件中某个变量的值(改变包含字符的一行的值)之二——只改变第一出现的那一行
  3. More Effective C++ 笔记
  4. bzoj1179[Apio2009]Atm
  5. java8 clock_java8新的时间api
  6. 骚操作!使用家里的服务器也能像用云服务器一样方便!
  7. 2017第九届中国云计算大会精彩日程曝光
  8. 试用D触发器和4选1数据选择器74153设计一个双向4位移位寄存器, 具体要求见下表(A、B为控制端), 左移串行输入信号为L, 右移串行输入信号为R
  9. github创建仓库以及上传项目到github
  10. AAE 对抗自编码(一)
  11. Java将图片压缩100KB以下,压缩图片,将几MB的图片压缩成100KB
  12. 单载波频域均衡matlab仿真,包括卷积编码维特比译码,矩阵交织,QPSK调制解调,导频插入,MMSE-FDE频域均衡
  13. 如何利用python将excel表格中筛选出来的每一份数据各自另存为新的excel文件?
  14. stormzhang
  15. Error relaunching VirtualBox VM process:5错误解决
  16. Cheat Engine游戏脚本修改器通关教程(脑残版Step9)
  17. Ptr ds 与ptr ss
  18. lisp绘制法兰_lisp语言画键槽_用LISP语言自定义AutoCAD命令
  19. 前端学习——JSON格式详解
  20. 小白教你制作3D旋转相册

热门文章

  1. C++ move()函数
  2. 5月有哪些值得传播人关注的企业新闻稿?
  3. 关键词热度分析工具_独立站网站数据分析工具:Similarweb 找到竞争对手的关键词和流量来源!...
  4. iSpring Suite教程:使用iSpring创建视频讲座只需简单6步(上)
  5. 专访肖佳:写一本技术图书是水到渠成的
  6. 哪些类型内容的短视频涨粉最快
  7. 自己搭建DNS--Linux
  8. 汽车租赁系统(对象+集合)
  9. 用ffmpeg在Windows11下的命令行模式推流到B站直播间
  10. php简单的日历代码,php日历制作代码分享