【Vue】组件

2021/09/09 10:10:15

组件注册方式

  1. 组件名为驼峰式(comName)定义时,可以通过 com-name 的形式引用(不区分大小写)
  2. 直接在页面中引用时只能用 kebab-case 的组件名引用(com-name)

全局注册

Vue.component('component-name', {})

局部注册

new Vue({
  components: {
    "component-a": ComponentA, // component-a 为使用时的组件名,ComponentA为要引入的组件的名字
  },
});

模块化

// 定义
<template></template>;
export default {};

// 引入
import ComponentA from "./ComponentA.vue";
export default {
  components: {
    ComponentA,
  },
};

组件内容

注册参数

Vue.component("component-name", {
  template: "<div></div>", // 组件内容, 一个组件只能有一个根元素
  components: {}, // 组件可以引用其他组件
  props: ["tit"], // 组件通过props接收父级传值, 在组件中可以直接通过this.tit取到tit的值
  data() {
    return {}; // 组件的数据需要通过一个函数来返回一个对象,避免组件复用时数据共享
    // 一个足迹可能会创建多个实例,如果data是一个纯粹的对象,则所有的实例会共享引用同一个数据对象
  },
  methods, // 事件注册,监听器,计算属性,钩子函数与vue实例相同
});

监听子组件事件

给组件绑定一个 myClick 事件,这个事件名是可以自定义的

<div id="wrapper">
  <hello @myClick="addSize"></hello>
</div>

父级中定义对应事件处理函数,子组件中通过$emit(),参数传入事件名(不是处理函数名)来触发

new Vue({
  el: "#wrapper",
  methods: {
    addSize() {
      console.log(1);
    },
  },
});

在组件内部绑定事件,通过$emit()来触发父级绑定到子组件上的对应事件,一参为要触发的事件名,其后的参数会作为父级绑定事件的参数

Vue.component("hello", {
  template: `<div @click='$emit("myClick",1,2,3)'>hello</div>`, // 通过点击该元素来触发父级的myClick事件
});

使用 v-model

v-model 的作用实际是给元素绑定一个名为 value的属性,然后监听该元素的 input 事件,在触发 input 事件时修改绑定的值

<input v-model="myAge" />
<!-- 等价于 -->
<input :value="myAge" @input="myAge = $event.target.value" />
<!-- $event是事件对象 -->

要在组件中使用 v-model 则需要模拟上述过程即可

  1. 通过 props 接收一个名为 value 的属性
  2. 给组件内的 input 绑定 value 属性,属性值为 value
  3. 给组件内的 input 绑定 input 事件处理函数,主动触发父级的 input 事件
<hello v-model="myAge"><hello></hello></hello>
Vue.component("hello", {
  template: `<div>
		<input :value='value' @input='$emit('input', $event.target.value)'>
	</div>`,
  props: ["value"],
});

双向绑定

利用 sync 实现双向绑定open in new window

Prop 类型

只定义属性名

Vue.component("hello", {
  props: ["value"],
});

定义类型,如果类型不对会打印警告,但是不影响运行

Vue.component("hello", {
  props: {
    title: String,
    likes: Number,
    isPublished: Boolean,
    commentIds: Array,
    author: Object,
    callback: Function,
    contactsPromise: Promise, // or any other constructor
  },
});

带验证的 props 对象,验证失败会打印警告,验证是在组件实例创建前进行的

type 的可选值 type:[String,Number,Boolean,Array,Object,Date,Function,Symbol] type 也可以是一个自定义的构造函数,检验时通过 instanceof 来判断

function Person() {}

Vue.component("hello", {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    propA: Number,
    // 多个可能的类型
    propB: [String, Number],
    // 必填的字符串
    propC: {
      type: String,
      required: true,
    },
    // 带有默认值的数字
    propD: {
      type: Number,
      default: 100,
    },
    // 带有默认值的对象
    propE: {
      type: Object,
      // 对象或数组默认值必须从一个工厂函数获取
      default: function() {
        return { message: "hello" };
      },
    },
    // 自定义验证函数
    propF: {
      validator: function(value) {
        // 这个值必须匹配下列字符串中的一个
        return ["success", "warning", "danger"].indexOf(value) !== -1;
      },
    },
    propG: {
      type: Person, // propG instanceof Person
    },
  },
});

