函數式組件和普通組件的區別
- 渲染快
- 沒有實例,意味着沒有(this)
- 沒有生命周期(沒有響應式數據)
組件函數的使用
以局部組件為例,將組件標記為 functional=ture;
因為函數式沒有實例,因此組件需要的一切都是通過 context
參數傳遞,它是一個包括如下字段的對象:
props
:提供所有 prop 的對象children
: VNode 子節點的數組slots
: 一個函數,返回了包含所有插槽的對象scopedSlots
: (2.6.0+) 一個暴露傳入的作用域插槽的對象。也以函數形式暴露普通插槽。data
:傳遞給組件的整個數據對象,作為createElement
的第二個參數傳入組件parent
:對父組件的引用listeners
: (2.3.0+) 一個包含了所有父組件為當前組件注冊的事件監聽器的對象。這是data.on
的一個別名。injections
: (2.3.0+) 如果使用了inject選項,則該對象包含了應當被注入的屬性。
在添加 functional: true
之后,需要更新我們的錨點標題組件的渲染函數,為其增加 context
參數,並將 this.$slots.default
更新為 context.children
,然后將 this.level
更新為 context.props.level
。
因為函數式組件只是函數,所以渲染開銷也低很多。
在作為包裝組件時它們也同樣非常有用。比如,當你需要做這些時:
- 程序化地在多個組件中選擇一個來代為渲染;
- 在將
children
、props
、data
傳遞給子組件之前操作它們。
1 data() { 2 return { 3 changer:1 4 } 5 },
1 components: { 2 MyCmp:{ 3 functional:true, //必要的設置 4 render: function (createElement, context) { 5 function getcomp(cmp){ 6 console.info(this); //輸出為undefined,證明沒有實例 7 if(cmp==1){ 8 return comp1; 9 }else{ 10 return comp2 11 } 12 } 13 return createElement(getcomp(context.props.changer), 14 { 15 props:{ 16 cmpData:context.props.data //為子組件傳遞數據 17 } 18 } 19 ); 20 },
定義要渲染的組件
1 var comp1={ 2 props:['cmpData'], 3 render:function(createElement,context){ 4 return createElement('el-input',{ 5 props:{ 6 type:this.cmpData 7 } 8 }); 9 }, 10 mounted() { 11 console.log(this) //這個組件為正常組件 12 }, 13 } 14 var comp2={ 15 props:['cmpData'], 16 render:function(createElement,context){ 17 return createElement('el-button',{ 18 props:{ 19 type:this.cmpData 20 } 21 }); 22 }, 23 mounted() { 24 console.log(this) //正常組件 25 }, 26 }
在父組件中使用
1 <template> 2 <div> 3 <el-input v-model="changer" placeholder="子組件"></el-input> 4 5 6 <my-cmp :changer="changer"></my-cmp> 7 </div> 8 </template> 9 <script>
理解渲染函數的參數
接下來說一下createElement
接受的參數:
第一個參數:可以是 {String | Object | Function}
不管是那種類型,最終返回到都是需要渲染的普通DOM標簽,
第二個參數:是一個對象,這個參數是可選的,定義了需要渲染組件的參數,相對於普通HTML標簽的屬性是一樣的。
還可以自定義指令的,Vue特有的東西,只是抽象一些,沒有直接用Vue.directive()用起來直觀。
第三個參數:子級虛擬節點,如果你這個節點只是單節點,沒有嵌套節點,這個參數可以忽略。如果有的你就要使用一個數據數組的值位cerateElement()返回的虛擬節點。套路都是一樣的。
1 // @returns {VNode} 2 createElement( 3 // {String | Object | Function} 4 // 一個 HTML 標簽名、組件選項對象,或者 5 // resolve 了上述任何一種的一個 async 函數。必填項。 6 'div', 7 8 // {Object} 9 // 一個與模板中屬性對應的數據對象。可選。 10 { 11 // 與 `v-bind:class` 的 API 相同, 12 // 接受一個字符串、對象或字符串和對象組成的數組 13 'class': { 14 foo: true, 15 bar: false 16 }, 17 // 與 `v-bind:style` 的 API 相同, 18 // 接受一個字符串、對象,或對象組成的數組 19 style: { 20 color: 'red', 21 fontSize: '14px' 22 }, 23 // 普通的 HTML 特性 24 attrs: { 25 id: 'foo' 26 }, 27 // 組件 prop 28 props: { 29 myProp: 'bar' 30 }, 31 // DOM 屬性 32 domProps: { 33 innerHTML: 'baz' 34 }, 35 // 事件監聽器在 `on` 屬性內, 36 // 但不再支持如 `v-on:keyup.enter` 這樣的修飾器。 37 // 需要在處理函數中手動檢查 keyCode。 38 on: { 39 click: this.clickHandler 40 }, 41 // 僅用於組件,用於監聽原生事件,而不是組件內部使用 42 // `vm.$emit` 觸發的事件。 43 nativeOn: { 44 click: this.nativeClickHandler 45 }, 46 // 自定義指令。注意,你無法對 `binding` 中的 `oldValue` 47 // 賦值,因為 Vue 已經自動為你進行了同步。 48 directives: [ 49 { 50 name: 'my-custom-directive', 51 value: '2', 52 expression: '1 + 1', 53 arg: 'foo', 54 modifiers: { 55 bar: true 56 } 57 } 58 ], 59 // 作用域插槽的格式為 60 // { name: props => VNode | Array<VNode> } 61 scopedSlots: { 62 default: props => createElement('span', props.text) 63 }, 64 // 如果組件是其它組件的子組件,需為插槽指定名稱 65 slot: 'name-of-slot', 66 // 其它特殊頂層屬性 67 key: 'myKey', 68 ref: 'myRef', 69 // 如果你在渲染函數中給多個元素都應用了相同的 ref 名, 70 // 那么 `$refs.myRef` 會變成一個數組。 71 refInFor: true 72 }, 73 74 // {String | Array} 75 // 子級虛擬節點 (VNodes),由 `createElement()` 構建而成, 76 // 也可以使用字符串來生成“文本虛擬節點”。可選。 77 [ 78 '先寫一些文字', 79 createElement('h1', '一則頭條'), 80 createElement(MyComponent, { 81 props: { 82 someProp: 'foobar' 83 } 84 }) 85 ] 86 )