Vue2-绑定样式、条件渲染、列表渲染、列表过滤、模糊查询、Vue监测数据原理

:想只有苦难,做才有答案

更多Vue知识请点击——Vue.js

VUE2-Day3

    • 绑定样式
      • 1、class绑定
      • 2、绑定style样式
    • 条件渲染
      • 1、v-show
      • 2、v-if
      • 条件渲染案例
    • 列表渲染
      • 1、v-for
      • 2、key的作用与原理(重要)
        • 面试题:react、vue中的key有什么作用?(key的内部原理)
        • 案例:给数组头部添加一个人
    • 列表过滤:实现模糊查询
      • 1、使用watch实现
      • 2、使用computed实现
    • 列表排序
    • Vue监测数据原理及其方法
      • 1、更新数据时的一个问题
      • 2、模拟Vue监视data对象中的数据
      • 3、Vue监测对象中的数据
        • (1)怎么监测对象中的数据?
        • (2)`Vue.set()`的使用
      • 4、Vue监测数组中的数据
        • 如何监测数组中的数据?
      • 5、回头看第一个
      • 监测数据综合案例

绑定样式

1、class绑定

写法:class=“xxx” xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
数组写法适用于:要绑定多个样式,但是不确定绑定哪几个,就写成数组,省的一个一个绑定。
对象写法适用于:要绑定多个样式,而且要动态决定用不用,这里面可以写一些判断的逻辑。

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>绑定class样式title>
    
    <script type="text/javascript" src="../js/vue.js">script>
    
    <style>
      .basic {
        width: 400px;
        height: 100px;
        border: solid 1px black;
        margin-top: 50px;
      }
      .happy {
        background-color: pink;
      }
      .normal {
        background-color: greenyellow;
      }
      .sad {
        background-color: grey;
      }
      .style1 {
        background-color: skyblue;
      }
      .style2 {
        font-size: 28px;
      }
      .style3 {
        text-align: center;
      }
    style>
  head>
  <body>
    <div id="root">
      
      
      <div class="basic" :class="mood" @click="changeMood">{{name}}div>

      
      <div class="basic" :class="classArr" @click="removeStyle">{{name}}div>
      <br />

      
      <div class="basic" :class="classObj">{{name}}div>
      <br />
      <button @click="useStyle1">点击添加样式1button>
      <button @click="useStyle2">点击添加样式2button>
    div>

    <script type="text/javascript">
      const vm = new Vue({
        el: "#root",
        data: {
          name: "我是最棒滴!!!",
          mood: "normal",
          classArr: ["style1", "style2", "style3"],
          classObj: {
            style1: false,
            style2: false,
          },
        },
        methods: {
          changeMood() {
            const arr = ["happy", "normal", "sad"];
            const index = Math.floor(Math.random() * 3);
            this.mood = arr[index];
          },
          //点击去掉第二个框的style样式
          //由于pop()是删掉最后一个元素,所以多次点击依次删除style3->style2->style1
          removeStyle() {
            this.classArr.pop();
          },
          //点击添加样式1
          useStyle1() {
            this.classObj.style1 = true;
          },
          //点击添加样式2
          useStyle2() {
            this.classObj.style2 = true;
          },
        },
      });
    script>
  body>
html>
  • 可以拿此例子自己敲敲,多到浏览器运行调试看看

2、绑定style样式

:style="{fontSize: xxx + 'px'}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。

对象写法比较常用。

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>绑定style样式title>
    
    <script type="text/javascript" src="../js/vue.js">script>
    
    <style>
      .basic {
        width: 400px;
        height: 100px;
        border: solid 1px black;
        margin-top: 50px;
      }
    style>
  head>
  <body>
    <div id="root">
      
      <div class="basic" :style="styleObj1">{{name}}div>
      
      <div class="basic" :style="[styleObj1,styleObj2]">{{name}}div>
      <div class="basic" :style="styleArr">{{name}}div>
    div>

    <script type="text/javascript">
      const vm = new Vue({
        el: "#root",
        data: {
          name: "我是最棒的!!!",
          styleObj1: {
            fontSize: "40px",
            backgroundColor: "pink",
          },
          styleObj2: {
            color: "red",
          },
          styleArr: [
            {
              fontSize: "40px",
              backgroundColor: "blue",
            },
            {
              color: "yellow",
            },
          ],
        },
      });
    script>
  body>
html>

条件渲染

1、v-show

