第二部分 VUE组件详解

目录

1 复用与组件化

1.1 组件概述

1.2 创建和使用组件

1.3 组件对象剖析

 

1.3.1 组件对象的data属性

1.3.2 组件的其它属性

2 使用porps父子组件传值

2.1 props属性

2.2 v-bind属性绑定

2.3 prop传值时数据验证

 

3 组件间的通信

3.1 父子组件间的通信

3.2 父子组件之间的访问

 

3.2.1 在子组件中访问父组件

3.2.2 父组件中访问子组件

3.3 非父子组件通信

3.3. 1 事件总线

4 组件插槽(slot)

4.1 插槽的基本使用

4.1.1 为solt提供默认值

4.1.2 定义带有名字的solt

4.1.3在slot中可以使用当前组件的props和data

4.2 作用域插槽

4.2.1 作用域插槽的用途

4.3 访问slot

5. 组件中的v-model(双向绑定)

5.1 v-model基础回顾

5.2 在组件中使用v-model

6 组件的其它高级用法 

6.1 动态组件

6.2 递归组件

6.3 内联模板

6.4 异步组件

6.5 $nextTick

 

 


 

1 复用与组件化

 

1.1 组件概述

 

Vue的组件能够在代码复用上得胜,一处理编写,到处使用,可以把一个复杂的页面分解成N个组件组成,有了组件后可以像搭积木一样构建出复杂的页面(和QT、Java Swing、Android界面组件等界面编程思想类似)

 

 

 

 

组件:vue组件可以是一段html代码,也可以html和js组合代码,还可以是html、js和css的组合体,如:

<template>
  <div class="model-data-content">
    组件的html代码
  </div>
</template>

<script>
  export default {
    data() {
      return {
        roleData: {
          name: '',
          remark: '',
        }
      }
    },
    methods: {
  
    }
  }
</script>

<style scoped>
  .model-data-content{
    background-color: #363e4f
  }
</style>

组件复用:

  1. 同一个Vue组件可以在同一个应用程序中多次使用
  2. 同一个Vue组件可以被不同的应用程序使用
div>
  <demo-component></demo-component>
  <demo-component></demo-component>
</div>

1.2 创建和使用组件

vue的组件遵循先创建再使用的原则,可以全局创建,也可以局部创建

全局创建:

<body>
    <div id="app">
        <!-- 2. 使用组件 -->
        <demo-component></demo-component>
    </div>
</body>
<script>
     // 1. 创建全局组件
     Vue.component('demo-component', {
        template: '<h3> this is a component</h3>'
    })

    new Vue({
        el: '#app'
    })
</script>

局部创建:

<body>
    <div id="app">
        <!-- 2. 使用组件 -->
        <demo-component></demo-component>
    </div>
</body>
<script>


    new Vue({
        el: '#app',
        components: {
            // 1. 创建局部组件
            'demo-component': {
                template: '<h3> this is a  component</h3>'
            }
        }
    })
</script>

1.3 组件对象剖析

Vue组件实质上一个VueComponent类的对象,而VueComponent是Vue的子类,因此组件继承了Vue对象的所有方法与属性

<body>
    <div id="app">
        <demo-component></demo-component>
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: '<h3> this is a component</h3>'
    })

    new Vue({
        el: '#app'
    })
    // 打印组件对象
    console.dir(demoComponent)
</script>

 

 

组件对象的template属性以HTML字符串形式给出:

