我的Vue学习笔记(1)

文章目录

1. Vue初识

1.1 初学编写hello world和Counter

1.1.1 hello world

在页面中引入Vue。

<script src="https://unpkg.com/vue@next"></script>

引入CDN之后,就可以编写Vue的代码。

Vue3.0中,使用Vue.createApp()来创建一个Vue的实例。

mount()函数代表在指定的元素上使用Vue。

template是一个模板,里面存放要展示的内容。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>hello world</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      Vue.createApp({
        template: "<div>hello world!</div>",
      }).mount("#root");
    </script>
  </body>
</html>

这段代码的意思是,找到id为root的元素,把template模板放到这个元素中。

<div id="root"></div>
    <script>
      Vue.createApp({
        template: "<div>hello world!</div>",
      }).mount("#root");

这就是使用Vue写hello world的最简单的一个demo。

1.1.2 counter

同样要使用Vue.createApp构建Vue实例。这次往模板中添加变量。要用两个花括号进行包裹{{}},代表一个JS表达式。

其中的变量要写在Vue.createAppdata()函数中。

data()这个函数返回一个对象,对象的内容就是Vue实例要用到的变量。

  Vue.createApp({
    data() {
      return {
        content: 1,
      };
    },
    template: "<div>{{content}}</div>",
  }).mount("#root");

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>hello world</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      Vue.createApp({
        data() {
          return {
            content: 1,
          };
        },
        template: "<div>{{content}}</div>",
      }).mount("#root");
    </script>
  </body>
</html>

这里引入一个mounted()函数

mounted()函数在页面加载完成后会自动执行。

改变数据,页面的内容就会跟着变。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>hello world</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      Vue.createApp({
        data() {
          return {
            content: 1,
          };
        },
        // 页面加载完成执行mounted函数
        mounted() {
          setInterval(() => {
            // this.$data.content 的简写
            this.content += 1;
          }, 1000);
        },
        template: "<div>{{content}}</div>",
      }).mount("#root");
    </script>
  </body>
</html>

小结:Vue——面向数据编程。

1.2 编写字符串反转和内容隐藏小功能

1.2.1 字符串反转(v-on指令)

现在,我们让content中的内容变为字符串"hello world",并把这个字符串写到模板中,然后把模板挂载到id为root的元素上去。

  Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },
    template: "<div>{{content}}</div>",
  }).mount("#root");

我们想在页面上新增一个按钮。写到template中去。

  Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },
    template: `
    <div>{{content}}</div>
    <button>反转</button>
    `,
  }).mount("#root");

给这个按钮绑定一个事件,要使用Vue的v-on指令。对应的函数要写在methods中。

      Vue.createApp({
        data() {
          return {
            content: "hello world",
          };
        },
        methods: {
          handleBtnClick() {
            console.log("hello world");
          },
        },
        template: `
        <div>{{content}}</div>
        <button v-on:click="handleBtnClick">反转</button>
        `,
      }).mount("#root");

现在来编写handleBtnClick()这个函数。

我们的目的是反转content的内容。

methods: {
  handleBtnClick() {
    this.content = this.content.split("").reverse().join("");
  },
},
1.2.2 内容隐藏(v-if指令)

使用v-if可以控制某一元素是否要渲染在页面上。

  Vue.createApp({
    data() {
      return {
        show: true,
        content: "hello world",
      };
    },
    methods: {
      handleBtnClick() {
        this.show = this.show ? false : true;
      },
    },
    template: `
    <div v-if="show">{{content}}</div>
    <button v-on:click="handleBtnClick">显示/隐藏</button>
    `,
  }).mount("#root");

1.3 编写TodoList小功能,了解循环和双向绑定(v-for指令、v-model指令)

1.3.1 v-for指令

现在,我们有一个数组,我们想让数组中的内容能够循环展示到页面上。

  Vue.createApp({
    data() {
      return {
        list: ["hello", "world", "cyy"],
      };
    },
  }).mount("#root");

想要把数组中的内容循环展示,需要使用v-for这个命令。

  Vue.createApp({
    data() {
      return {
        list: ["hello", "world", "cyy"],
      };
    },
    methods: {},
    template: `
    <ul>
      <li v-for="item of list">{{item}}</li>
    </ul>
    `,
  }).mount("#root");

item指向list中的每一项。

v-for指令中除了传入item,还可以传入index,比如这样:

  Vue.createApp({
    data() {
      return {
        list: ["hello", "world", "cyy"],
      };
    },
    methods: {},
    template: `
    <ul>
      <li v-for="(item, index) of list">{{item}}{{index}}</li>
    </ul>
    `,
  }).mount("#root");

list中的每一项放在item中,每一项的下标放在index中。

1.3.2 实现TodoList

新增input框,在点击按钮时,把input框中的内容添加到list中,并渲染到页面上进行显示。

想要获取Input框的内容,要使用v-model指令进行双向绑定。

v-model="inputValue"代表当前输入框中的值和inputValue的值是双向绑定在一起的。

      Vue.createApp({
        data() {
          return {
            list: [],
            inputValue: "",
          };
        },
        methods: {
          handleAddItem() {
            this.list.push(this.inputValue);
            this.inputValue = "";
          },
        },
        template: `
        <div>
          <input v-model="inputValue"/>
          <button v-on:click="handleAddItem">增加</button>
          <ul>
            <li v-for="(item, index) of list">{{item}}{{index}}</li>
          </ul>
        </div>
        `,
      }).mount("#root");

1.4 组件概念初探,对TodoList进行组件代码拆分(v-bind指令)

1.4.1 v-bind指令

一个场景:

  • 以前我们在标签中想使用一个数据,可以使用两个花括号{{}}这样的语法(插值表达式)比如:<button>{{item}}</button>
  • 如果是想给标签上的某个属性进行数据的绑定,就不能使用插值表达式,要使用v-bind指令。比如<button v-bind:title="item"></button>

