12Vue-组件化(三)

204 阅读1分钟

切换组件案例

不再使用v-if,而是使用动态组件来实现组件之间的切换。因为动态组件的:is="组件名",会更加的高效。通过:is的值,来决定具体组件页面。

图片.png

图片.png

App.vue

<template>
    <div>
        <button v-for="(item,index) in tabs" 
        :key = item 
        @click="itemClick(item)"
        :class="{active:currentTab === item}"
        >{{item}}</button>
        
        //动态组件的一个特殊值 :is  可以用来决定显示什么组件
        // :is="home" 表示 显示home组件
        <component :is = "currentTab"
        ></component>
        
        //1.动态组件传数据
        <component 
        :is="currentTab"
        name="coderwhy"
        :age="18"
//3.做出emit操作        
        @pageClick="pageClick"
        >
        这里age加上v-vind就是一个表达式,也就是number类型的,不加v-bind,就是一个字符串
        </component>
        
        
    </div>
</template>

<script>
    import Home from
    import About from
    import Category from
    
    export default{
        components:{
            Home,
            About,
            Category,
        },
        data(){
            return{
                tabs:["home","about","catagory"]
                currentTab:"home"
            }
        },
        methods:{
            itemClick(item){
                this.currentTab = item;
            },
            pageClick(){
                console.log("内部发生点击")
            }
        }
    }
</script>
<style scoped>
    .active{
        color:red;
    }
</style>

about.vue

<template>
    <div>
        About.vue
    </div>
</template>

<script>
    import 
    export default{
        components:{
            
        }
    }
</script>

home.vue

<template>
    <div @click="divClick">
    //这里就可以接收到App.vue的动态组件传来的数据
        {{name}}--{{age}}
    </div>
</template>

<script>
    import 
    export default{
        props:{
            name:{
                type:String
            },
            age:{
                type:Number
            }
        },
        //1.声明触发事件
        emits:["pageClick"],
        methods:{
            divClick(){
            //2.当我触发Home.vue的divClick的时候,我要求你App.vue也要做出相应的操作
                this.$emit("pageClick")
            }
        }
    }
</script>

category.vue

<template>
    <div>
        category.vue
    </div>
</template>

<script>
    import 
    export default{
        components:{
            
        }
    }
</script>

1.我首先将子组件局部注册,然后使用动态组件<component :is="currentTab" name="coderwhy" :age="18" @homeClick="homeClick" >来将我父组件中的attributes属性传输给子组件,而不是和以前一样,定义在div中然后传数据,高级了。

2.然后我子组件和以前一样用props接收父组件传来的数据。

3.我在子组件中emits给父组件,此时父组件的component动态组件标签上可以写emits来的方法,此后,我只需要在父组件上,点击子组件的组件,就可以传输数据给子组件,如下:我点Home组件:coderwhy-18,就会触发方法

demo07.gif

不推荐v-if的

图片.png

keep-alive

图片.png

上述讲到的方法虽然能够实现了动态组件的切换,但是每次切换都会把上一个组件销毁,然后渲染下一个组件,对于多次切换而言,显然每次的销毁和重新渲染,很大消耗了我们的性能。所以我们可以通过keep-alive对组件进行缓存,对于不显示的组件不是去销毁他,而是使他处理不激活的状态,当需要显示时再去激活。

对组件进行包裹即可

图片.png

keep-alive属性

include="..",include的值,需要在对应组件中,书写name书写,才能匹配

图片.png

图片.png

max:设置最多缓存组件的数量。会将最近没有被访问的实例,优先销毁。

name:不写的话,默认全部缓存,max不写,默认全部缓存

缓存组件的生命周期

图片.png 就是我从其他地方切入进来的时候,活跃的生命周期函数会触发,离开的时候也会触发不活跃的生命周期函数,方便我们在里面做出操作。

Webpack的代码分包

我们基于Vue CLI打包,CLI基于Webpack打包,所以最终还是依赖Webpack打包。

