复习Vue16、常用API说明

21 篇文章 0 订阅

复习Vue16、常用API说明

16.1、 Vue.set

向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且会触发视图更新。

使用方法:Vue.set(target,propertyName,value)

下面通过一个案例来演示一下,这个案例是在以前所做的用户列表的案例上进行修改的,

这里需求是给每个用户动态的添加身高。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
      .actived {
        background-color: #dddddd;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <p v-if="users.length===0">没有任何用户数据</p>
      <ul v-else>
        <li
          v-for="(item,index) in users"
          :key="item.id"
          :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
          @mousemove="selectItem=item"
        >
          编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
        </li>
      </ul>
      <p>
        总人数:{{totalCount}}
      </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      new Vue({
        el: "#app",
        data: {
          selectItem: "",
          num: 100,
          totalCount: 0,
          users: [],
        },
        //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },
        methods: {
          //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              c.height = 0;
            });
          },

          getTotal: function () {
            console.log("methods");
            return this.users.length + "个";
          },
          getUserList: function () {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve([
                  {
                    id: 1,
                    name: "张三",
                  },
                  {
                    id: 2,
                    name: "李四",
                  },
                  {
                    id: 3,
                    name: "老王",
                  },
                ]);
              }, 2000);
            });
          },
        },
        watch: {
          users: {
            immediate: true, //立即执行
            handler(newValue, oldValue) {
              this.totalCount = newValue.length + "个人";
            },
          },
        },
      });
    </script>
  </body>
</html>

在上面的代码中,我首先把列表中,展示的内容做了一个修改,这里不在显示索引值,而是展示身高。

 编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}

但是我们知道在users中是没有height这个属性的,所以下面可以动态添加这个属性。

所以在create方法中,调用了batchUpdate方法,来动态更新。

 //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },

methods中,添加了batchUpdate方法。

 //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              c.height = 0;
            });
          },

在上面的代码中,对users进行遍历,每遍历一次,取出一个对象后,动态添加一个属性height,并且初始值为0.

这样刷新浏览器,可以看到对应的效果。

下面,我们在做一个功能,就是用户在一个文本框中,输入一个身高值,单击按钮,统一把所有用户的身高进行更新。

首先在data中添加一个属性height,该属性会与文本框进行绑定。

data: {
          selectItem: "",
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
        },

下面创建文本框,以及更新按钮

 <p>
        <input type="text" v-model.number="height" />
        <button @click="batchUpdate">批量更新用户身高</button>
      </p>

这里我们可以看到,我们是用文本框中输入的值,更新了users数组中的height属性的值。

但是,当我们在浏览器中,单击按钮进行更新的时候,发现不起作用。

因为,现在动态所添加的height属性并不是响应式的。

但是,当把鼠标移动到列表项的时候,数据发生了变化,就是因为这时触发了我们给列表所添加的mousemove

这个事件,导致页面重新刷新,这时发现数据发生变化了。

那么我们应该怎样解决这个问题呢?

这就需要,在batchUpdate方法中,使用Vue.set()方法

batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              Vue.set(c, "height", this.height);
            });
          },

修改的代码含义就是通过Vue.set方法,给users数组中每个对象,设置一个height属性,这时该属性就变成了响应式的,同时把 data中的height属性的值赋值给height.

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
      .actived {
        background-color: #dddddd;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <p v-if="users.length===0">没有任何用户数据</p>
      <ul v-else>
        <p>
            <input type="text" v-model.number="height" />
            <button @click="batchUpdate">批量更新用户身高</button>
          </p>
        <li
          v-for="(item,index) in users"
          :key="item.id"
          :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
          @mousemove="selectItem=item"
        >
          编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
        </li>
      </ul>
      <p>
        总人数:{{totalCount}}
      </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      new Vue({
        el: "#app",
        data: {
          selectItem: "",
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
        },
        //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },
        methods: {
          //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              Vue.set(c, "height", this.height);
            });
          },

          getTotal: function () {
            console.log("methods");
            return this.users.length + "个";
          },
          getUserList: function () {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve([
                  {
                    id: 1,
                    name: "张三",
                  },
                  {
                    id: 2,
                    name: "李四",
                  },
                  {
                    id: 3,
                    name: "老王",
                  },
                ]);
              }, 2000);
            });
          },
        },
        watch: {
          users: {
            immediate: true, //立即执行
            handler(newValue, oldValue) {
              this.totalCount = newValue.length + "个人";
            },
          },
        },
      });
    </script>
  </body>
</html>
16.2 、Vue.delete

删除对象的属性,如果对象是响应式的,确保删除能触发更新视图。

使用方式:Vue.delete(target,propertyName)

如果使用delete obj['property'] 是不能更新页面的。

以上两个方法Vue.set()Vue.delete()等同于以下两个实例方法

vm.$set()
vm.$delete()

vm 表示的是Vue的实例。

所以我们在batchUpdate中也可以采用如下的方式,来批量更新用户的身高数据。

batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              //   Vue.set(c, "height", this.height);
              this.$set(c, "height", this.height);
            });
          },
16.3、 vm.$onvm.$emit
16.3.1 、列表组件设计

这两个api在前面的课程中,我们也已经讲解过,主要用来实现:事件总线。

下面,我们将这两个API应用到用户列表这个案例中。主要是把事件总线这个应用再次复习一下。

当然,这里首先是把用户列表这个案例,按照我们前面所学习的组件的知识,进行拆分一下,实现组件化的应用。

初步改造后的代码,如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
        .actived {
            background-color: #dddddd;
        }
    </style>
</head>

<body>
    <div id="app">
        <!-- 批量更新身高 -->
        <p>
            <input type="text" v-model.number="height" />
            <button @click="batchUpdate">批量更新用户身高</button>
        </p>
        <!-- 用户列表组件 -->
        <user-list :users="users"></user-list>

        <p>
            总人数:{{totalCount}}
        </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 用户列表组件创建
        Vue.component("user-list", {
            data() {
                return {
                    selectItem: "",
                };
            },
            props: {
                users: {
                    type: Array,
                    default: [],
                },
            },
            template: `
        <div>
                <p v-if="users.length===0">没有任何用户数据</p>
            <ul v-else>
                <li
                v-for="(item,index) in users"
                :key="item.id"
                :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
                @mousemove="selectItem=item"
                >
                编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
                </li>
            </ul>
      </div>
        `,
        });
        new Vue({
            el: "#app",
            data: {
                num: 100,
                totalCount: 0,
                users: [],
                height: 0,
            },
            //组件实例已创建时
            async created() {
                const users = await this.getUserList();
                this.users = users;
                //批量更新用户身高
                this.batchUpdate();
            },
            methods: {
                //批量更新身高,动态的给users中添加身高属性
                batchUpdate() {
                    this.users.forEach((c) => {
                        //   c.height = this.height;
                        //   Vue.set(c, "height", this.height);
                        this.$set(c, "height", this.height);
                    });
                },

                getTotal: function () {
                    console.log("methods");
                    return this.users.length + "个";
                },
                getUserList: function () {
                    return new Promise((resolve) => {
                        setTimeout(() => {
                            resolve([{
                                    id: 1,
                                    name: "张三",
                                },
                                {
                                    id: 2,
                                    name: "李四",
                                },
                                {
                                    id: 3,
                                    name: "老王",
                                },
                            ]);
                        }, 2000);
                    });
                },
            },
            watch: {
                users: {
                    immediate: true, //立即执行
                    handler(newValue, oldValue) {
                        this.totalCount = newValue.length + "个人";
                    },
                },
            },
        });
    </script>
</body>

</html>

在上面的代码中,我们首先创建了一个user-list组件,该组件首先会通过props接收传递过来的用户数据。

在这里我们将props定义成了对象的形式,这样更容易进行数据类型的校验,同时还可以设置默认值。

接下来将原来定义在<div id="app"></div> 中的用户列表,要剪切到user-list组件的template属性中,同时,我们知道在列表中会用到selectItem属性,所以在user-listdata中定义该属性,父组件就不用在定义该属性了。

下面,我们在<div id="app"></div>中使用该组件,并且传递了用户数据。

 <!-- 用户列表组件 -->
      <user-list :users="users"></user-list>

现在用户列表的组件,在这里我们就创建好了。

16.3.2 用户添加组件设计

下面我们在创建一个组件,该组件封装了一个文本框和添加用户信息的按钮。

代码如下:

 //新增用户组件
      Vue.component("user-add", {
        data() {
          return {
            userInfo: "",
          };
        },
        template: `
            <div>
             <p>
                <input type="text" v-model="userInfo" v-on:keydown.enter="addUser" />
             </p>
             <button @click="addUser">新增用户</button>
              </div>
            `,
        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            this.$emit("add-user", this.userInfo);
            this.userInfo = "";
          },
        },
      });

在上面的代码中,我们创建了user-add 这个组件,该组件最终呈现的就是就是一个文本框与一个添加按钮。并且通过v-modeluserInfo属性与文本框进行了绑定。同时,单击按钮的时候,执行addUser方法,在该方法中,通过$emit想父组件发送了一个事件,同时将用户在文本框中输入的数据也传递过去。

然后清空文本框,

下面看一下父组件的处理。

 <!-- 新增用户 -->
      <user-add @add-user="addUser"></user-add>

<div id="app"></div> 中使用user-add这个组件,同时接受传递过来的事件add-user,然后执行addUser方法。

下面看一下addUser这个方法的具体实现。

vue 实例的methods属性中,添加addUser这个方法。

//添加用户的信息
          addUser(userInfo) {
            this.users.push({
              id: this.users[this.users.length - 1].id + 1,
              name: userInfo,
            });
          },

