一, 自定义事件
父组件通过 prop 给子组件传递数据, 子组件则通过自定义事件和父组件通信.
1. 使用 v-on 绑定自定义事件
每个 vue 实例都实现了事件接口, 即
a. 使用 $on(eventName) 监听事件
b. 使用 $emit(eventName, optionalPayload) 触发事件
父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件. 不能使用 $on 监听子组件释放的事件, 而必须在模板里直接用 v-on 绑定.
- <div id="example1">
- <p>{{total}}</p>
- <!-- 子组件触发事件告知父组件, 父组件触发自己的处理事件 -->
- <button-counter v-on:increment="incrementTotal"></button-counter>
- <button-counter v-on:increment="incrementTotal"></button-counter>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- Vue.component('button-counter', {
- template: '<button v-on:click="incrementCounter">{{counter}}</button>',
- data: function () {
- return {
- counter:0
- }
- },
- methods: {
- incrementCounter: function () { // 子组件本身的处理事件
- this.counter += 1;
- this.$emit('increment'); // 触发事件告知父组件
- }
- }
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- total: 0
- },
- methods: {
- incrementTotal: function () {
- this.total += 1;
- }
- }
- });
- </script>
2. 使用载荷数据
- <div id="example1">
- <p v-for='msg in message'>{{msg}}</p>
- <button-msg v-on:message="handleMessage"></button-msg>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- Vue.component('button-msg', {
- template: '<div><input type="text"v-model="message"/><button v-on:click="handleSendMessage">send</button></div>',
- data: function () {
- return {
- message: '子组件数据'
- }
- },
- methods: {
- handleSendMessage: function () { // 子组件本身的处理事件
- this.$emit('message',{message: this.message}); // 触发事件告知父组件, 并传入数据给父组件
- }
- }
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- message: []
- },
- methods: {
- handleMessage: function (payload) {
- this.message.push(payload.message);// 父组件监听到事件, 接受子组件传过来的数据
- }
- }
- });
- </script>
3. 给组件绑定原生事件
如果要在组件的根元素上监听一个原生事件, 可以使用 v-on 的修饰符 .native .
如下: 只是监听子组件的点击事件.
<my-component v-on:click.native="doTheThing"></my-component>
4. 使用自定义事件的表单输入组件
自定义事件可以用来创建自定义的表单输入组件, 使用 v-model 来进行数据双向绑定.
- <input v-model="something">
- <!-- 上面的示例只是下面的语法糖 -->
- <input v-bind:value="something" v-on:input="something=$event.target.value">
- <!-- 在组建建中使用 v-model 相当于下面的简写 -->
- <custom-input v-bind:value="something" v-on:input="something=arguments[0]"></custom-input>
示例:
- <div id="example1">
- <!-- 这里相当于给子组件绑定了 v-bind:value=price v-on:input="arguments[0]" -->
- <currency-input v-model='price'></currency-input>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- Vue.component('currency-input', {
- props: ['pricec'],
- template: '<span>price:<input type="text"ref="iptId"v-bind:value="pricec"v-on:input="updateValue($event.target.value)"/></span>',
- methods: {
- updateValue: function (value) {
- var formatVal = value.trim()
- .slice(
- 0,
- value.indexOf('.') === -1
- ? value.length
- : value.indexOf('.')+3
- );
- if(formatVal !== value){
- this.$refs.iptId.value = formatVal;
- //ref 被用来给元素或组件注册引用信息, 引用信息在 $refs 对象上
- }
- this.$emit('input',Number(formatVal));// 这里子组件将自己的 input 时间告知父组件, 此时 example1.price 也已经改变
- }
- }
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- price:10
- }
- });
- </script>
二, 使用插槽分发内容
1. 内容分发
为了让组件可以组合, 需要一种方式来混合父组件的内容与子组件自己的模板, 这个过程称为内容分发.
Vue 使用特殊的 slot 元素作为原始内容的插槽.
2. 编译作用域
父组件模板的内容在父组件作用域内编译; 子组件模板的内容在子组件作用域内编译.
例如:
<!-- 无效 -->
- <child-component v-show="someChildProperty"></child-component>
- // 如果 someChildProperty 是子组件的属性, 上例不会生效. 父组件模板不会感知子组件的状态.
- // 如果要绑定子组件作用域内的指令到一个组件的根节点, 应当在子组件自己的模板里做:
- Vue.component('child-component', {
- // 有效, 因为是在正确的作用域内
- template: '<div v-show="someChildProperty">Child</div>',
- data: function () {
- return {
- someChildProperty: true
- }
- }
- })
3. 单个插槽
除非子组件模板包含至少一个 solt 口, 否则父组件的内容将会被丢弃. 当子组件模板只有一个没有属性的插槽时, 父组件传入的整个内容片段将插入到插槽所在的 DOM 位置, 并替换掉插槽标签本身.
最初在 slot 标签中的任何内容都被视为备用内容. 备用内容在子组件的作用域内编译, 并且只有在宿主元素为空, 且没有要插入的内容时才显示备用内容.
例如:
- <div id="example1">
- <h4 > 我是父组件的标题</h4>
- <my-component>
- <p > 我是父组件的内容</p>
- </my-component>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- Vue.component('my-component', {
- template:'<div><h4 > 我是子组件的标题</h4><slot > 只有在没有要分发的内容时才会显示</slot></div>'
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- }
- });
- </script>
以上代码会显示为
- <div id="example1">
- <h4 > 我是父组件的标题</h4>
- <div>
- <h4 > 我是子组件的标题</h4>
- <p > 我是父组件的内容</p>
- </div>
- </div>
如果子组件内没有 slot 插槽, 父组件 my-component 里面的
<p > 我是父组件的内容</p>
将会被丢弃, 不会显示.
如果父组件 my-component 里面没有任何内容, 即没有 p 元素时, slot 里面的内容才会显示.
4. 具名插槽
slot 元素可以用 name 特性配置如何分发内容. 多个插槽可以有不同的名字. 具名插槽将匹配内容片段中有对应 slot 特性的元素.
仍然可以有一个匿名插槽, 它是默认插槽, 作为找不到匹配的内容片段的备用插槽. 如果没有默认插槽, 这些找不到匹配的内容片段将被抛弃.
例如:
- <div id="example1">
- <app-layout>
- <h1 slot="header">这里可能是一个页面标题</h1>
- <p > 主要内容的一个段落</p>
- <p > 另一个主要段落</p>
- <p slot="footer">这里有一些联系信息</p>
- </app-layout>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- var html=
- '<div class="container">'+
- '<header>'+
- '<slot name="header"></slot>'+
- '</header>'+
- '<main>'+
- '<slot></slot>'+
- '</main>'+
- '<footer>'+
- '<slot name="footer"></slot>'+
- '</footer>'+
- '</div>';
- Vue.component('app-layout', {
- template: html
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- }
- });
- </script>
将会渲染为:
- <div class="container">
- <header>
- <h1 > 这里可能是一个页面标题</h1>
- </header>
- <main>
- <p > 主要内容的一个段落.</p>
- <p > 另一个主要段落.</p>
- </main>
- <footer>
- <p > 这里有一些联系信息</p>
- </footer>
- </div>
这就是 具名插槽匹配父组件中具有对应 slot 特性的元素.
子组件 main 中的匿名插槽, 作为没有 slot 特性元素的备用插槽.
如果子组件中没有那个匿名插槽, 父组件的
- <p > 主要内容的一个段落</p>
- <p > 另一个主要段落</p>
会被抛弃不显示.
5. 作用域插槽
作用域插槽是一种特殊类型的插槽, 用作一个 (能被传递数据的) 可重用模板, 来代替已经渲染好的元素.
在子组件中, 只需将数据传递到插槽, 就像将 prop 传递给组件一样.
例如:
- <div id="example1">
- <div class='parent'>
- <child>
- <div slot-scope="props">
- <span>hello from parent</span>
- <span>{{props.text}}</span>
- </div>
- </child>
- </div>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- var html=
- '<div class="child">'+
- '<slot text="hello from child"></slot>'+
- '</div>';
- Vue.component('child', {
- template: html
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- }
- });
- </script>
在父组件中, 具有特殊特性 slot-scope 的元素, 表示它是作用域插槽的模板. slot-scope 的值将被用作一个临时变量名, 此变量接收从子组件传递过来的 prop 对象. 如子组件的 slot 的 text 的值.
最终渲染为:
- <div class="parent">
- <div class="child">
- <div>
- <span>hello from parent</span>
- <span>hello from child</span>
- </div>
- </div>
- </div>
三, 动态组件
通过使用保留的 component 元素, 并对其 is 特性进行动态绑定, 可以在同一个挂载点动态切换多个组件.
例如:
- <div id="example1">
- <div class='parent'>
<span > 选择组件:
- <label for="">home <input type="radio"value="component1"v-model="currentView" /></label>
- <label for="">footer <input type="radio"value="component2"v-model="currentView" /></label>
- <label for="">main <input type="radio"value="component3"v-model="currentView" /></label>
- </span>
- <component v-bind:is='currentView'>
- </component>
- </div>
- </div>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
- <script>
- var html=
- '<div class="child">'+
- '<slot text="hello from child"></slot>'+
- '</div>';
- Vue.component('child', {
- template: html
- });
- var example1 = new Vue({
- el: '#example1',
- data: {
- currentView: 'component1'
- },
- components: {
- 'component1':{
- template: '<div > 动态组件 1</div>'
- },
- 'component2': {
- template: '<div > 动态组件 2</div>'
- },
- 'component3': {
- template: '<div > 动态组件 3</div>'
- }
- }
- });
- </script>
在这里动态改变 currentView 就可以切换 component 里面的组件.
1.keep-alive
keep-alive 包裹动态组件时, 会缓存不活动的组件实例, 而不是销毁它们, 避免重新渲染.
keep-alive 是一个抽象组件, 它自身不会渲染一个 DOM 元素, 也不会出现在父组件链中.
当组件在 keep-alive 内切换时, 它的 activated 和 deactivated 这两个生命周期函数将会被对应执行.
- <keep-alive>
- <component :is="currentView">
- <!-- 非活动组件将被缓存! -->
- </component>
- </keep-alive>
来源: http://www.qdfuns.com/article/46690/09eec9a5bf44b50ed7b707728a97696c.html