<body>
    <div id="app">
        <demo-component></demo-component>
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
            <div style="background-color: darkgrey;"> 
                版权所有@百度
            </div>
        `
    })

    new Vue({
        el: '#app'
    })
</script>

组件对象的template属性以template标签形式给出:

<body>
    <div id="app">
        <demo-component></demo-component>
    </div>
    <template id="footer">
        <div style="background-color: darkgrey;"> 
            版权所有@百度
        </div>
    </template>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: '#footer'
    })

    new Vue({
        el: '#app'
    })
</script>

 

1.3.1 组件对象的data属性

在创建组件时,可以使用任何有效的Vue对象属性,常用的就是data属性

data属性需要注意的有两点:

  • 注意一:组件中的data属性必须以函数出现,否则程序出错
  • 注意二:data属性中的函数必须返回一个独有对象,否则会有对象共享的bug问题
<body>
    <div id="app">
        <demo-component></demo-component>
    </div>
    <template id="footer">
        <div> 
            name:{{name}}</br>
            age:{{age}}</br>
        </div>
    </template>
</body>
<script>

    let demoComponent = Vue.component('demo-component', {
        template: '#footer',
        data (){
            return {name: 'admin', age: 18}   // data属性必须以函数出现
        }
    })
    new Vue({
        el: '#app'
    })
</script>

data属性中的函数必须返回一个独有对象,如果共享一个对象数据时会产生bug:

<body>
    <div id="app">
        <demo-component></demo-component>
        <hr/>
        <demo-component></demo-component>
    </div>
    <template id="footer">
        <div> 
            name:{{name}}</br>age:{{age}}</br>
            <button @click="name = 'haijun'">修改</button>
        </div>
    </template>
</body>
<script>
    let data = {name: 'admin', age: 18}
    let demoComponent = Vue.component('demo-component', {
        template: '#footer',
        data: function (){
            return data
        }
    })
    new Vue({
        el: '#app'
    })
</script>

 

正确的写法:

<body>
    <div id="app">
        <demo-component></demo-component>
        <hr/>
        <demo-component></demo-component>
    </div>
    <template id="footer">
        <div> 
            name:{{name}}</br>age:{{age}}</br>
            <button @click="name = 'haijun'">修改</button>
        </div>
    </template>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: '#footer',
        data: function (){
            return {name: 'admin', age: 18}
        }
    })
    new Vue({
        el: '#app'
    })
</script>

1.3.2 组件的其它属性

 

在创建组件时,可以使用任何有效的Vue对象属性,如data属性、computed属性、methods属性等

  • computed:Vue组件中使用的计息属性
  • methods属性:Vue组件中使用的自定义方法(函数)
  • 其它属性:生命周期回调方法(钩子方法)、过滤器

2 使用porps父子组件传值

2.1 props属性

组件实例之间是独立的,不能在子组件的模板内直接使用父组件的数据

<body>
    <div id="app">
        <demo-component></demo-component>
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <div>this message is from parent : {{message}} </div>
        `
    })
    new Vue({
        el: '#app',
        data: {
            message: 'hello world'
        }
    })
</script>

这时会报错:组件内部message没定义

props属性:

父元素的数据需要通过 props 传递到子组件中

  • 子组件中通过 props 属性,声明待接收的数据名称
  • 父元素中使用组件时,通过HTML属性为子组件传递数据
<body>
    <div id="app">
        <demo-component message="hello world"></demo-component>
    </div>

</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <div>this message is from parent : {{message}} </div>
        `,
        props: ['message']  // 声明要传入该属性的数据

    })
    new Vue({
        el: '#app',
        data: {
            message: 'hello world'
        }
    })
</script>
  • 注意一:定义子组件时props属性内声明的数据名称是驼峰命名,但是在父元素传入时属性名要用中划线命名
<body>
    <div id="app">
        <demo-component user-name="admin"></demo-component>
    </div>

</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <div>this message is from parent : {{userName}} </div>
        `,
        props: ['userName'] // 声明要传入该属性的数据

    })
    new Vue({
        el: '#app',
        data: {
            message: 'hello world'
        }
    })
</script>
  • 注意事项二:若 props指明数据,但使用时未传递该数据,则在组件中该数据为 undefined
  • 注意事项三:若传递了未在 props 里声明的数据,则该数据会被当作组件的HTML属性处理

2.2 v-bind属性绑定

可以使用 v-bind 来动态地将 prop 绑定到父元素的数据 ,每当父元素的数据变化时,该变化也会传递给子组件

<body>
    <div id="app">
        <demo-component user-name="admin"></demo-component>
        <demo-component :user-name="name"></demo-component>
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <div>this message is from parent : {{userName}} </div>
        `,
        props: ['userName'] // 声明要传入该属性的数据

    })
    var vm = new Vue({
        el: '#app',
        data: {
            name: 'haijun'
        }
    })
</script>

修改父组件的name数据值:

v-bind 另一个作用是可以传递 非字符串型数据(若不使用v-bind形式, 则为字符串数据)

如:对象、true或false、数值型数据

<body>
    <div id="app">
        <demo-component is-show="true" :is-color="isColor"></demo-component>    
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <div>this message is from parent : isShow->{{typeof(isShow)}}  isColor->{{typeof(isColor)}}</div>
        `,
        props: ['isShow', 'isColor']    // 声明要传入该属性的数据

    })
    var vm = new Vue({
        el: '#app',
        data: {
            isColor: true
        }
    })
