Bootstrap

vue的组件/data的参数/组件传值/插槽/侦听器/生命周期钩子函数

组件结构

  • 组件结构:
 Vue.component('自定义组件名', {
        template:'自定义模板',
        data(){
            return {
                key:value,
                ...
            }
        }
    })
  • 组件中的相关属性:

    是可复用的vue实例,vue实例对象拥有的属性,比如data, methods, computed, filter , 生命周期等 组件都有;
    除了挂载点el 是vue实例对象特有的属性除外

    组件中的data 必须是一个函数, 保持数据的独立性(每个组件都有自己的数据,互相不影响)

    vue-强制刷新组件-改变组件的key

vue强制刷新组件之$forceupdate、v-if、key属性三种方式的适用

组件的命名规则:

  1. 驼峰命名: 需要转换为连字符小写形式才能生效 这个转换只限于vue的实例化中使用,后期在单文件组件(index.vue)中或者组件中使用其它的组件不受限制
    2. 连字符

组件的模板推荐使用 es6的字符串模板

!!!组件必须只有一个根元素!!!

<body>
    <div id="app">
        <ul-list></ul-list>
        <!-- <divnode></divnode>名字错误 -->
        <!-- <d-ivnode></d-ivnode>名字错误 -->
        <div-node></div-node>
        <zhao></zhao>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    Vue.component('ul-list', {
        template: '<h1>hello  组件字符串</h1>'
    })
    Vue.component('DivNode', {
        template: '<h1>hello  组件命名驼峰</h1>'
    })
    Vue.component('zhao', {
        template: `<ul>
            <li>
                <p>这是标签</p>
                <p>100块</p>
            </li>
        </ul>`
    })
    const app = new Vue({
        el: "#app"
    })
</script>

在这里插入图片描述

组件的嵌套

            template: `<div>
            <p>这是一个房间</p>
            <bed></bed>
            <cat></cat>
        </div>`//正确示例
		
		<bed>
    <cat></cat>
    </bed>  //错误示例
<body>
    <div id="app">
        <room></room>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    //定义组件模板
    const Room = {
        template: `<div>
            <p>这是一个房间</p>
            <bed></bed>
            <cat></cat>
        </div>`
    }
    const Bed = {
        template: `<div>
            <p>这是一张床</p>
            <cat></cat>
        </div>`
    }
    const Cat = {
            template: `<div>
            <p>这是一只猫</p>
        </div>`
        }
        //注册组件(全局注册)
    Vue.component("Room", Room)
    Vue.component("Bed", Bed)
    Vue.component("Cat", Cat)
    const app = new Vue({
        el: "#app"
    })
</script>

在这里插入图片描述
组件的 /局部注册和/全局注册

<body>
    <div id="app">
        <p>{{msg}}</p>
        <order></order>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const order = {
            template: `<div class="order">
            <p>order组件-订单</p>
        </div>`
        }
        // Vue.component('order', order)//全局注册
    const app = new Vue({
        el: '#app',
        data: {
            msg: 'hello vue'
        },
        components: { //局部注册
            order
        }

    })
</script>

在这里插入图片描述

组件的data参数

面试回答: vue中的data为什么是一个函数?

  • 如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data(类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据)
  • Object是引用数据类型,里面保存的是内存地址,单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

JavaScript只有函数构成作用域(注意理解作用域,只有函数{}构成作用域,对象的{}以及if(){}都不构成作用域)

一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
保证deta数据的复用数据相互不受影响

<body>
    <div id="app">
        <zhao></zhao>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    // Vue.component('zhao', {
    //     template: `<ul><li v-for='(item,index) in movie' :key='index'>
    //         <p>{{item.name}}</p>
    //             <span>{{item.time}}</span>
    //             <button @click='remove(index)'>删除</button>
    //         </li></ul>`,
    //     data() {
    //         return {
    //             movie: [{
    //                 name: '刺杀小说家',
    //                 time: '1小时20分钟'
    //             }, {
    //                 name: '司藤',
    //                 time: '45分钟'
    //             }, {
    //                 name: '海贼王',
    //                 time: '20分钟'
    //             }, {
    //                 name: '吐槽大会',
    //                 time: '2小时20分钟'
    //             }]
    //         }
    //     },
    //     methods: {
    //         remove(index) {
    //             this.movie.splice(index, 1)
    //         }
    //     }
    // })
    // 把组件的模板提取出来>
    const list = {
        template: `<ul><li v-for='(item,index) in movie' :key='index'>
            <p>{{item.name}}</p>
                <span>{{item.time}}</span>
                <button @click='remove(index)'>删除</button>
            </li></ul>`,
        data() {
            return {
                movie: [{
                    name: '刺杀小说家',
                    time: '1小时20分钟'
                }, {
                    name: '司藤',
                    time: '45分钟'
                }, {
                    name: '海贼王',
                    time: '20分钟'
                }, {
                    name: '吐槽大会',
                    time: '2小时20分钟'
                }]
            }
        },
        methods: {
            remove(index) {
                this.movie.splice(index, 1)
            }
        }
    }
    Vue.component('zhao', list)
    const app = new Vue({
        el: "#app"
    })
</script>
</html>

在这里插入图片描述

组件的父子传值prop(通信)

vue组件各种传值讲解链接
传值git实例讲解链接

步骤:
通过 prop 接受父组件向子组件传递数据

  1. 需要在组件标签上自定义一个属性

        <组件标签名 自定义属性名='value'></组件标签名>
           <cat col='白色的猫'></cat>
    若 1-(1)传递动态的值: 父子通信 ; 
        在 子组件标签上通过 v-bind 绑定自定义属性
          <cat v-bind:自定义属性名='父组件中data中的key'></cat>
    
  2. 在组件模板中通过 props选项接受值

       const Cat = {
            template: `<div>
                <strong>这是一只 {{自定义属性名}}</strong>
            </div>`,
            props:['自定义属性名'] // props:['col']
            
        }
    
