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>
    

      

     

      

  • 相关阅读:
    day01--计算机硬件基础笔记
    22 Jun 18 Django,ORM
    21 Jun 18 Django,ORM
    20 Jun 18 复习, mysql
    20 Jun 18 Django,ORM
    19 Jun 18 复习, 正则表达式
    19 Jun 18 Django
    15 Jun 18 复习, shutil模块
    15 Jun 18 Django
    14 Jun 18 复习, form表单
  • 原文地址:https://www.cnblogs.com/wjw1014/p/10271242.html
Copyright © 2011-2022 走看看