写法:v-show=“表达式”
适用于:切换频率较高的场景。(不会动DOM树,只是隐藏,相当于添加display:none)
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
备注:使用v-if的时,元素可能无法获取到,而使用v-show一定可以获取到。这是因为v-if会一不小心把标签直接从页面上干掉,而v-show不会干掉,只会隐藏。

2、v-if

写法:
(1).v-if=“表达式”
(2).v-else-if=“表达式”
(3).v-else=“表达式”
适用于:切换频率较低的场景。(因为会动DOM树,节点删来删去不太好)
特点:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”。

条件渲染案例

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>条件渲染title>
    
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    <div id="root">
      
      <h1 v-show="true">你好呀,{{name}}h1>
      <h1 v-show="1===1">你好呀,{{name}}h1>

      
      <h1 v-if="true">再见,{{name}}h1>
      <h1 v-if="1===3">再见,{{name}}h1>

      
      <h2>{{n}}h2>
      <button v-on:click="n++">点击n+1button>

      
      <div v-if="n === 1">Angulardiv>
      <div v-else-if="n === 2">Reactdiv>
      <div v-else-if="n === 3">Vuediv>
      <div v-else>potatodiv>

      
      <template v-if="n === 1">
        <div>鱿鱼西一号div>
        <div>鱿鱼西二号div>
        <div>鱿鱼西三号div>
      template>
    div>

    <script type="text/javascript">
      const vm = new Vue({
        el: "#root",
        data: {
          name: "鱿鱼西",
          n: 0,
        },
      });
    script>
  body>
html>

列表渲染

1、v-for

  1. 用于展示列表数据

  2. 语法:v-for=“(item, index) in xxx” :key=“yyy”,其中xxx是遍历的目标,yyy是唯一的索引,用于区分每个嘎达

  3. 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

  4. 遍历数组的话,index是索引值(唯一),p是数组每个元素

  5. 遍历对象的话,index就是属性名(唯一),p是属性值

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>基本列表渲染title>
    
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      
      <h2>人员列表(遍历数组)h2>
      <ul>
        <li v-for="(p,index) in persons" :key="p.id">
          {{p.name}}-{{p.age}}-{{index}}
        li>
      ul>

      
      <h2>汽车信息(遍历对象)h2>
      <ul>
        <li v-for="(p,index) of car" :key="index">{{p}}-{{index}}li>
      ul>

      
      <h2>遍历字符串h2>
      <ul>
        <li v-for="(p,index) of str" :key="index">{{p}}-{{index}}li>
      ul>

      
      <h2>遍历指定次数h2>
      <ul>
        <li v-for="(p,index) of 5" :key="index">{{p}}-{{index}}li>
      ul>
    div>

    <script type="text/javascript">
      new Vue({
        el: "#root",
        data: {
          persons: [
            { id: "001", name: "张三", age: 18 },
            { id: "002", name: "李四", age: 19 },
            { id: "003", name: "王五", age: 20 },
          ],
          car: {
            name: "马沙拉弟",
            price: "10块钱",
            color: "骚粉色",
          },
          str: "hello",
        },
      });
    script>
  body>
html>

2、key的作用与原理(重要)

ey可以有三种情况:写唯一标识、写index、不写。其中唯一标识最合适,如果不写会默认是写index。

面试题:react、vue中的key有什么作用?(key的内部原理)

1、 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较(diff算法),比较规则如下:

2、对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
① 若虚拟DOM中内容没变, 直接使用之前的真实DOM!
② 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key,创建新的真实DOM,随后渲染到到页面。

3、 用index作为key可能会引发的问题:
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 => 界面效果没问题, 但效率低。
(2) 如果结构中还包含输入类的DOM:
会产生错误DOM更新 => 界面有问题。

4、 开发中如何选择key?:
(1)最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>key的原理和作用title>
    
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      
      <h2>人员列表(遍历数组)h2>
      <button @click="add">点击添加老六button>
      <ul>
        
        <li v-for="(p,index) in persons" :key="p.id">
          {{p.name}}-{{p.age}}-{{index}}
          <input type="text" />
        li>
      ul>
    div>

    <script type="text/javascript">
      new Vue({
        el: "#root",
        data: {
          persons: [
            { id: "001", name: "张三", age: 18 },
            { id: "002", name: "李四", age: 19 },
            { id: "003", name: "王五", age: 20 },
          ],
        },
        methods: {
          add() {
            const p = { id: "004", name: "老六", age: 19 };
            // unshift():此方法的作用是将指定元素添加到数组的开头
            this.persons.unshift(p);
          },
        },
      });
    script>
  body>