一个代码依赖一个模块,模块又依赖模块,webpack会将所依赖的全部一起打包,当过多的时候,就会造成打包后的静态资源,过于复杂,最终造成过首屏的渲染速度。

所以我希望将他打包到单独的一个包里,浏览器打开时可能不需要用到那个包,在后期不忙的时候再使用,这样在首屏渲染的时候会加快一点。

图片.png

import {sum} from '...'

不管我写了多少,我都会被打包到dist/js/app.js中

所以我希望把一些特定的包,打包到指定的文件夹里

通过import函数导入的模块,后续webpack对其进行打包的时候就会进行分包的操作

import("上方sum的文件夹").then((res)=>{
    console.log(res.sum(20,30))
})

新包:chunk-哈希值.js/map,该包:表示我请求的时候会先不请求他,后续才会使用,提高首屏渲染速度

图片.png

图片.png

Vue中实现异步组件

图片.png

AsyncCategory.vue

<template>
    <div>
        我是AsyncCategory.vue
    </div>
</template>

<script>
    export default{
        components:{
            
        }
    }
</script>

App.vue

<template>
    <div>
        <async-category/>
    </div>
</template>

<script>
    import { defineAsyncComponent } from 'vue'
    //使用组件,但是不希望组件后期打包放在一块
    //变成了异步组件,到时候打包就会被打包到单独包里
    //1.工厂函数写法
    const AsyncCategory = defineAsyncComponent(
    ()=>{
        import("./AsyncCategory.vue")
    })
    
    //2.对象写法,可以传更多属性
    const AsyncCategory = defineAsyncComponent({
        loader:()=>import("./AsyncCategory.vue")
        loadingComponent:Loading
        是一个组件,还在加载时候占位,先显示某个组件
        
        errorComponent:加载失败的时候,显示这个组件
        
        delay:延迟多久才显示组件,单位毫秒
               
        onError:function(err,retry,fail,attempts){
            err:错误信息对象
            retry:函数,调用retry尝试重新加载
            fail:函数,指示加载程序结束退出
            attempts:允许最大重试次数
        }
    })
    
    export default{
        components:{
            AsyncCategory
        }
    }
</script>

Loading.vue

<template>
    <div>
        我是Loading.vue,占位的
    </div>
</template>

<script>
    import 
    export default{
        components:{
            
        }
    }
</script>

异步组件和Suspense

图片.png

App.vue

<template>
    <div>
        <suspense>
            <template #default>
                <async-category>
                
                </async-category>
            </template>
//如果我default插槽没有的话,就显示fallback的
            <template #fallback>
                <loading>
                
                </loading>
            </template>
        </suspense>
        
    </div>
</template>

<script>
    import { defineAsyncComponent } from 'vue'
    //使用组件,但是不希望组件后期打包放在一块
    //变成了异步组件,到时候打包就会被打包到单独包里
    //1.工厂函数写法
    const AsyncCategory = defineAsyncComponent(
    ()=>{
        import("./AsyncCategory.vue")
    })
    
    export default{
        components:{
            AsyncCategory,
            loading,
        }
    }
</script>

$refs的使用

图片.png 意思就是:想拿到DOM元素,别用DOM操作(getElementById())

引用元素和组件(父用子) App.vue

<template>
    <div>
    //绑定到元素上
        <h2 ref="title">哈哈</h2>
    //绑定到组件实例
        <nav-bar ref="navBar"></nav-bar>
        
        <button @click="btnClick"></button>
    </div>
</template>

<script> 
    export default{
        import NavBar from './NavBar.vue'
        methods:{
            btnClick(){
//this.$refs可以拿到所有注册过ref的元素对象
                console.log(this.$refs.title)
                
        //实质是一个Proxy对象,不影响        
                console.log(this.$refs.navBar.message)
                this.$refs.navBar.sayHello()
            }
        },
        components:{
            NavBar,
        }
    }
</script>

NavBar.vue

<template>
    <div>
        
    </div>
</template>

