Day 83 VUE——组件、插槽、生命周期

VUE——组件

局部组件

创建子组件、挂载子组件、调用子组件
注意:在组件中,这个 data 必须是一个函数,返回一个对象。
<div id="app">
    <!-- 3、使用组件 -->
    <app></app>
</div>
<script src="./vue.js"></script>
<script>
    // 组件
    // 创建子组件、挂载子组件、调用子组件
    ///1、创建组件
    const app={
        data(){
            return {
                msg:'这是第一个局部组件'
            }
        },
        template:
            `
            <div>
                <h2>{{ msg }}</h2>
                <button @click='handClick'>改变</button>
            </div>
            `,
        methods:{
            handClick(){
                this.msg = '修改局部组件'
            }
        }
    }
    new Vue({
        el:'app',
        data:{

        },
        // 2、挂载组件
        components:{
            app
        }
    })
</script>
View Code

全局组件

// 全局组件
// 每个全局组件都需要定义一次 Vue.component
// 全局变量的使用:可以在任意 template 中进行引入。
// 引入方式:<Vheader></Vheader> or <Vconter />
// 使用手法:在任意 template 中引入

// 第一个参数:组件名(Vheader)
// 第二个参数:配置
Vue.component(
    'Vheader',{
        template:`
        <div>
            我是导航组件
        </div>`
    }
)

Vue.component(
    'Vsider',{
        template:`
        <div>
            我是侧边栏组件
        </div>`
    }
)
View Code

组件通信

父传子

父传子:通过 props 来进行通信

1.在子组件中声明 props 接收在父组件挂载的属性
2.可以在子组件的 template 中任意使用
3.在父组件绑定自定义的属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <parent></parent>
    </div>
    <script src="./vue.js"></script>
    <script>
        // 父传子:通过 props 来进行通信
        
        // 1.在子组件中声明 props 接收在父组件挂载的属性 
        // 2.可以在子组件的 template 中任意使用
        // 3.在父组件绑定自定义的属性
        
        // 全局组件为子组件
        Vue.component(
            'children',{
                template:`
                    <div>
                        <h3>这是子组件</h3>
                        <h3>{{ childData }}</h3>
                    </div>
                `,
                props:['childData']
            }
        )

        // 局部组件为父组件
        const parent = {
            data(){
                return {
                    msg:'这是父组件'
                }
            },
            template:`
            <div>
                <children :childData='msg'></children>
                
            </div>
            `,

        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                parent
            }
        })
    </script>
</body>
</html>
View Code

子传父

在父组件中,子组件上绑定自定义事件
在子组件中,触发原生的事件,在事件函数通过 this.$emit 触发自定义事件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <parent></parent>
    </div>
    <script src="./vue.js"></script>
    <script>
        // 子传父
        // 在父组件中,子组件上绑定自定义事件
        // 在子组件中,触发原生的事件,在事件函数通过 this.$emit 触发自定义事件
        
        // 全局组件为子组件
        Vue.component(
            'child',{
                template:`
                <div>
                    <h3>这是子组件</h3>
                    <input type="text" @input = 'handleInput'>
                </div>
                `,
                methods:{
                    handleInput(e){
                        const val = e.target.value
                        this.$emit('inputHand',val)
                    }
                }
            }
        )


        // 局部组件为父组件
        const parent = {
            data(){
                return {
                    msg:'这是父组件',
                    newVal:''
                }
            },
            methods:{
                input(newVal){
                    this.newVal = newVal
                }
            },
            template:`
            <div>
                <h3>{{ msg }}</h3>
                <p>显示:{{ newVal }}</p>
                <child @inputHand='input'></child>
            </div>
            `
        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                parent
            }
        })
    </script>
</body>
</html>
View Code

平行组件通信

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <!-- <app2></app2>
        <app1></app1> -->
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>
        // 平行组件
        const bus = new Vue();

        Vue.component('app1',{
            data(){
                return{
                    msg:0
                }
            },
            template:`
            <div>
                <h3>msg:{{ msg }}</h3>
            </div> 
            `,
            created(){
                // $on 绑定事件
                bus.$on('add',(n)=>{
                    this.msg += n
                }) 
            }
        })

        Vue.component('app2',{
            data(){
                return{
                
                }
            },
            template:`
            <div>
                <button @click='handClick'>+1</button>
            </div> 
            `,
            methods:{
                handClick(){
                    bus.$emit('add',1);
                }
            }
            
        })


        const App = {
            data(){
                return{

                }
            },
            template:`
                <div>
                    <app2></app2>
                    <app1></app1>
                </div>
            `,
        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }

        })
    </script>
