帶你復(fù)習(xí)vue中那些關(guān)于組件的基礎(chǔ)知識

一,什么是組件:

1, 擴(kuò)展HTML元素,封裝可重用的代碼

如下圖,左側(cè)是一個頁面 被拆分成小的區(qū)塊,每個區(qū)塊對應(yīng)一個組件,組件可以嵌套,最終組合成完成頁面


.png

2, 組件設(shè)計原則

頁面上每個獨立的可視/可交互區(qū)域視為一個組件
每個組件對應(yīng)一個工程目錄,組件所需要的各種資源在這個目錄下就近維護(hù)
頁面不過是組件的容器,組件可以嵌套自由組合形成完整的頁面

如下示例:把組件拆分成根組件和兩個子組件:

   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, 組件還具備復(fù)用性

如下示例:有三個組件,其中一個組件發(fā)生變化不會影響到其他組件,里面的數(shù)據(jù)屬于組件獨享

    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 聲明,聲明后 可在模板內(nèi)使用,性能較高,使用起來麻煩,局部組件定義盡量用駝峰式命名,使用時,要做一個名字和組件間的映射;

三,組件間傳值及通信

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');  

上面代碼的意思是 父組件調(diào)用子組件的標(biāo)簽 通過標(biāo)簽上的屬性向子組件傳遞值,子組件通過使用props來接收content屬性的內(nèi)容,接收定義好后,可直接在模板里使用;

當(dāng)要向子組件傳遞很多參數(shù)的時候,可使用下面方法:

    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,單向數(shù)據(jù)流的概念:

子組件可以使用父組件傳遞過來的數(shù)據(jù),但是絕對不能直接修改父組件傳遞過來的數(shù)據(jù)(原因是會造成數(shù)據(jù)耦合,無法區(qū)分開),如需對父組件傳遞過來的子組件內(nèi)容進(jìn)行修改,可單獨在子組件里定義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可以輕松實現(xiàn)跨級訪問祖先組件的數(shù)據(jù)
示例如下:

   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,父子組件通過事件進(jìn)行通信

由于子組件不能直接修改父組件,可在子組件調(diào)用 $emit()的方法,來實現(xiàn)點擊 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,展示在頁面中,當(dāng)點擊的時候,觸發(fā)了自身的事件 addCount,父組件通過接收此事件,執(zhí)行handleCountAdd方法,給count +=1;count變化會自動傳給子組件,子組件也會變化;

還可以通過事件傳參給父組件跟多參數(shù)

     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');

父子組件通過事件進(jìn)行通信 還可以通過 v-model進(jìn)行代碼簡化,(只能綁定基本類型的數(shù)據(jù))

     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標(biāo)簽插入子組件,子組件通過<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是不能直接綁定事件(父組件想往子組件傳遞一些節(jié)點或者元素標(biāo)簽,直接把元素寫在組件標(biāo)簽中間即可)
slot 使用數(shù)據(jù)作用域問題:
父模板里調(diào)用數(shù)據(jù)屬性,使用的都是父模板里的數(shù)據(jù);
子模板里調(diào)用的數(shù)據(jù)屬性,使用的都是子模板里的數(shù)據(jù);

如果未傳插槽的內(nèi)容,去調(diào)用插槽,沒有內(nèi)容,可用default value作為默認(rèn)值,示例如下:

      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進(jìn)行拆分,分開調(diào)用,示例如下:

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,作用域插槽:

作用域插槽解決了當(dāng)子組件渲染的內(nèi)容由父組件決定的時候,可通過作用域插槽實現(xiàn),能夠讓父組件調(diào)用子組件的數(shù)據(jù)
作用域插槽執(zhí)行流程:
父組件調(diào)用名為list的子組件,在子組件循環(huán)內(nèi)容時,調(diào)用slot時,把item數(shù)據(jù)傳給slot,父組件通過v-slot ="slotProps"數(shù)據(jù)對象接收子組件內(nèi)容,接收傳過來的內(nèi)容后,通過{{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');

四,動態(tài)組件和異步組件:

示例如下:


    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');

以上寫法代碼量稍大,可通過動態(tài)組件的概念進(jìn)行代碼簡化:
以下是簡化后的代碼:

 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>:具有緩存特性 當(dāng)動態(tài)組件第一次渲染的時候,會把組件狀態(tài),變更情況記錄下來;動態(tài)組件會結(jié)合<keep-alive>一起使用

異步組件:在大型項目中 ,我們可能需要將應(yīng)用分割成小一些的代碼塊,并且只在需要的時候才從服務(wù)器加載一個模塊,只有在這個組件需要被渲染的時候才會觸發(fā)該工廠函數(shù),且會把結(jié)果緩存起來供未來重渲染
示例如下:

   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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容