接受用户在文本框中输入的数据,然后添加到users数组中。

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
      .actived {
        background-color: #dddddd;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 批量更新身高 -->
      <p>
        <input type="text" v-model.number="height" />
        <button @click="batchUpdate">批量更新用户身高</button>
      </p>
      <!-- 新增用户 -->
      <user-add @add-user="addUser"></user-add>
      <!-- 用户列表组件 -->
      <user-list :users="users"></user-list>

      <p>
        总人数:{{totalCount}}
      </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      //新增用户组件
      Vue.component("user-add", {
        data() {
          return {
            userInfo: "",
          };
        },
        template: `
            <div>
             <p>
                <input type="text" v-model="userInfo" v-on:keydown.enter="addUser" />
             </p>
             <button @click="addUser">新增用户</button>
              </div>
            `,
        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            this.$emit("add-user", this.userInfo);
            this.userInfo = "";
          },
        },
      });

      // 用户列表
      Vue.component("user-list", {
        data() {
          return {
            selectItem: "",
          };
        },
        props: {
          users: {
            type: Array,
            default: [],
          },
        },
        template: `
        <div>
                <p v-if="users.length===0">没有任何用户数据</p>
            <ul v-else>
                <li
                v-for="(item,index) in users"
                :key="item.id"
                :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
                @mousemove="selectItem=item"
                >
                编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
                </li>
            </ul>
      </div>
        `,
      });
      new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
        },
        //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },
        methods: {
          //添加用户的信息
          addUser(userInfo) {
            this.users.push({
              id: this.users[this.users.length - 1].id + 1,
              name: userInfo,
            });
          },

          //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              //   Vue.set(c, "height", this.height);
              this.$set(c, "height", this.height);
            });
          },

          getTotal: function () {
            console.log("methods");
            return this.users.length + "个";
          },
          getUserList: function () {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve([
                  {
                    id: 1,
                    name: "张三",
                  },
                  {
                    id: 2,
                    name: "李四",
                  },
                  {
                    id: 3,
                    name: "老王",
                  },
                ]);
              }, 2000);
            });
          },
        },
        watch: {
          users: {
            immediate: true, //立即执行
            handler(newValue, oldValue) {
              this.totalCount = newValue.length + "个人";
            },
          },
        },
      });
    </script>
  </body>
</html>

16.3.3、 自定义组件实现双向绑定

在上一个案例中,我们创建了一个user-add这个组件,完成用户信息的添加。

并且在该组件的内部,维护了所添加的用户信息。

假如,我不想让user-add这个组件来维护这个用户信息,而是让父组件来维护,应该怎样处理呢?

 <!-- 新增用户 -->
      <user-add @add-user="addUser" v-model="userInfo"></user-add>

userInfo的值给v-model.

所以在父组件中要定义userInfo

new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
        },

下面看一下user-add组件的修改

Vue.component("user-add", {
        // data() {
        //   return {
        //     userInfo: "",
        //   };
        // },
        props: ["value"],
        template: `
            <div>
             <p>
                <input type="text" :value="value" @input="onInput" v-on:keydown.enter="addUser" />
             </p>
             <button @click="addUser">新增用户</button>
              </div>
            `,
        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            // this.$emit("add-user", this.userInfo);
            this.$emit("add-user");
            // this.userInfo = "";
          },
          onInput(e) {
            this.$emit("input", e.target.value);
          },
        },
      });

user-add组件中,定义props接收传递过来的值,也就是userInfo的值会传递给value

下面修改user-add组件中的模板,文本框绑定value值。通过给其添加input事件,在文本框中输入值后,调用onInput方法,在该方法中获取用户在文本框中输入的值,然后发送input事件。对应的值传递给父组件中的userInfo

同时单击“新增用户”按钮的时候,执行addUser方法,在该方法中发送事件add-user,也不需要传递数据了。

同时,父组件中的addUser方法实现如下:

addUser() {
            this.users.push({
              id: this.users[this.users.length - 1].id + 1,
              name: this.userInfo,
            });
            this.userInfo = "";
          },

直接从data中获取userInfo的数据。

总结:

以下的写法

<user-add @add-user="addUser" v-model="userInfo"></user-add>

等价以下的写法

<user-add
  v-bind:value="userInfo"
  v-on:input="userInfo = $event"
></user-add>

也就是说v-model就是v-bindv-on的语法糖。

在这里我们将userInfo的值给了value属性,而value属性传递到了user-add组件中,所以在user-add组件中要通过props来接收value属性的值。

user-add组件的文本中,输入内容后触发@input 事件,对应的会调用onInput方法,在该方法中,执行了

this.$emit("input", e.target.value);

发送了input事件,并且传递了用户在文本框中输入的值。

那很明显,这时会触发下面代码中的input事件,将传递过来的值给userInfo属性。

<user-add
  v-bind:value="userInfo"
  v-on:input="userInfo = $event"
></user-add>

以上就是v-model的原理,希望仔细体会,这也是面试经常会被问到的问题。

16.3.4. 使用插槽完成内容分发

关于插槽的内容,在前面的的课程中我们已经学习过了,那么什么是内容分发呢?

其实就是在使用组件的时候,我们提供具体的数据内容,然后这些内容会插入到组件内部插槽的位置,这就是所谓的内容分发。

