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 ...
随机推荐
- 手动安装GCC4.8.5
服务器是 redhat 6,安装xgboost时,提示自带gcc 太老, 需要手动升级. 1). 手动安装 mpc-0.8.2.tar.gz, 用默认参数, 安装完后添加系统变量 export LD_ ...
- GO、 智能合约、cannot use transactionRecordId + strconv.Itoa(id) (type string) as type byte in append
1.报错详情 2.在写fabric go智能合约发送的错误,像我这样的新手就是踩坑踩坑踩坑 3.下面是代码片段 4.研究了一下append用法.也看了下GO语言官网文章: var test_str [ ...
- 6)PHP,预定义变量
预定义变量也叫超全局变量: :预定义变量又叫超全局变量,包括: $_GET, $_POST, $_SERVER, $_REQUEST, $GLOBALS, $_COOKIE, $_SESSION, . ...
- MySQL获取或者查询数据库某个字段的特定几位(substring)
一.获取特定的几位: date字段值为(2019-12-13) 1.取date的后5位 select SUBSTRING(date,-5)from letter 结果为12-13 2从左开始第6位取( ...
- 29)PHP,自动加载类
(1)作用: 类的自动加载是指,在外面的页面中,并不需要去“引入”(包含)类文件,但是程序会在需要一个类的时候就自动去“动态加载”该类. (2)什么叫做“需要一个类”?通常是这样的情况: 1,创建一个 ...
- 应用HTML5 标签下载文件
使用HTML5 <a>标签可以直接下载文件而不用通过后台action. <a href="/uploadfolder/xxxx.txt">点击下载</ ...
- php 二维码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- F5 BIG-IP之一前期随笔(应用交付网络产品)
ADN:应用交付网络 TMOS: Traffic managment operation system (流量管理系统) 一个实时的全应用代理流量管理操作系统 PVA ...
- 限制IP每分钟访问10次
转载:https://www.jianshu.com/p/d1326ab657ff IP请求限制,之前用过redis的set设置时间戳一分钟过期:也用过nginx的IP限流配置.前者,没法解决“用户在 ...
- 五、RabbitMQ Java Client基本使用详解
Java Client的5.x版本系列需要JDK 8,用于编译和运行.在Android上,仅支持Android 7.0或更高版本.4.x版本系列支持7.0之前的JDK 6和Android版本. 加入R ...