何时使用 is

当使用DOM元素作为模板(即为el指定某个元素)时,会受到HTML的一些限制:
1. Vue只有在浏览器解析和标准化HTML后才能获取模板内容。
2. 某些标签元素限制了其所能包含的内容,比如 <ul><table>select等,如下渲染可能出错

<select>
    <my-component></my-component>
</seelct>

select 元素中规定只有 option 元素,可能会被认为是无效的内容,辗转的方式如下:

<select>
    <option is="my-component"></option>
</seelct>

不过,如果使用的是字符串模板,那么这些限制将不存在。


动态组件

上文提到的 is 属性,可令某标签变成一个组件。那么切换 is 属性也可以更换组件实例。

<component v-bind:is="currentView">
  <!-- 组件在 vm.currentview 变化时改变! -->
</component>

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})

既然是在组件实例之间切换,如果频繁的销毁与创建性能必定收到影响, keep-alive属性会缓存不活动的组件实例,而不是销毁他们。
使用方式如下:

<keep-alive>
  <component :is="currentView">
    <!-- 非活动组件将被缓存! -->
  </component>
</keep-alive>

模板的data要求必须是函数

  1. 如果不是函数,会发出警告
  2. 之所以这么要求,是因为若data是一个对象,那么多个模板实例将共享同一个对象,造成逻辑上的错误

父–>子组件协同工作 — Props传递

使用props,从父组件传递数据 —> 至子组件

  • 传递静态数据
    父组件使用一个自定义属性传递数据。子组件则在组件内部用props选项声明该自定义属性。
<component message="from parent"></component>

Vue.component('component', {
    template: '<div>{{ message }}<div>',
    props: ['message']
})

可见,message属性就像普通的属性一样被赋值,组件内部获取该值从而更新视图。

  • 传递动态数据
    既然组件自定义的属性与普通的html属性类似,那么也可以将子组件的自定义属性绑定至父组件的某个属性,从而实现该自定义属性的动态变化。
<input type="text" v-model="parentAttr">
<!-- html中的属性名与js中定义的不同,下文会提到 -->
<component :message-addd="parentAttr"></component>

Vue.component('component', {
    template: '<div>{{ messageAddd }}<div>',
    props: ['messageAddd']
})

new Vue({
    el: '#app',
    data: {
        'parentAttr': 'i am pa'
    }
})

每当父组件的属性发生变化,子组件的视图也会更新。


父–>子组件协同工作 — Props注意点

  1. HTML特性不区分大小写,当使用非字符串模版时(如果使用字符串模版,不用在意这些限制),prop的名字形式会从 camelCase 转为 kebab-case(短横线隔开)
    这也是为什么上面的例子中js中的messageAdd,在html中却是message-add的原因
  2. 如果我希望传递给子组件一个number类型的数字,该怎么传递呢?
<!--这里只传了一个字符串"1"-->
<comp some-prop="1"></comp>

<!--这里则是传了整型1-->
<comp :some-prop="1"></comp>
  1. prop是单项绑定的,当父组件属性变化时,传给子组件;但反过来不会发生。prop属性一般有两种情况:
    • 作为初始值传入,只用作初始值。
    • 定义一个computed属性,依赖prop属性
// 情况1, 初始值
props: ['initialCounter'],
data: function () {
    return { counter: this.initialCounter }
}
// 情况2, 计算属性依赖
props: ['size'],
computed: {
    normalizedSize: function () {
        return this.size.trim().toLowerCase()
    }
}

父–>子组件协同工作 — Props验证

在给组件的prop传递值时,往往需要一些验证,以满足逻辑需求。一般都是 属性名:属性验证 的格式

// type可以是:String,Number,Boolean,Function,Object,Array
props:{
    // 整型
    propA: Number, 
    // 多种类型(并不是说要传列表)
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: Boolean,
      required: true
    },
    // 数字,有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回,依然是多个实例可能共享同一个对象的问题
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
}