下面,要做的事情就是创建一个信息的提示窗口。例如:当添加用户成功后,给出相应的提示。

首先先创建样式:

<style>
      .actived {
        background-color: #dddddd;
      }
      .message-box {
        padding: 10px 20px;
        background-color: #4fc;
        border: 1px solid #42b;
      }
      .message-box-close {
        float: right;
      }
    </style>

下面创建对应的组件。

//创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        props: ["show"],
        template: `<div class='message-box' v-if="show">
            <slot></slot>
            <span class="message-box-close">关闭</span>
          </div>`,
      });

使用上面的组件

<div id="app">
      <!-- 弹窗组件 -->
      <message :show="isShow">
        添加用户成功
      </message>
      <!-- 批量更新身高 -->
      
 </div>     ;

data 中定义isShow属性,初始值为false.

 new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
          isShow: false,
        },

下面就是当用户完成添加的时候,弹出该窗口。

 //添加用户的信息
          addUser() {
            this.users.push({
              id: this.users[this.users.length - 1].id + 1,
              name: this.userInfo,
            });
            this.userInfo = "";
            //完成用户添加后,给出相应的提示信息
            this.isShow = true;
          },

addUser方法中完成了用户信息的添加后,将isShow的属性值设置为true.

这时弹出了对应的窗口。

下面要考虑的就是,单击窗口右侧的“关闭”按钮,将窗口关闭这个效果应该怎样实现。

首先给关闭按钮添加单击事件。

如下所示:

    //创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        props: ["show"],
        template: `<div class='message-box' v-if="show">
            <slot></slot>
            <span class="message-box-close" @click='$emit("close",false)'>关闭</span>
          </div>`,
      });

当单击关闭按钮后,会发送一个close事件,同时传递的值为false.

下面回到父组件中,对close事件进行处理。

 <!-- 弹窗组件 -->
      <message :show="isShow" @close="closeWindow">
        添加用户成功
      </message>

close事件触发后,执行closeWindow方法。

  //关闭窗口
          closeWindow(data) {
            this.isShow = data;
          },

closeWindow方法中,根据子组件传递过来的值false,修改isShow属性的值,这时isShow的值为false.这时窗口关闭。

下面要解决的问题就是,在使用弹窗组件的时候,不仅能传递窗口的内容,还能传递其它的内容,例如标题等。

那应该怎样处理呢?

这里,可以使用具名插槽

代码如下:

 <!-- 弹窗组件 -->
      <message :show="isShow" @close="closeWindow">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>恭喜</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          添加用户成功
        </template>
      </message>

下面修改一下message组件中的内容。

 //创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        props: ["show"],
        template: `<div class='message-box' v-if="show">
             <!--具名插槽-->
             <slot name="title">默认标题</slot>
            <slot></slot>
            <span class="message-box-close" @click='$emit("close",false)'>关闭</span>
          </div>`,
      });

在上面定义message组件的时候,指定了具名插槽,名称为title.要与在父组件中使用message组件的时候指定的名称保持一致,同时这里如果没有传递任何内容,将会显示"默认标题"。

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
      .actived {
        background-color: #dddddd;
      }
      .message-box {
        padding: 10px 20px;
        background-color: #4fc;
        border: 1px solid #42b;
      }
      .message-box-close {
        float: right;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 弹窗组件 -->
      <message :show="isShow" @close="closeWindow">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>恭喜</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          添加用户成功
        </template>
      </message>
      <!-- 批量更新身高 -->
      <p>
        <input type="text" v-model.number="height" />
        <button @click="batchUpdate">批量更新用户身高</button>
      </p>
      <!-- 新增用户 -->
      <user-add @add-user="addUser" v-model="userInfo"></user-add>
      <!-- 用户列表组件 -->
      <user-list :users="users"></user-list>

      <p>
        总人数:{{totalCount}}
      </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      //创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        props: ["show"],
        template: `<div class='message-box' v-if="show">
             <!--具名插槽-->
             <slot name="title">默认标题</slot>
            <slot></slot>
            <span class="message-box-close" @click='$emit("close",false)'>关闭</span>
          </div>`,
      });

      //新增用户组件
      Vue.component("user-add", {
        // data() {
        //   return {
        //     userInfo: "",
        //   };
        // },
        props: ["value"],
        template: `
            <div>
             <p>
                <input type="text" :value="value" @input="onInput" v-on:keydown.enter="addUser" />
             </p>
             <button @click="addUser">新增用户</button>
              </div>
            `,
        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            // this.$emit("add-user", this.userInfo);
            this.$emit("add-user");
            // this.userInfo = "";
          },
          onInput(e) {
            this.$emit("input", e.target.value);
          },
        },
      });

      // 用户列表
      Vue.component("user-list", {
        data() {
          return {
            selectItem: "",
          };
        },
        props: {
          users: {
            type: Array,
            default: [],
          },
        },
        template: `
        <div>
                <p v-if="users.length===0">没有任何用户数据</p>
            <ul v-else>
                <li
                v-for="(item,index) in users"
                :key="item.id"
                :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
                @mousemove="selectItem=item"
                >
                编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
                </li>
            </ul>
      </div>
        `,
      });
      new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
          isShow: false,
        },
        //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },
        methods: {
          //关闭窗口
          closeWindow(data) {
            this.isShow = data;
          },
          //添加用户的信息
          addUser() {
            this.users.push({
              id: this.users[this.users.length - 1].id + 1,
              name: this.userInfo,
            });
            this.userInfo = "";
            //完成用户添加后,给出相应的提示信息
            this.isShow = true;
          },

          //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              //   Vue.set(c, "height", this.height);
              this.$set(c, "height", this.height);
            });
          },

          getTotal: function () {
            console.log("methods");
            return this.users.length + "个";
          },
          getUserList: function () {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve([
                  {
                    id: 1,
                    name: "张三",
                  },
                  {
                    id: 2,
                    name: "李四",
                  },
                  {
                    id: 3,
                    name: "老王",
                  },
                ]);
              }, 2000);
            });
          },
        },
        watch: {
          users: {
            immediate: true, //立即执行
            handler(newValue, oldValue) {
              this.totalCount = newValue.length + "个人";
            },
          },
        },
      });
    </script>
  </body>