</script>

在实际业务场景中,一种是父组件传递的初始值过来,子组件将它作为初始化值保存起来,在自己的作用域下可以随意使用和修改

 

<body>
    <div id="app">
        <demo-component  :size="size"></demo-component>    
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <p :style="myStyle">子组件的内容</p>
        `,
        props: ['size'],    // 声明要传入该属性的数据
        data(){
            return {
                myStyle: {
                
                    fontSize: this.size + 'px',     // 父组件传递的初始值过来,子组件将它作为初始化值保存起来
                    color: 'red'
                }
            }
        }

    })
    var vm = new Vue({
        el: '#app',
        data: {
            size: 29
        }
    })
</script>

另一种情况就是prop作为需要被转变的原始值传入,这种情况可以用计算属性

<body>
    <div id="app">
        <demo-component  :size="size"></demo-component>    
    </div>
</body>
<script>
    let demoComponent = Vue.component('demo-component', {
        template: `
             <p :style="myStyle">子组件的内容</p>
        `,
        props: ['size'],    // 声明要传入该属性的数据
        computed: {
            myStyle(){
                return  {
                    fontSize: this.size + 'px',     // 用计算属性将prop作为需要被转变的原始值传入
                    color: 'red'
                }
            }
        },

    })
    var vm = new Vue({
        el: '#app',
        data: {
            size: 29
        }
    })
</script>

2.3 prop传值时数据验证

 

props选项的值,除了是一个数组外,当props需要验证时,就需要是一个对象的形式了

<body>
    <div id="app">
        <demo-component  :username="'admin'" :age="22" :is-married="true" :say="sayHello"></demo-component>    
    </div>
    <template id="comp">
        <div>
            <p>用户名:{{username}}</p>
            <p>婚否:{{isMarried}}</p>
            <p>爱好:{{hobbies}}</p>
            <p><button @click="say">讲话</button></p>
        </div>
    </template>
</body>
<script>
    Vue.component('demo-component', {
        template: '#comp',
        props: {
            username: String,
            age: {
                type: Number,
                default: 18,
                validator: function(val){
                    return val > 0 && val < 200
                }
            },
            isMarried: {
                type: Boolean,
                required: true
            },
            hobbies: {
                type: Array,
                default: function(){
                    return [1, 2, 3]
                }
            },
            say: {
                type: Function
            }

        },   
    })
    var vm = new Vue({
        el: '#app',
        data: {
            isMarried: false
        },
        methods: {
            sayHello: function(){
                alert('hello')
            }
        },
    })

由于在props中对age进行验证,在使用组件时如果age的值传为2000,则报下面的错:

 

3 组件间的通信

组件之间必然需要相互通信:

  • 父组件可能要给子组件下发数据
  • 子组件则可能要将它内部发生的动态行为告知父组件
  • 非父子组件之间也可能需要数据和行为的通信

3.1 父子组件间的通信

父子组件间通信

➢父元素通过 props 向子组件传递数据

➢子组件通过 事件 给父组件触发更新行为

子组件向父组件发送消息,使用Vue自定义事件机制

  1. 子组件使用 $emit 向父组件触发事件
  2. 父组件使用 $on 或 v-on 监听子组件事件
<body>
    <div id="app">
        <!-- 2 在父组件中监听子组件中事件 -->
        <demo-component value="hello world" @child-event='handleEvent'></demo-component>             
    </div>
    <template id="comp">
        <div>
            this is a component! {{value}}
            <button @click="emitEvent">触发事件</button>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp',
                props: ['value'],
                methods: {
                    emitEvent: function(){
                        // 1 触发事件
                        this.$emit('child-event')
                    }
                },
            }
        },
        methods:{
            handleEvent(){
                alert('接收到子组件触发的事件!')
            }
        }
    })

注意:自定义事件名要用中划线命名

流程:

  1. 子线程触发一个自定义事件
  2. 在父元素中使用v-on指令监听事件,交给父元素的函数处理

 

 

触发事件时携带附加数据

子组件向父组件发送消息,使用Vue自定义事件机制:

➢子组件向父组件发送事件消息时,可以一并传递其它附加数据

➢父组件直接在事件监听函数中通过形参获取这些附加数据参数(也可以 使用 arguments [ ] 数组接收)

 

<body>
    <div id="app">
        <!-- 2 在父组件中监听子组件中事件 -->
        <demo-component value="hello world" @child-event='handleEvent'></demo-component>             
    </div>
    <template id="comp">
        <div>
            this is a component! {{value}}
            <button @click="emitEvent">触发事件</button>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp',
                props: ['value'],
                methods: {
                    emitEvent: function(){
                        // 1 触发事件
                        // 参数1 事件名
                        // 参数2 传递给父元素的数据
                        this.$emit('child-event',"child component data")
                    }
                },
            }
        },
        methods:{
            handleEvent(params){
                alert('接收到子组件触发的事件!' + params)
                // 或者也可以使用arguments对象接收参数值
                alert('接收到子组件触发的事件!' + arguments[0]) 
            }
        }
    })
</script>

也可以使用arguments对象访问传过来的参数:

<body>
    <div id="app">
        <!-- 2 在父组件中监听子组件中事件 -->
        <demo-component value="hello world" @child-event='handleEvent'></demo-component>             
    </div>
    <template id="comp">
        <div>
            this is a component! {{value}}
            <button @click="emitEvent">触发事件</button>
        </div>
    </template>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'default value'
        },
        components: {
            'demo-component': {
                template: '#comp',
                props: ['value'],
                methods: {
                    emitEvent: function(){
                        // 1 触发事件
                        // 参数1 事件名
                        // 参数2 传递给父元素的数据
                        this.$emit('child-event',"child component data")

                    }
                },
            }
        },
        methods:{
            handleEvent(params){
                // 接收子元素触事件携带的数据
                //this.msg = params
                this.msg = arguments[0]
            }
        }
    })
</script>

 

3.2 父子组件之间的访问

 

3.2.1 在子组件中访问父组件

在子组件中,可以通过 $parent 或 $root 访问当前组件的父 元素或根元素实例对象(Vue对象)

➢this.$parent 表示当前组件的父元素组件

➢this.$root 表示当前组件所在的根元素组件

示例:子组件修改父组件的data数据

<body>
    <div id="app">
        <demo-component1 :msg="msg"></demo-component1>  
        <demo-component2></demo-component2>      
    </div>
    <template id="comp1">
        <div>
            this is a component1! {{msg}}
        </div>
    </template>
    <template id="comp2">
        <div>
            this is a component2!
            <button @click="updateParentComponent">更新组件1的数据</button>
        </div>
    </template>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                props: ['msg'],
                methods: {
                    updateParentComponent(){
                        this.$root.msg = 'msg value from child1'
                    }
                },
            }
    const DemoComponent2 =  {
        template: '#comp2',
        methods: {
            updateParentComponent(){
                // 子组件中修改父组件的数据
                //this.$parent.msg = 'msg value from child2'
                this.$root.msg = 'msg value from child2'
            }
        },
    }
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'default value'
        },
        components: {
            DemoComponent1,DemoComponent2
        },
        
    })
</script>

3.2.2 父组件中访问子组件

父元素使用子组件时,可以添加 ref属性,这样在父元素内部即可通过 this.$refs 访问相关子元素组件

<body>
    <div id="app">
        <demo-component1 ref="c1"></demo-component1>     
    </div>
    <template id="comp1">
        <div>
            this is a component1! {{name}}
        </div>
    </template>

</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                data(){
                    return {name: 'admin'}
                }
            }
    
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'default value'
        },
        components: {
            DemoComponent1
        },
        mounted() {
            // 在父元素中获取子组件c1
            console.dir(this.$refs.c1)
        },
        
    })
</script>

<body>
    <div id="app">
        <demo-component1 :msg="msg" ref="c1" ></demo-component1>  
        <demo-component2></demo-component2>      
    </div>
    <template id="comp1">
        <div>
            this is a component1! {{name}}
            <br/>
            {{msg}}
        </div>
    </template>
    <template id="comp2">
        <div>
            this is a component2!
            <button @click="updateParentComponent">更新组件1的数据</button>
        </div>
    </template>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                props: ['msg'],
                data(){
                    return {
                        name: ' name default value in component1 '
                    }
                }
            }
    const DemoComponent2 =  {
        template: '#comp2',
        methods: {
            updateParentComponent(){
                // 先访问父组件,再由父组件访问子组件
                this.$parent.$refs.c1.name = 'this value from comp2'
            }
        },
    }
    var vm = new Vue({
        el: '#app',
        data: {
            msg: 'default value'
        },
        components: {
            DemoComponent1,DemoComponent2
        },
    })
</script>

应用场景:

➢一般不建议使用此方式直接修改父元素或子元素的内容

➢在非父子组件数据通信时,可以使用此方式进行模拟

组件A 和 组件B 之间进行数据通信(在组件A中,要动态修改组件B显示的内容)

• 可以在组件A中,动态修改父元素中的值

• 组件B待修改的值,即为父元素中的指定值,从而达到组件A修改组件B数据的目的

 

3.3 非父子组件通信

非父子组件通信有两种方式:

  1. 数据共享(vux状态管理)
  2. 事件总线

3.3. 1 事件总线

使用一个空的 Vue对象 在不同组件之间触发和监听自定义事件

  1. 创建一个空的Vue对象
  2. 在组件A中使用 $emit 触发事件
  3. 在组件B中使用 $on 监听接收事件

注意:

✓监听过程应该写在 created() 回调函数中

✓在监听函数内部,this表示事件总线对象

<body>
    <div id="app">
        <child-component1></child-component1>
        <child-component2></child-component2>
    </div>
    <template id="comp1">
        <div>
            <button @click="updateChild2">更新组件2的内容</button>
        </div>
    </template>
    <template id="comp2">
        <div>
            {{name}}
        </div>
    </template>
</body>
<script>
    // 1 创建事件总线对象(vue对象)
    var bus = new Vue()

    const childComponent1 = {
        template: '#comp1',
        methods: {
            updateChild2: function(){
                // 2 使用事件总线触发自定义事件
                bus.$emit('comp1-event', 'update name from comp1 by bus!')
            }
        },
    }
    const childComponent2 = {
        template: '#comp2',
        data(){
            return {name: 'child2'}
        },
        created: function(){
            // 3 监听事件总线的事件
            _this = this
            bus.$on('comp1-event', function(parms){
                console.log('处理comp1-event事件')
                console.log(parms)
                _this.name = parms
            })
        }
    }
    var vm = new Vue({
        el: '#app',
        data: {
            isColor: true
        },
        components: {childComponent1, childComponent2}
    })
</script>

注意:监听函数中this表示的是bus对象

4 组件插槽(slot)

4.1 插槽的基本使用

在定义组件时,可以在组件内部使用 <slot> 标签定义插槽 ➢父元素中可以通过插槽向子组件传递HTML内容

<body>
    <div id="app">
        
        <demo-component>        <!-- 2 使用组件时向solt传递内容 -->
            <h3>this is parent content by slot</h3>
        </demo-component>
        <demo-component>        <!-- 2 使用组件时向solt传递内容 -->
            <p>this is parent content by slot</p>
        </demo-component>
    </div>
    <template id="comp">
        <div>
            <p>this is a component!</p>
            <slot></slot>           <!-- 1 在组件中定义solt -->       
            <p>other content!</p>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp'
            }
        }
    })
</script>

4.1.1 为solt提供默认值

在定义组件时,可以在组件内部使用 <slot> 标签定义插槽, 子组件通过插槽对外提供默认值

<body>
    <div id="app">
        
        <demo-component></demo-component>            

    </div>
    <template id="comp">
        <div>
            <p>this is a component!</p>
            <slot>defult content of solt!</slot>        <!-- 定义solt时提供默认内容 -->      
            <p>other content!</p>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp'
            }
        }
    })
</script>

4.1.2 定义带有名字的solt

在定义组件时,可以在组件内部使用 <slot> 标签定义插槽

➢定义插槽的时候,可以为插槽指定 name 属性,称之为命名插槽

➢父元素通过 slot="" 属性,指明要使用的是哪一个插槽

<body>
    <div id="app">
        
        <demo-component>
            <div slot="header">为名为header的插槽提供内容</div>
            <div>
                为没有名字的插槽提供内容
            </div>
        </demo-component>            
    </div>
    <template id="comp">
        <div>
            <slot name="header">defult header content of solt!</slot>           
            <p> content!</p>
            <slot name="footer">defult footer content of solt!</slot>
            <slot>默认solt</slot>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp'
            }
        }
    })
</script>

4.1.3在slot中可以使用当前组件的props和data

在定义组件时,可以在组件内部使用 <slot> 标签定义插槽

组件插槽内部可以使用当前组件的props和data数据,不能使用父元素数据

<body>
    <div id="app">
        
        <demo-component>
            <div></div>
        </demo-component>            
    </div>
    <template id="comp">
        <div>
            <slot>默认solt, 在slot中访问当前组件的data数据 {{name}}</slot>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp',
                data(){
                    return {name: 'admin'}
                }
            }
        }
    })
</script>

父元素中可以使用父元素的data数据,但是不能使用组件的数据

<body>
    <div id="app">
        <demo-component></demo-component>            
    </div>
    <template id="comp">
        <div>
            <slot>默认solt, 在slot中访问data数据 {{name}}</slot>
        </div>
    </template>
</body>
<script>
    
    var vm = new Vue({
        el: '#app',
        components: {
            'demo-component': {
                template: '#comp',
                data(){
                    return {name: 'admin'}
                }
            }
        }
    })
</script>

 

4.2 作用域插槽

4.2.1 作用域插槽的用途

<body>
    <div id="app">
        <demo-component1 :books="books"></demo-component1>  
    </div>
    <template id="comp1">
        <ul>
            <li v-for="(item, index) in books">
                {{item.name}}
            </li>
        </ul>
    </template>

</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                props: ['books']
        }
                
    var vm = new Vue({
        el: '#app',
        data: {
           books:[
               {name: 'Java讲义',  status: '0'},
               {name: 'Oracle实战经典', status: '1'},
               {name: 'Java实战经典',  status: '1'},
               {name: 'Java编程思想',  status: '0'}
           ]
        },
        components: {
            DemoComponent1
        },
    })
</script>

 

这时要求在前面要加一个对号:怎么办?

作用域插槽:

子组件向父元素通过 props 传递数据

➢使用场景:父元素要自定义渲染效果,需要使用到子组件中的数据

➢使用流程:

           ✓在子组件内,<slot>元素之上通过 v-bind: 创建作用域插槽

           ✓父元素中,通过 slot-scope 属性,引用子组件传递的prop数据

             • 注意:使用 es2015提供的 解构语法

<body>
    <div id="app">
        <demo-component1 :books="books">
            <!-- 接收组件传递过来的作用域插槽 -->
            <template slot-scope="{todo}">
                <span v-if="todo.status == 1 " >*</span>
                {{todo.name}}
            </template>
        </demo-component1>  
        <br/>
        <demo-component1 :books="books">
        <!-- 2 接收组件传递过来的作用域插槽 -->
        <template slot-scope="{todo}">
        </template>
        </demo-component1> 
    </div>
    <template id="comp1">
        <ul>
            <li v-for="(item, index) in books">
                <!-- 1 传递给父元素的props是todo -->
                <slot v-bind:todo="item">{{item.name}}</slot>
            </li>
        </ul>
    </template>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                props: ['books']
        }
                

    var vm = new Vue({
        el: '#app',
        data: {
           books:[
               {name: 'Java讲义',  status: '0'},
               {name: 'Oracle实战经典', status: '1'},
               {name: 'Java实战经典',  status: '1'},
               {name: 'Java编程思想',  status: '0'}
           ]
        },
        components: {
            DemoComponent1
        },
    })
</script>

4.3 访问slot

在组件中可以使用$slots来访问插槽

 

 

5. 组件中的v-model(双向绑定)

5.1 v-model基础回顾

Vue框架中表单控件可以使用 v-model 实现数据的双向绑定

➢当表单数据发生改变时,绑定的数据变量也一并发生改变

➢当数据变量发生改变时,表单数据也一并发生改变

v-model工作原理:

<body>
    <div id="app">
        <div>
            <h3>v-model工作原理</h3>
            <input type="text" v-bind:value="name" v-on:input="name = $event.target.value">
            <div>{{name}}</div>
        </div>
    </div>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                props: ['books']
        }
            
    var vm = new Vue({
        el: '#app',
        data: {
            name: 'admin'
        }
       
    })
</script>

 

5.2 在组件中使用v-model

组件中定义v-model:使用 v-bind:value 和 v-on:input 事件 进行处理

➢组件要接收 名为 value 的 prop数据,并使用 v-bind:value 绑定到表 单元素上

➢自定义组件,要手动 emit 父元素的 input事件 ,并传递新的数据值

使用组件时,直接使用 v-model 绑定数据即可

<body>
    <div id="app">
        {{name}}<br/>
        <!-- 在父组件中使用组件的v-model -->
        <demo-component1 v-model="name"></demo-component1>
    </div>
    <template id="comp1">
        <div>
            组件中的输入框:
            <input type="text" v-bind:value="value" v-on:input="handleInput($event.target.value)"/>
        </div>
    </template>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
                props: ['value'],
                methods: {
                    handleInput: function(val){
                        // 触发input事件
                        this.$emit('input', val)
                    }
                },  
        }
            
    var vm = new Vue({
        el: '#app',
        data: {
            name: 'admin'
        },
        components:{DemoComponent1}
    })
</script>

组件中定义v-model:使用 v-bind:value 和 v-on:input 事件 进行处理

 

可以在组件内使用 model 属性 指定其它的 prop 和 事件

注意:某些表单元素中,value属性和input事件可能有其他作用(如单选/复选框)

6 组件的其它高级用法 

6.1 动态组件

可以使用 <component> 标签添加动态组件

➢使用场景:程序中动态切换组件(选项卡、路由)

➢<component>标签可以渲染其它任意组件

➢通过 is="" 属性渲染其它组件

✓一般通过 v-bind:is 绑定,在程序中切换动态组件

✓当 is的属性值数据发生改变时,会自动渲染为其它组件

<body>
    <div id="app">
        <div>
            {{currentComponent}}
            <button @click="currentComponent = 'DemoComponent1'">组件1</button>
            <button @click="currentComponent = 'DemoComponent2'">组件2</button>
        </div>
        <div>
            <component :is="currentComponent"></component>
        </div>
    </div>
    <template id="comp1">
        <div>
            组件1内容
        </div>
    </template>
    <template id="comp2">
        <div>
            组件2内容
        </div>
    </template>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
        }
    const DemoComponent2 =  {
            template: '#comp2',
    }
    var vm = new Vue({
        el: '#app',
        data: {
            // 当前组件
            currentComponent: 'DemoComponent1'
        },
        components:{DemoComponent1, DemoComponent2}
    })
</script>

➢默认情况下,动态组件会重新渲染组件(重新生成组件对象),从而导致程序性能下降,导致原始组件的状态数据丢失

➢可以使用 <keep-alive> 标签包围 <component> 标签,从而避免 反复重复渲染问题,同时会保持原组件状态数据

<body>
    <div id="app">
        <div>
            {{currentComponent}}
            <button @click="currentComponent = 'DemoComponent1'">组件1</button>
            <button @click="currentComponent = 'DemoComponent2'">组件2</button>
        </div>
        <div>
            <keep-alive>
                <component :is="currentComponent"></component>
            </keep-alive>
        </div>
    </div>
    <template id="comp1">
        <div>
            <label>用户名:</label><input type="text" name="username"/>
        </div>
    </template>
    <template id="comp2">
        <div>
            组件2内容
        </div>
    </template>
</body>
<script>
    const DemoComponent1 =  {
                template: '#comp1',
        }
    const DemoComponent2 =  {
            template: '#comp2',
    }
    var vm = new Vue({
        el: '#app',
        data: {
            // 当前组件
            currentComponent: 'DemoComponent1'
        },
        components:{DemoComponent1, DemoComponent2}
    })
</script>

6.2 递归组件

在组件内部可以嵌套调用其他组件,甚至可以递归地调用当前自 身组件

➢递归组件,需要有递归的结束条件,否则会产生无穷递归

➢递归组件,需要在组件定义中,给出 name属性,表示组件的名称(标签名称)

<body>
    <div id="app">
        <menu-component :menus="menusData"></menu-component>
    </div>
    <template id="menu-comp">
        <ul>
            <li v-for="menu in menus">
                {{menu.name}}
                <menu-component v-if="menu.children" :menus="menu.children"></menu-component>
            </li>
        </ul>
    </template>
</body>
<script>
    const MenuComponent = {
        name: 'MenuComponent',   // 递归组件必须提供name属性
        template: '#menu-comp',
        props: ['menus']
    }
    var vm = new Vue({
        el: '#app',
        data: {
            menusData: [
                {id: '10001', name: "自然人信息列表"},
                {id: '10002', name: "企业信息列表", children: [
                    {id: '1000210001', name: "基本信息", children: null},   
                    {id: '1000210002', name: "资质信息", children: null},
                    {id: '1000210003', name: "业绩信息", children: null},
                    {id: '1000210004', name: "执业人员", children: [
                        {id: '100021000410001', name: "基本信息", children: null},  
                        {id: '100021000410001', name: "资格信息", children: null},  
                        {id: '100021000410001', name: "业绩信息", children: null}   
                    ]},
                ]},
                {id: '10001', name: "其它", children: []},

            ]
        },
        components:{MenuComponent}
    })
</script>

注意:递归组件必须提供name属性

6.3 内联模板

在使用组件时,在组件标签内使用inline-template属性,组件就会把它的内容当作模板,而不是把它当用内容分发

<body>
    <div id="app">
        <child-component :message="message" inline-template>
            <div>
                <p>{{msg}}</p>
                <p>{{message}}</p>
            </div>
        </child-component>

    </div>

</body>
<script>
    Vue.component('child-component', {
        data: function(){
            return {
                msg: '在子组件声明的数据'
            }
        },
        props:['message']
    })
    new Vue({
        el: '#app',
        data: {
            message: '在父组件中声明的数据'
        },

    })
</script>

6.4 异步组件

当项目很大,组件非常多时,一开始把所有组件加载是没必要的,这时Vue允许将组件定义为一个工厂函数,动态解析组件

<body>
    <div id="app">
        <child-component :message="message"></child-component>
    </div>
</body>
<script>
    // 1. 使用Vue.extend()方法定义一个组件类
    let DemoComponent = Vue.extend({
        template: '<div>hello {{name}}</div>',
        data(){
            return {
                name: 'world'
            }
        }
    })
    // 2. 创建组件对象,手动挂载到dom对象上
    // 方式一:
    //new DemoComponent().$mount('#app'), 或
  
    // 方式二:
    /*
    new DemoComponent({
        el:'#app'
    })
    */
    // 方式三:
    let demoComponent = new DemoComponent().$mount()
    document.getElementById('app').appendChild(demoComponent.$el)

</script>

6.5 $nextTick

<body>
    <div id="app">
       <div id="title" v-if="isShow">这里是标题</div>
       <button @click="getDom">获取dom对象</button>
    </div>
</body>
<script>
   new Vue({
    el: '#app',
    data:{
        isShow: false
    },
    methods: {
        getDom(){
            alert(1)
            this.isShow = true
            // 获取dom对象
            let titleHtml = document.getElementById('title').innerHTML
            console.log(titleHtml)
        }
    },
   })

</script>

 

这时,在获取dom时报错,原因:

Vue在观察到data变化时并不是直接更新DOM,而是开启一个队列,缓冲同一事件循环中的所有数据改变,在缓冲时会去除重复数据, 避免不必要的计算和DOM操作,然后在$nextTick中Vue刷新队列并执行实际工作

 

<body>
    <div id="app">
       <div id="title" v-if="isShow">这里是标题</div>
       <button @click="getDom">获取dom对象</button>
    </div>
</body>
<script>
   new Vue({
    el: '#app',
    data:{
        isShow: false
    },
    methods: {
        getDom(){
            alert(1)
            this.isShow = true
            this.$nextTick(function(){
                // 获取dom对象
                let titleHtml = document.getElementById('title').innerHTML
                console.log(titleHtml)
            })
        }
    },
   })

</script>

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值