数据驱动渲染(一)

回忆

        前端的工作,把数据在视图上展示,完善用户交互逻辑。数据驱动渲染,大幅减少前端工作人员对于数据在Dom上展示的工作,只要专注于数据处理即可。这一小节我们了解一下数据驱动渲染的大概过程。

        Vue就是一个Function类,new Vue->_initcreatedbeforeCreated在这个阶段调用。主要做了合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等初始化工作。

        ->$mount,我们不借助vue-loader等编译工具,采用compile版本的vue进行线上编译的话,会先把template编译成render函数。再调用Vue.prototype.$mount,会调用mountComponent(由New Watcher触发update(render),render把render渲染成vnode,update根据vnode通过creat、diff、patch把vnode变成真实Dom插入)。

主线流程图

概述

        Vue.js 一个核心思想是数据驱动。所谓数据驱动,是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据。它相比我们传统的前端开发,如使用 jQuery 等前端库直接修改 DOM,大大简化了代码量。特别是当交互复杂的时候,只关心数据的修改会让代码的逻辑变的非常清晰,因为 DOM 变成了数据的映射,我们所有的逻辑都是对数据的修改,而不用碰触 DOM,这样的代码非常利于维护

        在 Vue.js 中我们可以采用简洁的模板语法来声明式的将数据渲染为 DOM:

模板语法来声明式的将数据渲染为 DOM

        最终它会在页面上渲染出 Hello Vue。接下来,我们会从源码角度来分析 Vue 是如何实现的,分析过程会以主线代码为主,重要的分支逻辑会放在之后单独分析。数据驱动还有一部分是数据更新驱动视图变化,这一块内容我们也会在之后的章节分析,这一章我们的目标是弄清楚模板和数据如何渲染成最终的 DOM

new Vue 发生了什么

        从入口代码开始分析,我们先来分析 new Vue 背后发生了哪些事情。我们都知道,new 关键字在 Javascript 语言中代表实例化是一个对象,而 Vue 实际上是一个类,类在 Javascript 中是用 Function 来实现的。

 Vue 只能通过 new 关键字初始化

初始化

来看一下源码,在src/core/instance/index.js 中调用了init()

instance/init.js

        Vue 初始化主要就干了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等。

合并options到$options
instance/init.js中一堆初始化
$mount做挂载

我们来看看initState(instance/state.js)

instance/state.js看看参数里有什么,有就进行初始化

其中我们主要看initData

1、判断data格式,不是function就报错,是的话就调用data.call(vm,vm)返回return对象取到真正data数据。2、取到data的key分别和props、methods做对比,不能想同,相同就报错,因为他们都要挂载到当前实例。3、把对data的访问代理到_data上。4、响应式处理observe。

        Vue 的初始化逻辑写的非常清楚,把不同的功能逻辑拆成一些单独的函数执行,让主线逻辑一目了然,这样的编程思想是非常值得借鉴和学习的。

        由于我们这一章的目标是弄清楚模板和数据如何渲染成最终的 DOM,所以各种初始化逻辑我们先不看在初始化的最后,检测到如果有 el 属性,则调用 vm.$mount 方法挂载 vm,挂载(这一行为)的目标就是把模板渲染成最终的 DOM,那么接下来我们来分析 Vue 的挂载过程。

$mount实例挂载

        Vue 中我们是通过 $mount 实例方法去挂载 vm 的,$mount 方法在多个文件中都有定义,如 src/platform/web/entry-runtime-with-compiler.jssrc/platform/web/runtime/index.jssrc/platform/weex/runtime/index.js。因为 $mount 这个方法的实现是和平台、构建方式都相关的。接下来我们重点分析带 compiler 版本的 $monut 实现,因为抛开 webpack 的 vue-loader,我们在纯前端浏览器环境分析 Vue 的工作原理,有助于我们对原理理解的深入。

        compiler 版本的 $monut 实现非常有意思,先来看一下 src/platform/web/entry-runtime-with-compiler.js 文件中定义。        

1、缓存了原型上的 $mount 方法,再重新定义该方法。2、它对 el 做了限制,Vue 不能挂载在 body、html 这样的根节点上,否则会报错(因为它会替换掉index.html内相应el对应节点)。

         compile版本的话,先对我们el做一些处理,在没有定义render函数时候,尝试去获取reder函数(把整个template通过一系列逻辑判断,因为template有很多实现方式,比如可以直接写template,也可以template是个dom,不写的话通过el获取dom,再把template通过编译手段转化成render函数),再去调用mountComponent(定义了updateComponent函数),updateComponent函数实际上定一个Watcher进行渲染(实际上执行了真正的渲染),视图首次渲染、更新渲染,都通过它来完成。

        $mount 方法支持传入 2 个参数,第一个是 el,它表示挂载的元素,可以是字符串,也可以是 DOM 对象,如果是字符串在浏览器环境下会调用 query 方法转换成 DOM 对象的第二个参数是和服务端渲染相关,在浏览器环境下我们不需要传第二个参数。

         $mount 方法实际上会去调用 mountComponent 方法(core/instance/lifecyle.js)原先原型上的 $mount 方法在这里重新定义,之所以这么设计完全是为了复用,因为它是可以被 runtime only 版本的 Vue 直接使用的,同时也要被compile版本使用。

3、如果$options没有render方法,则会把el或者template字符串编译成render方法。因为无论我们是用单文件 .vue 方式开发组件,还是写了 el 或者 template属性,最终都会转换成 render 方法。
4、在线编译都是调用compileToFunction方法拿到render和static函数赋值给$options,过程我们之后看。5、编译完了,正常调用原型上的$mount方法挂载。

接下来我们看看原型上的mount方法 它在core/instance/lifecyle.js

1、把el缓存到vm.$el上。2、对$options.render函数判断,不存在就赋予一个空虚拟节点。3、对开发环境的$options的render和template做检查,报错。
4、定义undateComponent函数(用于真实dom渲染),由Watcher执行。渲染过程除了首次,之后在更新过程还是会触发渲染Watcher进行执行updateComponent。渲染Watcher实际上就是一个监听到执行的过程。

        mountComponent核心就是先调用 vm._render 方法先生成虚拟 Node,再实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,最终调用 vm._update 更新 DOM。

        Watcher 在这里起到两个作用,一个是初始化的时候会执行回调函数(updateComponent),另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数(updateComponent),这块儿我们会在之后的章节中介绍。

        函数最后判断为根节点的时候设置 vm._isMounted 为 true, 表示这个实例已经挂载了,同时执行 mounted 钩子函数。 这里注意 vm.$vnode 表示 Vue 实例的父虚拟 Node,所以它为 Null 则表示当前是根 Vue 的实例。

        mountComponent 方法的逻辑也是非常清晰的,它会完成整个渲染工作,接下来我们要重点分析其中的细节,也就是最核心的 2 个方法:vm._render 和 vm._update。    

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 源码版本:v2.1.10 分析目标 通过阅读源码,对 Vue2 的基础运行机制有所了解,主要是: Vue2 中数据...
    NARUTO_86阅读 12,292评论 6 26
  • 前言 Vue.js 的核心包括一套“响应式系统”。 “响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码...
    NARUTO_86阅读 37,470评论 8 86
  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 5,048评论 0 29
  • 从小A的生活讲起,没有界线的日子是如何失去生活的主导权: 小A原本想在晚上下班后给女儿准备演出的衣服,...
    伈漪阅读 202评论 1 1
  • (原谅我用苏哥的图片,太有气质勿怪) (一) 京中书店是华...
    西月微冷阅读 891评论 6 10