html>

案例:给数组头部添加一个人

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>key的原理和作用title>
    
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      
      <h2>人员列表(遍历数组)h2>
      <button @click="add">点击添加老六button>
      <ul>
        
        <li v-for="(p,index) in persons" :key="p.id">
          {{p.name}}-{{p.age}}-{{index}}
          <input type="text" />
        li>
      ul>
    div>

    <script type="text/javascript">
      new Vue({
        el: "#root",
        data: {
          persons: [
            { id: "001", name: "张三", age: 18 },
            { id: "002", name: "李四", age: 19 },
            { id: "003", name: "王五", age: 20 },
          ],
        },
        methods: {
          add() {
            const p = { id: "004", name: "老六", age: 19 };
            // unshift():此方法的作用是将指定元素添加到数组的开头
            this.persons.unshift(p);
          },
        },
      });
    script>
  body>
html>

遍历列表时key的作用(index作为key):

Vue2-绑定样式、条件渲染、列表渲染、列表过滤、模糊查询、Vue监测数据原理_第1张图片

遍历列表时key的作用(唯一标识作为key):

Vue2-绑定样式、条件渲染、列表渲染、列表过滤、模糊查询、Vue监测数据原理_第2张图片

列表过滤:实现模糊查询

1、使用watch实现

handler函数必须在keyword被改变时才执行,也就是说页面上来还是没有信息的,必须手动输入个东西再删掉(手动改变keyword),才能执行handler函数,才能显示所有人物的信息,所以必须加个属性immediate: true,才能默认初始化先调用handler,这样就能实现上来就显示所有人物的信息(最开始keyword=''

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表过滤title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    <div id="root">
      <h2>人员列表h2>
      <input type="text" placeholder="请输入关键字" v-model="keyWord" />
      <ul>
        <li v-for="(p,index) of filPersons" :key="p.id">
          {{p.name}}-{{p.age}}-{{p.sex}}
        li>
      ul>
    div>

    <script type="text/javascript">
      //   用监视(侦听)属性watch实现
      new Vue({
        el: "#root",
        data: {
          keyWord: "",
          persons: [
            { id: "001", name: "马冬梅", age: 18, sex: "女" },
            { id: "002", name: "周冬雨", age: 19, sex: "女" },
            { id: "003", name: "周杰伦", age: 18, sex: "男" },
            { id: "004", name: "温兆伦", age: 18, sex: "男" },
          ],
          filPersons: [],
        },
        watch: {
          keyWord: {
            //页面上来由于filPersons是空,不会显示数据,想要让页面初始化就显示所有人,就要加个immediate: true
            //这样就可以让handler函数初始化时先调用一次,由于开始keyword=''
            // 而字符串里都包含空字符串,就可以先筛选出来,初始化所有人物信息
            immediate: true,
            handler(newVal, oldVal) {
              this.filPersons = this.persons.filter((p) => {
                //判断keyword变化后的新值在不在每个对象的name中,并返回一个新的数组
                return p.name.indexOf(newVal) !== -1;
              });
            },
          },
        },
      });
    script>
  body>
html>

2、使用computed实现

使用计算属性可以解决watch中的一些繁琐写法,也不用在data中再新定义一个空数组newPersons。

计算属性和监视属性最大的区别就是handler函数是被监视的属性更改时执行,而get是属性被读取时就执行,所以页面加载时filPersons被读取直接就调用get函数返回了一个被筛选后的新数组(条件是name包含空字符串),之后每次keyword改动都会导致Vue模板重新解析,get重新调用。

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表过滤title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    <div id="root">
      <h2>人员列表h2>
      <input type="text" placeholder="请输入关键字" v-model="keyWord" />
      <ul>
        <li v-for="(p,index) of filPersons" :key="p.id">
          {{p.name}}-{{p.age}}-{{p.sex}}
        li>
      ul>
    div>

    <script type="text/javascript">
      //用计算属性computed实现;
      new Vue({
        el: "#root",
        data: {
          keyWord: "",
          persons: [
            { id: "001", name: "马冬梅", age: 18, sex: "女" },
            { id: "002", name: "周冬雨", age: 19, sex: "女" },
            { id: "003", name: "周杰伦", age: 18, sex: "男" },
            { id: "004", name: "温兆伦", age: 18, sex: "男" },
          ],
        },
        computed: {
          filPersons() {
            return persons.filters((p) => {
              return p.name.indexOf(this.keyWord) !== -1;
            });
          },
        },
      });
    script>
  body>
html>

列表排序

先过滤,再排序
首先定义一个sortType属性来判断排序的种类,然后在计算属性里做一个判断。
注意这里仔细理解理解数组的sort方法,很奇怪。
这个案例再次体现出计算属性的强大之处,只要get里用到的数据(keyword,sortType)发生改变,get都会重新执行,模板都会重新解析,这个业务逻辑就实现了

先复习一下数组的排序sort升序降序方法

      let arr = [1, 5, 3, 7, 4];
      //降序b-a
      //   arr.sort((a, b) => {
      //     return b - a;
      //   });
      //   console.log(arr);

      //升序a-b
      arr.sort((a, b) => {
        return b - a;
      });
      console.log(arr);

例子:

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表排序title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      <h1>人员列表h1>
      <input type="text" placeholder="请输入关键字" v-model="keyword" />
      <button @click="sortType = 0">原顺序button>
      <button @click="sortType = 1">年龄降序button>
      <button @click="sortType = 2">年龄升序button>
      <ul>
        <li v-for="(p,index) in newPersons" :key="p.id">
          {{p.name}}----{{p.age}}----{{p.sex}}
        li>
      ul>
    div>

    <script>
      const vm = new Vue({
        el: "#root",
        data: {
          sortType: 0, //0原顺序,1年龄降序,2年龄升序
          keyword: "",
          persons: [
            { id: "001", name: "马冬梅", age: 18, sex: "女" },
            { id: "002", name: "周冬雨", age: 19, sex: "女" },
            { id: "003", name: "周杰伦", age: 30, sex: "男" },
            { id: "004", name: "温兆伦", age: 50, sex: "男" },
          ],
        },
        computed: {
          newPersons() {
            //先过滤,再排序
            const arr = this.persons.filter((ele) => {
              return ele.name.includes(this.keyword);
            });

            // 或者if(this.sortType)
            if (this.sortType !== 0) {
              arr.sort((a, b) =>
                this.sortType === 1 ? b.age - a.age : a.age - b.age
              );
            }
            return arr;
          },
        },
      });
    script>
  body>
html>

Vue监测数据原理及其方法

1、更新数据时的一个问题

如果我要点击按钮实现更新马冬梅的信息,那么如果一个属性一个属性地改,可以修改成功,并且Vue也会检测到并更新到页面。但是我如果直接把整个对象改了,可以修改成功,但是Vue监测不到并且不更新到页面,这是为什么?

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表排序title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      <h1>人员列表h1>
      <button @click="updateMei">更新马冬梅信息button>
      <ul>
        <li v-for="(p,index) in persons" :key="p.id">
          {{p.name}}----{{p.age}}----{{p.sex}}
        li>
      ul>
    div>

    <script>
      const vm = new Vue({
        el: "#root",
        data: {
          persons: [
            { id: "001", name: "马冬梅", age: 18, sex: "女" },
            { id: "002", name: "周冬雨", age: 19, sex: "女" },
            { id: "003", name: "周杰伦", age: 30, sex: "男" },
            { id: "004", name: "温兆伦", age: 50, sex: "男" },
          ],
        },
        methods: {
          updateMei() {
            // this.persons[0].name = "马老师"; //奏效(页面和Vue都监测到了)
            // this.persons[0].age = 28; //奏效(页面和Vue都监测到了)
            // this.persons[0].sex = "男"; //奏效(页面和Vue都监测到了)

            //此处Vue监测不到数据改变,但是实际上已经改了
            this.persons[0] = { id: "001", name: "马老师", age: 28, sex: "男" };
          },
        },
      });
    script>
  body>
html>

2、模拟Vue监视data对象中的数据

用原生js写一个Vue监视data数据改变的效果
可以看出,Vue监视数据的原理,就是靠setter
如果不理解obj[k],想想前边讲Object.defineProperty时那个number

getter:当属性值被访问时,会触发getter方法,这时Vue会将当前的Watcher对象(即订阅者)存储起来,用于后续数据变化时的更新操作。

setter:属性被修改–>调用setter–>重新解析模板–>生成新的虚拟DOM–>新旧DOM对比(diff)–>更新页面

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documenttitle>
  head>
  <body>
    <script type="text/javascript">
      let data = {
        name: "小猪佩奇",
        age: 5,
      };
      //创建一个监视的实例对象,用于监视data中属性的变化
      const obs = new Observer(data);
      console.log(obs);

      //准备一个vm实例对象
      let vm = {};
      vm._data = data = obs;

      function Observer(obj) {
        //1.创建一个数组接收传入对象的属性名
        const keys = Object.keys(obj);
        //2.遍历属性名,让Observer实例对data中的每个数据进行数据代理
        keys.forEach((k) => {
          Object.defineProperty(this, k, {
            get() {
              //有人想读实例中的属性值,我就把data中对应的属性值拿过来
              return obj[k];
            },
            set(val) {
              console.log(
                `${k}被改了,我要去解析模板,生成虚拟DOM...我要开始忙了`
              );
              //有人想改实例中的属性值,我就把data中对应的属性值更改(数据代理)
              obj[k] = val;
            },
          });
        });
      }
    script>
  body>
html>

这只是一个简单的模拟,实际上Vue做了更多事情,比如:
1、在setter中不是简单的输出,而是重新解析模板,生成虚拟DOM,新旧虚拟DOM对比,更新页面实现响应式。
2、Vue中vm还对_data做了数据代理,可以直接vm.name,不用vm._data.name
3、上面的简单模拟例子,如果数据是套娃的(对象中还有对象还有对象),就监测不到。但是Vue就可以,不管你数据藏得多深,Vue都能找到,每个数据都有自己的getter和setter。只要有数据被改,就会实现响应式。

3、Vue监测对象中的数据

(1)怎么监测对象中的数据?

1、vue会监视data中所有层次的数据,不管你藏得有多深。
2、如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:

Vue.set(target,propertyName/index,value) 或 
vm.$set(target,propertyName/index,value)

(2)Vue.set()的使用

用法:
向响应式对象中添加一个 property(属性),并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.girlfriend.sex = '女')为啥呢?

