首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Vue基础:组件--组件及组件通信

组件

组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以is特性扩展。

使用组件

  • 注册一个全局组件,你可以使用 Vue.component(tagName, [definition]) // 注册组件,传入一个扩展过的构造器 Vue.component('my-component', Vue.extend({ /* ... */ })) // 注册组件,传入一个选项对象 (自动调用 Vue.extend) Vue.component('my-component', { /* ... */ }) // 获取注册的组件 (始终返回构造器) var MyComponent = Vue.component('my-component')
  • 使用组件实例选项注册局部组件 new Vue({ // ... components: { // <my-component> 将只在父模板可用 'my-component': Child } })

DOM模板解析说明

Vue 只有在浏览器解析和标准化 HTML 后才能获取模板内容。像 <ul><ol><table><select> 限制了能被它包裹的元素,或者像 <option> 这样的元素只能出现在某些其它元素内部。在自定义组件中使用会导致一些问题。

<table>
  <!-- 被认为是无效的内容,因此在渲染的时候会导致错误 -->
  <my-row>...</my-row>
  <!-- 使用特殊的 is 属性 -->
  <tr is="my-row"></tr>
</table>

应当注意,下述情况不受限制:

  • <script type="text/x-template"> <script type="text/x-template" id="hello-world-template"> <p>Hello hello hello</p> </script> Vue.component('hello-world', { template: '#hello-world-template' })
  • JavaScript 内联模板字符串 <my-component inline-template> <div> <p>These are compiled as the component's own template.</p> <p>Not parent's transclusion content.</p> </div> </my-component>
  • .vue 组件

注意,使用上述三种方式不会报错,单不能渲染到指定位置。is方式是可行的!

完整参考示例:https://jsfiddle.net/381510688/1LasaLhL/

data必须是函数

为了隔离作用域!

<div id="example">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // 技术上 data 的确是一个函数了,因此 Vue 不会警告,
  // 但是我们返回给每个组件的实例却引用了同一个 data 对象
  data: function () {
    return data
  }
})
new Vue({
  el: '#example'
})

上述需要修改为

data: function () {
  return {
    counter: 0
  }
}

组件之间的通信

父组件=>子组件通信

props down, events up

组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,我们需要通过子组件的 props 选项。

父组件通过props来给子组件传递数据,子组件需要显示的用props选项声明props。可以通过v-bind动态的绑定props的值到父组件的数据中,每次当绑定的数据在父组件中发生改变的时候,该组件也会相应的传递给子组件。需要注意的是,要使用v-bind这样传递下去的才是正真的字面量,否则都会当做字符串(下述age1是string,age2为number)!

<child name="ligang" age1="28" :age2="28"></child>

Vue.component('child', {
  // 子组件显示的声明props
  props: ['name', 'age1', 'age2'],
  computed: {
    ageType() {
      return {
        age1: typeof this.age1,
        age2: typeof this.age2
      }
    }
  },
  // prop就像data一样可以再模板中使用也可以通过this来调用
  template: '<span> {{ name }} - {{ageType}}</span>'
})

子组件内修改变父组件传递的prop值

prop是单向绑定的,当父组件的属性变化时,将传递给子组件,但是在子组件中改变数据的时候并不会传递给父组件(为了防止子组件无意间修改父组件的状态),所以不应该在子组件中改变prop的数据。如果想在子组件中想改变prop的值通常有二种方式:

  • 方式一:作为本地数据的初始值使用 props: ['initialCounter'], data: function () { return { couter: this.initialCounter } }
  • 方式二:作为计算属性使用 props: ['size'], computed: { childSize: function () { return this.size.trim().toLowerCase() } }

注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

Prop验证

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 意思是任何类型都可以)
    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 value > 10
      }
    }
  }
})

注意 props 会在组件实例创建之前进行校验,所以在 defaultvalidator 函数里,诸如 datacomputedmethods 等实例属性还无法使用。

非Prop特性

所谓非 prop 特性,就是它可以直接传入组件,而不需要定义相应的 prop。组件可以接收任意传入的特性,这些特性都会被添加到组件的根元素上。

<my-component data-index="1" class="xx1"></my-component>
  • 相同名称的属性,会覆盖组件内部的;
  • classstyle 特性会做merge操作。

完整参考示例:https://jsfiddle.net/381510688/afxex6vc/

子组件=>父组件通信

子组件通过自定义事件的方法将数据传递给父组件

<my-component :age="age" @increment="addAag"></my-component>
<script>
  const app = new Vue({
    el: '#app',
    data(){
      return {
        age: 28
      }
    },
    methods: {
      addAag() {
        this.age += 1;
      }
    },
    components: {
      // 内部组件
      'my-component': {
        props: ['age'],
        methods: {
          increment() {
            this.$emit('increment');
          }
        },
        template: `
          <div>
            <p>{{this.age}}-{{this.age2}}</p>
            <button @click="increment">增加年龄</button>
          </div>
         `
      }
    }
});
</script>

完整示例参考地址:https://jsfiddle.net/381510688/jvhtwc8b/

事件修饰符

  • .native修饰符:在某个组件的根元素上监听一个原生事件 <my-component v-on:click.native="doTheThing"></my-component>
  • .sync修饰符:2.0中移除了 .sync修饰符,但我们经常需要对一个prop进行『双向绑定』,所以在2.3.0进行了重新引入,但是这次它只是作为一个编译时的语法糖存在。 <comp :foo.sync="bar"></comp> <!-- 会被扩展为:--> <comp :foo="bar" @update:foo="val => bar = val"></comp> 当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件: this.$emit('update:foo', newValue)

示例:ElementUI中的el-dialog

  <el-dialog :visible.sync="dialogVisible">
  <el-dialog :visible="dialogVisible" :before-close="beforeClose">

第一种写法关闭或是点击空白处无需特别处理,el-dialog组件内部会修改当前值状态,通过.sync修饰符传递给父组件;第二种写法,需要再beforeClose方法内手动处理this.dialogVisible = false

剖析el-dialog源码

  handleClose() {
    // el-dialog组件上存在before-close,则先调用beforeClose方法,然后调用this.hide
    // 这也是为什么this.beforeClose处理完后,必须调用done(),done实际就是this.hide
    if (typeof this.beforeClose === 'function') {
      this.beforeClose(this.hide);
    } else {
      this.hide();
    }
  },
  hide(cancel) {
    if (cancel !== false) {
       this.$emit('update:visible', false);
       this.$emit('visible-change', false);
    }
  }

完整示例参考地址:https://jsfiddle.net/381510688/vqfoshff/

v-model

前文中已提及,v-mdoel是<input v-bind:value="something" v-on:input="something =$event.target.value">的语法糖。默认情况下,一个组件的 v-model 会使用 value 属性和 input 事件,但是诸如单选框、复选框之类的输入类型可能把 value 属性用作了别的目的。model 选项可以回避这样的冲突:

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean,
    // this allows using the `value` prop for a different purpose
    value: String
  },
  // ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>

上述代码等价于:

<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

非父子组件通信

非父子组件的通信如果情况简单,可以使用全局event bus var bus = new Vue();复杂的情况下往往用vuex。

下一篇
举报
领券