写代码啦
Vue 数据响应式
回复数(0) 浏览数(42)
{{topic.upvote_count || 0}} 编辑 回复

今天学习了 Vue 的数据响应式,有点不太好理解,所以写一篇博客整理一下思绪。
要理解 Vue 的数据响应式,首先我们要理解几个概念。

什么是响应式

如果一个物体能对外界的刺激做出反应,那么它就是响应式的。

Vue 的 data 是响应式的

const myData = {
  n: 0
}

new Vue({
  data: myData,
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");

setTimeout(()=>{
  myData.n += 10
}, 3000)

我们修改了 myData 的 n,我们在界面中看到 n 响应了我,这就是数据响应式。
Vue 2 通过 Object.defineProperty 来实现数据响应式,那么 Object.defineProperty 怎么用呢?

Object.defineProperty

let data0 = {
  n: 0
}

// 需求一:用 Object.defineProperty 定义 n
let data1 = {}

Object.defineProperty(data1, 'n', {
  value: 0
})

console.log(`需求一:${data1.n}`)
let data2 = {}

data2._n = 0 // _n 用来偷偷存储 n 的值

Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){
    if(value < 0) return
    this._n = value
  }
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)

Object.defineProperty 的用处:
1. 可以给对象添加属性 value
2. 可以给对象添加 getter / setter
3. getter / setter 用于对属性的读写进行监控

我们看看上面的代码,我们在思考,怎么样才能让我们可以监控对 n 的修改,而且不要让外部特意操作而影响到我们,我们可以使用代理。

啥是代理

let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问

function proxy2({data}/* 解构赋值 */){
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })

  return obj // obj 就是代理
}

// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)

对 myData 的属性读写,全权由另一个对象 vm 负责,那么 vm 就是 myData 的代理。我们不用 myData.n 来操作而是用 vm.n 来操作,这就是代理。
我们看上面的代码,就看到一个特别熟悉的结构。
vm = new Vue({data:myData}),那这会让我们发生什么呢。

vm = new Vue({data:myData})

所以这句话到底会产生什么结果?
1. 会让 vm 称为 myData 的代理
2. 会对 myData 的所有属性进行监控
3. 只要属性变化了,我们就可以进行重新的渲染了

这就是 Vue 的数据响应式的概念。但是其中似乎是存在了一些 bug,比如。

Vue 只会检查第一层属性

new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1; //请问,页面中会显示 1 吗?
    }
  }
}).$mount("#app");

这里的 b 并不会被监听,因为 Vue 不会监听一开始不存在的 obj.b。那我们有什么办法呢?
1. 先把所有的 key 都声明好,然后后面添加属性
2. 使用 Vue.set 或者 this.$set

对象中使用 set 可以新增 key,创建代理和监听,更新 UI。但是数组怎么办呢?

data 中有数组怎么办?

数组的长度是可以一直增加的,数组的 key 就是数组的下标,我们不可能把下标全都声明出来,难道我们要每次都使用 set 吗?
Vue 作者想到了一个办法,就是篡改数组的 API。作者添加了7个变异方法:push()、pop()、shift()、unshift()、splice()、sort()、reverse(),只要使用这些方法就会自动处理对数组的代理和监听并且更新 UI。

扯得稍微有一些远,这大概就是 Vue 的数据响应式的概念。暂时只能理解到这里,如果还有其他的想说的,会在以后的博客中继续说明。

{{topic.upvote_count || 0}}

今天学习了 Vue 的数据响应式,有点不太好理解,所以写一篇博客整理一下思绪。
要理解 Vue 的数据响应式,首先我们要理解几个概念。

什么是响应式

如果一个物体能对外界的刺激做出反应,那么它就是响应式的。

Vue 的 data 是响应式的

const myData = {
  n: 0
}

new Vue({
  data: myData,
  template: `
    <div>{{n}}</div>
  `
}).$mount("#app");

setTimeout(()=>{
  myData.n += 10
}, 3000)