_data在收集data中的数据之前,先做了一个加工(这个加工也可以称之为数据劫持),那就是给每个数据匹配一个getter和setter,前面说了,Vue监视数据的原理,就是靠setter。所以如果我们后期手动直接给_data添加属性(注意区别手动添加和从data中收集),是无法实现响应式的,因为没办法给它setter,只有从data中收集过来的属性才能有setter。

Vue.set()是有局限性的!!!它只能给data中的某个对象追加属性,不能给vm 或 vm的根数据对象(data或_data) 添加属性!!!
也就是说第一个参数不能是 Vue 实例,或者 Vue 实例的根数据对象(_data)

<div id="root">
    <h2>我的名字:{{name}}</h2>
    <h2>我的年龄:{{age}}</h2>
    <!-- v-if当属性值为undefined不显示,也不报错 -->
    <h3 v-if="sex">我的性别:{{sex}}</h3>
    <button @click="addmySex">点击添加我的性别</button>
    <hr>
    <h2>她的名字:{{girlfriend.name}}</h2>
    <button @click="addherSex">点击添加性别,属性值为女</button>
    <h2 v-if="girlfriend.sex">她的性别:{{girlfriend.sex}}</h2><h2>她的年龄:对外{{girlfriend.age.fakeAge}},真实{{girlfriend.age.realAge}}</h2>
    <h2>朋友们</h2>
    <ul>
        <li v-for="p in girlfriend.friends" :key="p.id">
            {{p.name}}----{{p.age}}
        </li>
    </ul>