<body>
    <div id="app">
        <!-- 静态的值  默认传递的是字符串类型-->
       <!-- 如果需要传递数值类型,数组,对象, 布尔值等需要通过 v-bind 进行绑定,告诉vue这是一个js的表达式而不是字符串-->
        <lops col='白色的'></lops>
        <!-- 传递动态的的值  父子通信-->
        <cat :msg='msg'></cat>
        <!-- 本质是这样 :msg='a'  下面父组件中data也可为a-->
    </div>
    <script src="./js/vue.js"></script>
    <script>
        const lops = { //从标签获取静态的值
            template: `<div>
        <p> 一只 {{col}}  猫  </p>
        </div>`,
            props: ['col']
        }

        //父子通信
        const Cat = {
                template: `<div>
        <h1> 一条 {{msg}}  狗 </h1>
        </div>`,
                props: ['msg']
            }
            //组件的注册
        Vue.component('lops', lops);
        Vue.component('Cat', Cat);
        const app = new Vue({
            el: '#app',
            data: {
                msg: '蓝白色'
            }
        })
    </script>
</body>

在这里插入图片描述

组件的子–>父通信(重要)

$emit和 $on
实现子父通信,要先知道一个知识
$emit(‘自定义事件名’, 传递的数据) 触发一个定义事件

$on(‘监听自定义事件’, callback) 监听自定义事件 第二个参数是一个callback (回调函数)
$on(‘监听自定义事件’, (data) => { data 就是接受的数据 })

     前提条件: $emit() 和 $on() 必须是同一个对象的

子父通信的实现步骤

  • 在子组件的模板中定义原生事件, 触发原生事件 提交一个自定义事件

(1) 在子组件的模板中定义原生事件

template: `<div>
                <button @click='sendMsg'>传数据</button>
                </div>`,

(2) 触发原生事件 提交一个自定义事件

 methods: {
            sendMsg() {
                this.$emit('sendtoparent', "传递的数据")
                // this.$emit('自定义的事件名',传递的数据)
            }
        }
  • 在子组件标签上监听自定义事件, 通过 v-on:自定义事件名=‘父组件methods中的方法名’,触发父组件中methods中的方法
 <cat v-on:sendtoparent='getMsg'></cat>
  • 在父组件的 methods 中定义方法
 methods: {
      自定义方法名(与监听事件绑定的方法名一致)
            getMsg(data) {
                console.log('事件成功传递到了父组件',);
                data就是"传递的数据"
            }
        }
     v-on后面的方法名和$emit中的方法名是一致的 
<body>
    <div id="app">
        <cat v-on:sendtoparent='getMsg'></cat>//(3)
        <p>它的习性:{{value}} </p>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const Cat = { //这是子组件的模板
            template: `<div>
                <button @click='sendMsg'>传数据</button>//(1)
                </div>`,
            //组件的data必须是一个函数
            data() {
                return {
                    sleep: "猫喜欢睡觉"
                }
            },
            methods: {
                sendMsg() {//(2)
                    this.$emit('sendtoparent', this.sleep)
                        // this.sleep 就是传递的数据
                }
            }
        }
        //组件的注册
    Vue.component('Cat', Cat)
    const app = new Vue({ //这是父组件
        el: '#app',
        data: {
            value: '' //存放子组件传过来的数据
        },
        methods: {
            getMsg(data) {//(4)
                console.log('事件成功传递到了父组件');
                // console.log(data);//猫喜欢睡觉
                this.value = data;
                //将子组件传过来的数据data放到app的value中
            }
        }
    })
</script>

在这里插入图片描述

子父通信标签上进行事件处理例子
实例:每点击一次字体变大一点

<body>
    <div id="app">
        <p :style="{fontSize:fontSize + 'px'}">hello world</p>
        <!-- <child v-on:changefontsize='fontSize += $event'></child> -->
        <!--第二种方法     直接在组件标签上进行事件处理  通过 $event 事件对象获取数据    -->

        <child v-on:changefontsize='changeFont'></child>
        <!-- 第一种方法   在组件标签上 通过 v-on监听自定义事件 `v-on:自定义事件名='父组件中的methods中的方法'`; 
        父组件中必须存在对应的方法 -->
    </div>
    <script src="./js/vue.js"></script>
    <script>
        Vue.component('child', {
            template: `
                <div>
                    <button @click='$emit("changefontsize", 1)'>方法字体</button>
                </div>
            `
        })
        const app = new Vue({
            el: '#app',
            data: {
                fontSize: 20,
            },
            methods: {
                changeFont(data) {
                    //console.log(data);//接收到子组件传来的1
                    this.fontSize += data;
                }
            }
        })
    </script>
</body>

.sync子父通信修饰符实现

官网–>深入了解组件–>.sync修饰符

.sync 自定义事件 修饰符 vue2支持
组件通信: 可以简化代码

 通常情况下:
    自定义事件格式:  this.$emit('自定义', '新的数据')
        <child :title='title' v-on:change='change'></child> 
        或者
        <child :title='title' v-on:change='title = $event'></child>

    如果使用 .sync 修饰符,则可以简化
        相当于 把   <child :title='title' v-on:change='change'></child>  整体简化为

          <child :title.sync='title'></child> 

    使用.sync 修饰符时,需要通过固定的形式  `update:绑定的prop属性名`   进行事件的触发,提交的自定义事件的固定格式 

     this.$emit('update:title', '新的数据')