<script>
    import 
    export default{
        data(){
            return:{
                message:"Hello"
            }
        },
        methods:{
            sayHello(){
                console.log("Hello")
            }
        }
    }
</script>

parentparent和root

NavBar.vue

<template>
    <div>
        <button @click="getParentAndRoot"></button>
    </div>
</template>

<script>
    import 
    export default{
        data(){
            return:{
                message:"Hello"
            }
        },
        methods:{
            sayHello(){
                console.log("Hello")
            },
            getParentAndRoot(){
            //拿到父组件的数据
                this.$parent
            
            //拿到根组件(这里就是App.vue)的数据
                this.$root

            }
        }
    }
</script>

App.vue拿到组件根元素

     this.$refs.navBar.$el

认识生命周期

图片.png

图片.png

组件的生命周期

图片.png

Home.vue

<template>
    <div>
        {{message}}
        <button @click="changeMessage">更新</button>
    </div>
</template>

<script>
    export default{
    data(){
        return:{
            message:"Hello"
        }
    },
    methods:{
        changeMessage(){
            this.message="啊"
        }
    }
        beforeCreate(){
        
        },
        created(){
        
        },
        beforeMount(){
        
        },
        mounted(){
        
        },
        beforeUnmount(){
        
        },
        Unmounted(){
        
        },
        beforeUpdate(){
        
        },
        updated(){
        
        },
    }
</script>

App.vue

<template>
    <div>
        <button @click="isShow = !isShow">切换实验 卸载
        </button>
        <template v-if="isShow">
            <home>v-if=false时,会直接卸载组件</home>
        </template>
    </div>
</template>

<script>
    import Home from '...'
    export default{
        components:{
            Home
        },
        data(){
            return:{
                isShow:true
            }
        },
    }
</script>

组件的v-model

一开始显示的是Hello,点击btn后变成了123

HyInput.vue

<template>
//2.并且书写一个方法,当子组件触发的时候,也要让父组件触发
    <div>
        <button @click="btnClick"></button>
    </div>
</template>

<script>
    export default{
    //父组件使用了v-model,1.子组件需要定义props接收modelValue
        props:{
            modelValue:String
        },
        methods:{
            btnClick(){
                this.$emit("update:modelValue","123")
            }
        },
        emits:['update:modelValue']
    }
</script>

App.vue

<template>
    <div>
    //组件上使用v-model
        <hy-input v-model="message"></hy-input>
    </div>
</template>

<script>
    import 
    export default{
        components:{
            HyInput,
        },
        data(){
            return:{
                message:"Hello"
            }
        }
    }
</script>

不要乱改props中的值

自定义表单事件

HyInput.vue

<template>
//2.并且书写一个方法,当子组件触发的时候,也要让父组件触发
    <div>
    我App里写了两个v-model,我这里可以写两个input
        <input v-model = "value"/>
        <input v-model = "title-model"/>
    </div>
</template>

<script>
    export default{
    //父组件使用了v-model,1.子组件需要定义props接收modelValue
        props:{
            modelValue:String,
            title:String,
        },
        emits:['update:modelValue','update:title'],
        computed:{
            value:{
                set(value){
                    this.$emit
                    ("update:modelValue",value)
                },
                get(){
                    return this.modelValue
                },
            },
            title-model:{
                set(title){
                    this.$emit
                    ("update:title",title)
                },
                get(){
                    return this.title
                },
            }
            
        }
    }
</script>

注:

<hy-input v-model="message">等价于
<hy-input :modelValue="message"
 @update:model-value="message"=$event>

App.vue

<template>
    <div>
    //组件上使用v-model,我希望我父组件里的值,子组件修改,父组件也能同步变化
        <hy-input 
         v-model="message"
//使用多个v-model的时候给他传值的形式才行         
         v-model:title="title"></hy-input>
    </div>
</template>

<script>
    import 
    export default{
        components:{
            HyInput,
        },
        data(){
            return:{
                message:"Hello",
                title:"哈哈哈"
            }
        }
    }
</script>