</html>

16.3.5 vm.$onvm.$emit应用

现在,关于用户管理这个案例的一些组件拆分,以及插槽的应用在这我们已经构建好了。

下面就看一下vm.$onvm.$emit的应用。

根据前面的学习,我们知道vm.$onvm.$emit的典型应用就是事件总线。

也就是通过在Vue 原型上添加一个Vue实例作为事件总线,实现组件间相互通信,而且不受组件间关系的影响

Vue.prototype.$bus=new Vue()

在所有组件最上面创建事件总线,

这样做的好处就是在任意组件中使用this.$bus访问到该Vue实例。

下面,我们来看一下事件总线的用法。

首先,我们这里先把事件总线创建出来。

    //创建事件总线
      Vue.prototype.$bus = new Vue();

下面,在创建一个警告的窗口,也就是当单击“新增用户”按钮的时候,如果用户没有填写用户名给出相应册错误提示。

在这里先把样式修改一下:

 <style>
      .actived {
        background-color: #dddddd;
      }
      .message-box {
        padding: 10px 20px;
      }
      .success {
        background-color: #4fc;
        border: 1px solid #42b;
      }
      .warning {
        background-color: red;
        border: 1px solid #42b;
      }
      .message-box-close {
        float: right;
      }
    </style>

然后创建出对应的窗口。


      <!-- 警告 -->
      <message :show="showWarn" @close="closeWindow" class="warning">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>警告</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          请输入用户名
        </template>
      </message>

注意:在上面的代码中,我们使用showWarn这个属性控制警告窗口的显示与隐藏。

同时,为其添加了warning样式,对应的成功的窗口需要添加success 样式。

同时在data中定义showWarn属性。

new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
          isShow: false,
          showWarn: false, // 控制警告窗口的显示与隐藏
        },

下面要修改的就是当单击"新增用户"按钮的时候,对addUser方法的修改

 //添加用户的信息
          addUser() {
            if (this.userInfo) {
              this.users.push({
                id: this.users[this.users.length - 1].id + 1,
                name: this.userInfo,
              });
              this.userInfo = "";
              //完成用户添加后,给出相应的提示信息
              this.isShow = true;
            } else {
              // 显示错误警告信息
              this.showWarn = true;
            }
          },

判断userInfo中是否有值,如果没有值,展示出错误警告信息。

通过浏览器,进行测试。发现如果用户没有在文本框中输入用户名,直接单击了“新增用户”,这时给出了错误提示的窗口。

但是用户没有关闭错误提示的窗口,而是直接在文本框中输入了用户名,然后又点击了"新增用户"按钮,这时“成功窗口”与“警告窗口”都显示出来了。

下面需要解决这个问题。

Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        props: ["show"],
        template: `<div class='message-box' v-if="show">
             <!--具名插槽-->
             <slot name="title">默认标题</slot>
            <slot></slot>
            <span class="message-box-close" @click='$emit("close",false)'>关闭</span>
          </div>`,
        mounted() {
          //给总线绑定`message-close`事件
          //也就是监听是否有`message-close`事件被触发。
          this.$bus.$on("message-close", () => {
            this.$emit("close", false);
          });
        },
      });

message组件加载完后,给事件总线绑定了message-close事件,当该事件触发后还是向父组件发送了close事件,这一点与单击关闭按钮是一样的。

下面,怎样触发总线的message-close事件呢?

我们可以在窗口中添加一个“清空提示栏”按钮,单击该按钮的时候可以触发message-close事件,从而关闭提示窗口。

  <!-- 清空提示栏 -->
      <div class="toolbar">
        <button @click="$bus.$emit('message-close')">
          清空提示栏
        </button>
      </div>

单击"清空提示栏"按钮后,触发事件总线的message-close事件。

最后完善一下closeWindow方法,该方法控制整个提示窗口的关闭

  //关闭窗口
          closeWindow(data) {
            this.isShow = data;
            this.showWarn = data;
          },

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
      .actived {
        background-color: #dddddd;
      }
      .message-box {
        padding: 10px 20px;
      }
      .success {
        background-color: #4fc;
        border: 1px solid #42b;
      }
      .warning {
        background-color: red;
        border: 1px solid #42b;
      }
      .message-box-close {
        float: right;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 弹窗组件 -->
      <message :show="isShow" @close="closeWindow" class="success">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>恭喜</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          添加用户成功
        </template>
      </message>

      <!-- 警告 -->
      <message :show="showWarn" @close="closeWindow" class="warning">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>警告</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          请输入用户名
        </template>
      </message>

      <!-- 清空提示栏 -->
      <div class="toolbar">
        <button @click="$bus.$emit('message-close')">
          清空提示栏
        </button>
      </div>
      <!-- 批量更新身高 -->
      <p>
        <input type="text" v-model.number="height" />
        <button @click="batchUpdate">批量更新用户身高</button>
      </p>
      <!-- 新增用户 -->
      <user-add @add-user="addUser" v-model="userInfo"></user-add>
      <!-- 用户列表组件 -->
      <user-list :users="users"></user-list>

      <p>
        总人数:{{totalCount}}
      </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      //创建事件总线
      Vue.prototype.$bus = new Vue();
      //创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        props: ["show"],
        template: `<div class='message-box' v-if="show">
             <!--具名插槽-->
             <slot name="title">默认标题</slot>
            <slot></slot>
            <span class="message-box-close" @click='$emit("close",false)'>关闭</span>
          </div>`,
        mounted() {
          //给总线绑定`message-close`事件
          //也就是监听是否有`message-close`事件被触发。
          this.$bus.$on("message-close", () => {
            this.$emit("close", false);
          });
        },
      });

      //新增用户组件
      Vue.component("user-add", {
        // data() {
        //   return {
        //     userInfo: "",
        //   };
        // },
        props: ["value"],
        template: `
            <div>
             <p>
                <input type="text" :value="value" @input="onInput" v-on:keydown.enter="addUser" />
             </p>
             <button @click="addUser">新增用户</button>
              </div>
            `,
        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            // this.$emit("add-user", this.userInfo);
            this.$emit("add-user");
            // this.userInfo = "";
          },
          onInput(e) {
            this.$emit("input", e.target.value);
          },
        },
      });

      // 用户列表
      Vue.component("user-list", {
        data() {
          return {
            selectItem: "",
          };
        },
        props: {
          users: {
            type: Array,
            default: [],
          },
        },
        template: `
        <div>
                <p v-if="users.length===0">没有任何用户数据</p>
            <ul v-else>
                <li
                v-for="(item,index) in users"
                :key="item.id"
                :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
                @mousemove="selectItem=item"
                >
                编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
                </li>
            </ul>
      </div>
        `,
      });
      new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
          isShow: false,
          showWarn: false, // 控制警告窗口的显示与隐藏
        },
        //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },
        methods: {
          //关闭窗口
          closeWindow(data) {
            this.isShow = data;
            this.showWarn = data;
          },
          //添加用户的信息
          addUser() {
            if (this.userInfo) {
              if (this.users.length > 0) {
                this.users.push({
                  id: this.users[this.users.length - 1].id + 1,
                  name: this.userInfo,
                });
                this.userInfo = "";
                //完成用户添加后,给出相应的提示信息
                this.isShow = true;
              }
            } else {
              // 显示错误警告信息
              this.showWarn = true;
            }
          },

          //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              //   Vue.set(c, "height", this.height);
              this.$set(c, "height", this.height);
            });
          },

          getTotal: function () {
            console.log("methods");
            return this.users.length + "个";
          },
          getUserList: function () {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve([
                  {
                    id: 1,
                    name: "张三",
                  },
                  {
                    id: 2,
                    name: "李四",
                  },
                  {
                    id: 3,
                    name: "老王",
                  },
                ]);
              }, 2000);
            });
          },
        },
        watch: {
          users: {
            immediate: true, //立即执行
            handler(newValue, oldValue) {
              this.totalCount = newValue.length + "个人";
            },
          },
        },
      });
    </script>
  </body>
</html>

16.4 vm.$oncevm.$off

关于这两个方法,大家只需要了解一下就可以了。

vm.$once
监听一个自定义事件,但是只触发一次。一旦触发之后,监听器就会被移除。

vm.$once('test', function (msg) { console.log(msg) })

vm.$off

移除自定义事件监听器。

  • 如果没有提供参数,则移除所有的事件监听器;

  • 如果只提供了事件,则移除该事件所有的监听器;

  • 如果同时提供了事件与回调,则只移除这个回调的监听器

vm.$off() // 移除所有的事件监听器 
vm.$off('test') // 移除该事件所有的监听器
vm.$off('test', callback) // 只移除这个回调的监听器