</body>
</html>
View Code

组件通信其它方式(provide和inject)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>
        // provide 提供变量
        // inject 注入变量
        // provide 在父组件中提供变量,然后在子组件中使用 inject 注入变量,无论组件嵌套有多深
        Vue.component('app1',{
            data(){
                return{

                }
            },
            inject:['msg'],
            template:`
            <div>
                <h3>{{ msg }}</h3>
            </div> 
            `,
            created(){
                console.log(this.msg)
                // 还可以通过 $pareent $chilred 的方式来获取父组件、子组件的值
                console.log(this.$parent.$parent)
                console.log(this.$children)
            }
        })

        const App = {
            data(){
                return{

                }
            },
            template:`
                <div>
                    <app1></app1>
                </div>
            `,
            provide(){
                return{
                    msg:'父组件中的数据'
                }
            }
        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }

        })
    </script>
</body>
</html>
View Code

组件异步加载

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script type="module">
        const App = {
            data(){
                return{
                    isShow:false
                }
            },
            methods:{
                handClick(){
                    this.isShow = !this.isShow
                }
            },
            components:{
                Test:()=>import('./Test.js')
            },
            template:`
                <div>
                    <Test v-if="isShow"></Test>
                    <button @click='handClick'>异步加载</button>
                </div>
                
            `
        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }
        })
    </script>
</body>
</html>
View Code

js文件

export default {
    data(){
        return{
            msg:'亦双弓'
        }
    },
    template:`
        <div>
            <h3>{{msg}}</h3>
        </div>
    `
}
View Code

 

VUE——插槽

匿名插槽

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>
        Vue.component('MBtn',{
            template:`
                <button>
                    <slot></slot>
                </button>
            `
        })

        const App = {
            data(){
                return{
                    title:'父组件'
                }
            },
            template:`
                <div>
                    <m-btn>登录</m-btn>
                    <m-btn><a href='#'>注册</a></m-btn>
                </div>
            `

        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }
        })
    </script>
</body>
</html>
View Code

注意:匿名插槽 <slot></slot> 只能出现一次,如果出现多次,则会出造成以下情况。

<button>
  <slot></slot> 
  <slot></slot> 
  <slot></slot>
</button>

具名插槽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>
        Vue.component('MBtn',{
            // 插入组件的顺序和命名时的顺序无关
            template:`
                <button>
                    <slot name = 'login'></slot> 
                    <slot name = 'register'></slot>
                    <slot name = 'submit'></slot> 
                </button>
            `
        })

        const App = {
            data(){
                return{
                    title:'父组件'
                }
            },
            template:`
                <div>
                    <m-btn>
                        <template slot = 'login'>
                            登录
                        </template>
                    </m-btn>
                    <m-btn>
                        <template slot = 'submit'>
                            提交
                        </template>
                    </m-btn>
                    <m-btn>
                        <template slot = 'register'>
                            注册
                        </template>
                    </m-btn>
                    
                </div>
            `

        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }
        })
    </script>
</body>
</html>
View Code

注意:插入组件的顺序和命名时的顺序无关

作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id='app'>
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>
        // 要求:
        // 1.之前数据格式和应用接口不变,正常显示
        // 2.新功能模块:增加对勾
        const todoList = {
            data(){
                return{

                }
            },
            props:{
                todos: Array,
                defaultValue:[]
            },
            template:`
                <ul>
                    <li v-for='item in todos' :key='item.id'>
                        <slot :itemValue = 'item'>
                            
                        </slot>
                        {{ item.title }}
                    </li>    
                </ul>
            `
        }

        const App = {
            data(){
               return{
                    todoList:[
                        {
                            title:'你的玫瑰',
                            isComplate:true,
                            id:1
                        },
                        {
                            title:'独善其身',
                            isComplate:false,
                            id:2
                        },
                        {
                            title:'失忆的金鱼',
                            isComplate:false,
                            id:3
                        },
                        {
                            title:'浪费',
                            isComplate:true,
                            id:4
                        },
                    ]
                }
            },
            components:{
                todoList
            },
            template:`
                <todoList :todos="todoList">
                    <template v-slot='data'>
                        <input type="checkbox" v-model="data.itemValue.isComplate" />
                    </template>
                </todoList>
            `,
        }
        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }
        })
    </script>
</body>
</html>
View Code

 

生命周期

生命周期钩子

