资讯专栏INFORMATION COLUMN

【Vue原理】从模板到DOM的简要流程

wenzi / 3124人阅读

摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理从模板到的简要流程今天的计划是,

</>复制代码

  1. 写文章不容易,点个赞呗兄弟
    专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧
    研究基于 Vue版本 【2.5.17

如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧

【Vue原理】从模板到DOM的简要流程

今天的计划是,探索Vue模板挂载到页面是怎么样的一个流程,内容是指 正常 HTML 标签的模板挂载,这部分内容很重要。

而这部分内容也是为了 讲解 Component 作为铺垫,因为到最后 Component 必然也是作为一个正常标签去挂载,所以先把这部分抽出来讲

首先,这个流程,个人认为可以分为两大部分,分别是 init 和 mount

顾名思义,init 必定是和初始化有关,mount 和 挂载DOM 有关

Init

首先,当你开始调用 Vue 的时候,比如这样

</>复制代码

  1. // js
  2. new Vue({
  3. el: document.getElementsByTagName("div")[0],
  4. })
  5. // html,够简洁了吧

那么,先进入的肯定是 Vue 这个构造函数,呈上来!

</>复制代码

  1. function Vue(options) {
  2. this._init(options);
  3. }
  4. Vue.prototype._init = function(options) {
  5. // 初始化 选项,computed,data 之类的
  6. // 初始化实例,给实例绑定些方法
  7. // 触发 beforeCreated,created 钩子
  8. }

这个 _init 方法,是构建Vue 实例的时候调用的,而创建Vue 实例,并非只有通过 new Vue 创建,有可能是 Vue 内部创建的,比如 component

所以,才需要提取出一个 init 方法

然后,init 到这里就结束了,下面就到了另一个流程 mount

Mount

init 结束,就开始解析模板啦,生成DOM 啦,挂载DOM 啦 之类的

开始正文,首先,从什么时候开始?此时需要亮出 _init 方法,没错,就是上面出现的方法

其实在这个方法的最后,有一个调用执行挂载DOM 的方法,如下

</>复制代码

  1. Vue.prototype._init = function(options) {
  2. .....
  3. if (vm.$options.el) {
  4. vm.$mount(vm.$options.el);
  5. }
  6. }

可以看到一句代码,vm.$mount ,没错,就在这里开启了 DOM 挂载的 里程碑

但是,等等,有限制条件 vm.$options.el,也就是,必须有传入 el 才会在 最后调用 挂载DOM

所以,并不是所有的 Vue 实例新建都会在 init 结尾调用 vm.$mount 去挂载DOM,比如 component 两个过程就是分开的

我们还是先来看看 vm.$mount 吧

</>复制代码

  1. Vue.prototype.$mount = function(el) {
  2. return mountComponent(this, query(el))
  3. };
  4. var mount = Vue.prototype.$mount;
  5. Vue.prototype.$mount = function(el) {
  6. ...解析模板,生成模板渲染函数,保存渲染函数到 options
  7. return mount.call(this, el)
  8. }

</>复制代码

  1. 原样呈现了,Vue 中有两个 $mount 函数,第一个的作用是给第二个 调用......如果大家看源码,不要搞混了喂

其中涉及到一个函数,mountComponent,速看

