Vue模板挂载到页面源码简要解析

先了解下挂载基础原理:可由html加载过程去联想vue模板挂载到页面的过程。

什么是挂载?

挂载一词来自操作系统的概念,原意是虚拟文件系统与硬件驱动建立关系,通过操作虚拟文件系统,间接操作真实的文件
打个比方,u盘操作的实质是通过虚拟文件系统建立与真实u盘文件的映射关系,用户操作的文件增删改查都是针对虚拟文件目录,虚拟文件系统会处理真实的文件操作。
u盘真实文件系统与虚拟文件系统建立关系的过程,叫做挂载。

解析

将html转化为dom树过程称为解析
Node 节点
Node节点包含了标签、属性、文本等,定义了结点对象的行为,就是利用对象的方法和属性,可以方便地访问、修改、添加和删除DOM树的结点和内容。

html加载

1.加载html文件过程
排队-》与代理服务器连接通讯-》DNS解析(不同域名)-》建立连接-》发送请求-》等待服务器相应,即第一个字节发过来-》接受相应数据,即整个html文档内容数据-》直到接收到最后一个字节。
2、开始解析html
2.1 解析meta信息,构建基本DOM树。
2.2 浏览器匹配出所有引用文件,使它们都处于挂起状态。
回到浏览器上,虚拟dom与真实dom建立关系的过程,也可以称作挂载。因为都是“虚拟”与“真实”建立联系,用户操作“虚拟”部分。
vue挂载,可以看成 让Vue实例控制页面中的某个区域的过程,称之为挂载

解析渲染过程

解析HTML
构建DOM树
DOM树与CSS样式进行附着构造呈现树
布局
绘制

Vue模板挂载到页面是怎么样的一个流程

初始化阶段 new Vue时,执行 Vue构造函数 ,执行_init方法 ,代码简写如下:
//初始化vue构造函数
function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword');
  }
  this._init(options);
}

//vue被引入后,相关初始化,初始化完成后,导出vue

//注册vm的_init方法,初始化vm
initMixin(Vue);

//定义vm的$data、$props、$set 、$delete属性和$watch 方法
stateMixin(Vue);

//初始化vm的$on、$once 、$off、$emit事件相关方法
eventsMixin(Vue);

//初始化生命周期相关方法 _update $forceUpdate  $destroy
lifecycleMixin(Vue);


//定义vm的 $nextTick、 _render方法
//执行render函数,生成虚拟dom 
renderMixin(Vue);

initGlobalAPI(Vue);//定义了静态方法和属性delete: ƒ del(target, key)
// nextTick: ƒ nextTick(cb, ctx)
// observable: ƒ (obj)
// options:
// components: {}
// directives: {}
// filters: {}
// _base: ƒ Vue(options)
// set: ƒ (target, key, val)
// util: {warn: ƒ, extend: ƒ, mergeOptions: ƒ, defineReactive: ƒ}
// config: (...)

export default Vue;
_init方法调用了$mount
Vue.prototype._init = function(options) {
    .....
    if (vm.$options.el) {
        vm.$mount(vm.$options.el);
    }
}

Vue.prototype.$mount = function (
  el,
  hydrating
) {
  el = el && inBrowser ? query(el) : undefined;
  return mountComponent(this, el, hydrating)
};
mountComponent 省略了部分代码,挑选主要的
 if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = function () {
    ...
      vm._update(vnode, hydrating);
     ...
    };
  } else {
    updateComponent = function () {
      vm._update(vm._render(), hydrating);
    };
  }
vm._render

这个函数的作用是,执行之前解析得到的【渲染函数】,渲染函数执行完会返回一个 模板对应的 【VNode】
vm._render 再把这个 vnode 返回
于是就把这个 vnode,传给了 vm._update 中当做了第一个参数
render 函数的内容其实非常的多,但是这里一笔带过,只用知道是用来生成Vnode 就好了

  Vue.prototype._render = function() {
     vnode = render.call(vm._renderProxy, vm.$createElement);
    return vnode

}
	

render.call执行过程:js call()可见我的另一篇博客 初识js call()方法以及call()使用实例

render:(function anonymous() {
		with(this){return _c('App')}
	})
	vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
	render.call(vm._renderProxy, vm.$createElement) 可以翻译成:
	(function anonymous(
	) {
	with(vm._renderProxy){
	    return _c('App')}
	})
	_c('App'):function _c ('App', b, c, d) { return createElement(vm, 'App', b, c, d, false); }
	即最终执行 : createElement(vm, 'App', b, c, d, false)