beforeCreate
created                                         重要
beforeMount
mounted                                         重要
beforeUpdate
updated                                         重要
activated       组件激活    配合 keep-alive     需要掌握 
deactivated     组件停用    配合 keep-alive     需要掌握
beforeDestroy   销毁之前                        需要掌握
destroyed       销毁完成                        需要掌握
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .active{
            color: red;
        }
    </style>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>

        /*
            beforeCreate
            created                                         重要
            beforeMount
            mounted                                         重要
            beforeUpdate
            updated                                         重要
            activated       组件激活    配合 keep-alive     需要掌握 
            deactivated     组件停用    配合 keep-alive     需要掌握
            beforeDestroy   销毁之前                        需要掌握
            destroyed       销毁完成                        需要掌握
        */

        Vue.component('Test',{
            data(){
                return{
                    msg:'亦双弓',
                    isRed:false
                }
            },
            methods:{
                handClick(){
                    this.msg = 'ysg';
                    this.isRed = true;
                }
            },
            template:`
                <div>
                    <button @click='handClick'>改变</button>
                    <h3 :class="{active:isRed}">{{msg}}</h3>
                </div>
            `,
            beforeCreate(){
                console.log('组件创建之前',this.$data)
            },
            created(){
                // 这时可以做一件非常重要的事情:发送 ajax 请求后端数据
                console.log('组件创建完成',this.$data)
            },
            beforeMount(){
                // 即将挂载
               console.log('DOM挂载之前', document.getElementById('app'))
            },
            mounted(){
                console.log('DOM挂载完成', document.getElementById('app'))
            },
            beforeUpdate(){
                // 获取更新之前 原始的DOM
                console.log('更新之前的DOM',document.getElementById('app').innerHTML)
            },
            updated(){
                // 获取更新之后 最新的DOM
                console.log('更新之后的DOM',document.getElementById('app').innerHTML)
            },
            beforeDestroy(){
                // 销毁之前
                console.log('销毁之前')
            },
            destroyed(){
                // 销毁完成
                console.log('销毁完成')
            },
            activated(){
                console.log('组件激活')
            },
            deactivated(){
                console.log('组件停用')
            },
        })

        const App = {
            data(){
                return{
                    isShow:true
                }
            },
            components:{

            },
            methods:{
                handClick(){
                    this.isShow = !this.isShow
                }
            },
            template:`
                <div>
                    <keep-alive>
                        <Test v-if="isShow"></Test>    
                    </keep-alive>
                    
                    <button @click='handClick'>销毁与否</button>
                </div>
                
            `
        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }
        })
    </script>
</body>
</html>
生命周期钩子示例

 

refs的使用

1.如果给标签添加 ref,获取的是真实的 DOM 节点
2.如果给子组件添加 ref,获取的是当前子组件对象

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script type="module">

        Vue.component('Test',{
            data(){
                return{
                    msg:'亦双弓'
                }
            },
            template:`
                <h3>{{msg}}</h3>
            `
        })

        const App = {
            data(){
                return{
                    
                }
            },
            mounted(){
                // 1.如果给标签添加 ref,获取的是真实的 DOM 节点
                // 2.如果给子组件添加 ref,获取的是当前子组件对象
                console.log(this.$refs.btn);
                console.log(this.$refs.test);
                // 加载界面 自动获取焦点
                this.$refs.input.focus();
                
            },
            template:`
                <div>
                    <Test ref='test'></Test>
                    <input type="text" ref="input"></input>
                    <button ref='btn'>改变</button>
                </div>
                `
        }

        new Vue({
            el:'#app',
            data:{

            },
            components:{
                App
            }
        })
    </script>
</body>
</html>
ref 的使用

 

nextTick

nextTick的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h3>{{ msg }}</h3>
    </div>
    <script src="./vue.js"></script>
    <script type="module">
        const vm = new Vue({
            el:'#app',
            data:{
                msg:'亦双弓'
            },
        })

        vm.msg = 'new msg'
        console.log(vm.$el.textContent) // 获取的还是没变化前的 msg
        Vue.nextTick(()=>{
            // 为了数据变化之后等待 vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick 在当前的
            // 回调函数中可以获取最新的 DOM
            console.log(vm.$el.textContent) // 获取的是变化后的 msg
        })

    </script>
</body>
</html>
nextTick 的简单使用

