带你复习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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容