<body>
    <div id="app">
        <!--(1) <child :title='title' v-on:change1='change'></child> -->
         <!--(2) <child :title='title' v-on:change1='title = $event'></child>-->
        <child :title.sync='title'></child>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const child = {
        template: `<div>
            {{title}}
            <button @click='change2'>传递数据</button></div>`,
        props: {
            title: String
        },
        // props: ['title'],
        methods: {
            change2() {
                //(1) this.$emit('change1', '新的数据')
                this.$emit('update:title', '新的数据')
            }
        }
    }

    const app = new Vue({
        el: "#app",
        data: {
            title: '这是给子组件的标题',
        },
        //(1) methods: {
        //     change(val) {
        //         console.log(val);
        //         this.title = val
        //     }
        // },
        components: { //注册组件
            child
        }
    })
</script>

在这里插入图片描述

非父子(子父)通信bus总线

bus总线vuecli使用步骤
解决了父子通信嵌套一层的局限

Bus总线: 本质是一个Vue的实例对象 (纯粹只是一个实例化对象,不需要任何设置)

const Bus = new Vue()

非父子通信:依然通过 事件传递: $emit() 和 $on() ; 必须隶属于同一个对象

<body>
    <div id="app">
        <!-- 非父子通信如下,不用考虑是不是父子 -->
        <ul>
            <li>
                <com1></com1>
            </li>
            <li>
                <com2></com2>
            </li>
        </ul>
        <!-- <com1></com1>
        <com2></com2>  他们两个是同一个父亲     -->
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const Bus = new Vue(); //Bus总线

    Vue.component('com1', { //com1组件
        template: `
        <div>
        <p>  接收com2传递过来的数据 :{{getmsg}}</p>
        <button @click='sendcom2'>c1传递给c2</button>
        </div>`,
        data() {
            return {
                com1msg: 'com1的数据',
                getmsg: ''
            }
        },
      //  $on() 监听通常都是在created()实例创建后 钩子函数中进行
        created() {
            Bus.$on('sendtocom1', (data) => {
                console.log(data);
                this.getmsg = data;
            })
        },
        methods: { //1条线     1给2传递数据
            sendcom2() {
                Bus.$emit('sendtocom2', this.com1msg);
            }
        }
    })

    Vue.component('com2', { //com2组件
        template: `
        <div>
        <p>  接收com1传递过来的数据 {{getmsg}}</p>
        <button @click='sendcom1'>c2传递给c1</button>
        </div>`,
        data() {
            return {
                com2msg: 'com2的数据',
                getmsg: '' //存com1发过来的数据
            }
        },
        created() { //1条线    1接受2传过来的数据
            Bus.$on('sendtocom2', (data) => {
                console.log(data); //data是c1的数据
                this.getmsg = data
            })
        },
        methods: {
            sendcom1() {
                Bus.$emit('sendtocom1', this.com2msg);
            }
        }
    })
    const app = new Vue({
            el: '#app'
        })
        //1条线是com1给com2传递数据(数据是com1的)
        //同等道理     com2给com1传递数据
</script>

在这里插入图片描述
注意:
发送事件时,监听on必须已经存在,不然通信没有内容
弊端:复杂的通信可能会不通

组件之间的通信

Vue2组件通信方式汇总
bus总线传值在复杂情况下容易出问题

组件通信方式(非父子通信:祖孙之间通信):$attrs 和 $listeners 属性实现

 官网解析:
        $attrs:  包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。
   当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),
   并且可以通过 v-bind="$attrs" 传入内部组件

        $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件

        
 如果项目比较复杂(大型项目) 使用 vuex 实现统一的状态管理
  • 祖孙传值$attrs
<body>
    <!-- 祖给孙传值 -->
    <div id="app">
        <p>{{msg}}</p>
        <parent :title='msg'></parent>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        const parent = {
            template: `<div>这是父组件
                <child v-bind='$attrs' ></child> 
                </div>`,
        }
        const child = {
            template: `<div> 这是子组件 
            <span>{{$attrs.title}} </span>
                </div>`,
        }
        Vue.component('parent', parent)
        Vue.component('child', child)
        const app = new Vue({
                el: "#app",
                data: {
                    msg: 'app数据'
                }
            })
            //.$mount('#app')
    </script>
</body>

在这里插入图片描述
在这里插入图片描述

  • 孙祖传值$listeners
    在这里插入图片描述

EventHub–>全局data中的值传递

provide/inject 只能从祖先传递给后代,且慎用

VUE中的provide和inject用法

ref访问子组件实例DOM对象

官网->教程->深入了解组件–>访问元素 & 组件
ref 属性:

1. ref 属性绑定到组件上,则可以通过 vm.$refs 属性获取到组件对象
2. ref 属性绑定到普通的标签上,则可以通过 vm.$refs 属性获取到对应的DOM元素

定义: ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:

用法:

  <input ref='自定义名字'  type="text" placeholder="请输入内容" >
        <child ref='自定义名字'></child>

获取:

this.$refs.自定义名字.相关操作

refs 只会在组件渲染完成之后生效,并且它们不是响应式的。

<body>
    <div id="app">
        <!-- <input ref='ipt' type="text" placeholder="请输入内容">  标签的情况 -->
        <child ref='child'></child>    <!--组件的写法-->
        <button @click='gain'>获取焦点</button>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const child = {
        template: `
        <input ref='ipt' type="text" placeholder="请输入内容">`,
        methods: {
            //方法传递就是将获取dom节点的方法提出来作为一个方法
            getFocus() {
                this.$refs.ipt.focus();
            }
        }
    }
    const app = new Vue({
        el: "#app",
        components: {
            child
        },
        methods: {
            gain() {
                // console.log(this.$refs.ipt);//输入框的dom节点
                //this.$refs.ipt.focus()

                // 链式写法(先找到child组件,然后和上面一样)
                // console.log(this.$refs);
                // this.$refs.child.$refs.ipt.focus()

                //  通过方法传递
                this.$refs.child.getFocus()
            }
        }
    })
</script>

封装组件

prop是父子传值放参数的
组件中的参数为什么这么写,回头看基础指令v-bind绑定类名的方法

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>

<body>
    <button class="btn btn-danger">按钮</button>
    <!-- 这是bootstrap的功能, class有这个属性值就会有样式-->
    <div id="app">
        <!--  绑定类名 -->
        <base-button classname="danger"></base-button>
        <!-- 这里的class中的内容添加属性,不会生效,意味着传参 -->
        <br>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        const BaseButton = {
                template: `
                <button :class='["btn",cname]'>按钮</button>
            `, //这里面的class属性是生效的地方  //btn是固有属性值   cname是动态类型
                props: ['classname'], //prop是接受值这里代表参数danger
                //计算属性
                computed: {
                    //计算属性的函数名可以在组件标签或者是html标签中使用
                    cname() {
                        return "btn-" + this.classname;
                        //return的内容就是属性值btn-danger
                    }
                }
            }
            //  全局注册
            // Vue.component('BaseButton', BaseButton)
        const app = new Vue({
            el: '#app',
            components: {
                // 局部注册
                BaseButton//属性名和属性值一样时,可以只写一个(语法自动补足),ES6的语法糖
            }
        })
    </script>

在这里插入图片描述
禁用属性的添加和上面的不太一样

prop 的值类型绑定 传递静态数据时, 只限制于字符串类型,如果传递的是数组,数值类型,布尔类型,对象等
需要通过v-bind指令进行绑定 :需要告诉vue 这是一个 JavaScript 表达式而不是一个字符串

    <title>禁用</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>

<body>
    <button class="btn btn-warning btn-lg" disabled>官方按钮</button>
    //有disabled就会禁用
    <hr>
    <!-- 这是bootstrap的功能, class有这个属性值就会有样式-->
    <div id="app">
        <!--  绑定类名和尺寸 和禁用属性 -->
        <!-- <base-button classname="danger" size='lg' forbidden='false'></base-button>//不论真假都生效,所以格式错误 -->
        
        <base-button classname="danger" size='md' :forbidden='false'></base-button><br>
        <base-button classname="danger" size='lg' forbidden></base-button>
        <!-- 这里的class中的内容添加属性,不会生效,意味着传参 -->
        <br>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        const BaseButton = {
                template: `
                <button :class='["btn",cname,btnsize]'  :disabled='forbidden'>按钮</button>
            `, //这里面的class属性是生效的地方  //btn是固有属性值   
                props: ['classname', 'size', 'forbidden'], //prop是接受值这里代表参数danger
                //计算属性
                computed: {
                    //计算属性的函数名可以在组件标签或者是html标签中使用
                    cname() {
                        return "btn-" + this.classname;
                        //return的内容就是属性值btn-danger
                    },
                    btnsize() {
                        return 'btn-' + this.size
                    }
                }
            }
            //  全局注册
            // Vue.component('BaseButton', BaseButton)
        const app = new Vue({
            el: '#app',
            components: {
                // 局部注册
                BaseButton
            }
        })
    </script>

在这里插入图片描述

组件标签和html标签的区别

  • 组件标签 : 定义的组件

    组件标签上的添加属性一般都意味着进行数据传递, 在组件的内部都需要通过prop进行接受
    组件标签上绑定事件 都是自定义事件(直接用不生效),需要在组件内部通过this.$emit() 进行触发

        !!!直接在组件上绑定原生的事件,而不是自定义事件,此时可以通过  事件修饰符 .native 实现!!!!!
    

    组件标签内添加内容 默认是会被忽略的,不会显示, 需要通过插槽实现

  • html标签 : html

    html标签上的属性就是给标签本身添加属性,添加自定义的或是固有的属性
    html 标签上绑定事件就是原生的事件,在vue实例中methods中调用
    html标签内添加内容就是标签的内容

<body>
    <!-- 这样写组件和html标签的属性和事件才能生效 -->
    <div id="app">
        <em title="html提示信息" @click='send'>这是html标签</em>
        <!-- <child :title="'组件提示信息'" @click='changs'></child> 配合用这个时写到send里面 this.$emit('click')-->
        <child :title="'组件提示信息'" @click.native='changs'></child>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        //局部注册组件
        components: {
            child: {
                //组件模块
                template: `<p :title="title" @click='send'>这是组件标签</p>`,
                props: ['title'],
                methods: {
                    send() {
                        // this.$emit('click')不用这个时  组件标签这样写@click.native='changs'
                    }
                }
            }
        },
        methods: {
            send() {
                console.log("html标签事件");
            },
            changs() {
                console.log("组件的事件");
            }
        }
    })
</script>

全局组件的写法

<body>
    <!-- 这样写组件和html标签的属性和事件才能生效 -->
    <div id="app">
        <em title="html提示信息" @click='send'>这是html标签</em>
        <!-- <child :title="'组件提示信息'" @click='changs'></child> 配合这个用写到send里面 this.$emit('click')-->
        <child :title="'组件提示信息'" @click.native='changs'></child>
    </div>
</body>
<script src="./js/vue.js"></script>
 <script>
    //组件的名称
    const child = {
        template: `<p :title="title" @click='send'>这是组件标签</p>`,
        props: ['title'],
        methods: {
            send() {
                // this.$emit('click')不用这个时  组件标签这样写@click.native='changs'
            }
        }
    }
    //全局注册组件
    Vue.component('child', child);
    const app = new Vue({
        el: '#app',
        methods: {
            send() {
                console.log("html标签事件");
            },
            changs() {
                console.log("组件的事件");
            }
        }
    })
</script> 

插槽

vuecli中使用插槽

vue 中插槽: 本质是实现内容分发功能(就是在组件内部插入内容) 在组件标签中 通过 template标签 插入内容
vue提供了一个 内置组件标签
一个组件内可以有多个插槽,并不冲突

插槽的分类:
1. 默认插槽 :
2. 具名插槽 : 给插槽命名
3. 作用域插槽 : 用来传递数据

/定义插槽/
<template>
  <div>
    <h2>插槽标题</h2>
    <slot> 插槽 </slot>
  </div>
</template>

/使用插槽/
<template>
  <div>
    <p>这是标签本来内容</p>
    <SlotData>组件标签中的内容</SlotData>
  </div>
</template>

<script>
import SlotData from "../components/slotData.vue";
export default {
  components: {
    SlotData,
  },
};
</script>

在这里插入图片描述

具名插槽

指令 v-slot 结合插槽使用 ; 可以绑定具名插槽
v-slot 指令 只能用于 template 内置标签上

  • 1定义默认插槽:

           <slot></slot>
    
  • 2定义具名插件: 需要借助name属性

         <slot name='自定义插槽名'></slot>
    
  • 1使用默认插槽:

         <组件标签名>
             <tempalte v-slot:default>
                 真正插入组件内的内容
             </template>
         </组件标签名>  
    
  • 2使用具名插槽:

         <组件标签名>
             <tempalte v-slot:自定义的插槽名>
                 真正插入组件内的内容
             </template>
         </组件标签名>
    

插槽的渲染结构: 由模板确定, 不是由插入的内容位置决定

    <!-- 具名插槽,模板决定是什么标签,组件标签提供插的位置 -->
    //定义插槽
<template>
  <div>
    <h1>
      <slot name="header"></slot>
    </h1>
    <p>
      <slot></slot>
    </p>
    <h3>
      <slot name="footer"></slot>
    </h3>
  </div>
</template>

//使用插槽
<template>
  <div>
    <p>原本标签</p>
    <SlotData>
      <template v-slot:header>
        <em>这是头部</em>
      </template>
      <template v-slot:footer>
        <p>这是尾部</p>
      </template>
      <template>
        <!-- 默认插槽可以不提供v-slot   默认插槽 : 如果提供那就是 v-slot:default-->
        <p>这是主体</p>
      </template>
    </SlotData>
  </div>
</template>

<script>
import SlotData from "../components/slotData.vue";
export default {
  components: {
    SlotData,
  },
};
</script>

在这里插入图片描述

插槽的默认值

当用插槽时,组件标签中具体的值会覆盖插槽模板的内容
可以给插槽提供默认值: 在slot标签内插入内容,就是插槽的默认值

   <slot>内容</slot> 

如果插槽模板没有提供具体的值,则会显示默认值,如果提供了具体的值, 则会替代默认值

<template>
  <div>
   <slot> <h2>插槽内容</h2> </slot>
  </div>
</template>
//使用插槽
<template>
  <div>
    <p>原本标签</p>
    <SlotData> </SlotData>
  </div>
</template>

<script>
import SlotData from "../components/slotData.vue";
export default {
  components: {
    SlotData,
  },
};
</script>

在这里插入图片描述

作用域插槽

如果只是内容分发,直接使用v-slot:自定义插槽名即可

如果是作用域插槽, 需要使用 v-slot:自定义插槽名=‘自定义名字’

作用域插槽作用: 作用域插槽是一个带绑定数据的插槽,插槽的样式由父组件决定,内容却由子组件控制。

通过 v-slot进行接收,

传递数据流程:

  1. 需要在组件模板中的 slot标签上自定义属性并赋值, 属性值就是传递的数据
  <template>
  <div>
    <slot a="插槽"></slot>
    <slot name="header" a="头部hello 插槽" b="100"></slot>
  </div>
</template>
  1. 接受数据: 在 组件标签内通过 v-slot 进行接受 ; 接受到的数据是一个对象,包含了 slot标签上传递所有的数据
 <template>
  <div>
    <p>原本标签</p>
    <SlotData>  v-slot:具名插槽名='自定义名字'
      <template v-slot:header="obj">
        <p>{{ obj }}</p>
        <p>{{ obj.a }}</p>
        <p>{{ obj.b }}</p>
      </template>
    </SlotData>
  </div>
</template>

<script>
import SlotData from "../components/slotData.vue";
export default {
  components: {
    SlotData,
  },
};
</script>

在这里插入图片描述

v-slot 指令可以进行简写: v-slot: 简写为 #

作用域插槽应用场景: 给开发人员提供的 ; 在实际开发过程中,很多情况下可以避免使用作用域插槽

默认插槽的情况

<template>
  <div>
    <slot a="hello 插槽" b="hello vue"></slot>
  </div>
</template>
//使用插槽
<template>
  <div>
    <p>原本标签</p>
    <SlotData>
      <template v-slot="obj">
        <p>{{ obj }}</p>
        <p>{{ obj.a }}</p>
      </template>
    </SlotData>
  </div>
</template>

在这里插入图片描述
组件中有参数的情况

<template>
  <div>
    <slot a="hello 插槽" :msg="msg"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "插槽的作用域",
    };
  },
};
</script>
//使用插槽
<template>
  <div>
    <p>原本标签</p>
    <SlotData>
      <template v-slot="obj">
        <p>{{ obj }}</p>
        <p>{{ obj.a }}</p>
        <p>{{ obj.msg }}</p>
      </template>
    </SlotData>
  </div>
</template>

在这里插入图片描述
v-slot接收的数据是一个对象,可以进行解构

<template>
  <div>
    <slot name="header" a="hello 插槽" :msg="msg"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      msg: "插槽的作用域",
    };
  },
};
</script>
//使用插槽
<template>
  <div>
    <p>原本标签</p>
    <SlotData>
      <template v-slot:header="{ a, msg }">
        <p>{{ a }}</p>
        <p>{{ msg }}</p>
      </template>
    </SlotData>
  </div>
</template>

在这里插入图片描述

vue 的2.6版本(老版本) 具名插槽和作用域插槽引入了一个新的统一的语法 新的指令 v-slot 代替 slot(具名插槽) 和 slot-scope(作用域插槽)

低版本的 slot 和 slot-scope
可以直接把 slot attribute 用在一个普通元素上,其它写法一样

prop属性拓展

在这之前prop都是以数组形式接受所有的值

<body>
    <div id="app">
        <child :count='count' :msg='msg' title="组件提示信息" :arr='arr'>
            <template>
                <ul>
                    <li v-for='item in arr'>{{item}}</li>
                </ul>
            </template>
            <template v-slot:objs>
                <strong>{{objs}}</strong>
            </template>
        </child>
    </div>

</body>
<script src="./js/vue.js"></script>
<script>
    const child = {
        template: `<div>
        <p >{{count}}-- {{msg}} --{{title}}</P>
            <slot></slot>
            <slot name='objs'></slot>
        </div>`,
        //  通过一个数组的形式接受所有的值,不能准确的区分数据类型
        props: ['count', 'msg', 'title', 'arr', 'objs'] //(2)接收数据
    }
    Vue.component('child', child)
    const app = new Vue({
        el: '#app',
        data: {
            count: 100,
            msg: 'hello vue',
            arr: [2, 34, 56, 7], //(1)数组的传值
            objs: {
                name: "张三",
                age: 18
            }
        }
    })
</script>

在这里插入图片描述

因为通过一个数组的形式接受所有的值,不能准确的区分数据类型,所以传入一个对象
第一种情况:组件标签直接绑定一个对象

<body>
    <div id="app">
        <!-- 直接绑定一个对象 -->
        <child :person='person'>
            {{person.age}}
            <!-- 相当于给插槽传入一个具体的值  -->
        </child>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const child = {
        template: `<div>
        <p >{{person.name}}</P>
            <slot></slot>
        </div>`,
        // 通常你希望每个 prop 都有指定的值类型  以对象的形式进行接受,这些 property 的名称和值分别是 prop 各自的名称和类型:
        props: {
        //绑定一个对象
            person: Object
        }
    }
    Vue.component('child', child)
    const app = new Vue({
        el: '#app',
        data: {
            person: {
                name: '张三',
                age: 18
            }
        }
    })
</script>

在这里插入图片描述
第二种情况:组件标签传入一个对象的所有属性

<body>
    <div id="app">
        <!-- 传入一个对象的所有属性 -->
         <!--  传入一个对象所有的属性 ; 在子组件中通过 props 接受时 你接受的对象的每个属性名,而不是整个对象 -->
        <child v-bind='person'></child>
        <!-- 等价形式 -->
        <!-- <child v-bind:name='person.name'></child>
        <child v-bind:age='person.age'></child> -->
    </div>

</body>
<script src="./js/vue.js"></script>
<script>
    const child = {
        template: `<div>
        <p >{{name}} --{{age}}</P>
        </div>`,
        props: {
        //传入一个对象的所有属性
            name: String,
            age: Number
        }
    }
    Vue.component('child', child)
    const app = new Vue({
        el: '#app',
        data: {
            person: {
                name: '张三',
                age: 18
            }
        }
    })
</script>

在这里插入图片描述
prop指定传递的类型

prop设置默认值

<body>
    <div id="app">
        <!-- <child></child>   //基础数据类型测试没有属性时,因为prop设置了默认值,所以为你好,默认值-->
        <!-- <child :arr='arr'></child>   123-->
        <child></child> // 默认值[100, 200]

    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const child = {
            template: `<div>
        <p>{{arr}} </p>
        </div>`,
            props: {
                // arr: {
                //     type: String,
                //     //直接指定默认值,适用于 字符串,数值等基本数据类型
                //     default: "你好,默认值"
                // }
                arr: {
                    type: Array,
                    default: () => {
                        return [100, 200]
                    }
                }

            }
        }
        //组件的注册
    Vue.component('child', child);
    const app = new Vue({
        el: '#app',
        data: {
            arr: [1, 2, 3]
        }
    })
</script>

prop自定义校验规则 validator

<body>
    <div id="app">
        <child :count='count'></child>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        const child = { //从标签获取静态的值
                template: `<div>
        <p>{{count}} </p>
        </div>`,
                props: {
                    //自定义校验的规则validator
                    count: {
                        type: Number,
                        validator: (value) => {
                            // console.log(value);
                            //  通过return 返回校验结果
                            return value > 100 && value < 200
                                //当data中参数满足这个条件才输出,否则报错(但仍会显示不在这个条件的结果)
                        }
                    }
                }
            }
            //组件的注册
        Vue.component('child', child);
        const app = new Vue({
            el: '#app',
            data: {
                count: 122
            }
        })
    </script>

在这里插入图片描述

帧听器watch

侦听器:
不存在缓存的,
不依赖于data中的数据
watch 可以进行异步操作

 watch: {
        //  接受两个参数:  第一个参数新值(改变后的值), 第二个参数是旧值(改变之前的值),
        msg(newVlue, oldVlue) {
            //  监听到对应的数据发生改变,则做出响应的处理
            this.msg = '你好,侦听器的值'
        }
    }
})
侦听器也是一个函数: 完整的写法
        watch:{
            / 直接指定事件处理程序
            count(newValue, oldValue){
                // 事件处理程序
            },
            / 使用对象的形式,通过 handler 属性指定事件处理程序
            count:{
                // 监听到count 改变直接执行handler
                handler(){
                    //  事件处理程序
                }
            },
            /侦听对象的指定属性
            "count.a"(newvalue, oldvalue){
            //  事件处理程序
            },
            / 加载完毕后立即执行一次 通过 immediate:true 属性实现
            count:{
                handler(newvalue, oldvalue){
                    console.log('hello123123');
                },
                immediate:true
            },
            /  实现深层次的监听对象的所有属性, 不管嵌套的有多深   通过deep:true 属性实现
             person:{
                handler(newvalue, oldvalue){
                    console.log('对象该百年了123123');
                },
                // immediate:true
                deep:true
            }
            / 指定监听函数 , 使用函数名(定义在methods方法中)
            count:'addCount'
            / 指定多个事件处理程序  使用数组的形式
            count:[handler1, handler2 ,....]
        }

侦听器的配置项:immediate 效果:页面加载后立即执监听器

<body>
    <div id="app">
        <!-- 侦听器的例子  当改变app.msg值得时候输出下面设置的100 -->
        <p>{{msg}}</p>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            msg: 'hello vue'
        },
        //watch 侦听器
        watch: {
            //  接受两个参数:  第一个参数新值(改变后的值), 第二个参数是旧值(改变之前的值),
            msg: {
                //  handler 是一个回调函数
                // !!!!!!!! 侦听器如果是对象的形式:不能使用箭头函数 因为箭头函数的this的指向是window !!!!!!
                handler: function(newVlue, oldVlue) {
                    this.msg = '100'
                },
                immediate: true
                    //  页面加载完成后立即执行一次监听器 需要一个 immediate 属性: 是一个布尔类型
            }
        }
    })
</script>

侦听器的配置项:deep 解决函数嵌套太深不触发帧听器的问题

<body>
    <div id="app">
        <p>{{school}}</p>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            school: {
                name: '学校',
                classRoom: {
                    name: '一年级'
                }
            }
        },
        watch: {
            //函数名对应methods中的方法  把事件处理程序单独独立出来, 存放到methods方法中,在去使用的时候直接用函数名,但是必须添加引号
            msg: 'changemag',

            school: {
                handler: function(newVlue, oldVlue) {
                    console.log("值被改变了");
                },
                deep: true // deep:true 该回调会在任何被侦听的对象的属性 改变时被调用,不论其被嵌套多深
            }
        },
        methods: {
            changemag(newVlue, oldVlue) {
                console.log(newVlue, oldVlue);
            }
        }
    })
</script>

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

计算属性和侦听器的区别

  • 计算属性:computed

    1.使用场景:当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性
    计算属性 computed 是基于data中数据进行处理的,data数据变化,他也跟着变化 ; 当data中数据没有发生改变时,我们调用computed中函数n次,只会进行缓存(执行一次)
    2.用法: 计算属性在调用时需要在模板中渲染,修改计算所依赖元数据

  • 侦听器:watch

          1.使用场景:数据变化时执行异步或开销较大的操作,可以随时修改状态的变化,不存在缓存
         通过监听data中的数据 进行处理,data数据变化,则执行handler处理程序,对data中的元数据进行处理
          2.用法:watch在调用时只需修改元数据 
    
<body>
    <div id="app">
        <!-- 计算属性的例子 页面输出[ 200 ]-->
        <p>{{slice}}</p>
        <!-- 侦听器的例子  当改变app.msg值得时候输出下面设置的 你好,侦听器 -->
        <p>{{msg}}</p>
    </div>
</body>
<script src="./js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            msg: 'hello vue'
        },
        // 计算属性
        computed: {
            slice() {
                return this.count = '200'; //这个只要写了就会执行
            }
        },
        watch: {
            //  接受两个参数:  第一个参数新值(改变后的值), 第二个参数是旧值(改变之前的值),
            msg(newVlue, oldVlue) {
                //  监听到对应的数据发生改变,则做出响应的处理
                this.msg = '你好,侦听器的值'
            }
        }
    })
</script>

在这里插入图片描述

方法methods和侦听watch、计算属性computed,过滤器filters的区别

watch和computed都是对数据的监听只有数据发生变化时才会触发,但是他们擅长的领域不一样

watch更擅长一对多:就是主要监听一个可以影响多个数据的数据,具备缓存机制
computed擅长多对一:主要监听多个数据影响一个数据的数据,一定要return,具备缓存机制

组合出的那个数据会自动定义不用在data中再次定义,
当然也可以使用watch来监听组成computed所监听数据的每一个数据,但是这样代码的重复率过高

methods和watch、computed的区别

这个很直观,methods是通过事件驱动来执行函数的是被动的
watch、computed是当监听的数据发生变化时主动执行这个函数

methods和computed的区别

区别一个是主动与被动,另一个是methods的运算是没有缓存的,computed运算是有缓存的(只要运算所依赖的数据没有发生变化就会从缓存中取出结果)

在这里插入图片描述计算属性
1、计算属性适合用在单个属性的计算;
2、计算属性只能在单个vue实例中使用;
3、计算属性不能接收参数,只能使用data中定义的变量进行计算;
4、计算属性有缓存机制,可减少调用次数;
5、计算属性相当于定义一个变量

过滤器
1、过滤器适合多个同样计算方法的属性的计算;
2、过滤器可以定义为全局过滤器,在多个vue实例中使用;
3、过滤器可以接收多个参数进行计算;
4、过滤器没有缓存机制,每调用一次都会计算一次;
5、过滤器相当于定义一个特殊的方法

vue生命周期钩子函数

在这里插入图片描述Vue错误处理机制链接

