vue 学习笔记(四)

一、自定义事件
父组件通过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/[email protected]/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/[email protected]/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/[email protected]/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.单个插槽
  除非子组件模板包含至少一个slot口,否则父组件的内容将会被丢弃,当子组件模板只有一个没有属性的插槽时,
  父组件传入的整个内容片段将插入到插槽所在的DOM位置,并替换掉插槽便签本身。
  最初在slot标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只用在宿主元素为空,
  且没有要插入的内容时才显示备用内容。例如:
<div id="example1">
            <h4>我是父组件的标题</h4>
            <my-component>
                <p>我是父组件的内容</p>
            </my-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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
  kepp-alive包囊动态组件时,会缓存不活动的组件实例 ,而不是销毁它们,避免重新渲染。
  keep-alive是一个抽象组件,他自身不会渲染一个DOM元素,也不会出现在父组件链中。
  当组件在keep-alive内切换时,它的activated和deactivated这两个生命周期函数将会被对应执行。
<keep-alive>
  <component :is="currentView">
    <!-- 非活动组件将被缓存! -->
  </component>
</keep-alive>

// 注:keep-alive要求被切换到的组件都有自己的名字,不论是通过组件的name选项还是局部或全局注册。

猜你喜欢

转载自blog.csdn.net/cxz792116/article/details/80772946