vue基础第二天

148 阅读5分钟

vue第二天

v-text和v-html

目标:更新DOM对象的innerText/innerHTML

语法:

  • v-text="Vue数据变量"

  • v-html="Vue数据变量"

<template>
  <div>
    <!-- v-text:作用 设置标签内容 -->
    <!-- 语法:v-text="变量" -->
    <!-- 变量声明在data方法返回对象里 -->
    <!-- v-text把变量内容作为文本直接显示,不解析内容/标签 -->
    <h1 v-text="hello"></h1>
    <h1 v-text="helloHTML"></h1>

    <!-- v-html:作用,设置标签内容为HTML -->
    <!-- 语法:v-html="变量" -->
    <!-- 变量声明在data方法返回对象里 -->
    <!-- v-html会解析内容中的html标签 -->
    <h1 v-html="hello"></h1>
    <h1 v-html="helloHTML"></h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      hello: "hello world",
      helloHTML: "<button>点我</button>",
    };
  },
};
</script>

<style>
</style>

总结:二者都可以设置标签显示的内容,v-text不会解析变量内的html标签,v-html会解析。另外二者都会覆盖插值表达式的内容!!

v-show和v-if

二者都可以控制标签的隐藏与显示

语法:

  • v-show="Vue变量" -- 通过控制display:none隐藏(频繁切换使用)

  • v-if="Vue变量" -- 页面显示满足条件的 移除不满足条件的(直接从DOM树上移除 /不满足条件的则被移除) 可以配合v-else-if / v-else使用!! 实现多个条件控制,但必须是相邻节点,中间不能夹杂其他标签。

    -------二者需要**(判断Vue变量)**或(判断某些条件)控制显示或隐藏-------

    -------v-show或v-if, 给变量赋予true/false, 显示/隐藏-----

例:

<template>
  <div>
    <!-- v-show -->
    <!-- 作用:控制标签显示和隐藏 -->
    <!-- v-show语法,v-show="表达式" -->
    <!-- v-show通过控制display: none属性来控制显示隐藏 -->
    <h1> v-show </h1>
    <h2 v-show="age >= 18">成年人</h2>
    <h2 v-show="age < 18">未成年</h2>

    <!-- v-if -->
    <!-- 作用:控制标签显示和隐藏 -->
    <!-- v-if语法:v-if="表达式" -->
    <!-- v-if通过控制是否插入标签来显示隐藏 -->
    <h1> v-if </h1>
    <h2 v-if="age >= 18">成年人</h2>
    <h2 v-if="age < 18">未成年</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      age: 18,
    };
  },
};
</script>
<style>
</style>

v-ifv-else-if / v-else

v-ifv-else-if / v-else搭配使用示例:

<template>
  <div>
    <input type="text" v-model="age" />
    <!-- v-if和v-else可以搭配使用 --必须是相邻节点 -->
    <h2 v-if="age < 18">给你甜甜圈</h2>
    <h2 v-else>给你快乐水</h2>

    <!-- v-if可以和多个v-else-if搭配使用,实现多个条件控制 -->
    <!-- 多个条件控制可以使用v-else结尾 -->
    <!-- v-else是可选的 -->
    <h2 v-if="age < 18">甜甜圈</h2>
    <h2 v-else-if="age < 60">快乐水</h2>
    <h2 v-else-if="age < 100">脑白金</h2>
    <h2  v-else>虫草</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      age: 18,
    };
  },
};
</script>
<style>
</style>

折叠面板案例:

<template>
  <div>
    <h1 class="tc">案例:折叠面板</h1>
    <h2>
      静夜思
      <!-- 根据show的布尔值设置按钮的显示文字 -->
      <button @click="toggle"> {{ show ? "收起" : "展开"}}</button>
    </h2>
    <!-- 使用v-show或者v-if控制内容显示 -->
    <div v-show="show">
      <p>床前明月光</p>
      <p>疑似地上霜</p>
      <p>举头望明月</p>
      <p>低头思故乡</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show:"ture",
    };
  },
    methods:{
      // 使用取反运算
      toggle(){
        // console.log('ok');
        this.show = !this.show
      }
    }
};
</script>
<style>
.tc {
  text-align: center;
}
</style>

v-for更新监测,key作用

当v-for遍历的数据解构/内容改变,Vue触发v-for的更新

  • 情况1: 数组翻转

  • 情况2: 数组截取

  • 情况3: 更新值

口诀:

  • 数组变更方法, 就会导致v-for更新, 页面更新

  • 数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()

<template>
  <div>
    <ul>
      <li v-for="item in list">{{ item }}</li>
    </ul>
    <!-- 数组反转 reverse方法会修改原始数组,会触发页面刷新-->
    <p><button @click="list.reverse()">反转</button></p>
    <!-- 数组截取 slice方法不会修改原始数组,不会触发页面更新-->
    <p><button @click="list.slice(0, 2)">数组截取</button></p>
    <!-- 更新值 直接通过索引修改数组,不会触发页面更新-->
    <p><button @click="list[1] = '宝宝'">通过索引更新数组元素</button></p>
    <!-- 更新值 $set方法手动触发更新 -->
    <p><button @click="fn">通过$set方法更新数组元素</button></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: ["六一", "小宝", "千金"],
    };
  },
  methods: {
    fn() {
      //this.$set(数组,索引,要更新的值)
      this.$set(this.list, 1, "宝宝");
    },
  },
};
</script>

<style>
</style>
这些方法会触发数组改变, v-for会监测到并更新页面
  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()
这些方法不会触发v-for更新
  • slice()
  • filter()
  • concat()

注意: vue不能监测到数组里赋值的动作而更新, 如果需要请使用Vue.set() 或者this.$set(), 或者覆盖整个数组

总结:改变原数组的方法才能让v-for更新

v-for就地更新

就地更新原则,新旧DOM产生后对比, 然后决定是否复用真实DOM/更新内容

v-for的默认行为会尝试原地修改元素而不是移动它们。

1652623872259.png

虚拟DOM本质是一个JS对象,保存DOM关键信息。 好处是提高DOM更新的性能 ,不频繁操作真实DOM,在内存中找到变化部分,在更新真是DOM(打补丁)!!

key的作用

v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key 来提供一个排序提示

  • 无key -- 就地更新

  • 有key为索引 -- 就地更新 (但不会报错了😊)

  • 有key为id(唯一不重复的字符串或者数值) -- 按照key比较

<template>
  <div>
    <ul>
      <!--有key以后  不再是就地更新  而是按照key比较  key为唯一不变的值 -->
      <!-- key的好处是可以配合虚拟DOM提高更新的性能 -->
      <!-- key为索引index 还是就地更新 -->
      <!-- key为id(唯一值)  按照key作比较 -->
      <!-- <li v-for="(item,index) in arr" :key="index"> -->
      <li v-for="(item) in arr" :key="item.id">
        <p>{{ item.name }}</p>
        <p>{{ item.age }}</p>
        <p>{{ item.gender }}</p>
      </li>
    </ul>
    <button @click="arr.reverse()">反转</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: [
        {
          id: 1,
          name: "zyz",
          age: 23,
          gender: "女",
        },
        {
          id: 2,
          name: "gyh",
          age: 27,
          gender: "男",
        },
        {
          id: 3,
          name: "baby",
          age: 0,
          gender: "女",
        },
      ],
    };
  },
};
</script>
<style>
</style>
v-for更新检测总结

v-for什么时候会更新页面呢?

  • 数组采用更新方法, 才导致v-for更新页面

vue是如何提高更新性能的?

  • 采用虚拟DOM+key提高更新性能

虚拟DOM是什么?

  • 本质是保存dom关键信息的JS对象

如何比较新旧虚拟DOM?

  • 根元素改变 – 删除当前DOM树重新建
  • 根元素未变, 属性改变 – 更新属性
  • 根元素未变, 子元素/内容改变
  • 无key – 就地更新 / 有key为索引 -- 就地更新 / 有key 为唯一值– 按key比较

动态class-动态style

动态class--用v-bind给标签class设置动态的值

动态style--给标签动态设置style的值

语法

  • 动态class语法: :class="{类名:布尔值}"

    ⚠️⚠️⚠️类名有横线,加引号.

  • 动态style语法: :style="{css属性: 值}"

    ⚠️⚠️⚠️样式名有横线,加引号或使用小驼峰写法.

动态设置class示例:
<template>
  <div>
    <!-- 动态设置class -->
    <!-- :class="{ 类名: 布尔值}" -->
    <!-- 布尔值为true,标签增加类名,为false删除类名 -->
    <!-- 可以控制多个类名 -->
    <button :class="{ on: isOn, off: !isOn }" @click="fn">开关</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isOn: false,
    };
  },
  methods: {
    fn() {
      this.isOn = !this.isOn;
    },
  },
};
</script>

<style>
.off {
  background-color: grey;
}

.on {
  background-color: red;
}
</style>
动态设置style示例:
<template>
  <div>
    <!-- 动态设置样式 -->
    <!-- :class="{ 类名:布尔值 }",类名如果包含横线-,采用引号 -->
    <!-- 动态设置style -->
    <!-- :style="{ 样式名: 样式的值 }",样式名如果带横线,改为小驼峰,也可以使用引号 -->
    <button
      @click="fontColor = 'blue'"
      :style="{ color: fontColor, 'font-size': '60px' }"
      :class="{ 'text-center': true, textTop: true }"
    >
      变色
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      fontColor: "red",
    };
  },
};
</script>

<style>
</style>

vue过滤器

vue过滤器定义和使用

作用:转换格式, 过滤器就是一个函数, 传入值返回处理后的值

注:过滤器只能用在, ==插值表达式v-bind表达式==

局部过滤器--声明在VUE文件data统计的filters对象属性里面

全局过滤器--声明在main.js文件里面 **语法:Vue.filter("过滤器名",方法)**🚨

Vue中的过滤器场景
  • 字母转大写, 输入"hello", 输出"HELLO"
  • 字符串翻转, "输入hello, world", 输出"dlrow ,olleh"

语法:

  • Vue.filter("过滤器名", (值) => {return "返回处理后的值"})

  • filters: {过滤器名字: (值) => {return "返回处理后的值"}

例子:

  • 全局定义字母都大写的过滤器
  • 局部定义字符串翻转的过滤器
  • ..............

示例:

局部过滤器和全局过滤器(声明在main.js里面的过滤器)

<template>
  <div>
    <!-- 过滤器作用:转换数据格式 -->
    商品价格:{{ price | priceFilter }} 

    <h2>{{ "hello" | toUpperCase }}</h2>
    <!-- 过滤器能使用在插值表达式和v-bind属性里 -->
    <!-- :title 是 v-bind方法悬停时显示 -->
    <p :title="'hello vue' | toUpperCase">hello vue</p>


  </div>
</template>

<script>
export default {
  data() {
    return {
      price: 100,
    };
  },
  // 声明在data同级下的filters对象里
  filters: {
    //过滤器声明为方法
    // 表达式的值是过滤器的第一个参数
    priceFilter(num) {
      return `¥${num}`;
      // 不足十元补0
      // return `¥${num < 10 ? "0" + num : num}`;
    },
    toUpperCase(value) {
      return value.toUpperCase();
    },
  },
};
</script>

<style>
</style>
vue过滤器 传参和多过滤器

目标: 可同时使用多个过滤器, 或者给过滤器传参

语法:

  • 过滤器传参: vue变量 | 过滤器(实参)
  • 多个过滤器: vue变量 | 过滤器1 | 过滤器2

1652627003073.png

vue计算属性

一个变量的值, 依赖另外一些数据计算而来的结果

注意⚠️: 计算属性也是vue数据变量, 所以不要和data里重名, 用法和data相同

语法:

computed: {

​      "计算属性名"() {

​            return "值"

​    }

}

例:

<template>
  <div>
    <!-- 计算属性,作用:根据一些数据计算出来一个属性 -->
    <!-- 当计算属性以来的数据变化时,计算属性也会重新运算 -->
    <!-- 计算属性是作为变量使用的,不要使用括号的语法 -->
    <!-- 计算属性不能和data里的变量重名 -->
    <h1>{{ sum }}</h1>
    a: <input type="text" v-model.number="a" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      a: 10,
      b: 20,
    };
  },
  // 计算属性声明在data统计的computed对象里面
  computed: {
    //计算属性声明为方法
    sum() {
      return this.a + this.b;
    },
  },
};
</script>

<style>
</style>

计算属性-缓存

计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果

❗计算属性的好处:

  • 带缓存

  • 依赖项不变,直接从缓存中取

  • 一拉想改变,函数自动执行并从新缓存

❗使用场景

  • 当变量值依赖其他变量计算而得来采用

计算属性完整写法

计算属性也是变量, 如果想要直接赋值, 需要使用完整写法

computed: {
    "属性名": {
       <!--  set更新/设置计算属性 -->
        set(值){
            
        },
       <!--  get获取计算属性 -->
        get() {
            return "值"
        }
    }
}
品牌管理案例
<template>
  <div class="container">
    <div class="row">
      <div class="col-12 pt-3">
        <table class="table table-bordered">
          <thead>
            <tr>
              <th scope="col">编号</th>
              <th scope="col">资产名称</th>
              <th scope="col">价格</th>
              <th scope="col">创建时间</th>
              <th scope="col">操作</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(item, index) in list" :key="item.id">
              <th scope="row">{{ item.id }}</th>
              <td>{{ item.name }}</td>
              <td :class="{ expensive: item.price > 100 }">{{ item.price }}</td>
              <td>{{ item.time | dataFilter }}</td>
              <td>
                <!-- 删除1.删除按钮,绑定事件 -->
                <!-- 删除2.绑定事件获取要删除的元素索引 -->
                <button
                  type="button"
                  class="btn btn-link"
                  @click="deleteProduct(index)"
                >
                  删除
                </button>
              </td>
            </tr>

            <tr class="bg-light">
              <th scope="row">统计</th>
              <td colspan="2">总价:{{ totalPrices }}</td>
              <td colspan="2">均价:{{ averagePrice }}</td>
            </tr>
          </tbody>
          <!-- 删除4.控制空状态显示 -->
          <tfoot v-show="list.length === 0">
            <tr>
              <td class="text-center" colspan="5">暂无数据</td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>

    <form class="row align-items-center">
      <div class="col-3">
        <input
          type="text"
          class="form-control"
          placeholder="资产名称"
          v-model="productName"
        />
      </div>

      <div class="col-3">
        <input
          type="text"
          class="form-control"
          placeholder="价格"
          v-model.number="productPrice"
        />
      </div>

      <div class="col-3">
        <button
          type="submit"
          class="btn btn-primary"
          @click.prevent="addProperty"
        >
          添加资产
        </button>
      </div>
    </form>
  </div>
</template>

<script>
import moment from "moment";
export default {
  name: "App",
  data() {
    return {
      list: [
        {
          id: 100,
          name: "外套",
          price: 199,
          time: new Date("2010-08-12"),
        },
        {
          id: 101,
          name: "裤子",
          price: 34,
          time: new Date("2013-09-01"),
        },
        {
          id: 102,
          name: "鞋",
          price: 25.4,
          time: new Date("2018-11-22"),
        },
        {
          id: 103,
          name: "头发",
          price: 19900,
          time: new Date("2020-12-12"),
        },
      ],
      productName: "",
      productPrice: 0,
    };
  },
  computed: {
    //计算总价业务相关代码
    totalPrices() {
      let total = 0;
      this.list.forEach((item) => {
        total += item.price;
      });
      return total;
    },
    // 计算均价业务相关代码
    averagePrice() {
      let average = this.totalPrices / this.list.length;
      return average
      
    }
  },
  filters: {
    dataFilter(time) {
      return moment(time).format("YYYY-MM-DD");
    },
  },
  methods: {
    addProperty() {
      if (!this.productName || !this.productPrice) {
        alert("资产名称或价格不能为空");
        return;
      }
      //删除5.处理空数据情况下新增逻辑异常
      let id;
      if (this.list.length > 0) {
        id = this.list[this.list.length - 1].id + 1;
      } else {
        id = 100;
      }
      this.list.push({
        id,
        name: this.productName,
        price: this.productPrice,
        time: new Date(),
      });
      (this.productName = ""), (this.productPrice = 0);
    },
    deleteProduct(index) {
      //删除3.删除元素
      // console.log(index);
      this.list.splice(index, 1);
    },
  },
};
</script>

<style scoped>
.expensive {
  color: red;
}
</style>

全选小选案例
<template>
  <div>
    <p>全选:<input type="checkbox" v-model="isAll" /></p>
    <ul>
      <li v-for="item in list" :key="item.id">
        <input type="checkbox" v-model="item.checked" /> {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        {
          id: 1,
          name: "八戒",
          checked: false,
        },
        {
          id: 2,
          name: "孙悟空",
          checked: false,
        },
        {
          id: 3,
          name: "唐僧",
          checked: false,
        },
        {
          id: 4,
          name: "白龙马",
          checked: false,
        },
      ],
    };
  },
  computed: {
    isAll: {
      // set 设置/更新计算属性
      set(value) {
        this.list.forEach((item) => {
          item.checked = value;
        });
      },
      //get 获取计算属性
      get() {
        let all = true;
        this.list.forEach((item) => {
          if (item.checked === false) {
            all = false;
          }
        });
        return all;
      },
    },
  },
};
</script>

<style>
</style>