v-bind指令:在标签上给某个属性绑定值。在标签内部绑定数据直接使用插值表达式即可。

1.4.2 组件的概念

组件就是页面上的一部分。

现在把TodoList的列表拆分成组件。

首先,把创建的Vue实例赋值给一个变量app

  const app = Vue.createApp({
    data() {
      return {
        list: [],
        inputValue: "",
      };
    },
    methods: {
      handleAddItem() {
        this.list.push(this.inputValue);
        this.inputValue = "";
      },
      handleItemDelete(index) {
        this.list.splice(index, 1);
      },
    },
    template: `
    <div>
      <input v-model="inputValue"/>
      <button v-on:click="handleAddItem" v-bind:title="inputValue">增加</button>
      <ul>
        <li v-for="(item, index) of list" v-on:click="handleItemDelete(index)">{{item}}</li>
      </ul>
    </div>
    `,
  });

之所以把Vue实例放到这个app中,是因为我们想要在这个名为app的实例中注册一些组件。

通过component()方法来注册组件。使用方法:Vue实例.component('组件名称', {}),其中第二个参数是一个对象,用来描述这个组件中的内容。比如:

  app.component("todo-item", {
    template: `<div>hello world</div>`,
  });

然后就可以在Vue的实例中使用<todo-item>这个标签来添加这个组件。

最后要把app这个Vue实例挂载到页面的DOM元素上。

    <div id="root"></div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            list: [],
            inputValue: "",
          };
        },
        methods: {
          handleAddItem() {
            this.list.push(this.inputValue);
            this.inputValue = "";
          },
          handleItemDelete(index) {
            this.list.splice(index, 1);
          },
        },
        template: `
        <div>
          <input v-model="inputValue"/>
          <button v-on:click="handleAddItem" v-bind:title="inputValue">增加</button>
          <ul>
            <todo-item v-for="(item, index) of list" v-on:click="handleItemDelete(index)">{{item}}</todo-item>
          </ul>
        </div>
        `,
      });

      app.component("todo-item", {
        template: `<div>hello world</div>`,
      });

      app.mount("#root");

一个组件应该包含该组件对应的展示内容(即DOM结构)、样式、JS逻辑。

正常情况下,一个组件中要包含要渲染的数据。

  app.component("todo-item", {
    data() {
      return {
        content: "hello",
      };
    },
    template: `<div>{{content}}</div>`,
  });

但是现在,我们把这个列表展示组件拆分了出去,变成了一个小的组件。这个组件中要展示的数据来自app实例中的list这个数组。

要把list的数据传递给这个小的组件,要在app实例中,使用这个组件的地方,使用v-bind把对应的数据绑定在标签上。这样,这个小组件todo-item就能够感应到这个组件。

// app实例中

template: `
<div>
  <input v-model="inputValue"/>
  <button v-on:click="handleAddItem" v-bind:title="inputValue">增加</button>
  <ul>
    <todo-item v-for="(item, index) of list" v-bind:content="item" />
  </ul>
</div>
`,

todo-item中,不需要再使用data()方法来定义自己的函数,要使用props来接受父组件传递过来的值。

// todo-item组件:

  app.component("todo-item", {
    props: ["content"],
    template: `<div>{{content}}</div>`,
  });

2. Vue.js基础语法、生命周期与事件

2.1 Vue中应用和组件的基础概念(mvvm设计模式)

2.1.1 引入mvvm

来看一个最基础的Vue实例。

  const app = Vue.createApp({});
  app.mount("#root");

解释一下这两句话是什么意思。

Vue.createApp({}):创建一个Vue的应用。

app.mount("#root"):这个名为app的应用只作用于id为root的DOM结点上。

再来丰富一下代码:

  // 创建一个 Vue 应用,存储到 app 变量中
  // 传入的参数表示,这个应用最外层的组件应该如何展示
  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },

    template: `<div>{{content}}</div>`,
  });
  app.mount("#root");

在使用Vue.createApp创建Vue实例时,我们为其中传入了参数。传入的参数表示,这个应用的最外层组件,应该如何展示。

现在,在创建应用之后,如果想获取对应创建的根组件,该如何获取?