nextTick的应用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <App></App>
    </div>
    <script src="./vue.js"></script>
    <script>
        /*
            要求:
                在页面拉取一个接口,这个接口返回一些数据,这些数据是这个页面的一个浮层组件要依赖的,
                然后在接口已返回数据就展示了这个浮层组件,
                并在展示的同时,上报一些数据给后台(这些数据是父组件从接口拿的)。
                这个时候,神奇的事情发送了,虽然拿到了数据,但是浮层展现的时候,这些数据还未更新到组件上去,上报时报
        */
        const Pop = {
            data(){
                return{
                    isShow:false
                }
            },
            props:{
                name:{
                    type:String,
                    default:''
                }
            },
            template:`
                <div v-if='isShow'>
                    {{ name }}
                </div>
            `,
            methods:{
                show(){
                    this.isShow = true // 弹窗组件展示
                    console.log(this.name)
                }
            },
        }
        const App = {
            data(){
                return{
                    name:''
                }
            },
            created(){
                // 模拟异步请求
                setTimeout(()=>{
                    this.name = 'ysg'
                    this.$nextTick(()=>{
                        this.$refs.pop.show()
                    })
                },1000)
            },
            components:{
                Pop
            },
            template:`
            <pop ref='pop' :name="name"></pop>
            `
        }
        new Vue({
            el:'#app',
            components:{
                App
            }
        })

    </script>
</body>
</html>
nextTick的应用

 

mixin

mixin混入偷懒技术

mixin 来分发 Vue 中可复用的功能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h3>
            {{ msg }}
        </h3>
    </div>

    <script src="./vue.js"></script>
    <script>
        const myMixin={
            data(){
                return{
                    msg:123     // 界面上就会显示 123
                }
            },
            created(){
                this.sayHello()
            },
            methods:{
                sayHello(){
                    console.log('Hello 123')  // console 中就会显示 Hello world
                }
            }
        }

        new Vue({
            el:'#app',
            data:{
                title:'亦双弓',
                msg:'ysging'    // 假如这里也有 msg 就会优先显示这里的
            },
            created(){
                console.log('111111')   // created() 会同时显示,假如这里存在 this.sayHello(),则会调用两次 这里的 sayHello()
            },
            mixins:[myMixin]      // 数组,可放入多个混入对象
        })

    </script>
</body>
</html>
mixin混入偷懒技术

mixin混入技术应用

创建全局的 mixin 要格外的小心,因为每个组件实例创建时,都会被调用

使用比较少
Vue.mixin({

})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">

    </div>
    <script src="./vue.js"></script>
    <script>
        /*
            创建全局的 mixin 要格外的小心,因为每个组件实例创建时,都会被调用
            使用比较少
            Vue.mixin({

            })
        */
        const Temp = {
            data(){
                return{
                    isShow:false
                }
            },
            methods:{
                toggleShow(){
                    this.isShow = !this.isShow
                }
            }
        }

        const app2 = {
            template :`
                <div v-if='isShow'> App2 组件 </div>
            `,
            // 局部的 mixin
            mixins: [Temp]
        }

        const app1 = {
            template :`
                <div v-if='isShow'> App1 组件 </div>
            `,
            mixins: [Temp]
        }

        new Vue({
            el:'#app',
            data:{
            },
            components:{
                app1, app2
            },
            template:`
                <div> 
                    <button @click='handleApp1'>app1</button>
                    <button @click='handleApp2'>app2</button>
                    <app1 ref='a1'></app1>
                    <app2 ref='a2'></app2>
                </div>
            `,
            methods:{
                handleApp1(){
                    this.$refs.a1.toggleShow()
                },
                handleApp2(){
                    this.$refs.a2.toggleShow()
                },
            }
        })

    </script>
</body>
</html>
mixin混入技术应用

 

对象变更检测注意事项

Vue 不能检测对象属性的添加与删除

如果想要动态 添加与删除 属性,使用以下方法
// 方法一
this.$set(this.user,'age',20);

// 方法二 添加多个响应式属性
this.user = Object.assign({},this.user,{
  age:20,
  phone:123456789
})

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">
        <h3>
            {{ user.name }} {{ user.age }} {{ user.phone }} 
        </h3>
        <button @click='handClick'>添加属性</button>
    </div>
    <script src="./vue.js"></script>
    <script>
        // Vue 不能检测对象属性的添加与删除
        new Vue({
            el:'#app',
            data:{
                user:{}
            },
            created(){
                setTimeout(()=>{
                    this.user = {
                        name:'张三',
                    }
                },2000)
            },
            methods:{
                handClick(){
                    // this.user.age = 20
                    // 如果想要动态 添加与删除 属性,使用以下方法
                    // 方法一
                    this.$set(this.user,'age',20);
                    // 方法二 添加多个响应式属性
                    this.user = Object.assign({},this.user,{
                        age:20,
                        phone:123456789
                    })

                }
            }
        })

    </script>
</body>
</html>
添加响应式数据

 

posted @ 2020-08-11 21:26  亦双弓  阅读(211)  评论(0编辑  收藏  举报