Vue組件開發基礎全面詳解

前言

用Vue已經有2年多了,用本文詳解講解Vue組件開發過程當中的方方面面,供你們學習,若是以爲還能夠,點個贊收藏一下,持續更新中。html

1、組件的註冊

一、註冊組件時,怎麼給組件命名,要什麼要求?

給組件命名有兩種方式,一種是使用鏈式命名my-component,一種是使用大駝峯命名MyComponentvue

在字符串模板中<my-component></my-component><MyComponent></MyComponent>均可以使用,ios

在非字符串模板中最好使用<MyComponent></MyComponent>,由於要遵循 W3C 規範中的自定義組件名 (字母全小寫且必須包含一個連字符),避免和當前以及將來的 HTML 元素相沖突。axios

二、怎麼註冊全局組件

Vue.component('my-component', {
    props:{
        title:{
            type: String,
            default:'全局組件',
        }
    },
    data(){
        return {}
    },
    created(){},
    mounted(){},
    methods:{
        handleClick(){
            alert(this.title)
        }
    },
    template: '<h3 @click="handleClick">{{ title }}</h3>',
})
複製代碼

三、若是template內容不少,那該怎麼辦?

在項目中static文件中創建my_component.html文件,文件內容以下數組

<h3 @click="handleClick">{{ title }}</h3>
複製代碼
Vue.component('my-component', (resolve, reject) =>{
    // 能夠請求一個html文件,既然存放模板仍是html文件存放比較好
    axios.get("../static/my_component.html").then(res =>{
        resolve({
            template: res,
            props: {
                title: {
                    type: String,
                    default: '全局組件',
                }
            },
            methods: {
                handleClick() {
                    alert(this.title)
                }
            },
        })
    });
});
複製代碼

四、局部組件怎麼註冊?

  1. 同步註冊瀏覽器

    import myComponent from './my_component';
    export default {
        components: {
            myComponent,
        },
    }
    複製代碼
  2. 異步註冊(懶加載)bash

    export default{
        components:{
            myComponent: resolve => require(['./my_component'],resolve)
        }
    }
    複製代碼

2、組件的props選項

一、props中每一個prop命名有什麼要注意的?

在非字符串模板中,若是prop是用小駝峯方式命名,在模板中要使用其等價的鏈式方式命名。iview

在字符串模板中就不要這麼處理了。dom

index.vue文件異步

<template>
    <myComponent :my-title="my_title"></myComponent>
</template>
<script>
    export default{
        data(){
            return{
                my_title:"個人組件"
            }
        },
        components:{
            myComponent:resolve => require(['./my_component'],resolve)
        }
    }
</script>
複製代碼

my_component.vue文件

<template>
    <div>
        <h1>{{myTitle}}</h1>
    </div>
</template>
<script>
    export default{
        props:{
            myTitle:{
                type:String,
                default:'',
            }
        },
        data(){
            return{}
        },
    }
</script>
複製代碼

二、爲何在非字符串模板中要這麼使用?

由於HTML(非字符串模板)中的特性名是大小寫不敏感的,因此瀏覽器會把全部大寫字符解釋爲小寫字符。

三、怎麼給prop傳入一個靜態值?

<myComponent my-title="個人組件"></myComponent>
複製代碼

四、怎麼給prop傳入一個數字和布爾值?

<myComponent :num="100"></myComponent>
<myComponent :isload="true"></myComponent>
複製代碼

五、在子組件中能夠改變prop值嗎?爲何

不能,防止從子組件意外改變父級組件的狀態。

六、在子組件中怎麼防止改變prop值?

  1. 在data中定義一個值,並將prop值賦值給它,若是prop值是對象和數組時,賦值時要深拷貝一下。

    <script>
        export default{
            props:{
                myTitle:{
                    type:String,
                    default:'',
                },
                myInfo:{
                    type:Object,
                    default(){
                        return:{}
                    }
                }
            },
            //this.deepClone爲自定義的深拷貝方法
            data(){
                return{
                    title:this.myTitle,
                    info:this.deepClone(this.myInfo),
                }
            },
        }
    </script>
    複製代碼

    2.若是prop以一種原始的值傳入且須要進行轉換。要使用這個 prop 的值來定義一個計算屬性。

    <script>
         export default{
             props:{
                 myAchievement:{
                     type:Number,
                     default:0,
                 },
             },
             data(){
                 return{}
             },
             computed: {
                 result: function () {
                     return this.myAchievement*0.7+12;
                 }
             }
         }
     </script>
    複製代碼

七、prop容許有哪些類型?

String、Number、Boolean、Array、Object、Date、Function、Symbol。 此外還能夠是一個自定義的構造函數Personnel,而且經過 instanceof 來驗證propwokrer的值是不是經過這個自定義的構造函數建立的。

function Personnel(name,age){
    this.name = name;
    this.age = age;
}
export default {
    props:{
        wokrer:Personnel
    }
}
複製代碼

八、怎麼對prop進行類型驗證攔截

export default {
    props:{
        propA:String,
        propB:{
            type:Number,
        },
        propC:Boolean,
    }
}
複製代碼

九、怎麼對prop進行多個類型驗證攔截

export default {
   props:{
       propA:[String,Number],
       propB:{
           type:[String,Number]
       }
   }
}
複製代碼

十、怎麼對prop進行必填驗證攔截

export default{
    props:{
        propA:{
            type: String,
            required: true,
        }
    }
}
複製代碼

十一、怎麼給對象和數組類型的prop設置默認值

export default{
    props:{
        propA:{
            type:Object,
            default(){
                return {
                    a: 1,
                }
            }
        },
        propB:{
            type:Array,
            default(){
                return [1,2,3]
            }
        }
        
    }
}
複製代碼

十二、會自定義prop的驗證嗎?

export default{
    props:{
        propA:{
            validator(val){
                return 0 < val < 18
            }
        }
    }
}
複製代碼

1三、prop在default和validator函數中能用data和computed的數據嗎?

不會,由於prop會在一個組件實例建立以前進行驗證,因此data和computed在default或validator函數中是不可用的。

1四、怎麼傳入一個對象的全部屬性,不是傳入一個對像

使用不帶參數的 v-bind,例:對於一個給定的對象 post

post: {
    id: 1,
    title: 'My Journey with Vue'
}
複製代碼
<myComponent v-bind="post"></myComponent>
複製代碼

至關

<myComponent :id="post.id" :title="post.title"></myComponent>
複製代碼

3、組件的model選項

先舉個例子,用v-model來控制一個組件的顯示隱藏

my_component.vue

<template>
    <div v-show="value">
        <span>個人組件</span>
        <button @click="$emit('input',false)">隱藏</button>
    </div>
</template>
<script>
    export default{
        props:{
            value:{
                type:Boolean,
                default:false,
            }
        },
        data(){
            return{}
        },
    }
</script>
複製代碼
<template>
    <div>
        <myComponent v-model="value"></myComponent>
        <button @click="value=true">顯示</button>
    </div>
</template>
<script>
    export default{
        data(){
            return{
                value:false,
            }
        },
        components:{
            myComponent:resolve =>require(['./my_component'],resolve),
        }
    }
</script>
複製代碼

那爲何這麼寫?不明白的能夠看的不是很清楚,那麼在看下面代碼就清楚。

<template>
    <div>
        <myComponent :value="value" @input="value=$event"></myComponent>
        <button @click="value=true">顯示</button>
    </div>
</template>
複製代碼

在父組件中用$event訪問經過$emit拋出的值。

這麼寫是否是很清楚了,組件上的v-model='value'至關 :value="value" @input="value=$event"

再舉個例子

<template>
    <div>
        {{value}}
        <myInput v-model="value"></myInput>
        <!-- 等價於 -->
        <myInput :value="value" @input="value=$event"></myInput>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                value: '',
            }
        },
        components: {
            myInput: resolve => require(['./my_input'], resolve),
        }
    }
</script>
複製代碼

my_input.vue

<template>
    <input :value="value" @input="$emit('input',$event.target.value)"/>
</template>
<script>
    export default{
        props:['value'],
        data(){
            return{}
        },
    }
</script>
複製代碼

從以上代碼能夠看出,組件上的v-model='value'至關 :value="value" @input="value=$event"

其組件的自定義事件必須是input

既而後能夠實現v-model,那麼model選項是用來幹嗎的,

由於一個組件上的v-model 默認會利用名爲value的prop和名爲input的事件,

可是像單選框、複選框等類型的輸入控件可能會將value 特性用於不一樣的目的,

model 選項能夠用來避免這樣的衝突。

那麼上面的例子也能夠這麼實現

my_input.vue

<template>
    <input :value="val" @input="$emit('change',$event.target.value)"/>
</template>
<script>
    export default{
        model:{
            prop:'val',
            event:'change'
        },
        props:['val'],
        data(){
            return{}
        },
    }
</script>
複製代碼

4、組件的插槽功能

一、插槽的寫法

<myComponent>
    經過插槽傳進去的內容
</myComponent>
複製代碼
<template>
    <div>
        <h1>個人組件</h1>
        <p>
            <slot></slot>
        </p>
    </div>
</template>
複製代碼

頁面渲染出

個人組件
經過插槽傳進去的內容
複製代碼

若是 <myComponent> 沒有包含一個 <slot></slot> 元素,則該組件起始標籤和結束標籤之間的任何內容都會被拋棄。

二、設置插槽的默認內容

<slot>默認內容</slot>
複製代碼

三、多個插槽使用(具名插槽)

<template>
    <div>
        <h2>默認插槽</h2>
        <slot>默認內容</slot>
        <h2>頭部插槽</h2>
        <slot name="header"></slot>
        <h2>側邊欄插槽</h2>
        <slot name="sider"></slot>
    </div>
</template>
複製代碼
<template>
    <div>
        <myComponent>
            我是默認插槽
            <template v-slot:header>我是頭部插槽</template>
            <template #sider>
                我是側邊欄插槽
            </template>
        </myComponent>
    </div>
</template>
複製代碼

v-slot 指令的縮寫是 #v-slot只能添加在<template></template>

<slot></slot>上面沒有name屬性,會自動渲染成<slot name="default"></slot>

也能夠這麼調用<template v-slot:default></template>

四、父組件中使用插槽時怎麼訪問子組件的數據

在實際項目中,咱們要獲取子組件的數據在父組件中渲染插槽的內容。怎麼辦?

首先要在子組件中給slot綁定一個子組件的data中的一個對象childrenData,

例:<slot :toParent="childrenData"></slot> topParent是給父組件中調用的,以上稱爲插槽prop

而後在父組件中這樣調用 <template v-slot:default="slotProps">{{slotProps.toParent}}</template>

其中slotProps是表明插槽prop的集合,其命名能夠隨便取的。此時slotProps.toParent的值就是childrenData的值

下面是完整例子

<template>
    <div>
        <h2>默認插槽</h2>
        <slot :toParent="childrenData" :toParent2="childrenData2">
            {{childrenData.data1}}
            {{childrenData2.data1}}
        </slot>
        <h2>頭部插槽</h2>
        <slot name="header" :headerData="headerData">
            {{headerData.data1}}
        </slot>
    </div>
</template>
<script>
    export default{
        data(){
            return{
               childrenData:{
                   data1:'子組件數據一',
                   data2:'子組件數據二',
               },
               childrenData2:{
                   data1:'子組件數據三',
                   data2:'子組件數據四',
               },
               headerData:{
                   data1:'子組件頭部數據一',
                   data2:'子組件頭部數據二',
               },
            }
        },
    }
</script>
複製代碼
<template>
    <div>
        <myComponent>
            <template v-slot:default="slotProps">
                {{slotProps.toParent.data2}}
                {{slotProps.toParent2.data2}}
            </template>
            <template v-slot:header="headerProps">
                {{headerProps.headerData.data2}}
            </template>
        </myComponent>
    </div>
</template>
複製代碼

當有子組件slot上有多個插槽prop時,父組件調用時候能夠ES6對象解構的方法,例:

<template>
    <div>
        <myComponent>
            <template v-slot:default="{toParent,toParent2}">
                {{toParent.data2}}
                {{toParent2.data2}}
            </template>
            <template v-slot:header="{headerData}">
                {{headerData.data2}}
            </template>
        </myComponent>
    </div>
</template>
複製代碼

也能夠給子組件slot上綁定的值從新命名,例:

<template>
   <div>
   	<myComponent>
   		<template #default="{toParent:a,toParent2:b}">
   			{{a.data2}}
   			{{b.data2}}
   		</template>
   		<template #header="{headerData:c}">
   			{{c.data2}}
   		</template>
   	</myComponent>
   </div>
</template>
複製代碼

5、組件的非Prop特性

一、什麼是組件的非Prop特性

在組件標籤上添加的屬性,沒有在組件中props中定義,這些屬性會被添加到這個組件的根元素上,這就是組件的非Prop特性,例:

<template>
    <div data="">
        <h2>個人組件</h2>
    </div>
</template>
複製代碼
<template>
    <div>
        <myComponent data="noProp"></myComponent>
    </div>
</template>
複製代碼

按F12打開調試工具能夠看到

二、組件的非Prop特性的替換

若是組件的根節點已有data="children",那麼在組件標籤再定義data="noProp",這樣根節點的data屬性就會被替換。

按F12打開調試工具能夠看到

三、組件的非Prop特性的合併

若是組件的根節點上有Class、Style屬性,那麼在組件標籤再定義Class、Style,這樣根節點的Class、Style是把二者合併後在賦值。例:

<template>
    <div class="children" style="font-size:16px;">
        <h2>個人組件</h2>
    </div>
</template>
複製代碼
<template>
    <div>
    	<myComponent class="parent" style="font-size:18px;color:red">
    	</myComponent>
    </div>
</template>
複製代碼

按F12打開調試工具能夠看到

四、怎麼指定組件中某個元素繼承非Prop特性的屬性

用實例屬性$attrs,包含了組件標籤上不做爲 prop被識別(且獲取)的特性綁定(class 和 style 除外)。

在寫基礎組件中常常用到,下面舉個基礎輸入框組件爲例子:

<template>
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        :value="value"
        @input="$emit('input', $event.target.value)"
      >
    </label>
</template>
<script>
    export default {
        inheritAttrs: false,
        props: ['label', 'value'],
        data(){
            return{}
        },
        mounted(){
            console.log(this.$attrs)
        }
    }
</script>
複製代碼
<template>
    <baseIinput class="input" style="color:red" v-model="val" placeholder="請輸入" label="基礎輸入框"></baseInput>
</template>
<script>
export default {
    data() {
        return {
            val:'',
        };
    },
    components:{
        baseInput:resolve => require(['./base_input.vue'],resolve)
    }
};
</script>
複製代碼

例子中。mounted鉤子函數打印出來是{placeholder:'請輸入'},用v-bind指令將 $attrs綁定到要繼承的元素上便可繼承。

組件中的inheritAttrs選項爲false時禁止組件的根節點繼承非Prop特性,但不影響Class、Style

當inheritAttrs爲true時,不由用

6、組件上監聽根元素的原生事件

v-on.native 修飾符,來實現。例:監聽輸入框獲取焦點事件

<baseInput @focus.native="onFocus"></baseInput>
複製代碼

可是你會發現,監聽失敗,也不產生任何報錯。

這是由於這個組件的根元素是<label></label>,而這標籤是沒有focus這個事件。

你能夠用實例屬性$listeners,其包含做用在這個組件上的全部監聽器,配合v-on="$listeners" 將全部的事件監聽器指向組件內部的任一個元素,若是改元素上還有其餘監聽器,能夠用computed屬性來合併監聽器。

下面請看實例:

<template>
    <label>
        {{ label }}
        <input v-bind="$attrs" v-bind:value="value" v-on="inputListeners">
    </label>
</template>
<script>
export default {
    props: ['label', 'value'],
    data() {
        return {}
    },
    computed: {
        inputListeners: function(){
            return Object.assign(
                {},
                // 組件標籤上添加全部的監聽器
                this.$listeners,
                //組件內部原來的監聽器
                {
                    input:(event) =>{
                        this.$emit('input',event.target.value)
                    }
                }
                
            )
        }
    }
}
</script>
複製代碼
<template>
    <div>
        {{val}}
        <baseInput v-model="val" @focus="onFocus"></baseInput>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                val:'',
            }
        },
        components: {
            baseInput: resolve => require(['./base_input'], resolve),
        },
        methods:{
            onFocus(){
                console.log('獲取到焦點')
            }
        }
    }
</script>
複製代碼

7、component 標籤和 is 特殊特性在組件上的應用

一、動態組件

<component :is="componentName"></component>
複製代碼

componentName能夠是在本頁面已經註冊的局部組件名和全局組件名,也能夠是一個組件的選項對象。

當控制componentName改變時就能夠動態切換選擇組件。

二、is的用法

有些HTML元素,諸如 <ul>、<ol>、<table> 和 <select>,對於哪些元素能夠出如今其內部是有嚴格限制的。而有些元素,諸如 <li>、<tr> 和 <option>,只能出如今其它某些特定的元素內部。

<ul>
    <card-list></card-list>
</ul>
複製代碼

因此上面<card-list></card-list>會被做爲無效的內容提高到外部,並致使最終渲染結果出錯。應該這麼寫

<ul>
    <li is="cardList"></li>
</ul>
複製代碼

8、組件的遞歸引用和組件的name選項

遞歸引用能夠理解爲組件調用自身,在開發多級菜單組件時就會用到,調用前要先設置組件的name選項。

注意必定要配合v-if使用,避免造成死循環

用element-vue組件庫中NavMenu導航菜單組件開發多級菜單爲例:

<template>
    <el-submenu :index="menu.id" popper-class="layout-sider-submenu" :key="menu.id">
        <template slot="title">
            <Icon :type="menu.icon" v-if="menu.icon"/>
            <span>{{menu.title}}</span>
        </template>
        <template v-for="(child,i) in menu.menus">
            <side-menu-item v-if="Array.isArray(child.menus) && child.menus.length" :menu="child"></side-menu-item>
            <el-menu-item :index="child.id" :key="child.id" v-else>
                <Icon :type="child.icon" v-if="child.icon"/>
                <span>{{child.title}}</span>
            </el-menu-item>
        </template>
    </el-submenu>
</template>
<script>
    export default{
        name: 'sideMenuItem',
        props: {
            menu: {
                type: Object,
                default(){
                    return {};
                }
            }
        }
    }
</script>
複製代碼

9、父子組件的通訊

一、父組件向子組件通訊

經過props來通訊

二、子組件向父組件通訊

經過在父組件上自定義一個監聽事件<myComponent @diy="handleDiy"></myComponent> 在子組件用this.$emit('diy',data)來觸發這個diy事件,其中data爲子組件向父組件通訊的數據, 在父組件中監聽diy個事件時,能夠經過$event訪問data這個值。

三、父組件訪問子組件的實例或者元素

  • 先用ref特性爲子組件賦予一個ID引用<baseInput ref="myInput"></<baseInput>
  • 好比子組件有個focus的方法,能夠這樣調用this.$refs.myInput.focus()
  • 好比子組件有個value的數據,能夠這樣使用this.$refs.myInput.value
  • $refs 是在組件渲染完成以後生效,不是響應式的,因此要避免在模板和computed計算屬性中使用

四、子組件訪問父組件的實例或元素

this.$parent來訪問

10、組件的依賴注入

上面寫到,子組件能夠用this.$parent訪問父組件的實例,孫子組件能夠用this.$parent.$parent來訪問,那曾孫子組件呢,是否是要寫不少個$parent

若是父組件下不少個子孫組件都要用祖先組件中的一個數據,這時候就要用到組件的依賴注入。

依賴注入,是經過provide inject 這兩選項實現,provide是寫在祖先組件中,inject是寫在須要注入的子孫組件中。

provide選項應該是一個對象或返回一個對象的函數.

inject 選項應該是一個字符串數組或一個對象

對象的key是本地的綁定名,value是provide的 key (字符串或 Symbol)。

value也能夠是個一個對象,該對象的:

from 屬性是provide的 key (字符串或 Symbol)

default屬性是在form定義的key在provide中未定義,使用的默認值。

祖先組件

<template>
    <div>
        <son></son>
    </div>
</template>
<script>
    export default{
        data(){
            return {}
        },
        provide(){
            return{
                top:20,
            }
        },
        components:{
            son:resolve => require(['./son.vue'],resolve)
        }
    }
</script>
複製代碼

son兒子組件

<template>
    <div>
        <grandson></grandson>
    </div>
</template>
<script>
    export default{
        data(){
            return {}
        },
        inject:['top'],
        components:{
            grandson:resolve => require(['./grandson.vue'],resolve)
        },
        mounted(){
            console.log(this.top)//20
        }
    }
</script>
複製代碼

grandson兒子組件

<template>
    <div></div>
</template>
<script>
    export default{
        data(){
            return {}
        },
        inject:{
            top:{
                from:'top',
                default:'30'
            },
            bottom:{
                from:'bottom',
                default:'30'
            }
            
        },
        components:{
            grandson:resolve => require(['./grandson.vue'],resolve)
        },
        mounted(){
            console.log(this.top)//20
            console.log(this.bottom)//30
        }
    }
</script>
複製代碼

11、.sync修飾符在組件中應用

一般在子組件中是不容許對props中的值進行修改,可是有些狀況下,要對一個 prop 進行「雙向綁定」。真正的雙向綁定會帶來維護上的問題,由於子組件能夠修改父組件,且在父組件和子組件都沒有明顯的改動來源。

這時候咱們能夠用.sync修飾符來進行「雙向綁定」,其原理是經過update:myPropName 的模式觸發事件。

<template>
    <div>
        <button @click="show=true">顯示</button>
        <myComponent :show="show" v-on:update:show="show = $event"></myComponent>
    </div>
</template>
<script>
    export default{
        data(){
            return{
                show:false,
            }
        },
        components:{
            myComponent:resolve=>require(['./my_conponent'],resolve)
        }
    }
</script>
複製代碼
<template>
    <div v-show="show">
        <button @click="hide">隱藏</button>
    </div>
</template>
<script>
    export default{
        props:{
            show:{
                type:Boolean,
                default:false,
            }
        },
        data(){
            return{}
        },
        methods:{
            hide(){
                this.$emit('update:show',false)
            }
        }
    }
</script>
複製代碼

爲了方便起見,咱們爲這種模式提供一個縮寫,即 .sync 修飾符:

<my-component :show.sync="show"></my-component>
複製代碼

注意帶有 .sync 修飾符的 v-bind 不能和表達式一塊兒使用,:show.sync="name=='小明'? true : false" 是無效的

將 v-bind.sync 用在一個對象上,例:v-bind.sync='{ title: doc.title }'也是無效的。

<myComponent 
    :show.sync="info.show" 
    :name.sync="info.name" 
    :age.sync="info.age"
></myComponent>
複製代碼

若是遇到上述狀況能夠怎麼這麼寫

<myComponent v-bind.sync="info"></myComponent>
複製代碼

這樣會把 info 對象中的每個屬性 (如 show) 都做爲一個獨立的 prop 傳進去,而後各自添加用於更新的 v-on 監聽器。

12、組件的render選項

render選項的值是一個函數,一般叫渲染函數。

該函數接收一個createElement的方法做爲第一參數來建立VNode(虛擬DOM),並返回。

先舉個全局組件的例子

Vue.component('myComponent', {
    render: function (createElement) {
        return createElement(
            'h1',
            this.title
        )
    },
    props: {
        title: {
            type: String,
            default: '我是渲染出來的組件'
        }
    }
})
複製代碼

以上組件調用後渲染出<h1>我是渲染出來的組件</h1>

在單頁面組件中使用要去掉<template></template>標籤

<script>
export default {
    props: {
        title: {
            type: String,
            default: "我是渲染出來的組件"
        }
    },
    data() {
        return {};
    },
    render: function(createElement) {
        return createElement("h1", this.title);
    },
    
};
</script>
複製代碼

以上組件被調用後也渲染出<h1>我是渲染出來的組件</h1>

一、createElement函數的第一個參數,必填

接收一個String類型的元素標籤如'div',或者接收一個Function類型的函數返回元素標籤;

總之第一個參數是必填項,接收一個元素標籤。

二、createElement函數的第二個參數用來接收組件的數據對象,類型爲Object,可選

  • class項,綁定ClassName,對應:class,值爲一個字符串、對象或字符串和對象組成的數組,例:

    值爲對象:

    data(){
        classA:true
    },
    render: function (createElement) {
        return createElement('div',
            {
                class:{'classA':this.classA},
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼

    值爲字符串:

    render: function (createElement) {
        return createElement('div',
            {
                class:'classA',
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼

    值爲字符串和對象組成的數組

    data() {
        return {
            classB:true
        };
    },
    render: function (createElement) {
        return createElement('div',
            {
                class:['classA',{'classB':this.classB}],
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼
  • style項,綁定style,對應:style,接受一個字符串、對象,或對象組成的數組,例:

    值爲字符串

    render: function (createElement) {
        return createElement('div',
            {
                style:'color:red'
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼

    值爲對象

    render: function (createElement) {
        return createElement('div',
            {
                style:{
                    color:'red',
                    fontSize: '14px'
                }
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼

    值爲對象組成的數組

    render: function (createElement) {
        return createElement('div',
            {
                style:[
                    {
                        color:'red',
                    },
                    {
                        fontSize: '14px'
                    }
                ]
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼
  • attrs項,普通的HTML特性,如id、title特性,例:

    render: function (createElement) {
        return createElement('div',
            {
                attrs:{
                    id:'idA',
                    title:'我是渲染出來的組件'
                }
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼

    渲染出<div id="idA" title="我是渲染出來的組件">我是渲染出來的組件</div>

  • domProps項,DOM屬性,如innerHTML,列:

    render: function (createElement) {
        return createElement('div',
            {
                domProps: {
                    innerHTML: 'DOM屬性內容'
                }
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼

    渲染出<div>DOM屬性內容</div>我是渲染出來的組件

  • props項,用法和組件的props同樣;

  • on項,事件監聽器,其它用法和v-on同樣,例:

    render: function (createElement) {
        return createElement('div',
            {
                on: {
                    click: function(e){
                        console.log(e)
                    },
                }
            },
            '我是渲染出來的組件',
        );
    },
    複製代碼
  • nativeOn項,只用於第一個參數爲組件標籤時,用來監聽組件內根節點上的事件,列:

    components:{
        myComponent:resolve =>require(['./my_component'],resolve)
    },
    render: function (createElement) {
        return createElement('myComponent',
            {
                nativeOn: {
                    click: function(e){
                        console.log(e)
                    }
                }
            },
        );
    },
    複製代碼
  • scopedSlots項,在父組件中用子組件的數據渲染插槽內容,例:

    子組件

    <template>
        <div>
            <slot :title="title"></slot>
            <slot name="content" :content="content"></slot>
        </div>
    </template>
    <script>
        export default {
            data() {
                return {
                    title: '個人組件',
                    content: '內容'
                }
            },
        }
    </script>
    複製代碼

    父組件

    components: {
        myComponent: resolve => require(['./my_component'], resolve)
    },
    render: function (createElement) {
        return createElement('myComponent',
            {
                scopedSlots: {
                    default: ({ title }) => createElement('span', title),
                    content: ({ content }) => createElement('p', content)
                }
            },
        );
    },
    複製代碼
  • slot項,若是組件是其它組件的子組件,需爲插槽指定名稱(demo待續);

  • directives項,自定義指令(demo待續);

  • key項,定義組件的key(demo待續);

  • ref項,給組件增長ref屬性,經過this.$refs,在render函數組件中調用其子組件的方法,例:

    子組件

    <template>
        <div>{{title}}</div>
    </template>
    <script>
        export default {
            data() {
                return {
                    title: '個人組件',
                }
            },
            methods:{
                show(){
                    console.log(this.title)
                }
            }
        }
    </script>
    複製代碼

    render函數組件

    <script>
        export default {
            data() {
                return {};
            },
            components: {
                myComponent: resolve => require(['./my_component'], resolve)
            },
            render: function (createElement) {
                var myChild = createElement('myComponent', { ref: 'myRef' });
                const _this = this;
                return createElement('div', {
                    on: {
                        click: function () {
                            _this.$refs.myRef.show();//個人組件
                        }
                    },
                    ref: 'myRef',
                }, [myChild])
            },
        };
    </script>
    複製代碼

三、createElement函數的第三個參數用來接收子級虛擬節點,可選

  • 類型能夠是String,能夠是文字類型的節點,例:

    render: function (createElement) {
        return createElement('div', '我是渲染出來的組件');
    },
    複製代碼

    或者

    <script>
        export default {
            props: {
                title: {
                    type: String,
                    default: "我是渲染出來的組件"
                }
            },
            data() {
                return {};
            },
            render: function (createElement) {
                return createElement('div', this.title);
            },
        };
    </script>
    複製代碼
  • 類型能夠是Array,其中值可爲字符串、createElement函數,例:

    <script>
        export default {
            props: {
                title: {
                    type: Function,
                    default: "我是渲染出來的組件"
                }
            },
            data() {
                return {};
            },
            render: function (createElement) {
                return createElement('div', [this.title,'我是字符串',createElement('h1','我是子節點')]);
            },
        };
    </script>
    複製代碼
  • 利用this.$slots在render函數組件中實現插槽功能,例:

    子組件

    <script>
        export default {
            data() {
                return {
                };
            },
            render: function (createElement) {
                return createElement(
                    'div',
                    [
                        createElement('h1',this.$slots.default),
                        createElement('h2',this.$slots.header)
                    ]
                )
            },
        };
    </script>
    複製代碼

    父組件

    <template>
        <div>
            <myRender>
                父默認內容
                <template v-slot:header>父內容</template>
            </myRender>
        </div>
    </template>
    <script>
        export default {
            data() {
                return {}
            },
            components: {
                myRender: resolve => require(['./my_render.vue'], resolve),
            },
        }
    </script>
    複製代碼

    渲染出

    <div>
        <h1>父默認內容</h1>
        <h2>父內容</h2>
    </div>
    複製代碼
  • 利用this.$scopedSlots實現調用render函數組件時用render函數組件中的數據編輯插槽,例:

    子組件

    <script>
        export default {
            data() {
                return {
                    info:'我是render函數組件'
                };
            },
            render: function(createElement) {
                return createElement("div", [
                    this.$scopedSlots.header({
                        title: this.info
                    })
                ]);
            }
        };
    </script>
    複製代碼

    父組件

    <template>
        <div>
            <myRender>
                <template v-slot:header="{title}">{{title}}</template>
            </myRender>
        </div>
    </template>
    <script>
        export default {
            data() {
                return {};
            },
            components: {
                myRender: resolve => require(["./my_render.vue"], resolve)
            }
        };
    </script>
    複製代碼
  • 組件樹中的全部 VNode 必須是惟一的,因此當參數類型爲Array時,每一個值不可重複,例:

    render: function (createElement) {
        var myChild = createElement('p', 'hi')
        return createElement('div', [
            // 錯誤 - 重複的 VNode
            myChild,myChild,myChild
        ])
    },
    複製代碼

    應該這麼作

    render: function (createElement) {
        return createElement('div',
            Array.from({ length: 20 }).map(function () {
                return createElement('p', 'hi')
            })
        )
    }
    複製代碼

四、render函數中實現v-if v-else v-for 模板功能,例:

<ul v-if="lists.length">
    <li v-for="item in lists">{{ item.name }}</li>
</ul>
<p v-else>沒有數據</p>
複製代碼
props: ['lists'],
render: function (h) {
    if (this.lists.length) {
        return h('ul', this.lists.map(function (item) {
            return h('li', item.name)
        }))
    } else {
        return h('p', '沒有數據')
    }
}
複製代碼

五、render函數中實現v-model模板功能,例:

props: ["value"],
render: function(h) {
    var _this = this;
    return h("input", {
        domProps: {
            value: _this.value
        },
        on: {
            input: function(event) {
                _this.$emit("input", event.target.value);
            }
        }
    });
}
複製代碼

六、在父組件中使用render自定義子組件

在使用iview中Tree組件,你會發現組件提供了render屬性讓你能夠自定義樹節點。那是怎麼實現的,下面舉個簡單列子:

子組件

<script>
    export default {
        props: {
            render: {
                type: Function,
            },
            data: {
                type: Object,
                default(){
                    return {}
                },
            }
        },
        render: function (h) {
            const params = {
                data: this.data,
            };
            return this.render(h,params);
        },
    };
</script>
複製代碼

父組件

<template>
    <div>
        <myRender :render="this.render" :data="this.data">
        </myRender>
    </div>
</template>
<script>
export default {
    data() {
        return {
            render: function(h,{data}) {
                const info= `我是${data.name},今年${data.age}歲`;
                return h('h1', info);
            },
            data:{
                name:'小明',
                age:18,
            },
        }
    },
    components: {
        myRender: resolve => require(['./my_render.vue'], resolve),
    },
}
</script>
複製代碼

渲染出

我是小明,今年18歲
複製代碼

注意 render值不能用箭頭函數,不然this指向會出錯。

七、函數式組件的應用

  • 什麼是函數式組件,知足如下幾個特色能夠稱爲函數式組件

    • 沒有管理任何狀態
    • 沒有監放任何傳遞給它的狀態
    • 沒有生命週期方法
    • 只是接收一些prop的函數
  • 函數式組件優勢,渲染開銷低

  • 怎麼使用函數式組件

    functional選項標記爲true,此時組件成爲

    • 無狀態 == 無響應式數據
    • 無實例 == 無this上下文

    那麼上個例子的子組件能夠這麼寫

    <script>
        export default {
            functional:true,
            render: function (h,context) {
                const params = {
                    data: context.props.data,
                };
                return context.props.render(h,params);
            },
        };
    </script>
    複製代碼

    組件須要的一切都是經過 context 參數傳遞,它是一個包括以下字段的對象:

    • props:提供全部 prop 的對象
    • children: VNode 子節點的數組
    • slots: 一個函數,返回了包含全部插槽的對象
    • scopedSlots: 一個暴露傳入的做用域插槽的對象。也以函數形式暴露普通插槽
    • data:傳遞給組件的整個數據對象,做爲 createElement 的第二個參數傳入組件
    • parent:對父組件的引用
    • listeners: 一個包含了全部父組件爲當前組件註冊的事件監聽器的對象。這是 data.on 的一個別名
    • injections: 若是使用了 inject 選項,則該對象包含了應當被注入的屬性

    在模板中使用函數式組件這樣聲明:

    <template functional>
        <div>
            <p v-for="item in props.lists" @click="props.listClick(item);">
                {{ item }}
            </p>
        </div>
    </template>
    複製代碼
相關文章
相關標籤/搜索