vue2.x响应式原理,vue与react响应式简单对比
配合ppt食用更佳ppt 实现的最终目标const demo = new Vue({
data: {
text: "before",},// 对应的template 为 <div><span>{{text}}</span></div>
render(h){
return h('div',{},[
h('span',[this.__toString__(this.text)])
])
}
})
setTimeout(function(){
demo.text = "after"
},3000)
对应的虚拟DOM会从 <div><span>before</span></div>
变成 <div><span>after</span></div>
第一步,监听data下边的所有属性,转换为响应式思路
方案:ES5中新添加了一个方法:Object.defineProperty,通过这个方法,可以自定义 代码如下: class Vue {
constructor(options) {
this.$options = options
this._data = options.data
observer(options.data,this._update.bind(this))
this._update()
}
_update(){
this.$options.render()
}
}
function observer(obj,cb) {
Object.keys(obj).forEach((key) => {
defineReactive(obj,key,obj[key],cb)
})
}
function defineReactive(obj,val,cb) {
Object.defineProperty(obj,{
enumerable: true,configurable: true,get: () => {
console.log('你访问了' + key)
return val
},set: newVal => {
if (newVal === val)
return
console.log('你设置了' + key)
console.log('新的' + key + ' = ' + newVal)
val = newVal
cb()
}
})
}
var demo1 = new Vue({
el: '#demo',data: {
text: "before"
},render(){
console.log("我要render了")
}
})
var demo2 = new Vue({
el: '#demo',data: {
text: "before",o: {
text: "o-before"
}
},render(){
console.log("我要render了")
}
})
方案:用递归完善上边的响应式,需要在它开始对属性进行响应式转换的时候,前边加个判断,即如下 function observer(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object') {
new observer(obj[key],cb)
}
defineReactive(obj,obj[key])
})
}
代码如下: _proxy(key) {
const self = this
Object.defineProperty(self,{
configurable: true,enumerable: true,get: function proxyGetter() {
return self._data[key]
},set: function proxySetter(val) {
self._data[key] = val
}
})
}
然后在构造函数中加上这么一句话 Object.keys(options.data).forEach(key => this._proxy(key))
到此,我们的 第二步,解决准确度问题,引出虚拟dom比如下边的demo new Vue({
template: `
<div>
<span>name:</span> {{name}} <div>`,data: { name: 'js',age: 24 } }) setTimeout(function(){ demo.age = 25 },3000)
解决这个问题,先要简单说下虚拟dom。vue有两种写法: // template模板写法(最常用的)
new Vue({
data: {
text: "before",template: `
<div>
<span>text:</span> {{text}} </div>` }) // render函数写法,类似react的jsx写法 new Vue({ data: { text: "before",render (h) { return ( <div> <span>text:</span> {{text}} </div> ) } })
由于vue2.x引入了虚拟dom的原因,这两种写法最终都会被解析成虚拟dom,但在这之前,他们会先被解析函数转换成同一种表达方式,即如下: new Vue({
data: {
text: "before",render(){
return this.__h__('div',[
this.__h__('span',[this.__toString__(this.text)])
])
}
})
透过上边的 function VNode(tag,data,children,text) {
return {
tag: tag,// html标签名
data: data,// 包含诸如 class 和 style 这些标签上的属性
children: children,// 子节点
text: text // 文本节点
}
}
写一个简单的虚拟dom: function VNode(tag,data: data,children: children,text: text
}
}
class Vue {
constructor(options) {
this.$options = options
const vdom = this._update()
console.log(vdom)
}
_update() {
return this._render.call(this)
}
_render() {
const vnode = this.$options.render.call(this)
return vnode
}
__h__(tag,attr,children) {
return VNode(tag,children.map((child)=>{
if(typeof child === 'string'){
return VNode(undefined,undefined,child)
}else{
return child
}
}))
}
__toString__(val) {
return val == null ? '' : typeof val === 'object' ? JSON.stringify(val,null,2) : String(val);
}
}
var demo = new Vue({
el: '#demo',[this.__toString__(this.text)])
])
}
})
回头看问题,也就是说,我需要知道 第三步,依赖收集,准确渲染思路:在这之前,我们已经把 其实不仅是 首先需要写一个依赖收集的类,每一个 class Dep {
constructor() {
this.subs = []
}
add(cb) {
this.subs.push(cb)
}
notify() {
console.log(this.subs)
this.subs.forEach((cb) => cb())
}
}
function defineReactive(obj,cb) {
const dep = new Dep()
Object.defineProperty(obj,{
// 省略
})
}
那么执行过程就是:
最后完整代码如下: function VNode(tag,text: text
}
}
class Vue {
constructor(options) {
this.$options = options
this._data = options.data
Object.keys(options.data).forEach(key => this._proxy(key))
observer(options.data)
const vdom = watch(this,this._render.bind(this),this._update.bind(this))
console.log(vdom)
}
_proxy(key) {
const self = this
Object.defineProperty(self,set: function proxySetter(val) {
self._data[key] = val
}
})
}
_update() {
console.log("我需要更新");
const vdom = this._render.call(this)
console.log(vdom);
}
_render() {
return this.$options.render.call(this)
}
__h__(tag,children.map((child) => {
if (typeof child === 'string') {
return VNode(undefined,child)
} else {
return child
}
}))
}
__toString__(val) {
return val == null ? '' : typeof val === 'object' ? JSON.stringify(val,2) : String(val);
}
}
function observer(obj) {
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object') {
new observer(obj[key])
}
defineReactive(obj,obj[key])
})
}
function defineReactive(obj,val) {
const dep = new Dep()
Object.defineProperty(obj,get: () => {
if (Dep.target) {
dep.add(Dep.target)
Dep.target = null
}
console.log('你访问了' + key)
return val
},set: newVal => {
if (newVal === val)
return
console.log('你设置了' + key)
console.log('新的' + key + ' = ' + newVal)
val = newVal
dep.notify()
}
})
}
function watch(vm,exp,cb) {
Dep.target = cb
return exp()
}
class Dep {
constructor() {
this.subs = []
}
add(cb) {
this.subs.push(cb)
}
notify() {
this.subs.forEach((cb) => cb())
}
}
Dep.target = null
var demo = new Vue({
el: '#demo',test: {
a: '1'
},t: 1
},render() {
return this.__h__('div',[this.__toString__(this.text)]),this.__h__('span',[this.__toString__(this.test.a)])
])
}
})
vue react响应式简单对比综上发现,利用 没有对比就没有伤害:
通过对比发现, 参考
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |