返回

初识 Vue3

Table of contents

Open Table of contents

如何使用 Vue

模板语法(Mustache 语法)

data 属性

methods 属性

原因

简单使用:

<div id="app">
  <h2>当前计数:{{count}}</h2>
  <button @click="add">+1</button>
  <button @click="minus">-1</button>
</div>
...
<script>
  const app = Vue.createApp({
    data() {
      return {
        count: 4,
      };
    },
    methods: {
      add() {
        this.count++;
      },
      minus() {
        this.count--;
      },
    },
  });
</script>

指令

v-for(列表渲染)(重要)

v-for指令可以遍历所有可迭代数据类型

列表渲染

当我们需要遍历渲染的是内容区域时,可以使用template元素,例如:<template v-for="item in arr">...</template>

v-bind(绑定属性)(重要)

在内容需要动态渲染的同时(插值语法),某些属性也需要动态绑定(v-bind)。

绑定基本属性

v-bind用于绑定一个或多个属性值,或者向另一个组件传递 props 值

<template>
  <!-- 完整写法 -->
  <img v-bind:src="src" alt="" />
  <!-- 语法糖写法(简写) -->
  <img :src="src" alt="" />
  <!-- 这种写法,右侧等式是纯静态数据,不是 data 中的变量 -->
  <img src="src" alt="" />

  <!-- 绑定 a 元素 的动态 href -->
  <a :href="href"></a>
</template>

v-bind修饰的属性,右侧都属于变量,会被Vue进行编译,不再是一个普通的字符串或固定值

绑定动态 class

绑定动态 style

我们可以利用v-bind:style来绑定一些CSS 内联样式 CSS property 名可以用驼峰式 (camelCase)短横线分隔(kebab-case,记得用引号括起来) 来命名;

绑定动态属性

在某些情况下,我们属性的名称可能也不是固定的:

绑定动态对象属性

如果我们希望将一个对象的所有属性,绑定到元素上的所有属性:可以直接使用 v-bind 绑定一个 对象

<div id="app">
  <div v-bind="info">info 对象会被拆解成div的各个属性</div>
</div>
<script>
  data() {
    return {
      info: {
        name: "zs",
        age: 20,
        sex: "",
      },
    };
  },
</script>

结果

v-on 事件绑定(重要)

用户对网页进行各种各样的交互:这个时候,我们就必须监听用户发生的事件,比如点击、拖拽、键盘事件等等在 Vue 中使用v-on指令可以监听各种事件的触发。v-on 的总结使用:

v-on 的基本使用

<div id="app">
  <p>{{ count }}</p>
  <!-- 基本使用 click 点击事件 -->
  <!-- 绑定一个表达式 -->
  <button v-on:click="count++">表达式的点击+1</button>
  <!-- 绑定一个 methods 中的方法 -->
  <button v-on:click="minus">methods方法的点击-1</button>
  <!-- v-on 简写 @ -->
  <button @click="minus">@格式的点击-1</button>

  <!-- mousemove鼠标移动事件 -->
  <div
    @mousemove="mouseMove"
    style="width: 100px; height: 100px; background-color: aqua"
  >
    鼠标移动区域
  </div>

  <!-- 一个元素绑定多个事件 -->
  <div
    v-on="{click:divClick,mousemove:mouseMove}"
    style="width: 100px; height: 100px; background-color: blueviolet"
  >
    鼠标移动且点击区域
  </div>
</div>
<script>
  data() {
      return {
        count: 0,
      };
    },
    methods: {
      minus() {
        this.count--;
      },
      mouseMove() {
        console.log("鼠标在盒子中移动了");
      },
      divClick() {
        console.log("盒子被点击了");
      },
    },
</script>

v-on 传递参数

当通过 methods 中定义方法,以供@click调用时,需要注意参数问题:

<div id="app">
  <!-- 事件会默认吧event对象传入 -->
  <button @click="btnClick1">按钮1</button>
  <!-- 传入参数时,若要使用 event事件,需主动传入 $event ,并接收-->
  <button @click="btnClick2($event,count)">按钮2</button>
</div>
<script>
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    btnClick1(event) {
      console.log(event);
    },
    btnClick2(event, count) {
      console.log(event, count);
    },
  },
</script>

v-on 的修饰符

详情请见 v-on 事件绑定 中的修饰符。基本使用:

<div id="app">
  <button @click.stop="btnClick">按钮</button>
  <input type="text" @keyup.enter="onEnter" />
</div>

v-if 条件渲染(重要)

在某些情况下,我们需要根据当前的条件决定某些元素或组件是否渲染,这个时候我们就需要进行条件判断了。Vue 提供了下面的指令来进行条件判断:v-if、 v-else、 v-else-if、 v-show

v-if、v-else、v-else-if

v-if、v-else、v-else-if用于根据条件来渲染某一块的内容

<div id="app">
  <input type="text" v-model.number="score" />
  <!-- 若一个判断中有多个 v-if,那么 v-else-if 会匹配与它最近的 v-if-->
  <h2 v-if="score > 90">优秀</h2>
  <h2 v-else-if="score > 80">良好</h2>
  <h2 v-else-if="score > 70">普通</h2>
  <h2 v-else>不及格</h2>
</div>
<script>
  data() {
    return {
      score: 10,
    };
  },
</script>

v-if 的渲染原理: v-if 是惰性的;当条件为 false 时,其判断的内容完全不会被渲染或者会被销毁掉。当条件为 true 时,才会真正渲染条件块中的内容。

如图所示:

结果

template 元素

v-if是一个指令,所以必须将其添加到一个元素例如div上。但是如果我们希望切换的是某个元素区域,且不希望div这种元素被渲染,此时我们可以选择使用template标签。 template元素可以当做不可见的包裹元素,并且在 v-if 上使用,但是最终template不会被渲染出来。类似于小程序中的block

<div id="app">
  <template v-if="show">
    <h2>哈哈哈</h2>
    <h2>哈哈哈</h2>
  </template>
  <template v-else>
    <h2>呵呵呵</h2>
    <h2>呵呵呵</h2>
  </template>
  <button @click="toggle">切换</button>
</div>
<script>
  data() {
    return {
      show: true,
    };
  },
  methods: {
    toggle() {
      this.show = !this.show;
    },
  },
</script>

结果

v-show

v-showv-if的用法基本一致,也是根据一个条件决定是否显示元素或者组件:

<div id="app">
  <h2 v-show="isShow">呵呵呵</h2>
</div>

v-show 和 v-if 的区别

开发中如何进行选择如果我们的原生需要在显示和隐藏之间频繁的切换,那么使用 v-show;如果不会频繁的发生切换,那么使用 v-if;

v-model(重要)

基本使用

<div id="app">
  <input type="text" v-model="message" />
  <h2>{{ message }}</h2>
</div>
<script>
  data() {
    return {
      message:"你好"
    };
  },
</script>

结果

v-model 的原理

官方有说到,v-model的原理其实是背后有两个操作:

<input v-model="searchText" />
<!-- 等价于 -->
<input :value="searchText" @input="searchText = $event.target.value" />

v-model 绑定其它元素

  1. textarea

    <div id="app">
      <textarea v-model="content" cols="30" rows="10" />
      <h2>article的值为:{{ article }}</h2>
    </div>
  2. checkbox

    • 单个勾选框v-model布尔值,此时 value值并不影响v-model的属性
    <div id="app">
      <label for="agreement">
        <input id="agreement" type="checkbox" v-model="isAgree" />同意
      </label>
      <!-- 这里的 isAgree 显示的是 true/false -->
      <h2>{{ isAgree }}</h2>
    </div>
    • 多个复选框
      • 当是多个复选框时,因为可以选中多个,所以对应的data 中属性是一个数组
      • 当选中某一个时,就会将对应的input 的 value 添加到数组中。
    <div id="app">
      <input type="checkbox" value="sing" v-model="hobbies" />
      <input type="checkbox" value="jump" v-model="hobbies" />
      <input type="checkbox" value="rap" v-model="hobbies" />Rap
      <h2>{{ hobbies }}</h2>
    </div>
    <script>
      data() {
          return {
            hobbies: [],
          };
        },
    </script>

    结果

  3. radio

    <div id="app">
      <input type="radio" value="male" v-model="gender" />
      <input type="radio" value="female" v-model="gender" />
      <!-- gender 显示的就是 选中项的 value -->
      <h2>{{ gender }}</h2>
    </div>
  4. select

<div id="app">
  <select v-model="fruit">
    <option value="apple">苹果</option>
    <option value="orange">橘子</option>
    <option value="banana">香蕉</option>
  </select>
  <!-- 这里的 fruit 显示的就是 option 选中项的 value -->
  <h2>{{ fruit }}</h2>
</div>

v-model 修饰符

  1. lazy
    • 默认情况下,v-model 在进行双向绑定时,绑定的是 input 事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步;
    • 如果我们在 v-model 后跟上 lazy 修饰符,那么会将绑定的事件切换为 change 事件,只有在提交时(比如回车)才会触发;
  2. number
    • v-model 绑定的值总是string类型,即使在我们设置type="number"也是”string类型
    • 如果我们希望转换为数字类型,那么可以使用 .number 修饰符
  3. trim 如果要自动过滤用户输入的空白字符,可以给v-model添加 trim 修饰符

v-html

默认情况下,如果我们展示的内容本身是 html 的,那么 vue 并不会对其进行特殊的解析,默认都当做普通字符串渲染。如果我们希望这个内容被 Vue 可以解析出来,那么可以使用 v-html 来展示;

<div id="app">
  <div v-html="info"></div>
</div>
<script>
  data() {
    return {
      info: "<h2 style='color:red;'>你好</h2>",
    };
  },
</script>

v-html

v-pre

v-pre 用于跳过元素和它的子元素的编译过程,显示原始的 Mustache 标签跳过不需要编译的节点,加快编译的速度

v-pre

v-cloak

这个指令保持在元素上直到关联组件实例结束编译 和 CSS 规则如[v-cloak] { display: none }一起用时,这个指令可以隐藏未编译的 Mustache 标签直到组件实例准备完毕

v-cloak

页面在渲染时,当编译时间过长,未解析到模板语法的时候,页面实际显示的是{{info}},编译完成时,才会替换为属性值。为了防止这种现象,可以使用v-cloak

v-once(了解)

v-once 用于指定元素或者组件只渲染一次

如果是子节点,也是只会渲染一次

<div v-once>
  <h2>当前计数:{{count}}</h2>
  <button @click="add">+1</button>
  <button @click="minus">-1</button>
</div>

简单概括就是 v-once修饰的节点以及其下属节点,都全部视为静态内容

v-text(了解)

<h2 v-text="message">{{count}}</h2>
<!-- 等价于 -->
<h2>{{count}}</h2>

数组更新检测

Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。这些被包裹的方法包括:push()、pop()、shift()、unshift()、splice()、sort()、reverse() 上面的方法会直接修改原来的数组。但是某些方法不会替换原来的数组,而是会生成新的数组,比如 filter()、concat() 和 slice()

选项式 API(Options API)

复杂 data 的处理

初识计算属性 computed

  1. 什么是计算属性?官方并没有给出直接的概念解释;而是说:对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性 计算属性将被混入到组件实例中。所有 getter 和 setter 的 this 上下文自动地绑定为组件实例;
  2. 计算属性的用法选项:computed 类型:{ [key: string]: Function | { get: Function, set: Function } }

案例实现思路

我们来看三个案例:

思路一:模板语法

<div id="app">
  <h2>{{ firstName + lastName }}</h2>
  <h2>{{ score >= 60 ? "及格":"不及格" }}</h2>
  <h2>{{ message.split("").reverse().join("") }}</h2>
</div>
<script>
  data() {
    return {
      firstName: "",
      lastName: "狗蛋",
      score: 75,
      message: "你好啊!",
    };
  },
</script>

缺点一:模板中存在大量的复杂逻辑,不便于维护(模板中表达式的初衷是用于简单的计算)。缺点二:当有多次一样的逻辑时,存在重复的代码。缺点三:多次使用的时候,很多运算也需要多次执行,没有缓存。

思路二:method 实现

<div id="app">
  <h2>{{ getFullName() }}</h2>
  <h2>{{ getResult() }}</h2>
  <h2>{{ getFullName() }}</h2>
</div>
<script>
  data() {
    return {
      firstName: "",
      lastName: "狗蛋",
      score: 75,
      message: "你好啊!",
    };
  },
  methods: {
    getFullName() {
      return this.firstName + this.lastName;
    },
    getResult() {
      return this.score >= 60 ? "及格" : "不及格";
    },
    getReverseMsg() {
      return this.message.split("").reverse().join("");
    },
  },
</script>

缺点一:我们事实上先显示的是一个结果,但是都变成了一种方法的调用,且此类代码过多会提高代码的复杂度和难以维护性。缺点二:多次使用方法的时候,没有缓存,也需要多次计算

思路三:computed 实现

<div id="app">
  <h2>{{ fullName }}</h2>
  <h2>{{ result }}</h2>
  <h2>{{ reverseMsg }}</h2>
</div>
<script>
   data() {
    return {
      firstName: "",
      lastName: "狗蛋",
      score: 75,
      message: "你好啊!",
    };
  },
  computed: {
    fullName() {
      return this.firstName + this.lastName;
    },
    result() {
      return this.score >= 60 ? "及格" : "不及格";
    },
    reverseMsg() {
      return this.message.split("").reverse().join("");
    },
  },
</script>

注意:计算属性看起来像是一个函数,但是我们在使用的时候不需要加(),可以看做一个已经调用过的函数,得到的相当于 data 中的值。我们会发现无论是直观上,还是效果上计算属性都是更好的选择。并且 计算属性是有缓存 的。

computed VS methods

<div id="app">
  <h2>{{ getFullName() }}</h2>
  <h2>{{ getFullName() }}</h2>
  <h2>{{ getFullName() }}</h2>
  <h2>{{ fullName }}</h2>
  <h2>{{ fullName }}</h2>
  <h2>{{ fullName }}</h2>
</div>
<script>
  data() {
    return {
      firstName: "",
      lastName: "狗蛋",
    };
  },
  computed: {
    fullName() {
      console.log("调用了computed中的fullName");
      return this.firstName + this.lastName;
    },
  },
  methods: {
    getFullName() {
      console.log("调用了methods中的getFullName");
      return this.firstName + this.lastName;
    },
  },
</script>

结果

由图可得:渲染同样个数多个标签,methods调用了 3 次,而computed只调用了 1 次,区别就在于计算属性的缓存

计算属性的缓存

  1. 这是因为计算属性会基于它们的依赖关系进行缓存
  2. 数据不发生变化时,计算属性是不需要重新计算
  3. 但是如果依赖的数据发生变化,在使用时,计算属性依然会重新进行计算

对比

计算属性的 setter 和 getter

计算属性在大多数情况下,只需要一个 getter 方法即可,所以我们会将计算属性直接写成一个函数

computed: {
    fullName() {
      return this.firstName + this.lastName;
    },
  },

但是想设置计算属性的值呢,也可以给计算属性设置一个 setter 的方法,此时要写成对象格式。

computed: {
  fullName: {
    get() {
      console.log("getter");
      return this.firstName + "0" + this.lastName;
    },
    set(value) {
      const name = value.split("0");
      console.log(value, name);
      this.firstName = name[0];
      this.lastName = name[1];
    },
  },
},

侦听器 watch

使用 watch

在某些情况下,我们希望在代码逻辑中监听某个数据的变化,需要用侦听器 watch来完成。即,当某个数据发生变化时,我们需要根据这个数据的最新值去做出一些响应操作,比如:搜索框。侦听器用法:选项:watch 类型:{ [key: string]: string | Function | Object | Array}

<div id="app">
  <label for="question">请输入问题:</label>
  <input type="text" id="question" v-model="question" />
</div>
<script>
  data() {
    return {
      question: null,
    }
  }
  watch: {
    // question 就是需要侦听的变量
    question(newV, oldV) {
      console.log("newV:" + newV, "oldV:" + oldV);
      this.getAnwser(newV);
    },
  },

  methods: {
    getAnwser(question) {
      console.log(`问题是:${question}。正在请求后台数据...`);
    },
  },
</script>

结果

watch内部的监听的函数会接收到两个值,第一个是监听的值的最新值,第二个是监听的值的上一次的值

watch 的配置选项

[!WARNING] 当我们点击按钮的时候会 修改 info.name 的值;我们使用 watch 来侦听 info, 可以侦听到吗? 答案是 不可以

watch:{
  info: {
    hanlder(newV, oldV){
      console.log(newV, oldV)
    },
    deep: true,  // 开启深度监听
    immediate: true  // 首次加载立即执行一次
  },
  // 侦听对象的属性
  "info.name": function(newV, oldV){
    console.log(newV, oldV)
  }
}

watch 的其它侦听方式

  1. watch Option API 中的其它监听方式使用
data() {
  return {
    a: "字符串方法名",
    b: "数组格式的侦听",
  };
},
watch: {
  a: "someMethod", // 字符串方法名称
  // 你可以传入回调数组,它们将会被逐一调用
  b: [
    "handle1",
    function handle2(val, oldVal) {
      console.log("b 的 handle2 触发");
    },
    {
      handler: function handle3(val, oldVal) {
        console.log("b 的 handle3 触发");
      },
      /* ... */
    },
  ],
},

methods: {
  someMethod(question) {
    console.log("a 改变了");
  },
  handle1() {
    console.log("b 的 handle1 触发");
  },
},
  1. $watch

我们可以在created的生命周期中,使用 this.$watch 来侦听;

created(){
  this.$watch('message', (newV, oldV) => {
    console.log(newV, oldV)
  },{deep: true, imediate: true})
}


上一篇
Vue 组件
下一篇
前后端分离宝塔部署