render() 会调取createElement 方法 生成 Vnode 通过_update方法 渲染在页面上

function createElement(
    context, tag, data, 
    children, normalizationType
) {    
    var vnode;    
    if (tag是正常html标签) {
        vnode = new VNode(
            tag, data, children, undefined, 
            undefined, context
        );
    } 
    else if (tag 是组件) {
        vnode = createComponent(
            Ctor, data, context, 
            children, tag
        );
    }    
    return vnode;
插播,虚拟节点知识

VNode 表示 虚拟节点 Virtual DOM,为什么叫虚拟节点呢,因为不是真的 DOM 节点。
他只是用 javascript 对象来描述真实 DOM,这么描述,把DOM标签,属性,内容都变成 对象的属性
就像用 JavaScript 对象描述一个人一样
结构


<h1 class="parent" style="height:0" href="2222">hello h1</h1>
VNode {
    tag: 'h1', 
    data:{
        attrs: {href: '2222'} ,
        staticClass: "parent",
        staticStyle: {height: '0'}
    }, 
    children:[{text: "hello h1",}],
    ........
}
typeof vnode 'object'
图片 模板

请添加图片描述

我们看到,Vue用一个JavaScript对象描述了编译出来的模板(如果有数据绑定,它还会描述模板与数据的绑定关系)。
接下来只需要调用原生的DOM方法依次创建这里的每一个节点,然后将它们挂载成一棵DOM子树,并插入页面,就可以得到真正的HTML。
我们一般把这个树状JavaScript对象称为虚拟DOM树。下面是上面的JavaScript对象对应的DOM结构:

图片js虚拟dom树请添加图片描述
VNode有什么用?

·兼容性强,不受执行环境的影响。VNode 因为是 JS 对象,不管 Node 还是 浏览器,都可以统一操作, 从而获得了服务端渲染、原生渲染、手写渲染函数等能力
2减少操作 DOM。任何页面的变化,都只使用 VNode 进行操作对比,只需要在最后一步挂载更新DOM,不需要频繁操作DOM,从而提高页面性能

vm._update

这个函数的作用是,对比 vnode,挂载更新DOM
1、如果存在旧 vnode,那么会对比旧 vnode 和 刚传入的新 vnode,不断地 patch 得到最小变化单位,从而只更新这部分DOM
2、如果不存在旧 vnode,那么就直接把 vnode 转换为 dom 挂载到页面
其中,生成DOM 和 挂载DOM 用到的方法是 createElm
方法很简单,无非就是通过 标签名创建DOM,然后插入到页面中 insert
vm._update简写

Vue.prototype._update = function (vnode, hydrating) {
    var vm = this;
    var prevEl = vm.$el;
    var prevVnode = vm._vnode;
    var restoreActiveInstance = setActiveInstance(vm);
    vm._vnode = vnode;
    
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode);
    }
    restoreActiveInstance();
   
    // if parent is an HOC, update its $el as well
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el;
    }
    // updated hook is called by the scheduler to ensure that children are
    // updated in a parent's updated hook.
  };

  Vue.prototype.__patch__ = inBrowser ? patch : noop;

function createElm(vnode, parentElm, refElm) {  
    var children = vnode.children;    
    var tag = vnode.tag;
    vnode.elm = document.createElement(tag);   
    // 不断递归遍历子节点
    createChildren(vnode, children);    
    // 插入DOM 节点
    insert(parentElm, vnode.elm, refElm);
}



function createChildren(vnode, children) {    
    if (Array.isArray(children)) {        
        for (var i = 0; i < children.length; ++i) {
            createElm(children[i], vnode.elm, null);
        }
    }
}


function insert(parent, elm, ref) { 
    if (parent) {   
        // 如果存在兄弟节点,就查到兄弟前面
        if (ref) {       
            // 兄弟节点的父节点和 本节点父节点相同
            if (ref.parentNode === parent) {
                parent.insertBefore(elm, ref);
            }
        } 
        // 如果没有兄弟节点,就直接查到父节点最后
        else {
            parent.appendChild(elm);
        }
    }

}

参考 https://blog.csdn.net/qq_27460969/article/details/95043803
https://blog.csdn.net/qq_41694291/article/details/108435096

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值