vue 的生命周期钩子函数:
            创建前/后 : beforeCreate()   created()
            挂载前/ :  beforeMount()    mounted()
            更新前/ :  beforeUpdate()   updated()
            销毁前/ :  beforeDestroy()   destroyed()

vue 的生命周期 组件也都可以使用

这是在script标签直接传值使用,了解即可

 <!--  独立的模板文件 : 不建议使用   -->
    <!-- <script id="order" type='text/x-template'>
         <p>这是vue 模板 !!!!!!!!!!!!! {{msg}} </p>
     </script> -->

组件的生命周期
例子:order组件在child组件上面

控制台输出的顺序:
在这里插入图片描述

beforeDestroy() destroyed() 销毁前/后的触发条件 三种情况

 beforeDestroy()   destroyed()  触发:
        1.  需要手动触发vm.$destroy() 销毁方法触发;
        2.   v-if 触发 切换组件的时候会触发 
        3.  路由切换时 
    <div id="app">
        <!--  !1手动销毁例子-->
        <!-- <order></order> -->

        <!--!2 v-if的案例 -->
        <order v-if='flag'></order>
        <discover v-else></discover>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        const order = {
            template: ' <p>这是order</p>',
            //  组件被销毁时触发
            beforeDestroy() {
                console.log('order子组件 销毁前');
            },
            destroyed() {
                console.log('order子组件 销毁后');
            }
        }
        const discover = {
            template: ' <p>这是discover</p>'
        }
        const app = new Vue({
            el: '#app',
            components: {
                order,
                //es6   之前的写法为 a:order
                discover
            },
            data: {
                flag: true
            }
        })
    </script>
</body>

第一种情况图解
在这里插入图片描述
第二种情况图解
路由的切换会触发组件销毁示例

<div id="app">
    <router-view></router-view>
</div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.js"></script>
<script>
     const Order = {
        template: `<div>
            我的订单
        </div>`,
        created(){
            console.log('order 组件创建');                
        },
        destroyed(){
            console.log('order 组件被销毁');        
        }
    }
    const Pay = {
        template: `<div>
            代付款页面
        </div>`,
        created(){
            console.log('pay 组件创建');                
        },
        destroyed(){
            console.log('pay 组件被销毁');        
        }
    }
    const router = new VueRouter({
        routes:[
            {
                path:'/order',
                component:Order
            },{
                path:'/pay',
                component:Pay
            }
        ]
    })

    const app = new Vue({
        el:'#app',
        router
    })
</script>

在这里插入图片描述在这里插入图片描述

  • 注意:tab页的切换不会触发生命周期,如果业务需要, 可以将组件用v-if做判断
      <el-tab-pane label="用户管理" name="first">
      <AA v-if="activeName == 'first'"></AA>  
       </el-tab-pane>

keep-alive 组件缓存

keep-alive是Vue的内置组件,包裹动态组件时,会将不活动的组件实例留在内存中,优化请求,防止DOM重新渲染
比如:在APP.vue中单纯使用keep-alive组件包裹,会直接缓存所有页面
在这里插入图片描述
keep-alive用于保存组件的渲染状态

实例

a.vue

<template>
  <div class="about">
    <h2>小a页面</h2>
    <span>组件会缓存</span>
    <input type="text" />
  </div>
</template>

APP.vue

<div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/B">About</router-link>
    </div>
    <keep-alive include="A">
      <!-- name 为A的组件将被缓存!-->
      <router-view />
    </keep-alive>
    <!-- <keep-alive exclude="A">
    除了 name 为A的组件都将被缓存! 
      <router-view />
    </keep-alive> -->

在这里插入图片描述
缓存效果:跳转页面不刷新在跳回来,输入框文字仍然存在

在 router.js 中来控制某个组件是否需要缓存

keep-alive是一个抽象组件:
它自身不会渲染一个DOM元素,也不会出现在父组件链中;
使用keep-alive包裹动态组件时,
会缓存不活动的组件实例,而不是销毁它们。

动态组件 通过 :is 属性实现

is与:is的用法和区别

    使用vue 提供的内置组件标签 <component></component> ,绑定is属性实现动态组件的渲染

    keep-alive 内置标签 : 进行缓存,会缓存命中的组件
<div id="app">
    <!--  动态组件 -->
    <!-- <component is='order'></component> 错误示例-->
    <keep-alive>
        <component :is='cname'></component>
    </keep-alive>
</div>
<script src="./js/vue.js"></script>
<script>
    const order = {
        template: ' <p>这是order</p>',
        beforeCreate() {
            console.log('order子组件 创建前 。。。 ');
        },
        created() {
            console.log('order子组件 创建后 。。。 。。。');
        },
        beforeMount() {
            console.log('order子组件 挂载前。。。');
        },
        mounted() {
            console.log('order子组件 挂在后。。。。。。。。。。');
        },
        //  组件的生命周期(组件残留的钩子函数) 结合 keep-alive 一起使用
        activated() {
            console.log('order组件被缓存');
        },
        deactivated() {
            console.log('order组件被停用。。');
        },
        //  组件被销毁时触发
        beforeDestroy() {
            console.log('order子组件 销毁前');
        },
        destroyed() {
            console.log('order子组件 销毁后');
        }
    }

    const discover = {
        template: ' <p>这是discover</p>'
    }

    const app = new Vue({
        el: '#app',
        components: {
            order,
            //es6   之前的写法为 a:order
            discover
        },
        data: {
            //  通过组件名动态显示组件  例如将order改为discover
            cname: 'order',
        }
    })
</script>
  • is 可以直接传递一个组件
  • :is 是查找父组件中的一个赋值,然后找到相应的组件

在这里插入图片描述
动态组件中的activated与deactivated钩子函数

vue里的$attrs大白话讲解

vue里的$attrs大白话讲解在这里插入图片描述

;