44·灵魂前端工程师养成-前端框架Vue数据响应式

2022-11-08 17:10:35 浏览数 (1)

  • Vue对data做了什么

  • Vue对data做的事情总结
  • Vue的data存在bug
  • data中数组变异
  • 新增key总结

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


Vue对data做了什么


小实验

data变了

codesandbox示例代码:TP

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;

const myData = {
  n:0
}

console.log(myData)

const vm = new Vue({
  data:myData,
  template:`
    <div>
      {{n}}
      <button @click="add">
         10
      </button>
    </div>
  `,
  methods:{
    add(){
      this.n  = 10
    }
  }
}).$mount("#app")

setTimeout(()=>{
  myData.n =10
  console.log(myData)
  console.log(vm)
},3000)

我们总共打印了两次myData

第一次打印是{n:0}

然后3秒钟之后再打印一次,应该是{n:10}对吧

myData变成了一个奇奇怪怪的东西

一开始{n:0},传给new Vue之后立马变成了{n:(...)}

{n:(...)}这是个什么玩意?为什么表现和{n:0}一致?

我们需要先学习一下ES6的gettersetter

codesandbox示例代码:TP

代码语言:javascript复制
let obj0 = {
  姓: "邓",
  名: "紫琪",
  age: 18
}

// 需求一:得到姓名

let obj1 = {
  姓: "邓",
  名: "紫琪",
  姓名(){
    return this.姓   this.名
  },
  age: 18
}

console.log('需求一:'   obj1.姓名())

// 姓名后面的括号能删除么?
// 不能,因为他是一个函数
// 如何去掉括号?

// 需求二,姓名不要括号也能得出值

let obj2 = {
  姓: "邓",
  名: "紫琪",
  get 姓名(){
    return this.姓   this.名
  },
  age: 18
}

console.log('需求二:'   obj2.姓名)

// 总结:getter就是这样用的,不加括号的函数,仅此而已

// 需求三,姓名可以被写

let obj3 = {
  姓: "邓",
  名: "紫琪",
  get 姓名(){
    return this.姓   this.名
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  age: 18
}

obj3.姓名 = "曾老湿"
console.log(`需求三:姓:${obj3.姓},名:${obj3.名}`)
//总结:setter就是这样赢的。 用 = xxx 触发set函数

我们把obj3打印出来,看看结果

代码语言:javascript复制
console.log(obj3)

可以发现... 有个姓名:(...) 和刚才的{n:(...)}是不是有点蛛丝马迹了。这个姓名,不是真实的姓名,浏览器,允许读写,所以模拟姓名的操作。

所以{n:(...)}并不存在这样的一个n,只是浏览器模拟n的操作。

那么为什么要{n:(...)}用这种方法呢?

我们又要了解一个新的知识Object.defineProperty

Object.defineProperty

继续做实验:TP

我们现在已经把obj3定义完了,那么我们调用的时候,如果想要get age,怎么办?我们不可能去修改定义的部分,如果这个功能是别的开发写好的代码给你的,你不可能改别人的代码吧?所以此时,我们需要用到Object.defineProperty

代码语言:javascript复制
let obj0 = {
  姓: "邓",
  名: "紫琪",
  age: 18
};

// 需求一:得到姓名

let obj1 = {
  姓: "邓",
  名: "紫琪",
  姓名() {
    return this.姓   this.名;
  },
  age: 18
};

console.log("需求一:"   obj1.姓名());

// 姓名后面的括号能删除么?
// 不能,因为他是一个函数
// 如何去掉括号?

// 需求二,姓名不要括号也能得出值

let obj2 = {
  姓: "邓",
  名: "紫琪",
  get 姓名() {
    return this.姓   this.名;
  },
  age: 18
};

console.log("需求二:"   obj2.姓名);

// 总结:getter就是这样用的,不加括号的函数,仅此而已

// 需求三,姓名可以被写

let obj3 = {
  姓: "邓",
  名: "紫琪",
  get 姓名() {
    return this.姓   this.名;
  },
  set 姓名(xxx) {
    this.姓 = xxx[0];
    this.名 = xxx.slice(1);
  },
  age: 18
};

var _xxx = 0

Object.defineProperty(obj3,'xxx',{
  get(){
    return _xxx
  },
  set(value){
    _xxx = value
  }
})

obj3.姓名 = "曾老湿";
console.log(`需求三:姓:${obj3.姓},名:${obj3.名}`);
//总结:setter就是这样赢的。 用 = xxx 触发set函数

这就是Object.defineProperty,如果已经定义好的对象,你想给它添加虚拟属性,那么就使用这种方法。

Vue对data做的事情总结


Object.define.Property

可以 给对象添加属性value 可以 给对象添加getter/setter getter/setter用于对属性的读写进行监控


代理(设计模式)

对myData对象 的属性读写,全权由另一个对象vm负责 那么vm就是myData的代理 比如myData.n不用,偏要用vm.n来操作myData.n


vm=new Vue({data:myData})

1.会让vm称为myData的代理(proxy) 2.会对myData的所有属性进行监控

为什么要监控,为了防止世界被破坏,为了守护世界的和平...对不起,乱入了,为了防止myData的属性变了,vm不知道

vm知道了又如何?知道属性变了就可以调用render(data)呀!!!UI=render(data)

Vue的data存在bug


数据响应式

什么是数据响应式?

我打你一拳,你会喊疼,那你就是响应式。 若一个物体能对外界的刺激做出反应,它就是响应式


Vue的data是响应式

const vm = new Vue({data:{n:0}}) 我如果修改vm.n,那么UI中的n就会来响应我 Vue2通过Object.defineProperty来是想数据响应式


响应式网页是啥? 如果我改变窗口的大小,网页内容就会做出响应,那就是响应式网页。 比如:https://www.smashingmagazine.com/ 但是要注意,用户没事不会拖动网页大小。


data的bug

Object.defineProperty的问题 Object.defineProperty(obj,'n',{...}) 必须要有一个'n',才能监听&代理obj.n对吧 如果前端开发者比较"睿智",没有给n怎么办?

示例一:Vue会给出一个警告 TP

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
    data: {},
    template: `
    <div>{{n}}</div>
  `,
}).$mount("#app");