我们修改了 myData 的 n,我们在界面中看到 n 响应了我,这就是数据响应式。
Vue 2 通过 Object.defineProperty 来实现数据响应式,那么 Object.defineProperty 怎么用呢?

Object.defineProperty

let data0 = {
  n: 0
}

// 需求一:用 Object.defineProperty 定义 n
let data1 = {}

Object.defineProperty(data1, 'n', {
  value: 0
})

console.log(`需求一:${data1.n}`)
let data2 = {}

data2._n = 0 // _n 用来偷偷存储 n 的值

Object.defineProperty(data2, 'n', {
  get(){
    return this._n
  },
  set(value){
    if(value < 0) return
    this._n = value
  }
})
console.log(`需求二:${data2.n}`)
data2.n = -1
console.log(`需求二:${data2.n} 设置为 -1 失败`)
data2.n = 1
console.log(`需求二:${data2.n} 设置为 1 成功`)

Object.defineProperty 的用处:
1. 可以给对象添加属性 value
2. 可以给对象添加 getter / setter
3. getter / setter 用于对属性的读写进行监控

我们看看上面的代码,我们在思考,怎么样才能让我们可以监控对 n 的修改,而且不要让外部特意操作而影响到我们,我们可以使用代理。

啥是代理

let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问

function proxy2({data}/* 解构赋值 */){
  // 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
  let value = data.n
  Object.defineProperty(data, 'n', {
    get(){
      return value
    },
    set(newValue){
      if(newValue<0)return
      value = newValue
    }
  })
  // 就加了上面几句,这几句话会监听 data

  const obj = {}
  Object.defineProperty(obj, 'n', {
    get(){
      return data.n
    },
    set(value){
      if(value<0)return
      data.n = value
    }
  })

  return obj // obj 就是代理
}

// data3 就是 obj
console.log(`需求五:${data5.n}`)
myData5.n = -1
console.log(`需求五:${data5.n},设置为 -1 失败了`)
myData5.n = 1
console.log(`需求五:${data5.n},设置为 1 成功了`)

对 myData 的属性读写,全权由另一个对象 vm 负责,那么 vm 就是 myData 的代理。我们不用 myData.n 来操作而是用 vm.n 来操作,这就是代理。
我们看上面的代码,就看到一个特别熟悉的结构。
vm = new Vue({data:myData}),那这会让我们发生什么呢。

vm = new Vue({data:myData})

所以这句话到底会产生什么结果?
1. 会让 vm 称为 myData 的代理
2. 会对 myData 的所有属性进行监控
3. 只要属性变化了,我们就可以进行重新的渲染了

这就是 Vue 的数据响应式的概念。但是其中似乎是存在了一些 bug,比如。

Vue 只会检查第一层属性

new Vue({
  data: {
    obj: {
      a: 0 // obj.a 会被 Vue 监听 & 代理
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      this.obj.b = 1; //请问,页面中会显示 1 吗?
    }
  }
}).$mount("#app");

这里的 b 并不会被监听,因为 Vue 不会监听一开始不存在的 obj.b。那我们有什么办法呢?
1. 先把所有的 key 都声明好,然后后面添加属性
2. 使用 Vue.set 或者 this.$set

对象中使用 set 可以新增 key,创建代理和监听,更新 UI。但是数组怎么办呢?

data 中有数组怎么办?

数组的长度是可以一直增加的,数组的 key 就是数组的下标,我们不可能把下标全都声明出来,难道我们要每次都使用 set 吗?
Vue 作者想到了一个办法,就是篡改数组的 API。作者添加了7个变异方法:push()、pop()、shift()、unshift()、splice()、sort()、reverse(),只要使用这些方法就会自动处理对数组的代理和监听并且更新 UI。

扯得稍微有一些远,这大概就是 Vue 的数据响应式的概念。暂时只能理解到这里,如果还有其他的想说的,会在以后的博客中继续说明。

42
回复 编辑