-
Notifications
You must be signed in to change notification settings - Fork 316
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
深入浅出 - vue1.0之State原理 #13
Labels
Comments
Open
九五,来个vue2的 |
@william-xue 哈哈哈,过段时间的,最近我们组的项目没怎么用vue,所以就一直没太深究vue2的实现原理。 |
@liutao 厉害了涛哥 |
学习了,谢谢博主 |
请教个问题: 如我下面的例子,msg_1 改变了会触发 getMsg1 重新计算,但getMsg2 不会重新计算。上面也提到了是因为 computed 有对依赖做了缓存。问题是怎么知道 getMsg1 的依赖对应的是 msg_1 呢?还望解答。
|
你的书什么时候出版呢。 |
@yrl 目前在和出版社审校中,,审校的过程会比较慢,具体什么时候我现在也不太确定~(其实我也很急,哈哈哈) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
深入浅出 - vue之State
本文讲的内容是 vue 1.0 版本~
有些同学可能不知道state是什么,可能还会有疑问,这个跟vuex中的state是不是有啥联系?
在vue文档当中没有在任何地方提到过关于
state
这个单词,所以同学们发蒙是正常的,不用担心所以在一开始我先说说state是什么以及它都包含哪些内容。
State
state 是源码当中的一个概念,State中包含了大家非常熟悉的
Props
、Methods
、Data
、Computed
,vue内部把他们划分为state中方便管理所以本篇文章会详细介绍State中这四个大家常用的api的内部是怎样工作的
Methods
Methods 在我们日常使用vue的时候,使用频率可能是最高的一个功能了,那么它的内部实现其实也特别简单,我先贴一段代码
在看逻辑之前有几个地方我先翻译一下:
_initMethods
这个内部方法是在初始化Methods时执行,就是上面的流程图中的初始化Methodsthis
是当前vue的实例this.$options
是初始化当前vue实例时传入的参数,举个栗子上面实例化Vue的时候,传递了一个Object字面量,这个字面量就是
this.$options
清楚了这些之后,我们看这个逻辑其实就是把
this.$options.methods
中的方法绑定到this
上,这也就不难理解为什么我们可以使用this.xxx
来访问方法了Data
Data 跟 methods 类似,但是比 methods 高级点,主要高级在两个地方,
proxy
和observe
proxy
Data 没有直接写到
this
中,而是写到this._data
中(注意:this.$options.data
是一个函数,data
是执行函数得到的),然后在this
上写一个同名的属性,通过绑定setter和getter来操作this._data
中的数据proxy的实现:
observe
observe 是用来观察数据变化的,先看一段源码:
上面源码中可以看到先处理
_proxy
,之后把data
传入了observe
中,observe
会把data
中的key转换成getter与setter,当触发getter时会收集依赖,当触发setter时会触发消息,更新视图,具体可以看之前写的一篇文章《深入浅出 - vue之深入响应式原理》这地方可能有一个地方不容易理解,observe 在转换 getter 和 setter 的时候是这样转换的
但是我们操作数据是代理到
_data
上的,实际上操作的是_data
,那这个observe
监听的是this.value
,好像有点不对劲?后来我才发现有一个地方忽略了。其实这个地方是同一个引用,
observe
中的this.value
其实就是_initData
中的this._data
,所以给this.value
添加getter 和 setter 就等于给 this._data 设置getter
和setter
总结
总结起来 data 其实做了两件事
this.$options.data
中的数据可以在 this 中访问Computed
计算属性在vue中也是一个非常常用的功能,而且好多同学搞不清楚它跟watch有什么区别,这里就详细说说计算属性到底是什么,以及它是如何工作的
简单点说,
Computed
其实就是一个 getter 和 setter,经常使用Computed
的同学可能知道,Computed
有几种用法如果不希望Computed有缓存还可以去掉缓存
先说上面那两种用法,一种 value 的类型是function,一种 value 的类型是对象字面量,对象里面有get和set两个方法,talk is a cheap, show you a code...
可以看到对两种不同的类型做了两种不同的操作,
function
类型的会把函数当做getter
赋值给def.get
而
object
类型的直接取def.get
当做getter
取def.set
当做setter
。就是这么easy
但是细心的同学可能发现了一个问题,
makeComputedGetter
是什么鬼啊?????直接把def.get
当做getter
就好了啊,为毛要用makeComputedGetter
生成一个getter
???嘿嘿嘿
其实这是vue做的一个优化策略,就是上面最后说的缓存,如果直接把
def.get
当做getter
其实也可以,但是如果当getter
中做了大量的计算那么每次用到就会做大量计算比较消耗性能,如果有很多地方都使用到了这个属性,那么程序会变得非常卡。但如果只有在依赖的数据发生了变化后才重新计算,这样就可以降低一些消耗。
实现这个功能我们需要具备一个条件,就是当
getter
中使用的数据发生变化时能通知到我们这里,也就是说依赖的数据发生变化时,我们能接收到消息,接收到消息后我们在进行清除缓存等操作而vue中具备这项能力的很明显是
Watcher
,当依赖的数据发生变化时 watcher 可以帮助我们接收到消息上面就是
makeComputedGetter
的实现原理代码中
watcher.evaluate()
可以先暂时理解为,执行了getter
求值的过程,计算后的值会保存在watcher.value
中。我们看到求值操作的外面有一个判断条件,当
watcher.dirty
为true
时会执行求值操作其实,这就相当于缓存了,求值后的值存储在
watcher.value
中,当下一次执行到computedGetter
时,如果watcher.dirty
为false
则直接返回上一次计算的结果那么这里就有一个问题,
watcher.dirty
何时为true
何时为false
呢??默认一开始是
true
,当执行了watcher.evaluate()
后为false
,当依赖发生变化接收到通知后为true
上面是
evaluate
的实现,就是这么easy~当
watcher
接收到消息时,会执行update
这个方法,这个方法因为我们的watcher
是lazy
为true
的所以走第一个判断条件里的逻辑,里面很直接,就是把this.dirty
设置了true
这里就又引发了一个问题,我们怎么知道
getter
中到底有哪些依赖,毕竟使用Computed开发的人并不会告诉我们他用到了哪些依赖,那我们怎么知道他使用了哪些依赖?这个问题非常好
vue在全局弄了一个
Dep.target
用来存当前的watcher,全局只能同时存在一个当watcher执行get求值的时候,会先把
Dep.target
设为自己,然后在执行 用户写的getter
方法计算返回值,这时候其实有一个挺有意思的逻辑,data上面我们说过,当数据触发getter
的时候,会收集依赖,那依赖怎么收集,就是通过全局的Dep.target
来收集,把Dep.target
添加到观察者列表中,等日后数据发生变化触发setter
时 执行Dep.target
的notify
,到这不知道大家明白过来没???就是我先把全局的唯一的一个
Dep.target
设置成我自己,然后用户逻辑里爱依赖谁依赖谁,不管你依赖谁都会把我添加到你依赖的那个数据的观察者中,日后只要这个数据发生了变化,我就把this.dirty
设置为true
所以上面看
Watcher.prototype.evaluate
这个代码的逻辑,this.get()
里会设置Dep.target
,等逻辑执行完了他在把Dep.target
设置回最初的到这里关于 Computed 就说完了,在使用上其实它跟 watch 没有任何关系,一个是事件,一个是getter和setter,根本不是同一个性质的东西,但在内部实现上 Computed 又是基于watcher实现的。
Props
props 提供了父子组件之间传递数据的能力,在本文讲的vue 1.x.x 版本中,props分三种类型
静态
、一次(oneTime)
、单向
、双向
静态
我们先说静态,什么是静态props?
静态props就是父组件把数据传递给子组件之后,就不在有任何联系,父组件把数据改了子组件中的数据不会变,子组件把数据改了父组件也不会变,数据传过去后他们俩互相之间就没什么事了~
静态的内部工作原理也比较简单:
组件内会通过
props: ['message']
这样的语法来明确指定子组件组要用到的props,而内部需要做的事就是拿着这些key
直接通过node.getAttribute
在当前el
上读一个value
,然后将读到的value
通过observer
绑到子组件的上下文中,绑定后的value
与当前组件内的data
数据一样一次(oneTime)
其实与静态差不多,只有一点不同,oneTime 的值是从父组件中读来的,什么意思呢?
静态的值是通过
node.getAttribute
读来的,读完后直接放到子组件里。而
oneTime
的值是通过node.getAttribute
先读一个key
,然后用这个key
去父组件的上下文读一个值放到子组件里。所以
oneTime
更强大,因为他可以传递一个用Computed计算后的值,也可以传递一个方法,或什么其他的等等...单向
单向的意思是说父组件将数据通过props传递给子组件后,父组件把数据改了子组件的数据也会发生变化。
单向props内部的工作原理其实也挺简单的,实现单向props其实我们需要具备一项能力:当数据发生变化时会发出通知,而这项能力就是能够接收到通知。
具备这项能力后,当数据发生变化我们可以得到通知,然后将变化后的数据同步给子组件
而具备这项能力的只有
Watcher
,当数据发生变化时,会通知给Watcher
,而Watcher
在更新子组件内的数据。这样就实现了单向props,废话不多说,上代码:解释一下上面代码:
parent
是父组件实例parentKey
是父组件中的一个key,也就是传递给子组件的那个key,是通过这个key在父组件实例中取值然后传递给子组件用的parent
组件的parentKey
发生变化时,执行这个函数,并把新数据传进来updateProp
是用来更新prop的,逻辑很简单,写个伪代码所以工作原理就是当
parent
中的parentKey
这个值发生了变化,会执行更新函数,执行函数中拿到新数据把子组件中的数据更新一下就是这么easy
双向
双向不只是父组件改数据子组件会发生变化,子组件修改数据父组件也会发生变化,实现了父子组件间的数据同步。
双向prop的工作原理与单向的基本一样,只不过多了一个子组件数据变化时,更新父组件内的数据,其实就是多了一个Watcher
其实就是单向prop一个Watcher,双向Prop两个Watcher
twoWay 是用来判断当前Prop的类型是单向还是双向用的
下面提供一个关于Props的流程图
总结
State中的
Props
、Methods
、Data
、Computed
这四个在实际应用中是非常常用的功能,如果大家能弄明白它内部的工作原理,对日后开发效率的提升会有很大的帮助如果有不明白的地方,或者意见或建议都可以在下方评论。
The text was updated successfully, but these errors were encountered: