带你复习vue中那些关于组件的基础知识

一,什么是组件:

1, 扩展HTML元素,封装可重用的代码

如下图,左侧是一个页面 被拆分成小的区块,每个区块对应一个组件,组件可以嵌套,最终组合成完成页面

 

.png

2, 组件设计原则

页面上每个独立的可视/可交互区域视为一个组件
每个组件对应一个工程目录,组件所需要的各种资源在这个目录下就近维护
页面不过是组件的容器,组件可以嵌套自由组合形成完整的页面

如下示例:把组件拆分成根组件和两个子组件:

 

   const app = Vue.createApp({
      template:`<div>
                  <moduleone />
                  <moduletwo />
                </div>`
   })

   app.component('moduleone',{
        template:`<div>moduleone</div>`
   });

   app.component('moduletwo',{
        template:`<div>moduletwo</div>`
   });
   const vm = app.mount('#contentMain');

3, 组件还具备复用性

如下示例:有三个组件,其中一个组件发生变化不会影响到其他组件,里面的数据属于组件独享

 

    const app = Vue.createApp({

        template: `<div>
                  <modulecounter />
                  <modulecounter />
                  <modulecounter />
                </div>`
    })

    app.component('modulecounter', {
        data() {
            return {
                count: 1
            }
        },
        template: `<div @click ="count += 1">{{count}}</div>`
    });
    
    const vm = app.mount('#contentMain');

二,局部组件和全局组件

1,全局组件

由于app.component 定义的组件为全局组件,父组件及其他子组件都可使用
示例如下:

 

    const app = Vue.createApp({

        template: `<div>
                  <modulecounter-parent />
                  <modulecounter />
                </div>`
    })

    app.component('modulecounter-parent', {
        template: `<modulecounter />`
    });
    app.component('modulecounter', {
        data() {
            return {
                count: 1
            }
        },
        template: `<div @click ="count += 1">{{count}}</div>`
    });
    const vm = app.mount('#contentMain');

全局组件,定义好后,即使不用 也是一直挂载在 vue实例上的,对性能有一定影响,但是可随时使用;

2,局部组件

局部组件定义方式如下示例:

 

    const CounterChild = {
        data() {
            return {
                count: 1
            }
        },
        template: `<div @click ="count += 1">{{count}}</div>`
    }

    const app = Vue.createApp({
        components: {
            'counter-child': CounterChild
        },
        template: `<div>
                  <counter-child />
                </div>`
    })
    
    const vm = app.mount('#contentMain');

局部组件 定义一个常量,通过 components 声明,声明后 可在模板内使用,性能较高,使用起来麻烦,局部组件定义尽量用驼峰式命名,使用时,要做一个名字和组件间的映射;

三,组件间传值及通信

1,组件间传值:

示例如下:

 

    const app = Vue.createApp({
        data(){
            return{
                message:'hello world'
            }
        },
        template: `<div>
                  <moduletest  :content ="message"/>
                </div>`
    });
    app.component('moduletest', {
        props:['content'],
        template: `<div>{{content}}</div>`
    });
    
    const vm = app.mount('#contentMain');  

上面代码的意思是 父组件调用子组件的标签 通过标签上的属性向子组件传递值,子组件通过使用props来接收content属性的内容,接收定义好后,可直接在模板里使用;

当要向子组件传递很多参数的时候,可使用下面方法:

 

    const app = Vue.createApp({
        data(){
            return{
                params:{
                    message:'hello world',
                    msgone:'java',
                    msgtwo:'javascript'
                }
                
            }
        },
        template: `<div>
                  <moduletest  v-bind ="params"/>
                </div>`
    });
    app.component('moduletest', {
        props:['message','msgone','msgtwo'],
        template: `<div>{{message}}--{{msgone}}--{{msgtwo}}</div>`
    });
    
    const vm = app.mount('#contentMain');   

v-bind ="params" 等价于 :contnet ="params.contnet"

2,单向数据流的概念:

子组件可以使用父组件传递过来的数据,但是绝对不能直接修改父组件传递过来的数据(原因是会造成数据耦合,无法区分开),如需对父组件传递过来的子组件内容进行修改,可单独在子组件里定义data,
示例如下:

 

    const app = Vue.createApp({
        data(){
            return{
               count:1
                
            }
        },
        template: `<div>
                  <moduletest  :count ="count"/>
                </div>`
    });
    app.component('moduletest', {
        props:['count'],
        data(){
            return{
                countNum:this.count
            }
        },
        template: `<div @click ="countNum += 2">{{countNum}}</div>`
    });

    const vm = app.mount('#contentMain');   

3,provide/inject 多级组件传值

vue中可以让子组件访问父组件,孙组件想要访问祖先组件就比较麻烦,可以通过provide/inject可以轻松实现跨级访问祖先组件的数据
示例如下:

 

   const app = Vue.createApp({
        data(){
            return{
               count:1
                
            }
        },
        provide:{
            count:1
        },
        template: `<div>
                  <moduletest-child :count ="count"/>
                </div>`
    });

    //子组件
    app.component('moduletest-child', {
        template: `<moduletest-child-child />`
    });

    //孙组件
    app.component('moduletest-child-child', {
        inject:['count'],
        template: `<div>{{count}}</div>`
    });

    const vm = app.mount('#contentMain');   

4,父子组件通过事件进行通信

由于子组件不能直接修改父组件,可在子组件调用 $emit()的方法,来实现点击 count加1的功能,示例如下:

 

   const app = Vue.createApp({
        data(){
            return{
               count:1
                
            }
        },
        methods:{
            handleCountAdd(){
                this.count += 2;
            }
        },
        template: `<div>
                  <moduletest-child :count ="count" @add-count ="handleCountAdd"/>
                </div>`
    });


    app.component('moduletest-child', {
        props:['count'],
        methods:{
            handleClickCount(){
               this.$emit('addCount');
            }
        },
        template: `<div @click ="handleCountClick">{{count}}</div>`
    });

    const vm = app.mount('#contentMain');   

以上代码逻辑是:子组件接收父组件传递过来的count,展示在页面中,当点击的时候,触发了自身的事件 addCount,父组件通过接收此事件,执行handleCountAdd方法,给count +=1;count变化会自动传给子组件,子组件也会变化;

还可以通过事件传参给父组件跟多参数

 

     const app = Vue.createApp({
      data(){
        return{
            count:1
        }
      },
      methods:{
        handleaddClick(count){
            this.count = count;
        }
      },
      template:`<div>
                  <counter :count ="count" @add-count ="handleaddClick" />
                </div>`
   })

   app.component('counter',{
        props:['count'],
        emits:['add'],
        methods:{
            handleCount(){
                this.$emit('addCount',this.count + 3)
            }
        },
        template:`<div @click ="handleCount">{{count}}</div>`
   });
   const vm = app.mount('#contentMain');

父子组件通过事件进行通信 还可以通过 v-model进行代码简化,(只能绑定基本类型的数据)

 

     const app = Vue.createApp({
      data(){
        return{
            count:1
        }
      },
      template:`<div>
                  <counter v-model ="count" />
                </div>`
   })

   app.component('counter',{
        props:['modelValue'],
        methods:{
            handleCount(){
                this.$emit('update:modelValue',this.modelValue + 3)
            }
        },
        template:`<div @click ="handleCount">{{modelValue}}</div>`
   });
   const vm = app.mount('#contentMain');

update:modelValue 及 modelValue是固定写法 不可变

5,插槽 slot 和具名插槽:

示例如下,通过插槽 把dom标签插入子组件,子组件通过<slot></slot>来使用

 

     const app = Vue.createApp({
      template:`
                  <counter>
                    <button>点击</button>
                  </counter>
                  <counter>
                    <div>点击</div>
                  </counter>
               `
   })

   app.component('counter',{
        template:`<div>
                    <input />
                    <slot></slot>
                 </div>`
   });
   const vm = app.mount('#contentMain');

slot是不能直接绑定事件(父组件想往子组件传递一些节点或者元素标签,直接把元素写在组件标签中间即可)
slot 使用数据作用域问题:
父模板里调用数据属性,使用的都是父模板里的数据;
子模板里调用的数据属性,使用的都是子模板里的数据;

如果未传插槽的内容,去调用插槽,没有内容,可用default value作为默认值,示例如下:

 

      const app = Vue.createApp({
      template:`
                  <counter>
                    <button>点击</button>
                  </counter>
                  <counter>
                    <div>点击</div>
                  </counter>
                  <counter>
                  </counter>
               `
   })

   app.component('counter',{
        template:`<div>
                    <input />
                    <slot>default value</slot>
                 </div>`
   });
   const vm = app.mount('#contentMain');

还可吧slot进行拆分,分开调用,示例如下:

 

const app = Vue.createApp({
        data() {
            return {
                text: '点我'
            }
        },
        template: `
                  <layout>
                    <template #header>
                        <div>header</div>
                    </template>
                    <template  #footer>
                        <div>footer</div>
                    </template>
                 </layout/>
                `

    })  
        
    app.component('layout', {
        methods: {
            handleFormClick() {
                alert('hahahaha')
            }
        },
        template: `<div>
                    <slot name ="header"></slot>
                    <div>content</div>
                    <slot name ="footer"></slot>
                </div>` 
    });
    const vm = app.mount('#contentMain');

具名插槽:可通过#号来简写 v-slot:header 可简写成 #header

6,作用域插槽:

作用域插槽解决了当子组件渲染的内容由父组件决定的时候,可通过作用域插槽实现,能够让父组件调用子组件的数据
作用域插槽执行流程:
父组件调用名为list的子组件,在子组件循环内容时,调用slot时,把item数据传给slot,父组件通过v-slot ="slotProps"数据对象接收子组件内容,接收传过来的内容后,通过{{slotProps.item}} 使用子组件item传过来的值

 

      const app = Vue.createApp({
        template: `
                 <list v-slot ="{item}">
                    <div>{{item}}</div>
                 </list>
                `

    })
        
    app.component('list', {
        data(){
            return{
                list:['1','2','3']
            }
        },
        template: `<div>
                    <slot v-for ="item in list" :item ="item" />
                 </div>`
    });
    const vm = app.mount('#contentMain');

四,动态组件和异步组件:

示例如下:

 


    const app = Vue.createApp({
        data() {
            return {
                componentshow: 'commont-hello'
            }
        },
        methods: {
            handleBtnClick() {
                this.componentshow == 'commont-hello' ? this.componentshow = 'commont-world' : this.componentshow = 'commont-hello';
            }
        },
        template: `
                 <commont-hello v-show ="componentshow == 'commont-hello'"/>
                 <commont-world  v-show ="componentshow == 'commont-world'"/>
                 <button @click ="handleBtnClick">点击切换</button>
                `

    })

    app.component('commont-hello', {
        template: `<div>hello</div>`
    });

    app.component('commont-world', {
        template: `<div>world</div>`
    });

    const vm = app.mount('#contentMain');

以上写法代码量稍大,可通过动态组件的概念进行代码简化:
以下是简化后的代码:

 

 const app = Vue.createApp({
        data() {
            return {
                componentshow: 'commont-hello'
            }
        },
        methods: {
            handleBtnClick() {
                this.componentshow == 'commont-hello' ? this.componentshow = 'commont-world' : this.componentshow = 'commont-hello';
            }
        },
        template: `
                 <component :is ="componentshow"/>
                 <button @click ="handleBtnClick">点击切换</button>
                `

    })

    app.component('commont-hello', {
        template: `<div>hello</div>`
    });

    app.component('commont-world', {
        template: `<div>world</div>`
    });
    
    const vm = app.mount('#contentMain');

如要需要缓存可使用<keep-alive>:具有缓存特性 当动态组件第一次渲染的时候,会把组件状态,变更情况记录下来;动态组件会结合<keep-alive>一起使用

异步组件:在大型项目中 ,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块,只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染
示例如下:

 

   const app = Vue.createApp({
        template: `
                 <div>
                    <common-item />
                    <async-common-item />
                 </div>
                  
                `

    })

    app.component('common-item', {
        template: `<div>hahahaha</div>`
    });

    app.component('async-common-item', Vue.defineAsyncComponent(() => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve({
                    template: `<div>this is async component</div>`
                })
            }, 4000)
        })

    }))

    const vm = app.mount('#contentMain');

comon.gif

相关推荐
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页