</>复制代码

  1. function mountComponent(vm, el) {
  2. new Watcher(vm, function() {
  3. vm._update(vm._render()
  4. })
  5. return v
  6. }
  7. function Watcher(vm, expOrFn) {
  8. this.getter = expOrFn;
  9. this.get();
  10. }
  11. Watcher.prototype.get = function() {
  12. value = this.getter(vm);
  13. }

上面代码的作用可以说是,为 Vue 实例新建监听者 watcher,并设置一个更新函数

而这个更新函数,会在新建 watcher后 马上执行,就是马上执行了一遍这行代码

</>复制代码

  1. vm._update(vm._render())
1、vm._render

这个函数的作用是,执行之前解析得到的【渲染函数】,渲染函数执行完会返回一个 模板对应的 【VNode】

vm._render 再把这个 vnode 返回

于是就把这个 vnode,传给了 vm._update 中当做了第一个参数

render 函数的内容其实非常的多,但是这里一笔带过,只用知道是用来生成Vnode 就好了,具体的内容会有具体的文章讲解

</>复制代码

  1. Vue.prototype._render = function() {
  2. vnode = render();
  3. return vnode
  4. }
2、vm._update

这个函数的作用是,对比 vnode,挂载更新DOM

1、如果存在旧 vnode,那么会对比旧 vnode 和 刚传入的新 vnode,不断地 patch 得到最小变化单位,从而只更新这部分DOM

2、如果不存在旧 vnode,那么就直接把 vnode 转换为 dom 挂载到页面

其中,生成DOM 和 挂载DOM 用到的方法是 createElm

方法很简单,无非就是通过 标签名创建DOM,然后插入到页面中

</>复制代码

  1. function createElm(vnode, parentElm, refElm) {
  2. var children = vnode.children;
  3. var tag = vnode.tag;
  4. vnode.elm = document.createElement(tag);
  5. // 不断递归遍历子节点
  6. createChildren(vnode, children);
  7. // 插入DOM 节点
  8. insert(parentElm, vnode.elm, refElm);
  9. }
  10. function createChildren(vnode, children) {
  11. if (Array.isArray(children)) {
  12. for (var i = 0; i < children.length; ++i) {
  13. createElm(children[i], vnode.elm, null);
  14. }
  15. }
  16. }
  17. function insert(parent, elm, ref) {
  18. if (parent) {
  19. // 如果存在兄弟节点,就查到兄弟前面
  20. if (ref) {
  21. // 兄弟节点的父节点和 本节点父节点相同
  22. if (ref.parentNode === parent) {
  23. parent.insertBefore(elm, ref);
  24. }
  25. }
  26. // 如果没有兄弟节点,就直接查到父节点最后
  27. else {
  28. parent.appendChild(elm);
  29. }
  30. }
  31. }
总结

两个过程如下

init

1、初始化选项

2、初始化实例

mount

1、解析模板,生成并保存渲染函数

2、新建 watcher 并立即执行更新函数 vm._update(vm._render)

3、vm._render 调用渲染函数生成 VNode,传给 vm._update

4、调用 vm._update,根据 VNode 生成 DOM 并挂载

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105403.html

相关文章

  • Vue原理】Component - 源码版 之 挂载组件DOM

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理源码版之挂载组件由这篇文章从模 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于...

    lbool 评论0 收藏0
  • Vue原理】Component - 白话版

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理白话版从模板上使用到挂载到页面 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于...

    liuyix 评论0 收藏0
  • Vue原理】Event - 源码版 之 绑定标签DOM事件

    摘要:写文章不容易,点个赞呗兄弟专注源码分享,文章分为白话版和源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于版本如果你觉得排版难看,请点击下面链接或者拉到下面关注公众号也可以吧原理源码版之绑定标签事件这里的绑定 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于...

    phoenixsky 评论0 收藏0
  • Vue原理】Compile - 源码版 之 新建实例 compile结束主要流程

    摘要:页面这个实例,按理就需要解析两次,但是有缓存之后就不会理清思路也就是说,其实内核就是不过是经过了两波包装的第一波包装在中的内部函数中内部函数的作用是合并公共和自定义,但是相关代码已经省略,另一个就是执行第二波包装在中,目的是进行缓存 写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 ...

    CODING 评论0 收藏0
  • Vue原理】VNode - 源码版

    摘要:表示虚拟节点,为什么叫虚拟节点呢,因为不是真的节点。因为是对象,不管还是浏览器,都可以统一操作,从而获得了服务端渲染原生渲染手写渲染函数等能力减少操作。 写文章不容易,点个赞呗兄弟专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下...

    layman 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<