概念

  • 当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。

    方法:代理与监听

    示意图

    image.png

    getter 和 setter

    ```javascript //需求一,得到姓名 let obj1={ 姓:”高”, 名:”圆”, 姓名(){
    1. return this.姓+this.名;}
    } console.log(“需求ー:”+obj1.姓名()) //姓名后面的活号能删掉吗?不能因为它是函数

//需求二,姓名不要括号也能得出值 let obj2= { 姓:”高”, 名:”圆圆”, get 姓名(){ return this.姓+this.名 } } console.log(“需求二:” + bj2.姓名)} //使用 gettter 函数后面不需要加括号

//需求3,写入姓名 let obj3 = { 姓:”高”, 名:”圆圆”, get 姓名(){ return this.姓+this.名 } set 姓名(x){ this.姓 = xxx[0] this 名 = xxx.substring(1) } } obj3.姓名 = ‘刘诗诗’ console.log(需求三:姓 ${obj3.姓},名 ${obj3.名}) //需求三: 姓刘,名诗诗

  1. <a name="g6Kpv"></a>
  2. ### Object.defineProperty
  3. - 可以给对象添加属性 value
  4. - 可以给对象添加 getter/setter
  5. getter/ setter 用于对属性的读写进行监控
  6. ```javascript
  7. var _xxx = 0
  8. Object.defineProperty(obj3, xxx, {
  9. get(){return xxx}
  10. set(value){this._xxx = value}
  11. })
  • 需求一:n 不能小于 0 (此时 n 可以被直接篡改)

    1. let data1 = {}
    2. data1.n = 0 //n 用来的存 n 的值
    3. Object.defineproperty(data1, 'n', {
    4. get(){return this. n},
    5. set(value){
    6. if(value < 0) return
    7. this._n = value
    8. }})
  • 需求二:使用代理

    • 代理:对一个对象的读写,全权由另一对象负责

      1. let data2 = proxy({data:{n:0}})
      2. function proxy({data}){
      3. const obj = {}
      4. Object.defineProperty(obj,'n',{
      5. get(){return data.n},
      6. set(value){
      7. if(value < 0) return
      8. data.n = value}
      9. })
      10. return obj //obj 作为代理,此时无法直接修改数据
      11. }
    • 当使用代理时使用了引用值

      1. const myData = {n:0} //此时改变 myData 还是可以改变数据
      2. let data3 = proxy({data: myData})
      3. function proxy({data}){
      4. const obj = {}
      5. Object.defineProperty(obj,'n',{
      6. get(){return data.n},
      7. set(value){
      8. if(value < 0) return
      9. data.n = value}
      10. })
      11. return obj
      12. }
  • 需求三:当用户直接修改 myData 时,也要拦截

    1. const myData = {n:0} //此时改变 myData 还是可以改变数据
    2. let data4 = proxy({data: myData})
    3. function proxy({data}){
    4. let value = data.n
    5. Object.defineProperty(data,'n',{ //监听
    6. get(){return value},
    7. set(newValue){
    8. if(newValue < 0) return
    9. value = newValue}
    10. })
    11. const obj = {}
    12. Object.defineProperty(obj,'n',{ //代理
    13. get(){return data.n},
    14. set(value){
    15. if(value < 0) return //此时可省略
    16. data.n = value}
    17. })
    18. return obj
    19. }

    data 的 bug

  • 当 template 中使用了 n,而 data 中未定义 n

    • 情况一:报错,vue 会检测第一层是否定义了

      1. new Vue({
      2. el:"#app",
      3. data:{m: 0},
      4. template:`<div class="red">
      5. {{ n }}
      6. <button @click="add">添加</button>
      7. </div>`,
      8. })
    • 情况二:当第一层定义了值时,默认认为有该值。然而此时点击按钮页面无响应,因为此时没有对 obj.n 添加监听和代理。

      • 原理:Object.defineProperty(obj,’n’,{}),必须有一个 ‘n’,才能进行代理和监听,无法代理和监听一开始未定义的 key。
      • 解决办法:使用 Vue.set(this.obj,’n’,value) 或 this.$set(this.obj,’n’,value)。

      这个 API 会新增 key,创建监听和代理,触发 UI 更新。

      1. new Vue({
      2. el:"#app",
      3. data:{
      4. obj:{m:0}
      5. },
      6. template:`<div class="red">
      7. {{ obj.n }}
      8. <button @click="add">添加</button>
      9. </div>`,
      10. methods: {
      11. add() {
      12. obj.n = 1
      13. },
      14. },
      15. })
  • 当 data 中使用了数组时,可使用 Vue 的变异方法,Vue 对这些方法进行了包裹,实现与对象上相同的功能。

    • 包括:shift() unshift() pop() push() splice() sort() reverse()

      1. new Vue({
      2. el:"#app",
      3. data:{
      4. array:[1,2,3]
      5. },
      6. template:`<div class="red">
      7. {{ array }}
      8. <button @click="add">添加</button>
      9. </div>`,
      10. methods: {
      11. add() {
      12. this.array.push(4) //原来的 push 方法被 Vue 篡改了
      13. },
      14. },
      15. })
    • 模拟包裹 push

      1. //ES6写法
      2. Class VueArray extends Array{
      3. push(...args){
      4. const oldLength = this.length //this就是当前数组
      5. super.push(...args)
      6. console.log('你 push 了')
      7. for(let i= oldLength; i < thisLength; i++){
      8. Vue.set(this, i, this[i]) //将每个新增的key都告诉Vue
      9. }
      10. }
      11. }