其实,当我们调用app.mount()方法的时候,它的返回值就是对应的根组件。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },

    template: `<div>{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

当我们理解了应用和根组件之后,我们再来做一波分析。

实际上,我们已经感知到,Vue的编程是一种面向数据的编程。

我们定义了数据和模板,Vue会自动把数据和模板关联起来,变成我们想要展示的页面效果。

实际上,Vue这种面向数据的编程模式,是参考了mvvm这种设计模式

⭐什么是mvvm?

m:model,数据

v:view,视图

vm:view Model,视图数据连接层

结合上面的例子来看,我们定义了数据+模板。数据对应的就是m(model,数据),模板对应的就是v(view,视图)。模板和数据会自动做连接,实际上这一层就是vm(view model,视图数据连接层)。

数据是我们自己写代码定义的,模板也是我们自己写代码定义的,但是数据和视图进行关联,并不是我们自己去做的,而是Vue帮助我们做的。更具体地讲,是Vue的组件帮助我们做的。

这就是为什么把const vm = app.mount("#root");这句代码的返回值取名为vm的原因。

以后我们谈论到vm的时候,指的就是Vue的一个一个的组件。

2.1.2 vm的作用

我们说,数据和视图的关联是由vm来维护的,那么我们就可以借助vm来搞一些事情。

在vm上面,我们可以通过$data的方式获取到对应根组件上面的数据。

比如对于这段代码:

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },

    template: `<div>{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

我们可以使用下面这行代码获取到content的内容:

vm.$data.content  // 'hello world'

也可以修改根组件的数据:

vm.$data.content = 'bye';

这样修改了数据,页面对应的数据也会随之发生变化。

2.1.3 本节小结
  1. 通过const app = Vue.createApp()可以创建一个Vue的应用。传递进去的参数表示的是,这个应用的根组件应该如何被创建。
  2. const vm = app.mount()方法返回的是Vue应用对应的根组件。这个根组件充当了一个vm层的角色
  3. mvvm设计模式:m数据,v视图,vm数据视图连接层。
  4. 使用vm可以搞事情。vm.$data可以获取到组件中的数据。

2.2 理解 Vue 中的生命周期函数(1)

先下定义:生命周期函数是在某一时刻自动执行的函数。

在这里插入图片描述

      const app = Vue.createApp({
        data() {
          return {
            content: "hello world",
          };
        },
        // 在实例生成之前自动执行
        beforeCreate() {
          console.log("beforeCreate");
        },
        // 在实例生成之后自动执行
        created() {
          console.log("created");
        },
        // 将template变成render函数之后执行
        // 在组件内容被渲染到页面之前自动执行
        beforeMount() {
          console.log("beforeMount");
        },
        // 在组件内容被渲染到页面之后自动执行
        mounted() {
          console.log("mounted");
        },
        template: `<div>{{content}}</div>`,
      });

      // vm 代表的就是 Vue 应用的根组件
      const vm = app.mount("#root");

2.3 理解 Vue 中的生命周期函数(2)

使用app.unmount()可以使Vue应用失效(从页面卸载,销毁Vue实例)。

      const app = Vue.createApp({
        data() {
          return {
            content: "hello world",
          };
        },

        // 在实例生成之前自动执行
        beforeCreate() {
          console.log("beforeCreate");
        },

        // 在实例生成之后自动执行
        created() {
          console.log("created");
        },

        // 将template变成render函数之后执行
        // 在组件内容被渲染到页面之前自动执行
        beforeMount() {
          console.log("beforeMount");
        },

        // 在组件内容被渲染到页面之后自动执行
        mounted() {
          console.log("mounted");
        },

        // 当data中的数据发生变化时自动执行
        beforeUpdate() {
          console.log("beforUpdate");
        },

        // 当data中的数据发生变化,页面重新渲染后,自动执行的函数
        updated() {
          console.log("updated");
        },

        // 当Vue应用失效时,自动执行的函数
        beforeUnmount() {
          console.log("beforeUnmount");
        },

        // 当Vue应用失效时,且dom完全销毁之后,自动执行的函数
        unmounted() {
          console.log("unmounted");
        },

        template: `<div>{{content}}</div>`,
      });

      // vm 代表的就是 Vue 应用的根组件
      const vm = app.mount("#root");

2.4 常用模板语法讲解(1)

2.4.1 v-html指令

如果数据是一个html代码片段,不能直接使用插值表达式,否则Vue会将数据原样输出。

  const app = Vue.createApp({
    data() {
      return {
        content: "<h1>Hello World</h1>",
      };
    },

    template: `<div>{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

页面显示:

在这里插入图片描述

如果想页面能把HTML标签解析成对应的节点,要使用v-html指令

  const app = Vue.createApp({
    data() {
      return {
        content: "<h1>Hello World</h1>",
      };
    },

    template: `<div v-html="content"></div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这样html标签就不会被转义。

在这里插入图片描述

2.4.2 v-bind指令

给标签的属性绑定数据。如果某个标签的属性值想使用数据变量,就要用v-bind指令

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },

    template: `<div v-bind:title="content">{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

再举一个例子:

  const app = Vue.createApp({
    data() {
      return {
        disable: true,
      };
    },

    template: `<input v-bind:disabled="disable"/>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.4.3 v-once指令

如果data发生改变,但不想让页面的内容跟着改变,就可以使用v-once指令。表示这个标签中的内容只会渲染一次。

这个指令可以减少无用的渲染,提高渲染的性能。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },

    template: `<div v-once>{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.4.5 v-if指令

控制页面是否渲染对应标签。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
        show: false,
      };
    },

    template: `<div v-if="show">{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

2.5 常用模板语法讲解(2)

2.5.1 v-on指令

用来绑定事件。

在模板上绑定的方法一定要卸载methods对象中。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },
    methods: {
      handleBtnClick() {
        alert("按钮被点击了");
      },
    },

    template: `<button v-on:click="handleBtnClick">一个按钮</button>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.5.2 v-on、v-bind指令的缩写

v-on指令可以缩写为一个@,比如

template: `<button v-on:click="handleBtnClick">一个按钮</button>`

等价于

template: `<button @click="handleBtnClick">一个按钮</button>`

v-bind指令可以直接简写为一个:,比如:

template: `<button v-bind:title="content">一个按钮</button>`

等价于

template: `<button :title="content">一个按钮</button>`
2.5.3 自定义属性名

当template的元素上绑定的元素名称不固定,可以使用方括号语法来绑定一个变量,比如:

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
        name: "title",
      };
    },
    methods: {
      handleBtnClick() {
        alert("按钮被点击了");
      },
    },

    template: `<button :[name]="content">一个按钮</button>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这里还是给button元素的title属性绑定了content作为值。

2.5.4 阻止默认事件 .prevent修饰符

修饰符可以帮助我们简化一些常用代码的编写。Vue事件修饰符官网文档

定义一个表单,点击提交,跳转到百度。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
        name: "title",
      };
    },
    template: `<form action="https://www.baidu.com">
      <button type="submit">提交</button>
      </form>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

如果想阻止默认事件,首先想到的是,给元素绑定一个方法,在这个方法中阻止默认事件。

      const app = Vue.createApp({
        data() {
          return {
            content: "hello world",
            name: "title",
          };
        },
        methods: {
          handleSubmit(e) {
            e.preventDefault();
          },
        },

        template: `<form action="https://www.baidu.com">
          <button type="submit" @click="handleSubmit">提交</button>
          </form>`,
      });

      // vm 代表的就是 Vue 应用的根组件
      const vm = app.mount("#root");

其实在Vue中,我们不需要写这么麻烦。Vue提供了.prevent修饰符,可以帮助我们阻止默认行为。(@click.prevent="..."

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
        name: "title",
      };
    },
    methods: {
      handleSubmit() {
        alert("我被点击了");
      },
    },

    template: `<form action="https://www.baidu.com">
      <button type="submit" @click.prevent="handleSubmit">提交</button>
      </form>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

