Vue数据绑定(一)
Vue作为当下炙手可热的前端三大框架之一,一直都想深入研究一下其内部的实现原理,去学习MVVM
模式的精髓。如果说MVVM
是当下最流行的图形用户界面开发模式,那么数据绑定则是这一模式的根基。这也是我为什么要从数据绑定开始了解Vue的原因。
本篇文章首先从Vue构建开始,后面主要了解methods
、data
的执行过程以及原理,结合Vue文档来分析,做到知其然且知其所以然。对于计算属性、组件系统、指令等将在后续文章中分析。
源代码基于vue1.0,最新版本为2.x,其中的差异我会在文章尽量列出来。
Vue构造过程
1 |
function (options) { |
Vue构造函数调用了一个_init
函数,Vue所有的内置属性和方法都以_或者$开头:
1 |
exports.isReserved = function (str) { |
_init
函数调用了若干个初始化函数其中就包含了一个初始化状态属性相关的函数:
1 |
//instance/state.js |
看到调用函数的名称都知道是什么意思,这里主要研究一下_initMethods
和_initData
两个函数的实现原理。其余的会在后续文章分析。
_initMethods
:
1 |
exports._initMethods = function () { |
对于methods
的初始化相对比较简单,这个函数的主要作用就是把用户定义在methods属性内的一些方法绑定到当前的Vue实例中。由于ES6的箭头函数会导致bind
失败,这也是为什么Vue在文档中提示:
不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch(‘a’, newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。
_initData
:
1 |
exports._initData = function () { |
对于子组件而言,propsData
表示父组件传递过来的数据,因为initProp先执行_data
填充的是父组件传递过来的数据。optionsDataFn
表示组件自身的数据。 为什么这里看到的是一个函数呢?这是因为在Vue的初始化函数_init
内调用了util/option.js
下的mergeOptions
这个方法,为了方便合并父组件和子组件的数据,它定义了一系列策略把组件传入的参数替换了。为了避免父组件的数据被子组件原生的数据覆盖需要做一次判定,发现有数据覆盖就警告用户。需要注意的是属性值为null且子组件原生就有的数据字段是不会被覆盖的。
在把数据合并之后,接下来要对组件数据做一个代理:
1 |
//... |
数据代理的作用就是为了实现:vm.prop === vm._data.prop
的效果。代码位置在instance/state.js
下的_proxy
函数:
1 |
exports._proxy = function (key) { |
为了避免覆盖Vue内置的属性所以做一次判定,接下来就是对数据的访问做一个代理。
仅仅代理数据是不够的,接下来要看到的是监控数据的变化:
1 |
exports._initData = function () { |
Observer.create
是Vue响应式数据绑定的核心:
1 |
Observer.create = function (value, vm) { |
数据监听只针对对象类型,监听对象会内嵌到被监听的对象,这样可以避免重复监听数据对象:
1 |
function Observer (value) { |
需要注意的是,Vue对象实例不会被监听,通过_isVue
属性来辨别。对于被冻结的对象也是不能监听的,Vue通过接口Object.isFrozen
来判定,官方文档也有说明:
这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。
Observer对象会反向引用Vue实例对象,这是为了在用户调用$delete
的时候能够反向通知到Vue实例对象, 把挂在实例上的被删除属性去除:
1 |
exports.delete = function (obj, key) { |
数据的变化追踪分为两类:对象和数组类型。对象类型遍历属性监听每个属性的变化:
1 |
Observer.prototype.walk = function (obj) { |
convert
函数调用了数据追踪最关键的一个函数:
1 |
function defineReactive (obj, key, val) { |
由于对象的属性可能还是一个对象或者数组。所以需要递归的追踪内嵌数据的变化。数据的监听者存放在Dep
模块内。每次设置新的对象需要重新监听数据属性。
数组类型的数据监听追踪比较特殊,Vue通过拦截几个数组方法来追踪数组的变化
1 |
function Observer (value) { |
arrayMethods是一个以Array.prototype
为原型的对象://observer/array.js
1 |
var arrayProto = Array.prototype |
通过_.hasProto
方法判定代理数组对象的若干个方法:
1 |
function protoAugment (target, src) { |
至此Vue的数据追踪流程执行完毕。Vue提供了两个全局方法Vue.set
和Vue.delete
。下面来研究一下两个函数的实现,Vue.set
最终会调用到util/lang.js
下的set
方法:
1 |
exports.set = function set (obj, key, val) { |
如果设置的属性之前已经有了,这个时候直接设置就行,会促发相应的更新逻辑。如果是Vue对象则设置到_data
属性内。如果数据对象不是响应式的则直接新增数据属性。这个时候不会触发视图更新等操作。反之通知相应的监听方,并且递归追踪新增的数据值。Vue官方文档有如下提示:
向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性。
Vue.delete
最终调用util/lang.js
下的delete
方法。
1 |
exports.delete = function (obj, key) { |
被删除的属性如果不是响应式的,则直接删除然后退出函数。反之,通知各个监听对象,并且通过_unproxy
方法把挂在Vue
实例上的属性删除。
Vue数据绑定(一)的更多相关文章
- Vue数据绑定
gitHub地址:https://github.com/lily1010/vue_learn/tree/master/lesson04 一 双括号用来数据绑定 (1)写法一: {{message}}, ...
- 浅析vue数据绑定
前言:最近团队需要做一个分享,脚进脑子,不知如何分享.最后想着之前一直想研究一下 vue 源码,今天刚好 "借此机会" 研究一下. 网上研究vue数据绑定的文章已经非常多了,但是自 ...
- Vue数据绑定和响应式原理
Vue数据绑定和响应式原理 当实例化一个Vue构造函数,会执行 Vue 的 init 方法,在 init 方法中主要执行三部分内容,一是初始化环境变量,而是处理 Vue 组件数据,三是解析挂载组件.以 ...
- 17: VUE数据绑定 与 Object.defineProperty
VUE数据绑定原理:https://segmentfault.com/a/1190000006599500?utm_source=tag-newest Object.defineProperty(): ...
- (三)vue数据绑定及相应的命令
vue数据绑定及相应的命令 {{ Text }} 双括号进行数据渲染 动态绑定数据 例如:{{message}} data: { return{ message: 'Hello Vue!' } } 2 ...
- 「每日一题」有人上次在dy面试,面试官问我:vue数据绑定的实现原理。你说我该如何回答?
关注「松宝写代码」,精选好文,每日一题 时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 来源:原创 一.前言 文章首发在「松宝写代码」 2020. ...
- vue数据绑定原理
一.定义 vue的数据双向绑定是基于Object.defineProperty方法,通过定义data属性的get和set函数来监听数据对象的变化,一旦变化,vue利用发布订阅模式,通知订阅者执行回调函 ...
- vue 数据绑定实现的核心 Object.defineProperty()
vue深入响应式原理 现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简 ...
- vue数据绑定数组,改变元素时不更新view问题
关于这个问题,官网上说的很清楚官方文档 写个例子HTML<body> <div class="box"> <div v-for="aa i ...
随机推荐
- [原]PInvoke导致栈破坏
原, 总结, 调试, 调试案例 项目中遇到一个诡异的问题,程序在升级到.net4.6.1后会崩溃,提示访问只读内存区.大概现象如下: debug版不崩溃,release版稳定崩溃. 只有x64位的程 ...
- Kubernetes系列三:二进制安装Kubernetes环境
安装环境: # 三个节点信息 192.168.31.11 主机名:env11 角色:部署Master节点/Node节点/ETCD节点 192.168.31.12 主机名:env12 角色:部署Node ...
- c++17 optional 简介
c++17 的optional 作为返回值,提高异常的处理 对于构造可能失败的对象,似乎在构造函数里抛异常不太合适.所以 首先是使用静态方法来返回对象,在外部申请资源后,再通过传参给构造函数. 这样的 ...
- Java统计内存
在目标代码前放置 Runtime r = Runtime.getRuntime(); r.gc(); long startMem = r.freeMemory(); // 开始时的剩余内存 目标代码执 ...
- 量化投资_TB交易开拓者A函数和Q函数常见组合应用
1 在交易开拓者当中,关于交易的做单方式一般分为:图表函数和A函数两类. 两类的主要区别为:如果采用图表函数的话,所有的交易内容都是以图表上面的信号为准,当前仓位运行的实际状态是没有的,但是可以显示交 ...
- 吴裕雄--天生自然python学习笔记:python 用pygame模块处理音频文件
除了对图片. Word 等普通格式的文件进行处理外, Python 还有强大的多媒体文件操作能力,如对音频.视频 文件的操作 . 如果要播放音乐,我们可以用 pygame 包中的 mixer 对 象. ...
- Seikimatsu Occult Tonneru(网络流,状态数(建不建边)不多时,可考虑直接进行枚举
http://acm.hdu.edu.cn/showproblem.php?pid=4309 总结:边可存东西时,可新建一个点x连接u.v,x再连向汇点: #include<iostream&g ...
- [LC] 398. Random Pick Index
Given an array of integers with possible duplicates, randomly output the index of a given target num ...
- HashMap相关知识
HashMap的工作原理是近年来常见的Java面试题.几乎每个Java程序员都知道HashMap,都知道哪里要用HashMap,知道Hashtable和HashMap之间的区别,那么为何这道面试题如此 ...
- linux环境下卸载mysql
第一种使用yum安装的mysql,使用如下命令进行卸载(不能确定使用何种方式安装的mysql情况下,按后续步骤一一进行处理即可): # yum remove mysql mysql-server my ...