16.5 refvm.$refs

ref被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的$refs对象上,如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件的实例。

如下代码示例,是用来设置输入框的焦点

<input type="text"  ref="inp" />
mounted(){
    //mounted之后才能访问到inp
    this.$refs.inp.focus()
}

下面在用户管理案例中,看一下具体的实现效果。

   //新增用户组件
      Vue.component("user-add", {
        // data() {
        //   return {
        //     userInfo: "",
        //   };
        // },
        props: ["value"],
        template: `
              <div>
               <p>
                  <input type="text" :value="value" @input="onInput" v-on:keydown.enter="addUser" ref="inp" />
               </p>
               <button @click="addUser">新增用户</button>
                </div>
              `,

        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            // this.$emit("add-user", this.userInfo);
            this.$emit("add-user");
            // this.userInfo = "";
          },
          onInput(e) {
            this.$emit("input", e.target.value);
          },
        },
        mounted() {
          this.$refs.inp.focus();
        },
      });

在上面的代码中,我们首先给user-add组件模板中的文本框添加了ref属性。

然后,在其所对应的mounted方法中,通过$refs找到文本框,然后为其添加焦点。

回到浏览器中,刷新浏览器,可以看到对应的文本框获取了焦点。

下面,我们在将弹出窗口修改一下:

下面修改一下message模板中的内容。

 //创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        // props: ["show"],
        data() {
          return {
            show: false,
          };
        },

        template: `<div class='message-box' v-if="show">
               <!--具名插槽-->
               <slot name="title">默认标题</slot>
              <slot></slot>
              <span class="message-box-close" @click='toggle'>关闭</span>
            </div>`,
        mounted() {
          //给总线绑定`message-close`事件
          //也就是监听是否有`message-close`事件被触发。
          this.$bus.$on("message-close", () => {
            // this.$emit("close", false);
            this.toggle();
          });
        },
        methods: {
          toggle() {
            this.show = !this.show;
          },
        },
      });

在上面的代码中,取消了props,而定义了data属性,表明的含义就是整个窗口的状态的控制,也就是提示窗口的显示与隐藏,都是有自己控制,而不是受外部传递的参数来进行控制了。

同时,在该组件中,添加了toggle方法,修改对应的show的状态。

所以模板中,按钮的单击事件触发以后,调用的就是toggle方法,也就是单击了窗口的右侧的关闭按钮,是通过调用toggle方法来完成,窗口的关闭。

同样事件message-close触发以后,也是调用toggle方法来关闭窗口。

下面看一下关于message模板的使用。

 <!-- 弹窗组件 -->
      <message ref="msgSuccess" class="success">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>恭喜</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          添加用户成功
        </template>
      </message>

在上面的代码中,我们为message组件,添加了ref属性。

同理表示警告的窗口,也需要添加ref的属性.

 <!-- 警告 -->
      <message ref="msgWaring" class="warning">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>警告</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          请输入用户名
        </template>
      </message>

关于data中定义的isShowshowWarn就可以取消了。

data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
          // isShow: false,
          // showWarn: false, // 控制警告窗口的显示与隐藏
        },

当用户点击“新增用户”按钮的时候,执行addUser方法,下面也需要对该方法进行如下修改:

 //添加用户的信息
          addUser() {
            if (this.userInfo) {
              if (this.users.length > 0) {
                this.users.push({
                  id: this.users[this.users.length - 1].id + 1,
                  name: this.userInfo,
                });
                this.userInfo = "";
                //完成用户添加后,给出相应的提示信息
                // this.isShow = true;
                this.$refs.msgSuccess.toggle();
              }
            } else {
              // 显示错误警告信息
              // this.showWarn = true;
              this.$refs.msgWaring.toggle();
            }
          },

在上面的代码中,我们都是通过$ref 找到对应的窗口,然后调用toggle方法,来修改对应的状态。

因为,我们前面讲过如果ref用在子组件上,引用就指向组件的实例.所以可以调用组件内部的toggle方法。