</div>
<script>
    const vm = new Vue({
        el: '#root',
        data: {
            name: 'www',
            age: 18,
            girlfriend: {
                name: 'zzz',
                // sex: '女',
                age: {
                    realAge: 23,
                    fakeAge: 18
                }
            }
        },
        methods: {
            addherSex() {
                // Vue.set(this.girlfriend, 'sex', '女');
                this.$set(this.girlfriend, 'sex', '女');                         //vm.$set(vm.girlfriend, 'sex', '女');
            },
            addmySex() {
                Vue.set(this, 'sex', '男');
            }
        },
    })
</script>

4、Vue监测数组中的数据

如何监测数组中的数据?

Vue定义了一个非常厉害的方法,目的只有一个:实现响应式。

方法里包装的代码本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新。然后第二步在这个基础上再加东西
(2)重新解析模板,生成虚拟DOM,diff算法……进而更新页面,实现响应式。

所以在Vue修改数组中的某个元素实现响应式一定要用如下方法:
(1)使用这些API:push()在后面追加、pop()删除最后一个、shift()删除第一个、unshift()在前面追加、splice()在指定位置增删改、sort()数组排序、reverse()反转数组,这些api都是可以修改原数组的,可以在(MDN Web Docs (mozilla.org))查看。
(2)Vue.set()vm.$set()也可以实现响应式,第二个参数写索引,第三个写元素,可以改也可以加