修饰符可以帮助我们简化一些常用代码的编写。

2.6 数据,方法,计算属性和侦听器(1)

本节内容:

  1. data
  2. methods
  3. computed
  4. watcher(侦听器)
2.6.1 data–数据

我们在定义数据的时候,是在创建Vue实例的时候,将数据写在data()函数中,并以对象形式返回我们的数据。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },
    methods: {},

    template: `<div>{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

在浏览器的控制台里,我们可以使用如下代码来获取这个组件中的数据:

vm.$data.content

因为content是data中的第一层数据,因此我们想访问它,写法还可以被简化成这样:

vm.content
2.6.2 methods–方法

Vue实例中,所有被绑定的事件,都要写在methods对象中。

  const app = Vue.createApp({
    data() {
      return {
        content: "hello world",
      };
    },
    methods: {
      handleClick() {
        console.log("click");
      },
    },

    template: `<div @click="handleClick">{{content}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

⭐methods有几个需要注意的点:

  1. 在Vue的方法中,所有的this都统一指向对应的Vue实例。因此,如果要用到这一个特性,methods中的方法一定不能使用箭头函数

  2. 除了将方法绑定在元素的事件上,还可以在插值表达式中使用methods中定义的方法。

      const app = Vue.createApp({
        data() {
          return {
            content: "hello world",
          };
        },
        methods: {
          handleClick() {
            console.log("click", this.content);
          },
          formatString(string) {
            return string.toUpperCase();
          },
        },
    
        template: `<div @click="handleClick">{{formatString(content)}}</div>`,
      });
    
      // vm 代表的就是 Vue 应用的根组件
      const vm = app.mount("#root");
    
2.6.3 computed–计算属性

computed计算属性,是依赖其他的数据属性,所计算出来的一个新的属性。

特点:只有当依赖的数据发生变化时,才会重新计算,返回新的计算属性。

举例:Vue实例中有两个数据,count代表数量,price代表单价。我们想在页面上显示出物品的总价,可以使用表达式,这么写:

  const app = Vue.createApp({
    data() {
      return {
        count: 2,
        price: 5,
      };
    },
    methods: {},

    template: `<div>{{count * price}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这么写一点问题都没有。

但是理想情况下,我们应该让这些数据做到语义化。单独在页面上写{{count * price}}难免会让人摸不着头脑。

如果我们有一个total属性,直接将其写在页面上{{total}},这显然比之前的写法在语义化上强不少。

实际上,Vue中的计算属性,就可以帮助我们做到这一点。

我们要在这个实例中定义一个computed对象,在其中可以写一些方法,这些方法被当作属性来使用。

  const app = Vue.createApp({
    data() {
      return {
        count: 2,
        price: 5,
      };
    },
    computed: {
      total() {
        return this.count * this.price;
      },
    },

    template: `<div>{{total}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

把计算属性单独拎出来:

computed: {
  total() {
    return this.count * this.price;
  },
},

这段代码的意思是,现在有一个计算属性,名叫total,它的值始终等于this.count * this.price

定义了这个计算属性后,直接把名字拿出来,扔到插值表达式中使用就可以了。

如果一个计算属性依赖的数据发生了变化,这个计算属性则会重新进行计算,然后渲染到页面上。

⭐⭐既然使用一个method也能实现计算属性的功能,那么计算属性的意义在哪呢?

当计算属性依赖的内容发生变更时,才会重新执行计算;

使用method,只要页面重新渲染,就会重新计算。

计算属性内部会带一种缓存的机制,所以在做页面渲染的时候,计算属性会更高效一点

2.6.4 watcher–侦听器

watcher可以监听一个数据属性是否发生了变化。

要写一个侦听器,只需要定义一个watch对象。

如果遇到异步的情况,可以使用watch。比如当数据发生改变,等待5秒钟,输出字符串'change'

通过watch,可以监听我们在data中定义的数据是否发生了改变,并且可以做一些异步的操作。

举例:监听price这个数据,当他发生变化时,3秒后在控制台输出'price change'

  const app = Vue.createApp({
    data() {
      return {
        count: 2,
        price: 5,
      };
    },
    watch: {
      price() {
        setTimeout(() => {
          console.log("price change");
        }, 3000);
      },
    },
    computed: {
      total() {
        return this.count * this.price;
      },
    },

    template: `<div>{{total}}</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

在侦听器中,传入了两个参数,一个是数据改变后的值,另一个是数据改变之前的值。

watch: {
  price(current, prev) {
    console.log(current, prev);
  },
},

现在通过侦听器watch来实现和computed计算属性中一样的效果:

      const app = Vue.createApp({
        data() {
          return {
            count: 2,
            price: 5,
            // 新增newTotal属性
            newTotal: 10,
          };
        },
        watch: {
          // 监听price,当price发生改变时,修改newTotal的值
          price(current, prev) {
            this.newTotal = current * this.count;
          },
        },
        computed: {
          total() {
            return this.count * this.price;
          },
        },
        // 插值表达式中使用newTotal
        template: `<div>{{newTotal}}</div>`,
      });

      // vm 代表的就是 Vue 应用的根组件
      const vm = app.mount("#root");

这样,使用侦听器watch也可以实现和计算属性一样的效果,但是相比计算属性只有一行代码而言,watch的这种实现方式显然更加复杂。

所以,如果一个功能能同时用watch和computed来实现,推荐使用computed。

2.6.5 小结
  • computed和method都能实现的功能,建议使用computed,因为有缓存。
  • computed和watcher都能实现的功能,建议使用computed,因为更简洁。

2.7 样式绑定语法

2.7.1 通过字符串的形式改变样式(用到v-bind)

创建一个最简单的Vue实例。

  const app = Vue.createApp({
    template: `<div>hello world</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

定义两个样式:

  .red {
    color: red;
  }

  .green {
    color: green;
  }

Vue中使用样式,和平常写法一样,直接添加class。如下:

  const app = Vue.createApp({
    template: `<div class="red">hello world</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

问题来了。现在的class是写死的。

如果想通过数据控制文字的颜色,要怎么做?

简单,用v-bind

  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
      };
    },
    template: `<div :class="classString">hello world</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这样就通过使用字符串来控制文字的颜色,修改vm.classString='green'就可以把文字颜色改成绿的。

除了这种通过字符串的方式改变数据,Vue还扩增了一些语法。

2.7.2 通过对象的形式改变样式
  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classObject: { red: true, green: true },
      };
    },
    template: `<div :class="classObject">hello world</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这里定义了一个classObject对象。<div>标签的样式是由这个对象决定的。

这个对象的属性名表示对应class的名字,属性值表示要不要展示这个class。

2.7.3 通过数组的形式改变样式
  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classArray: ["red", "green"],
      };
    },
    template: `<div :class="classArray">hello world</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这里使用数组来控制<div>标签的样式。数组classArray的内容是所有要添加到这个标签上的class名字。

数组中同样可以包含对象,这和使用对象控制样式的含义是一样的。

  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classArray: ["red", "green", {brown: true}],
      };
    },
    template: `<div :class="classArray">hello world</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.7.4 父子组件的样式

现在创建一个Vue实例,并且添加一个名为demo的子组件。

在子组件上直接添加class的名称,就可以为其修改样式。

  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classObject: { red: true, green: true },
        classArray: ["red", "green"],
      };
    },
    template: `
    <div :class="classString">
      hello world
      <demo />
    </div>`,
  });

  app.component("demo", {
    template: `<div class="green">single</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

demo子组件最外层只有一个<div>标签,那我们除了可以把样式写在子组件定义时候的标签上,还可以在调用这个子组件的时候为其添加class,比如这样:

  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classObject: { red: true, green: true },
        classArray: ["red", "green"],
      };
    },
    // 调用子组件时添加class
    template: `
    <div :class="classString">
      hello world
      <demo  class="green"/>
    </div>`,
  });

  app.component("demo", {
    template: `<div>single</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

如果子组件最外层不是只有一个节点,而是有两个或者更多,那么就不能在调用子组件时为其添加样式。

比如这样的子组件:

  app.component("demo", {
    template: `
	<div>one</div>
	<div>two</div>
	`,
  });

这个子组件最外层有两个<div>,所以不能在父组件调用子组件时为其添加class。如果这么做,添加的样式不会生效。

如果想要子组件有两个以上的最外层标签,同时,想在调用时再为其添加样式,有办法吗?

2.7.5 $attrs.class

如果子组件有两个以上的最外层标签,并且要在父组件调用子组件的时候添加样式,需要在子组件的标签上绑定:class="$attrs.class"

  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classObject: { red: true, green: true },
        classArray: ["red", "green"],
      };
    },
    // 调用子组件时添加class
    template: `
    <div :class="classString">
      hello world
      <demo  class="green"/>
    </div>`,
  });

  app.component("demo", {
    template: `
    <div :class="$attrs.class">one</div>
    <div>two</div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.7.6 行内样式

Vue中写行内样式,和在HTML页面中书写行内样式是一样的。

  const app = Vue.createApp({
    data() {
      return {
        classString: "red",
        classObject: { red: true, green: true },
        classArray: ["red", "green"],
      };
    },
    // 添加行内样式
    template: `
    <div style="color: blue;">
      hello world
    </div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.7.7 字符串形式绑定行内样式

同样,使用v-bind绑定变量,变量中储存行内样式的字符串。

  const app = Vue.createApp({
    data() {
      return {
        styleString: "color: blue;",
      };
    },
    // v-bind绑定行内样式字符串
    template: `
    <div :style="styleString">
      hello world
    </div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.7.8 对象形式绑定行内样式(推荐使用)

Vue做了一些扩展,允许以对象形式书写行内样式。

  const app = Vue.createApp({
    data() {
      return {
        styleString: "color: blue;",
        styleObject: {
          color: "orange",
          background: "yellow",
        },
      };
    },
    // 对象形式添加行内样式
    template: `
    <div :style="styleObject">
      hello world
    </div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

2.8 条件渲染(v-if指令、v-show指令)

2.8.1 v-if指令

v-if控制一个标签是否要展示。要给它一个布尔值。

  const app = Vue.createApp({
    data() {
      return {
        show: true,
      };
    },
    
    template: `<div v-if="show">v-if</div>`,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

v-if指令是通过控制标签在DOM上的存在与否,来控制这个元素要显示还是隐藏。

2.8.2 v-show指令

v-show同样也是控制一个标签是否要展示。要给它一个布尔值。

  const app = Vue.createApp({
    data() {
      return {
        show: true,
      };
    },
    
    template: `
    <div v-if="show">v-if</div>
    <div v-show="show">v-show</div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

v-if不同的是,v-show是通过style样式来控制的。如果v-show接收到的值为false,那么就给这个元素添加style="display:none;";如果v-show接收到的值为true,则把style="display:none;"去掉。

2.8.3 v-if和v-show的比较

v-ifv-show都是用来控制DOM元素的展示与否,这就引申出来一个问题:到底哪个更好?

  • 如果我们要频繁改变一个DOM元素的展示与否,建议使用v-show。因为v-show不会频繁销毁DOM,在性能上会更好一点;
  • 如果不涉及频繁改变DOM元素,其实v-ifv-show都差不多。根据具体的场景选择即可。
2.8.4 v-if的其他用法(v-else指令、v-else-if指令)

v-if还可以与v-else配套使用。

  const app = Vue.createApp({
    data() {
      return {
        show: false,
        conditionOne: true,
      };
    },
    
    template: `
    <div v-if="conditionOne">v-if</div>
    <div v-else>else</div>
    <div v-show="show">v-show</div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这段代码中,如果conditionOne的值为true,则显示<div v-if="conditionOne">v-if</div>这个DOM元素;如果conditionOne的值为false,则显示<div v-else>else</div>这个元素。这个逻辑和if-else语句是一样的。

需要注意的一点是,如果要将v-ifv-else结合起来使用,要将这两个指令紧贴在一起写。

JS中有if - else if - else的语法,Vue中也同样有类似的指令:v-else-if

  const app = Vue.createApp({
    data() {
      return {
        show: false,
        conditionOne: false,
        conditionTwo: true,
      };
    },

    template: `
    <div v-if="conditionOne">v-if</div>
    <div v-else-if="conditionTwo">v-else-if</div>
    <div v-else>else</div>
    <div v-show="show">v-show</div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这段代码的逻辑如下:

  • 如果conditionOne值为true,则渲染<div v-if="conditionOne">v-if</div>;否则,看下一行。
  • 如果conditionTwo值为true,则渲染<div v-else-if="conditionTwo">v-else-if</div>;否则,看下一行。(这里代码中为true,会渲染这个DOM元素,后面的v-else不渲染)
  • 前面两个值都为false,于是轮到了v-else这个指令,直接渲染<div v-else>else</div>

同样要注意的是,这三个指令要紧挨着写。中间不能有其他的东西。

2.9 列表循环渲染(1)

使用v-for指令。1.3小节中详细写过,这里复习一下用法。

  const app = Vue.createApp({
    data() {
      return {
        list: ["hello", "world", "item"],
      };
    },

    template: `
    <ul>
      <li v-for="(item, index) in list">{{index}}--{{item}}</li>  
    </ul>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

v-for除了对数组进行循环,还能对对象进行循环。循环对象的时候,可以接收三个参数,顺序依次是:(属性值,属性名,索引)

  const app = Vue.createApp({
    data() {
      return {
        listArray: ["hello", "world", "item"],
        listObject: {
          first: "hello",
          second: "world",
          third: "bye",
        },
      };
    },

    template: `
    <ul>
      <li v-for="(value, key, index) in listObject">
        {{value}}--{{key}}--{{index}}
      </li>  
    </ul>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

页面显示结果:

  • hello–first–0
  • world–second–1
  • bye–third–2
2.9.1 v-for的key值

回到遍历数组上来。

当被遍历的数组中的值发生改变时,页面会重新渲染

比如下面这段代码,添加一个按钮,每次点击,都往listArray中新增数据。

  const app = Vue.createApp({
    data() {
      return {
        listArray: ["hello", "world", "item"],
      };
    },
    methods: {
      handleBtnClick() {
        this.listArray.push("new item");
      },
    },

    template: `
    <button @click="handleBtnClick">新增</button>
    <ul>
      <li v-for="(item, index) in listArray">
        {{index}}--{{item}}
      </li>  
    </ul>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这里有一个问题。只要数据发生改变,页面就会重新渲染,但是原本页面上的内容并没有发生改变,只是新增了一些DOM元素,但是原来页面上的那些DOM元素也依然会被重新渲染,这就造成了性能上的一些浪费。

Vue底层为了优化性能,会尽量让那些能够复用的DOM元素进行复用。但是有些情况下,Vue不知道有些东西到底应不应该复用。

这时候,为了更好地辅助Vue,让Vue的效率、性能更高,在使用v-for指令的时候,最好给每一项添加一个唯一的key值。比如:

  <li v-for="(item, index) in listArray" :key="item">
    {{index}}--{{item}}
  </li> 

有了key值之后,列表在渲染的时候,如果在第二次渲染的时候,key值没有发生改变,Vue就会去查看之前key值对应的DOM元素是否能够在本次渲染中复用。如果能复用,就不会再去创建这个DOM元素了,这样能够提升一些性能。

小结:当使用v-for做循环时,循环的每一项要尽量给一个唯一的key,以提升性能。

2.9.2 数组的变更函数

当数组中内容发生变化,页面会重新渲染。有几个变更函数可以让数组发生变化:

push, pop, shift, unshift, splice, sort, reverse

2.9.3 直接替换数组

直接替换数组的内容也可以让页面重新渲染。

this.listArray = [1, 2, 3];

this.listArray = ['bye'].concat(['world']);
2.9.4 直接更新数组的内容

改变数组某一项的内容,也可以使页面重新渲染。

this.listArray[1] = 'hello';

2.10 列表循环渲染(2)

上一节中讲到了如何对数组形式的数据进行更改。

现在来介绍如何对对象形式的数据进行更改。

  const app = Vue.createApp({
    data() {
      return {
        listObject: {
          first: "hello",
          second: "world",
          third: "bye",
        },
      };
    },

    template: `
    <ul>
      <li v-for="(value, key, index) in listObject" :key="index">
        {{key}}--{{value}}--{{index}}
      </li>  
    </ul>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.10.1 直接添加对象的内容

直接添加对象的内容,也可以直接展示出来。

  const app = Vue.createApp({
    data() {
      return {
        listObject: {
          first: "hello",
          second: "world",
          third: "bye",
        },
      };
    },
    methods: {
      handleBtnClick() {
        this.listObject.name = "mike";
        this.listObject.sex = "male";
      },
    },

    template: `
    <button @click="handleBtnClick">新增</button>
    <ul>
      <li v-for="(value, key, index) in listObject" :key="index">
        {{key}}--{{value}}--{{index}}
      </li>  
    </ul>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.10.2 不显示某一项的内容

现在listObject中有三项:

listObject: {
  first: "hello",
  second: "world",
  third: "bye",
},

使用v-for对其进行遍历时,我们希望第二项second: "world"不出现在页面上。该怎么做?

这里想到使用v-if指令。

错误的写法:

  const app = Vue.createApp({
    data() {
      return {
        listArray: ["hello", "world", "item"],
        listObject: {
          first: "hello",
          second: "world",
          third: "bye",
        },
      };
    },
    methods: {
      handleBtnClick() {
        this.listObject.name = "mike";
        this.listObject.sex = "male";
      },
    },

    template: `
    <button @click="handleBtnClick">新增</button>
    <ul>
      <li v-for="(value, key, index) in listObject"
      :key="index"
      v-if="key !== 'second'"
      >
        {{key}}--{{value}}--{{index}}
      </li>  
    </ul>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

v-forv-if不能写在同一个标签上,因为v-for的优先级比v-if等级高,会导致v-if失效。

我们需要把它们写在不同的标签上,比如这样:

  const app = Vue.createApp({
    data() {
      return {
        listArray: ["hello", "world", "item"],
        listObject: {
          first: "hello",
          second: "world",
          third: "bye",
        },
      };
    },
    methods: {
      handleBtnClick() {
        this.listObject.name = "mike";
        this.listObject.sex = "male";
      },
    },

    template: `
    <button @click="handleBtnClick">新增</button>

    <div v-for="(value, key, index) in listObject"
    :key="index"
    >
      <span v-if="key !== 'second'">
        {{key}}--{{value}}--{{index}}
      </span>
    </div>  

    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

v-for的标签里面,添加一个带有v-if的标签,来控制是否要显示对应key的属性值。

这样就可以控制“过滤”掉某一项的内容。

2.10.3 template占位符

上一小节的写法其实存在一些问题。

我们本身只想过滤掉某一项的内容,让他不显示,所以在v-for所在的标签里面又添加了一个标签。

因此,页面上多出来一个标签。但是我们本不想让他出现。

这时就可以使用<template>占位符标签。

<template>长得像一个DOM元素,但其实它只是一个占位符,并不会被渲染。

  const app = Vue.createApp({
    data() {
      return {
        listArray: ["hello", "world", "item"],
        listObject: {
          first: "hello",
          second: "world",
          third: "bye",
        },
      };
    },
    methods: {
      handleBtnClick() {
        this.listObject.name = "mike";
        this.listObject.sex = "male";
      },
    },

    template: `
    <button @click="handleBtnClick">新增</button>

    <template v-for="(value, key, index) in listObject"
    :key="index"
    >
      <div v-if="key !== 'second'">
        {{key}}--{{value}}--{{index}}
      </div>
    </template>  

    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

2.11 事件绑定——事件修饰符(1)

本节学习Vue中事件相关的内容。

2.11.1 获取原生event对象

看一段代码。这段代码会在页面上展示一个数字和一个按钮,每点击一次按钮,数字会+1。

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleBtnClick() {
        this.counter += 1;
      },
    },

    template: `
    {{counter}}
    <button @click="handleBtnClick">Button</button>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

绑定的事件函数可以接收到一个event对象:

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleBtnClick(event) {
        this.counter += 1;
        console.log(event.target);  // <button>Button</button>
      },
    },

    template: `
    {{counter}}
    <button @click="handleBtnClick">Button</button>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

如果当点击按钮时,想额外传其他参数,可以直接在v-on:click后面的函数中传参。比如:

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleBtnClick(num) {
        this.counter += num;
      },
    },

    template: `
    {{counter}}
    <button @click="handleBtnClick(2)">Button</button>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

但是这样做,对应的函数就无法接收到event对象。

想要在传入其他参数的同时,还要传递event对象,可以在v-on:click的函数中传一个$event

<button @click="handleBtnClick(2, $event)">Button</button>

这样,methods中对应的方法就能接收到event

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleBtnClick(num, event) {
        this.counter += num;
        console.log(event.target); // <button>Button</button>
      },
    },

    template: `
    {{counter}}
    <button @click="handleBtnClick(2, $event)">Button</button>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

所以,想要额外传递参数,同时要获取到原生的event对象,就需要在模板中使用$event

2.11.2 为一个事件绑定多个函数

假设点击按钮时,想触发两个函数。这两个函数应该绑定到v-on:click上。但是不能直接绑定函数的引用,比如这样:

  // 错误的写法
  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleBtnClick1() {
        alert(1);
      },
      handleBtnClick2() {
        alert(2);
      },
    },

    // 错误的写法
    template: `
    {{counter}}
    <button @click="handleBtnClick1, handleBtnClick2">Button</button>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

在绑定多个事件的时候,要把括号带上

  // 正确的写法
  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleBtnClick1() {
        alert(1);
      },
      handleBtnClick2() {
        alert(2);
      },
    },
    // 正确的写法
    template: `
    {{counter}}
    <button @click="handleBtnClick1(), handleBtnClick2()">Button</button>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.11.3 事件修饰符.stop——阻止冒泡

现在有一个模板,内容是一个<div>套一个<div>,在这两个<div>上都绑定click事件,那么,点击内层的<div>,事件会冒泡,触发父级<div>绑定的事件。代码如下:

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleDivClick1() {
        alert(1);
      },
      handleDivClick2() {
        alert(2);
      },
    },

    template: `
    <div @click="handleDivClick1">
      <div @click="handleDivClick2">123</div>  
    </div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

如果我们想阻止事件向上层冒泡,触发父级元素绑定的事件,就要用到.stop事件修饰符。用法如下:

template: `
<div @click="handleDivClick1">
  <div @click.stop="handleDivClick2">123</div>  
</div>
`,

在子元素的@click上添加一个.stop即可。

2.11.4 事件修饰符.self

现在依然有两个嵌套的<div>,最外层<div>中除了嵌套了一个<div>,还有一些其它内容。

最外层<div>上绑定一个click事件。当点击内部的<div>或者内部的其他元素时,这个click事件都会触发。

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleDivClick() {
        alert(1);
      },
    },

    template: `
    <div @click="handleDivClick">
      最外层div中的文字
      <div>最内层div中的文字</div>  
    </div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

现在我只想让最外层的div被点击时才能触发绑定的事件,就要用到.self修饰符。.self的作用是,只当在 event.target 是当前元素自身时触发处理函数,即事件不是从内部元素触发的

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleDivClick() {
        alert(1);
      },
    },

    template: `
    <div @click.self="handleDivClick">
      最外层div中的文字
      <div>最内层div中的文字</div>  
    </div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.11.5 事件修饰符.prevent

.prevent修饰符用于阻止默认事件。在2.5.4小节中已经详细介绍过,不再赘述。

2.11.6 事件修饰符.capture

.capture修饰符,表示添加事件监听器时使用事件捕获模式(从外到内)。默认情况下使用的是冒泡模式(从内到外),使用.capture修饰符可以修改冒泡模式为捕获模式。

2.11.7 事件修饰符.once

.once修饰符,代表对应绑定的事件只会执行一次。执行后不会再被触发。

  const app = Vue.createApp({
    data() {
      return {
        counter: 0,
      };
    },
    methods: {
      handleDivClick() {
        alert("我只会弹出一次,关了就没有了。");
      },
    },

    template: `
    <div @click.once="handleDivClick">
      这个div上绑定的事件只会被触发一次!
    </div>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.11.8 事件修饰符.passive
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作。加上passive就是为了告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作。

可以提高性能,尤其是移动端的性能。

2.12 事件绑定(2)

除了上节讲到的事件修饰符,还有按键修饰符鼠标按钮修饰符精确修饰符

2.13 表单中双向绑定指令的使用(1)

双向绑定一般都和表单中的元素相关,比如<input>,checkbox, radio, <select>等。

2.13.1 v-model指令

使用v-model指令可以进行双向绑定。

  const app = Vue.createApp({
    data() {
      return {
        message: "hello",
      };
    },

    template: `
	{{message}}
    <input v-model="message"/>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

输入框中的内容和message的值绑定在一起。当input框中的内容发生改变,message的值也会随之改变,页面上显示的内容也会改变。

<textarea v-model="message"/>
2.13.2 checkbox使用v-model

定义三个checkbox类型的input。

当在checkbox上使用v-model,可以使用一个数组来存储勾选的内容。

  const app = Vue.createApp({
    data() {
      return {
        message: [],
      };
    },

    template: `
    {{message}}
    1:<input type="checkbox" v-model="message" value="1"/>
    2:<input type="checkbox" v-model="message" value="2"/>
    3:<input type="checkbox" v-model="message" value="3"/>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

当某一项被勾选时,该项的value值就会被push进message数组中。

2.13.3 radio使用v-model

radio和checkbox相似,只不过radio是单选框,每次只能选中一个选项,用数组存储意义不大。直接用字符串存储即可。

  const app = Vue.createApp({
    data() {
      return {
        message: "",
      };
    },

    template: `
    {{message}}
    1:<input type="radio" v-model="message" value="1"/>
    2:<input type="radio" v-model="message" value="2"/>
    3:<input type="radio" v-model="message" value="3"/>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");
2.13.4 select使用v-model

<select>标签上绑定v-model。

  const app = Vue.createApp({
    data() {
      return {
        message: "",
      };
    },

    template: `
    {{message}}
    <select v-model="message">
      <option disabled>--请选择数据--</option>
      <option value="A">A</option>  
      <option value="B">B</option>  
      <option value="C">C</option>  
    </select>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

这样,选中的内容和message就进行了绑定。

如果select可以多选,那么就可以和一个数组使用v-model进行双向绑定。

  const app = Vue.createApp({
    data() {
      return {
        message: [],
      };
    },

    template: `
    {{message}}
    <select v-model="message" multiple>
      <option disabled>--请选择数据--</option>
      <option>A</option>  
      <option>B</option>  
      <option>C</option>  
    </select>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

⭐可以使用v-for来循环展示option选项。

  const app = Vue.createApp({
    data() {
      return {
        message: [],
        options: [
          {
            text: "A",
            value: "A",
          },
          {
            text: "B",
            value: "B",
          },
          {
            text: "C",
            value: "C",
          },
        ],
      };
    },

    template: `
    {{message}}
    <select v-model="message" multiple>
      <option disabled>--请选择数据--</option>
      <option v-for="item in options" :value="item.value">{{item.text}}</option>  
    </select>
    `,
  });

  // vm 代表的就是 Vue 应用的根组件
  const vm = app.mount("#root");

2.14 表单中双向绑定指令的使用(2)

2.14.1 修饰符.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。可以添加 lazy 修饰符,从而转为在输入框失焦之后进行同步。

<!-- 在“onblur”时而非“input”时更新 -->
<input v-model.lazy="msg">
2.14.2 修饰符.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">
2.14.3 修饰符.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值