示例二:Vue只会检查第一层属性,我们有方法绕过他 TP

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
  data:{
    obj:{
      a:0
    }
  },
  template:`
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods:{
    setB(){
      this.obj.b = 1  //请问,页面会显示1嘛?
    }
  }
}).$mount("#app")

这样一来,vue就没有警告了。

此时如果我店家set b,请问图中会显示1嘛? 答案是:不会 因为,Vue再牛逼,它也不可能监听一个不存在的字符串 ,obj.b 一开始就不存在,如何监听?不能把所有字符串都监听一遍吧?

解决办法 1.那我把key都声明好,后面不再加属性不就行了 2.使用Vue.set或者this.$set

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
  data: {
    obj: {
      a: 0
    }
  },
  template: `
    <div>
      {{obj.b}}
      <button @click="setB">set b</button>
    </div>
  `,
  methods: {
    setB() {
      //this.obj.b = 1; //请问,页面会显示1嘛?
      Vue.set(this.obj,'b',1)
      this.$set(this.obj,'b',1)
    }
  }
}).$mount("#app");

Vue.set 和 this.$set

作用: 1.新增key 2.自动创建代理和监听(如果没创建就自动创建) 3.触发UI更新(但不会立刻更新,异步更新)

data中数组变异


data中有数组怎么办?

你没法提前声明所有key 示例1:数组的长度可以一直增加,下标就是key 你没有办法提前把数组的key都声明出来 Vue也不能检测对你新增了下标 难道每次改数组都要用Vue.set或者this.$set

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      this.array[3] = "d"; //请问页面会显示d嘛?
      //等下,你为什么不用this.array.puysh('d')
    }
  }
}).$mount("#app");

页面中,会显示d嘛?答案是不会,因为这个数组就相当于是 array:{0:'a',1:'b',2:'c'},你只设置了0,1,2所以Vue监听不到3,那这个时候我们用set可不可以呢?

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      // this.array[3] = "d"; //请问页面会显示d嘛?
      this.$set(this.array,3,'d')
      //等下,你为什么不用this.array.puysh('d')
    }
  }
}).$mount("#app");

尤雨溪的做法

篡改数组的API,见文档中异更方法章节

这7个API都会被Vue篡改,调用后会更新UI

代码语言:javascript复制
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
new Vue({
  data: {
    array: ["a", "b", "c"]
  },
  template: `
    <div>
      {{array}}
      <button @click="setD">set d</button>
    </div>
  `,
  methods: {
    setD() {
      // this.array[3] = "d"; //请问页面会显示d嘛?
      // this.$set(this.array,3,'d')
      //等下,你为什么不用this.array.puysh('d')
      this.array.push('d')
    }
  }
}).$mount("#app");

怎么篡改的?ES6写法

代码语言:javascript复制
class VueArray extends Array{
  push(...args){
    const oldLength = this.length //this就是当前数组
    super.push(...args)
    console.log('你push了')
    for(let i = oldLength;i<this.length;i  ){
      Vue.set(this,i,this[i]) //将每个新增的key都告诉Vue
    }
  }
}

注意:者不代表Vue的真实实现,此代码仅用于教学目的,实际上我没看过相关源码

新增key总结


对象新增的key

Vue没有办法实现监听和代理 要使用set来新增key,创建监听和代理,更新UI 最好前提把属性都写出来,不要新增key 但数组做不到"不新增key"


数组新增的key

也可以使用set来新增key,更新UI(set新增key不会创建监听和代理) 不过尤雨溪篡改了7个API方便你对数组进行增删 这7个API会自动处理监听和代理,并更新UI

0 人点赞