zoukankan      html  css  js  c++  java
  • 深入理解 Vue 组件

    深入理解 Vue 组件


     组件使用中的细节点

    使用 is 属性,解决组件使用中的bug问题 

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 
     4 <head>
     5     <meta charset="UTF-8">
     6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     8     <title>组件使用中的细节点</title>
     9     <script src="./vue.js"></script>
    10 </head>
    11 
    12 <body>
    13     <div id="root">
    14         <table>
    15             <tbody>
    16                 <!-- H5编码规范要求,tbody内必须是tr,因此row组件不能用,会产生bug,
    17                 因此 is 关键字起到了很好的作用,将此时的 tr 标签等于我们创建的 row 子组件。
    18                 完美解决了既要使用组件永不会影响H5编码规范的问题 
    19                 不仅仅是table标签,ul  ol  select 标签都有相同的问题。-->
    20                 <tr is="row"></tr>
    21                 <tr is="row"></tr>
    22                 <tr is="row"></tr>
    23             </tbody>
    24         </table>
    25     </div>
    26 
    27     <script>
    28         // 创建全局子组件
    29         Vue.component('row',{
    30             template:"<tr><td>this is a row</td></tr>"
    31         })
    32 
    33         var vm = new Vue({
    34             el:"#root",
    35 
    36         })
    37     </script>
    38 </body>
    39 
    40 </html>

      

    子组件定义data数据,data必须是个函数 

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 
     4 <head>
     5     <meta charset="UTF-8">
     6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     8     <title>Document</title>
     9     <script src="./vue.js"></script>
    10 </head>
    11 
    12 <body>
    13     <div id="root">
    14         <table>
    15             <tbody>
    16                 <tr is="row"></tr>
    17                 <tr is="row"></tr>
    18                 <tr is="row"></tr>
    19             </tbody>
    20         </table>
    21     </div>
    22     <script>
    23         // 子组件
    24         Vue.component("row", {
    25             // 子组件定义数据data的方法必须是一个函数返回,不能像根对象一样
    26             data: function () {
    27                 return {
    28                     content: 'this is a row'
    29                 }
    30             },
    31             template: '<tr><td>{{content}}</td></tr>'
    32         })
    33 
    34         var vm = new Vue({
    35             el: "#root",
    36         })
    37     </script>
    38 </body>
    39 
    40 </html>

       

     Vue中的 ref 引用的内容

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>ref</title>
        <script src="./vue.js"></script>
    </head>
    
    <body>
        <div id="root">
            <!-- 在vue当中,可以通过ref获取dom节点 -->
            <div ref='hello' @click="handleClick">hello world</div>
        </div>
        <script>
    
            var vm = new Vue({
                el: "#root",
                methods: {
                    handleClick: function () {
                        // 获取dom中的内容
                        // this.$refs.hello 获取ref=hello的dom节点
                        alert(this.$refs.hello.innerHTML)
                    }
                }
            })
        </script>
    </body>
    
    </html>

       

    vue实现计数器功能

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>计数器功能</title>
        <script src="./vue.js"></script>
    </head>
    
    <body>
        <div id="root">
            <counter ref='one' @change="handleChange"></counter>
            <counter ref='two' @change="handleChange"></counter>
            <!-- 求和 -->
            <div>{{total}}</div>
        </div>
        <script>
        // 子组件
            Vue.component('counter', {
                template: '<div @click="handleClick">{{number}}</div>',
                data: function () {
                    return {
                        number: 0
                    }
                },
                methods: {
                    handleClick: function () {
                        this.number++
                        // 向外发送change事件
                        this.$emit('change')
                    }
                }
            })
    
            var vm = new Vue({
                el: "#root",
                data: {
                    total: 0
                },
                methods: {
                    handleChange: function () {
                        this.total = this.$refs.one.number + this.$refs.two.number
                        // console.log(this.$refs.one.number)
                        // console.log(this.$refs.two.number)
                    }
                }
            })
        </script>
    </body>
    
    </html>

      

     父子组件间传值

     父组件向子组件传递数据

    1. 父组件通过属性的形式向子组件传递数据。
    2. 父组件可以随意的向子组件传递参数。
    3. 但是子组件绝对不能去修改父组件传进来的参数(单向数据流)。
     1 <!DOCTYPE html>
     2 <html lang="en">
     3 
     4 <head>
     5     <meta charset="UTF-8">
     6     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
     8     <title>父子间组件传值</title>
     9     <script src="./vue.js"></script>
    10 </head>
    11 
    12 <body>
    13     <div id="root">
    14         <!-- 父组件都是通过属性的形式向子组件传递数据 -->
    15         <counter :count="1"></counter>
    16         <counter :count="2"></counter>
    17     </div>
    18 
    19     <script>
    20 
    21         // 局部组件
    22         var counter = {
    23             // props 表示子组件接受父组件的内容
    24             props: ['count'],
    25             data: function () {
    26                 return {
    27                     // 子组件自己的data number值
    28                     number:this.count
    29                 }
    30             },
    31             template: "<div @click='handleClick'>{{number}}</div>",
    32             methods: {
    33                 // 点击累加方法
    34                 handleClick: function () {
    35                     // 父组件可以随意的向子组件传递参数
    36                     // 但是子组件绝对不能去修改父组件传进来的参数 单向数据流
    37                     // 因此修改自己的Number值
    38                     this.number++
    39                 },
    40             }
    41         }
    42 
    43         var vm = new Vue({
    44             el: "#root",
    45             // 注册局部组件.
    46             components: {
    47                 counter: counter,
    48             }
    49         })
    50     </script>
    51 
    52 </body>
    53 
    54 </html>

      

    子组件向父组件传值

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>父子间组件传值</title>
        <script src="./vue.js"></script>
    </head>
    
    <body>
        <div id="root">
            <!-- 父组件都是通过属性的形式向子组件传递数据 -->
            <counter :count="3" @change="handleChange"></counter>
            <counter :count="2" @change="handleChange"></counter>
            <div>{{total}}</div>
        </div>
    
        <script>
    
            // 局部组件
            var counter = {
                // props 表示子组件接受父组件的内容
                props: ['count'],
                data: function () {
                    return {
                        // 子组件自己的data number值
                        number:this.count
                    }
                },
                template: "<div @click='handleClick'>{{number}}</div>",
                methods: {
                    // 点击累加方法
                    handleClick: function () {
                        // 父组件可以随意的向子组件传递参数
                        // 但是子组件绝对不能去修改父组件传进来的参数 单向数据流
                        // 因此修改自己的Number值
                        this.number++
                        // 向外触发事件,后可以跟参数
                        this.$emit('change',1)
                    },
                }
            }
    
            var vm = new Vue({
                el: "#root",
                data:{
                    total:5,
                },
                // 注册局部组件.
                components: {
                    counter: counter,
                },
                methods:{
                    handleChange:function(step){
                        // step = 1  步长为2
                        // 求和等于默认值+点击一下的步长
                        this.total  += step
                    }
                }
            })
        </script>
    
    </body>
    
    </html>

       

    组件参数校验与非props特性

    组件参数校验   

      组件参数校验是指:父组件向子组件传递参数的时候,子组件有权向父组件提出参数的形式和要求,并检验父组件传进的参数是否合乎要求。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>组件参数校验与非props特性</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <!-- <child :content="123"></child> -->
            <child content="123"></child>
        </div>
    
        <script>
    
            Vue.component('child',{
                props:{
                    // content:String,  // 子组件接收到的content数据,必须是一个字符串类型
                    // content:[Number,String]  // 子组件接收到的content数据,要么是字符串,要么是数字
                    content:{ // 接收content
                        type:String, //类型type必须是string
                        // required:true, // 表示content必需传
                        // default:'default value', // 如果没有传进来,默认显示这个
                        validator:function(value){ // 校验器校验传入的内容长度必须大于5
                            return (value.length>5)
                        },
                    }
                },
                template:'<div>{{content}}</div>',
            })
    
            var vm = new Vue({
                el:"#root",
                
            })
        </script>
    
    </body>
    </html>

      

    非 Props 特性

    Props 特性是指:当你的父组件使用子组件的时候通过属性向子组件传值的时候,恰好子组件里面声明了对父组件传递过来的属性的接收。

       

    非Props 特性是指:父组件向子组件传递了一个属性,但是子组件并没有props接收的内容,也就是说,子组件并没有声明要接受父组件传递进来的属性。

      

    非Props 特性特点一:如果子组件没人接收父组件传进的属性,则子组件不能使用父组件传进的值。

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>非 Props 特性</title>
        <script src="./vue.js"></script>
    </head>
    
    <body>
    
        <div id="root">
            <!-- <child :content="123"></child> -->
            <child content="hell"></child>
        </div>
    
        <script>
    
            Vue.component('child', {
                // props: {
                //     content: { // 接收content
                //         type: String, //类型type必须是string
                //     }
                // },
    
                // content 找不到,就会报错
                template: '<div>{{content}}</div>',
            })
    
            var vm = new Vue({
                el: "#root",
    
            })
        </script>
    </body>
    
    </html>

      

    非Props 特性特点二:DOM中会保留父组件传递给子组件的属性标识

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>非 Props 特性</title>
        <script src="./vue.js"></script>
    </head>
    
    <body>
    
        <div id="root">
            <!-- <child :content="123"></child> -->
            <child content="hell"></child>
        </div>
    
        <script>
    
            Vue.component('child', {
                // props: {
                //     content: { // 接收content
                //         type: String, //类型type必须是string
                //     }
                // },
    
                template: '<div>hello</div>',
    
                // content 找不到,就会报错
                // template: '<div>{{content}}</div>',
            })
    
            var vm = new Vue({
                el: "#root",
    
            })
        </script>
    </body>
    
    </html>

      

    给组件绑定原生事件

    很简单,在绑定事件的click后面加一个修饰符就行。

    修饰符为 .native

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>给组件绑定原生事件</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        
        <div id="root">
            <!-- 原生点击事件 -->
            <child @click.native="handleClick"></child>
        </div>
    
        <script>
    
            Vue.component('child',{
                template:'<div @click="handleChildClick">Child</div>', 
            })
    
            var vm = new Vue({
                el:"#root",
                methods:{
                    handleClick:function(){
                        alert('click')
                    }
                }
            })
        </script>
    
    </body>
    </html>    
    

      

    非父子组件间的传值

    情景分析

    我们可以把一个网页拆分成很多个部分,每个部分就是我们代码中是我一个组件,如下面的一张图:

      

    如果 1  2 层需要进行传值,则为父子组件之间的传值,通信方式在之前的内容讲到过。

        

    如果 1  3 层进行传值,则为非父子组件间的传值,应该怎么办呢?

      

    第一中方式:和父子组件间传值一样,一层一层的传递,第一层传给第二层,第二层在传给第三层,反之亦然。但是这种传值方式显然不方便太繁琐。

    加入 3  3 层进行的非父子组件传值,又会是怎样的处理方法呢?

      

    这种情况显然更加不适合层层传值,即第三层传给第二层,第二层传给第一层,第一层传给第二层,第二层传给第三层,累死了!代码变得非常的复杂。

    非父子组件传值解决方法

    第一种方法,我们可以使用 VUE 官方提供的一个数据层的框架,名字叫做 VUEX 来解决,但是使用有难度。

    第二种方法,使用 发布订阅模式 来解决非父子组件的传值问题,在vue中叫做 总线机制

    使用总线机制解决非父子组件传值问题

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>非父子组件间的传值(Bus|总线|发布订阅模式|观察者模式)</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <child content="Jayvee"></child>
            <child content="Wong"></child>
        </div>
    
        <script>
    
            Vue.prototype.bus = new Vue()
    
            Vue.component('child',{
                data:function(){
                    return{
                        selfContent:this.content
                    }
                },
                template:'<div @click="handleClick">{{selfContent}}</div>',
                props:{
                    content:String,
                },
                methods:{
                    handleClick:function(){
                        this.bus.$emit('change',this.selfContent)
                    }
                },
                mounted:function(){
                    var this_ = this
                    this.bus.$on('change',function(msg){
                        this_.selfContent = msg
                    })
                }
            })
    
            var vm =new Vue({
                el:"#root",
            })
        </script>
    
    </body>
    </html>
    

      

     VUE 中的插槽 - slot

     父组件通过传值的方式向子组件添加标签

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>vue中的插槽(slot)</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <child content="<p>wjw</p>"></child>
        </div>
    
        <script>
        
            Vue.component('child',{
                props:['content'],
                template:'<div><p>hello</p><div v-html="this.content"></div></div>'
            })
    
            var vm = new Vue({
                el:"#root",
            })
    
        </script>
    
    </body>
    </html>

      

    使用插槽

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>vue中的插槽(slot)</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <child>
                <p>wjw</p>
            </child>
        </div>
    
        <script>
        
            Vue.component('child',{
                template:'<div><p>hello</p><slot>默认内容</slot></div>'
            })
    
            var vm = new Vue({
                el:"#root",
            })
    
        </script>
    
    </body>
    </html>
    

      

     传入header和footer

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>vue中的插槽(slot)</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <body-content>
                <div slot='header' class="header">header</div>
                <div slot='footer' class="footer">footer</div>
            </body-content>
        </div>
    
        <script>
        
            Vue.component('body-content',{
                template:`<div>
                    <slot name='header'></slot>
                    <div class="content">content</div>
                    <slot name='footer'></slot>
                    </div>`
            })
    
            var vm = new Vue({
                el:"#root",
            })
    
        </script>
    
    </body>
    </html>
    

      

     Vue中的作用域插槽

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>vue中的作用域插槽(slot)</title>
        <script src="./vue.js"></script>
    </head>
    
    <body>
        <div id="root">
            <child>
                <template slot-scope="props">
                    <li>{{props.item}} -- hello</li>
                </template>
            </child>
        </div>
    
        <script>
            Vue.component('child', {
                data: function () {
                    return {
                        list: [1, 2, 3, 4]
                    }
                },
                template: `<div>
                    <ul>
                        <slot v-for="item of list" :item=item></slot>
                    </ul>
                    </div>`
            })
    
            var vm = new Vue({
                el: "#root",
            })
        </script>
    
    </body>
    
    </html>
    

      

    Vue的动态组件与 v-once 指令

     点击按钮实现两个组件显隐切换

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>VUE的动态组件与v-once指令</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <child-one v-if="type === 'child-one'"></child-one>
            <child-two v-if="type === 'child-two'"></child-two>
            <button @click="handleBtnClick">change</button>
        </div>
    
        <script>
    
            Vue.component('child-one',{
                template:"<div>child-one</div>"
            })
    
            Vue.component('child-two',{
                template:"<div>child-two</div>"
            })
    
            var vm = new Vue({
                el:'#root',
                data:{
                    type:'child-one'
                },
                methods:{
                    handleBtnClick:function(){
                        this.type = this.type === 'child-one'?'child-two':'child-one'
                    },
                }
            })
        </script>
    
    </body>
    </html>
    

        

    动态组件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>VUE的动态组件与v-once指令</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <!-- component是vue自带的,表示动态组件 -->
            <component :is="type"></component>
            <!-- <child-one v-if="type === 'child-one'"></child-one>
            <child-two v-if="type === 'child-two'"></child-two> -->
            <button @click="handleBtnClick">change</button>
        </div>
    
        <script>
    
            Vue.component('child-one',{
                template:"<div>child-one</div>"
            })
    
            Vue.component('child-two',{
                template:"<div>child-two</div>"
            })
    
            var vm = new Vue({
                el:'#root',
                data:{
                    type:'child-one'
                },
                methods:{
                    handleBtnClick:function(){
                        this.type = this.type === 'child-one'?'child-two':'child-one'
                    },
                }
            })
        </script>
    
    </body>
    </html>
    

     

    V-once 节约性能,提高静态文件的展示效率

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>VUE的动态组件与v-once指令</title>
        <script src="./vue.js"></script>
    </head>
    <body>
        <div id="root">
            <!-- component是vue自带的,表示动态组件 -->
            <!-- <component :is="type"></component> -->
            <child-one v-if="type === 'child-one'"></child-one>
            <child-two v-if="type === 'child-two'"></child-two>
            <button @click="handleBtnClick">change</button>
        </div>
    
        <script>
    
            Vue.component('child-one',{
                template:"<div v-once>child-one</div>"
            })
    
            Vue.component('child-two',{
                template:"<div v-once>child-two</div>"
            })
    
            var vm = new Vue({
                el:'#root',
                data:{
                    type:'child-one'
                },
                methods:{
                    handleBtnClick:function(){
                        this.type = this.type === 'child-one'?'child-two':'child-one'
                    },
                }
            })
        </script>
    
    </body>
    </html>
    

      

     

      

  • 相关阅读:
    C++中unique函数的用法总结
    洛谷P1039侦探推理题解
    洛谷P1040 加分二叉树题解
    洛谷P1038 神经网络题解
    emmm
    biiset用法
    浅谈接口与抽象类的区别
    递归调用——数学观点看递归
    反转字符串
    SQL 时间戳转DateTime类型
  • 原文地址:https://www.cnblogs.com/wjw1014/p/10271242.html
Copyright © 2011-2022 走看看