5、回头看第一个

说了这么多,再回头看第一个问题,恍然大悟
1、单独改每个属性可以奏效,是因为Vue可以监测到对象里的每个属性,不管它藏的多深,只要是个对象,就有相应的getter和setter
2、将数组中的元素单独替换,数据修改成功,但是Vue没有监测到,页面不显示,这是因为数组中的每个元素是没有getter和setter的,如果修改数组中的元素无法实现响应式.
3、要想让更改数组中的元素实现响应式,就得调用数组的七个api或者用Vue.set()方法

解决:

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表排序title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      <h1>人员列表h1>
      <button @click="updateMei">更新马冬梅信息button>
      <ul>
        <li v-for="(p,index) in persons" :key="p.id">
          {{p.name}}----{{p.age}}----{{p.sex}}
        li>
      ul>
    div>

    <script>
      const vm = new Vue({
        el: "#root",
        data: {
          persons: [
            { id: "001", name: "马冬梅", age: 18, sex: "女" },
            { id: "002", name: "周冬雨", age: 19, sex: "女" },
            { id: "003", name: "周杰伦", age: 30, sex: "男" },
            { id: "004", name: "温兆伦", age: 50, sex: "男" },
          ],
        },
        methods: {
          updateMei() {
            // this.persons[0].name = "马老师"; //奏效(页面和Vue都监测到了)
            // this.persons[0].age = 28; //奏效(页面和Vue都监测到了)
            // this.persons[0].sex = "男"; //奏效(页面和Vue都监测到了)

            //不奏效,此处Vue监测不到数据改变,但是实际上已经改了
            // this.persons[0] = { id: "001", name: "马老师", age: 28, sex: "男" };

            this.persons.splice(0, 1, {
              id: "001",
              name: "马老师",
              age: 28,
              sex: "男",
            });
          },
        },
      });
    script>
  body>
html>

监测数据综合案例

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>数据监测综合案例title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    <div id="root">
      <h1>学生信息h1>

      
      <button @click="student.age++">年龄加1button><br />
      <button @click="addSex">增加性别属性,默认值:男button><br />
      <button @click="addFriend">在列表首位添加一个朋友button><br />
      <button @click="updateFirstFriendName">
        修改第一个朋友的名字为:小张,年龄改为20button
      ><br />
      <button @click="addHobby">添加一个爱好button><br />
      <button @click="updateHobby">修改一个爱好为:开车button><br />

      <h3>姓名:{{student.name}}h3>
      <h3>年龄:{{student.age}}h3>
      <h3 v-if="student.sex">性别:{{student.sex}}h3>
      <h3>爱好:h3>
      <ul>
        <li v-for="(h,index) in student.hobby" ::key="index">{{h}}li>
      ul>
      <h3>朋友们:h3>
      <ul>
        <li v-for="(f,index) in student.friends" ::key="index">
          {{f.name}}-{{f.age}}
        li>
      ul>
    div>

    <script type="text/javascript">
      const vm = new Vue({
        el: "#root",
        data: {
          student: {
            name: "小吴",
            age: 18,
            hobby: ["羽毛球", "乒乓球", "篮球"],
            friends: [
              { name: "小黄", age: 19 },
              { name: "小何", age: 19 },
            ],
          },
        },
        methods: {
          addSex() {
            Vue.set(this.student, "sex", "男");
            //this.$set(this.student, "sex", "男")
          },
          addFriend() {
            this.student.friends.unshift({ name: "小王", age: 18 });
          },
          updateFirstFriendName() {
            // this.student.friends[0].name = "小张";
            // this.student.friends[0].age = 20;
            // 另一种写法
            this.student.friends.splice(0, 1, { name: "小张", age: 20 });
          },
          addHobby() {
            this.student.hobby.push("排球");
          },
          updateHobby() {
            //把第二个修改为开车
            this.student.hobby.splice(1, 1, "开车");
            //this.$set(this.student.hobby,1,'开车')
          },
        },
      });
    script>
  body>
html>

你可能感兴趣的:(Vue2+Vue3,vue.js,前端,javascript,ajax,前端框架,观察者模式)