完整代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>列表渲染</title>
    <style>
      .actived {
        background-color: #dddddd;
      }
      .message-box {
        padding: 10px 20px;
      }
      .success {
        background-color: #4fc;
        border: 1px solid #42b;
      }
      .warning {
        background-color: red;
        border: 1px solid #42b;
      }
      .message-box-close {
        float: right;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 弹窗组件 -->
      <message ref="msgSuccess" class="success">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>恭喜</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          添加用户成功
        </template>
      </message>

      <!-- 警告 -->
      <message ref="msgWaring" class="warning">
        <!-- titile的插槽 -->
        <template v-slot:title>
          <h2>警告</h2>
        </template>
        <!-- 默认插槽 -->
        <template>
          请输入用户名
        </template>
      </message>

      <!-- 清空提示栏 -->
      <div class="toolbar">
        <button @click="$bus.$emit('message-close')">
          清空提示栏
        </button>
      </div>
      <!-- 批量更新身高 -->
      <p>
        <input type="text" v-model.number="height" />
        <button @click="batchUpdate">批量更新用户身高</button>
      </p>
      <!-- 新增用户 -->
      <user-add @add-user="addUser" v-model="userInfo"></user-add>
      <!-- 用户列表组件 -->
      <user-list :users="users"></user-list>

      <p>
        总人数:{{totalCount}}
      </p>
    </div>
    <!-- <script src="vue.js"></script> -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
      //创建事件总线
      Vue.prototype.$bus = new Vue();
      //创建弹出的组件
      Vue.component("message", {
        //show表示的含义,控制弹出窗口的显示与隐藏。
        //slot:表示占坑。也就是窗口中的内容,是通过外部组件传递过来的。
        // props: ["show"],
        data() {
          return {
            show: false,
          };
        },

        template: `<div class='message-box' v-if="show">
               <!--具名插槽-->
               <slot name="title">默认标题</slot>
              <slot></slot>
              <span class="message-box-close" @click='toggle'>关闭</span>
            </div>`,
        mounted() {
          //给总线绑定`message-close`事件
          //也就是监听是否有`message-close`事件被触发。
          this.$bus.$on("message-close", () => {
            // this.$emit("close", false);
            //当警告窗口和提示信息的窗口,展示出来了才关闭。
            if (this.show) {
              this.toggle();
            }
          });
        },
        methods: {
          toggle() {
            this.show = !this.show;
          },
        },
      });

      //新增用户组件
      Vue.component("user-add", {
        // data() {
        //   return {
        //     userInfo: "",
        //   };
        // },
        props: ["value"],
        template: `
              <div>
               <p>
                  <input type="text" :value="value" @input="onInput" v-on:keydown.enter="addUser" ref="inp" />
               </p>
               <button @click="addUser">新增用户</button>
                </div>
              `,

        methods: {
          addUser() {
            //将输入的用户数据通知给父组件,来完成新增用户操作.
            // this.$emit("add-user", this.userInfo);
            this.$emit("add-user");
            // this.userInfo = "";
          },
          onInput(e) {
            this.$emit("input", e.target.value);
          },
        },
        mounted() {
          this.$refs.inp.focus();
        },
      });

      // 用户列表
      Vue.component("user-list", {
        data() {
          return {
            selectItem: "",
          };
        },
        props: {
          users: {
            type: Array,
            default: [],
          },
        },
        template: `
          <div>
                  <p v-if="users.length===0">没有任何用户数据</p>
              <ul v-else>
                  <li
                  v-for="(item,index) in users"
                  :key="item.id"
                  :style="{backgroundColor:selectItem===item?'#dddddd':'transparent'}"
                  @mousemove="selectItem=item"
                  >
                  编号:{{item.id}} 姓名:{{item.name}}---身高:{{item.height}}
                  </li>
              </ul>
        </div>
          `,
      });
      new Vue({
        el: "#app",
        data: {
          num: 100,
          totalCount: 0,
          users: [],
          height: 0,
          userInfo: "abc",
          // isShow: false,
          // showWarn: false, // 控制警告窗口的显示与隐藏
        },

        //组件实例已创建时
        async created() {
          const users = await this.getUserList();
          this.users = users;
          //批量更新用户身高
          this.batchUpdate();
        },
        methods: {
          //关闭窗口
          closeWindow(data) {
            this.isShow = data;
            this.showWarn = data;
          },
          //添加用户的信息
          addUser() {
            if (this.userInfo) {
              if (this.users.length > 0) {
                this.users.push({
                  id: this.users[this.users.length - 1].id + 1,
                  name: this.userInfo,
                });
                this.userInfo = "";
                //完成用户添加后,给出相应的提示信息
                // this.isShow = true;
                this.$refs.msgSuccess.toggle();
              }
            } else {
              // 显示错误警告信息
              // this.showWarn = true;
              this.$refs.msgWaring.toggle();
            }
          },

          //批量更新身高,动态的给users中添加身高属性
          batchUpdate() {
            this.users.forEach((c) => {
              //   c.height = this.height;
              //   Vue.set(c, "height", this.height);
              this.$set(c, "height", this.height);
            });
          },

          getTotal: function () {
            console.log("methods");
            return this.users.length + "个";
          },
          getUserList: function () {
            return new Promise((resolve) => {
              setTimeout(() => {
                resolve([
                  {
                    id: 1,
                    name: "张三",
                  },
                  {
                    id: 2,
                    name: "李四",
                  },
                  {
                    id: 3,
                    name: "老王",
                  },
                ]);
              }, 2000);
            });
          },
        },
        watch: {
          users: {
            immediate: true, //立即执行
            handler(newValue, oldValue) {
              this.totalCount = newValue.length + "个人";
            },
          },
        },
      });
    </script>
  </body>
</html>

下面在对refvm.$refs的使用做一个总结:

  • ref是作为渲染结果被创建的,在初始渲染时不能访问它们。也就是必须在mounted构造函数中。
  • $refs不是响应式的,不要试图用它在模板中做数据绑定。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值