vue实现响应式,vue2响应式原理
本文主要介绍Vue2.x响应式和简单的例子,用简单的源代码来说明。感兴趣的朋友可以参考一下,希望能帮到你。
一、回顾Vue响应式用法
Vue响应式,我们都很熟悉。当我们在vue中修改数据对象中的属性时,该属性在页面中被引用的地方也会随之改变。它阻止我们操作dom和绑定数据。
二、Vue响应式实现分析
对于vue的响应式原则,官网上有一段描述https://cn.vuejs.org/v2/guide/reactivity.html的文字。
Vue主要通过数据劫持和观察者模式实现。
数据劫持:
Vue2.x在内部使用object . define property https://developer . Mozilla . org/zh-cn/docs/web/JavaScript/reference/global _ objects/object/define property。
proxy https://developer . Mozilla . org/zh-cn/docs/web/JavaScript/reference/global _ objects/proxy供vue3.x内部使用
观察员模式:https://www.jb51.net/article/219790.htm
成员图表
每个成员的职能
Vue:
将数据中的成员注入到Vue实例中,并将数据中的成员转换为getter和setter。
观察者:
监视数据对象中的简单数据和对象,并在数据发生变化时通知Dep。
编译器:
解析每个元素中的指令/差异表达式,并用相应的数据替换它。
Dep:
观察者模式下的通知程序,添加观察者,并在数据改变时通知观察者。
观察者:
数据中每个引用属性的地方都有一个watcher对象,它负责更新视图
附:数据对象中的属性充当被观察者,数据对象中的属性被引用的地方充当观察者。
三、Vue响应式源码实现
Vue对象实现
功能
负责接受初始化的参数
将数据中的属性注入到数据实例中,并将其转换为getter和setter。
调用Observer来监控数据中所有属性的变化。
调用编译器解析指令和差异表达式。
Vue类{
构造函数(选项){
//1.通过属性保存穿透的属性。
这个。$ options=options { };
这个。$ data=options . data { };
这个。$el=typeof options.el===string ?document . query selector(options . El):options . El;
//2.将数据参数中的数据转换成getter和setter,并将它们挂载到Vue实例上。
这个。_proxyData(this。$data)
//3.调用observe对象来监控数据的变化。
新观察家(这个。$data)
//4.调用编译器对象来呈现页面
新编译器(this)
}
_proxyData(data){
if(data object . keys(data). length 0){
for(数据中的常量键){
Object.defineProperty(this,key,{
可配置:真,
可枚举:真,
get(){
返回数据[键]
},
设置(值){
if (data[key]===value) {
返回;
}
数据[键]=值;
}
})
}
}
}
}
观察者对象实现
功能
劫持数据选项中属性的数据。
如果数据中的一个属性也是一个对象,递归地把它转换成一个响应对象。
发送数据更改通知
//数据劫持类观察者{
构造函数(数据){
this.walk(数据)
}
行走(数据){
//1.确定数据是否是对象。
如果(!数据数据类型!==object) {
返回
}
//2.循环调用defineReactive来劫持数据。
Object.keys(数据)。forEach(key={
this.defineReactive(data,key,data[key])
})
}
defineReactive(obj,key,val) {
//创建通知程序
const dep=新dep()
//使用walk使引用对象中的属性具有响应性
这条路(瓦尔)
const that=this
Object.defineProperty(obj,key,{
可配置:真,
可枚举:真,
get() {
//通知者收集观察者
离开目标部门。添加sub(dep。目标)
返回英国压力单位
},
set(newVal) {
if (newVal===val) {
返回;
}
val=newVal
散步(纽瓦尔)
//被观察者发生变化的时候,通知者对象给每个观察者发送通知
dep.notify()
}
})
}
}
编制对象实现
功能
负责编译模板,解析指令、差值表达式
负责页面首次渲染
当数据发生改变后,负责重新渲染视图
//编译器类编译器{
构造函数(虚拟机){
this.el=vm .$ el
this.vm=vm
this.compile
}
//编译模板判断节点是文本节点还是元素节点
编译(el) {
设子节点=El。子节点;
//处理第一层子节点
Array.from(childNodes).forEach(node={
if (this.isTextNode(node)) {
this.compileText(节点)
} else if(这个。iselementnode(node)){
this.compileElement(节点)
}
//如果当前节点还有子节点递归调用编译指令
如果(节点。子节点节点。子节点。长度){
this.compile(节点)
}
})
}
//编译元素节点,处理指令
编译元素(节点){
//遍历所有的指令
数组. from(节点。属性)。forEach(属性={
//判断是不是指令节点
如果(这个。is指令(属性名称)){
常量节点名=属性名
常量键=属性节点值
const指令=节点名。substr(2)
这个. updater(指令,节点,关键字)
}
})
}
更新程序(指令,节点,键){
const updaterFn=this[指令更新程序]
updaterFn updaterFn.call(this,node,this.vm[key],key)
}
//v文本
文本更新器(节点,值,键){
node.textContent=值
//使用虚拟文本表达式的地方就是一个观察者
新观察器(this.vm,key,newValue={
node.textContent=newValue
})
}
//虚拟模型
模型更新器(节点,值,键){
节点值=值
//使用v型车表达式的地方就是一个观察者
新观察器(this.vm,key,newValue={
节点值=新值
})
//实现双向绑定
node.addEventListener(input ,()={
this.vm[key]=node.value
})
}
//v-html
htmlUpdater(节点,值,键){
node.innerHTML=value
//使用虚拟超文本标记语言表达式的地方就是一个观察者
新观察器(this.vm,key,newValue={
node.innerHTML=newValue
})
}
//处理差值表达式
编译文本(节点){
//匹配差值表达式的正则
设reg=/\{\{(.)\}\}/
//用正则匹配结节的文本内容,如果匹配到了就替换
如果(注册。测试(节点。文本内容)){
//获取插值表达式的键
让key=RegExp .$1;
let value=node.textContent
节点。文本内容=值。replace(reg,this.vm[key])
//使用差值表达式的地方就是一个观察者
新观察器(this.vm,key,newValue={
node.textContent=newValue
})
}
}
//是否是指令
isDirective(attrName) {
返回attrName.startsWith(v-)
}
//是否是文本节点
isTextNode(node) {
返回node.nodeType===3
}
//是否是元素
isElementNode(node) {
返回node.nodeType===1
}
}
资料执行防止对象实现
功能
收集依赖,添加观察者
通知所有观察者
//通知者类类资料执行防止
构造函数(){
//存储观察者
this.subs=[]
}
/**
* 收集观察者
*/
addSub(sub) {
如果(子子更新){
这个。subs.push(子)
}
}
/**
* 通知观察者改变状态
*/
通知(){
this.subs.forEach(sub={
子更新()
})
}
}
看守人对象实现
功能
当数据变化时,Dep通知所有看守人实例更新视图
自身实例化的时候往资料执行防止对象中添加自己
//观察者类类监视器{
构造函数(虚拟机、密钥、cb) {
//Vue实例
this.vm=vm
//数据中的键对象
this.key=key
//更新视图的回调函数
this.cb=cb
//把当前观察者实例存放在资料执行防止的目标静态属性中
部门目标=这
//触发观察的吸气剂方法,把当前实例存放在副潜艇中
//数据中键对应的旧值
这个。旧值=这个。VM【这个。关键]
Dep.target=null
}
//每个观察者都有一个更新方法来改变状态
update(){
const新值=this。VM【这个。关键]
如果(这个。新值===这个。旧值){
返回
}
this.cb(新值)
}
}
测试
头
meta charset=UTF-8
meta http-equiv= X-UA-Compatible content= IE=edge
meta name= viewport content= width=device-width,initial-scale=1.0
标题索引/标题
脚本src= ./js/dep.js/script
脚本src= ./js/watcher.js/script
脚本src= ./js/compiler.js/script
脚本src= ./js/observer.js/script
脚本src= ./js/vue.js/script
/头
身体
p id=应用程序
氕差值表达式/h1
h3{{msg}}/h3
h3{{count}}/h3
h1v-文本/h1
p v-text=msg/p
h1v-型号/h1
输入类型=text v-model=msg attr=msg
输入类型=text v-model=count
h1v-html/h1
p v-html=htmlText/p
/p
脚本
让vm=新Vue({
埃尔: #app ,
数据:{
消息:信息,
计数:数量,
人员:{姓名:张三},
htmlText:p style=color:red 你好/p
}
})
/脚本
/body
到此这篇关于Vue2.x响应式简单讲解及示例的文章就介绍到这了,更多相关Vue2.x响应式内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。