当 prop 验证失败了, Vue 将拒绝在子组件上设置此值


子–>父组件协同工作 — 自定义事件

之前提到,子组件是不被允许改变prop的值的,如果子组件要把数据传递回去,那就是自定义事件。

使用 $on(eventName) 监听事件
使用 $emit(eventName) 触发事件

  • 一般流程为

    1. 子组件监听某自定义事件
    2. 父组件在使用子组件的地方监听子组件的自定义事件
    3. 子组件触发自定义事件
    4. 父组件监听的子组件的自定义事件也被触发
  • 示例
    点击任何一个子组件,该子组件内部计数器会增加,同时父组件计数器都会增加

<!--html 部分-->
<div id="app-6">
    {{ total }}
    <div>
        <component-6 @increament="increament"></component-6>
        <component-6 @increament="increament"></component-6>
    </div>
</div>

//js 部分
Vue.component('component-6', {
    template: '<button @click="partClick">{{ partTotal }}</button>',
    data: function () {
        return {
            partTotal: 0
        }
    }, 
    methods: {
        partClick: function () {
            this.partTotal += 1;
            this.$emit('increament')
        }
    }
})

var app6 = new Vue({
    el: '#app-6',
    data: {
        total: 0
    },
    methods: {
        increament: function () {
            this.total += 1
        }
    }
})

除了自定义事件,如果想要绑定原生事件的话,那么可以如下使用:

<my-component v-on:click.native="doTheThing"></my-component>

子–>父组件协同工作 — 表单控件的 v-model

之前了解到:v-modle = v-bind:value + v-on:input
那么,父组件如和监听子组件的表单控件的内容呢?想法肯定是利用子组件的事件将数据向上传递.
至于为什么v-model可以放在此,看官网介绍

<div class="part">
    <p class="title">子组件的表单控件值传递到父组件</p>
    <div id="app-7">
        <p>子组件的消息:{{ message }}</p>
        <!-- 相当于子组件的value属性绑定了父组件的message, 所以子组件的value属性会跟着父组件的message变化 -->
        <!-- 也相当于父组件监听了子组件的input事件, 子组件只需要emit input事件,且带上一个value参数即可-->
        <component-7 v-model="message" :parent-info="info"></component-7>
    </div>
</div>


Vue.component('component-7', {
    props: ['value', 'parentInfo'],
    data: function () {
        return {
            inputId: 'input' + Math.random(),
        }
    },
    methods: {
        onput: function (event) {
            this.$emit('input', event.target.value)
        }
    },
    // 在子组件的input内容发生变化后,触发input事件,从而触发onput事件
    // onput事件将子组件的input的值传递到父组件,父组件获取到该值从而给message赋值
    // message值发生变化,将其变动传递到子组件中,从而引发子组件中内容的变化
    template: '\
    <div>\
        <p> {{ parentInfo }} </p>\
        <input :id="inputId" v-on:input="onput">\
        <p>{{ value }}</p>\
    </div>\
    '
})

var app7 = new Vue({
    el: '#app-7',
    data: {
        message: 'init of parent',
        info: "parent's info"
    }
})

父组件使用Slots为子组件分发内容

  1. 主要用于混合父组件的内容与子组件自己的模板, 使用了 <slot> 元素作为原始内容的插槽。
  2. 关于slot, 除非子组件模板包含至少一个 <slot> 插口,否则父组件的内容将被丢弃。
  3. 当子组件模板只有一个没有属性的 slot 时,父组件整个内容片段将插入到 slot 所在的 DOM 位置,并替换掉 slot 标签本身
  4. 最初在 <slot> 标签中的任何内容都被视为备用内容,在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。
  5. 如果子组件有多个slot, 那么可以为每个slot指定name属性作为标识,父组件通过声明name属性来指定内容所属slot。 另外,可以有一个没有name属性的slot, 作为父组件中没有声明name属性的内容的去处。
Logo

前往低代码交流专区

更多推荐