未定义 prop 的属性值

如果组件上绑定数据,但是组件内没有对应的 prop 来接收,这个属性会被绑定到该组件的根元素上 除了 classstyle 之外,父级传递给组件的属性都会替换组件内定义的属性

Vue.component("hello", {
  template: `<div>
      <div>hello</div>
    </div>`,
});
<div id="wrapper">
  <hello :noProp="39"></hello>
</div>

<!-- 编译后 -->
<div id="wrapper">
  <div noProp="39">
    <div>hello</div>
  </div>
</div>

禁用 prop 继承

在组件中设置 inheritAttrsfalse 就能让组件根元素的属性不被传值覆盖 classstyle 不受此属性影响

Vue.component("my-component", {
  inheritAttrs: false,
});

自定义事件

触发事件

事件名不存在自动化和大小写转换,要触发的事件名需要与定义的事件名完全一致

<my-component @myEvent="doSomething"></my-component>
this.$emit("myEvent");

修改 v-model 默认行为

v-model 默认会利用名为 valueprop 和名为 input 的事件,可以用 model 选项修改

Vue.component("base-checkbox", {
  model: {
    prop: "checked", // 将value属性修改为checked属性
    event: "change", // 将input事件修改为change事件
  },
  props: {
    checked: Boolean, // 任需要定义对应的prop
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `,
});

监听原生事件

想要直接在组件根元素上监听原生事件需要使用.native 修饰符

<base-input v-on:focus.native="onFocus"></base-input>

插槽

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。 v-slot:header 可简写为 #header

后备内容

  1. 组件中如果没有 slot 标签则父级写在组件标签内的元素都会被抛弃
  2. 在组件的 slot 标签内写入的内容会在父级没有在组件标签内传入内容时展示
Vue.component("hello", {
  template: `
  <div>
    hello <slot>normal Text</slot> 
  </div>
  `,
});
<hello></hello>
<!-- 展示为 -->
<div>
  hello normal Text
</div>

<hello>is new Text</hello>
<!-- 展示为 -->
<div>
  hello is new Text
</div>

具名插槽

一个不带 name 的 slot 出口会带有隐含的名字 'default' 任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容

// 定义组件时给slot标签一个name属性,引入时传入对应的属性
Vue.component("hello", {
  template: `<div class="container">
        <header>
            <slot name="header"></slot>
        </header>
        <main>
            <slot></slot>
        </main>
        <footer>
            <slot name="footer"></slot>
        </footer>
    </div>`,
});
<base-layout>
  <!-- 传入属性即可 -->
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>
  <!-- 等价于
    <template v-slot:default>
      <p>A paragraph for the main content.</p>
      <p>And another one.</p>
    </template> 
    -->

  <template #:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

作用域插槽

插槽总是在父级组件中使用的,这时的作用域是在父级中,插槽拿不到组件内的数据 这时如果需要调用组件里的数据就要用到作用域插槽,在组件内把属性值赋给插槽

// 定义组件时给slot标签一个name属性,引入时传入对应的属性
Vue.component('hello', {
  template:
  `<div class="container">
      <slot name="header" :uName='innerName' :uAge='innerAge'></slot>
  </div>`,
  data() {
      return {
          innerName:'zhangkb',
          innerAge: 099
      }
  }
}
<hello>
  <!-- msg就是接收到的组件内传给插槽的数据对象 -->
  <template v-slot:header="{msg}">
    <h1>get slot data: {{mag.uName + msg.uAge}}</h1>
  </template>
</hello>

<!-- 展示为 -->
<h1>get slot data: zhangkb099</h1>