Vue中 render 函數應用

前言

因爲最近接手維護一個基於 ivew 的項目, 新增模塊中包含很多自定義功能, 所以大量使用到了 render 函數; 故對其做一下總結...關於 render 函數, 官方文檔也做了比較詳細的介紹: render 函數: https://cn.vuejs.org/v2/guide... ; 一般組件我們都是用 template模板的方式去寫; 有時候會造成代碼上的冗餘, 不好擴展.

瞭解 render 函數

在學習 render 函數之前, 最好先了解一下虛擬節點 vNode, 以及虛擬節點樹組成的虛擬vDom, 這樣會更好的理解 render 函數

/*
 *@describe {渲染函數}
 *@params {見下方} 
 *@returns {一段VNode}
 */
createElement函數: 通常寫爲 h 函數, 也是官方推薦的

h(
  // {String | Object | Function} => 'p' | 'Select' | (h, p) => {}
  // 一個 HTML 標籤名、組件選項對象,或者
  // resolve 了上述任何一種的一個 async 函數。必填項。
  'div',

  // {Object}
  // 一個與模板中屬性對應的數據對象。可選。
  {
     style: {
         width: '100px'
     },
  },

  // {String | Array}
  // 子級虛擬節點 (VNodes),一般是數組: 由 `h()` 構建而成,參數: ('標籤|組件', {attrs}, text)
  // 也可以使用字符串來生成“文本虛擬節點”。可選。
  [
    '先寫一些文字',
   h('h1', '一則頭條'),
   h(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

render 函數應用1: 函數式組件

函數式組件 可以看做是組件裏的一個函數,入參是渲染上下文(render context),返回值是渲染好的HTML字符串
對於函數式組件,可以這樣定義:

Stateless(無狀態):組件自身是沒有狀態的
Instanceless(無實例):組件自身沒有實例,也就是沒有this  
由於函數式組件中沒有this,參數需要靠context來傳遞;
export default {
  name: 'functional-button',
  functional: true,
  render (h, context) {
    return h('button', '按鈕 1 號')
  }
}

由官方文檔可知: context 參數如下:

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

使用函數式組件

<template>
  <div class="home">
    <func-button>
      hello button
    </func-button>
  </div>
</template>

<script>
import FuncButton from '../function-components/func-button'
export default {
  name: 'home',
  components: {
    FuncButton
  }
}
</script>

現在我們將這個 func-button.js 組件改造一下, 上面的hello button 文本節點爲func-button.js的childern屬性(數組|String),我們可以讓父組件去控制組件的button按鈕內容, 改寫如下:

// export default {
//   name: 'functional-button',
//   functional: true,
//   render (h, context) {
//     return h('button', '按鈕 1 號')
//   }
// }
export default {
  name: 'funtional-button',
  functional: true,
  render (h, { children }) {
    return h('button', children)
  }
}

圖片描述

如何添加事件呢?

code如下:

<template>
  <div class="home">
    <func-button @click="handleClick">
      hello button
    </func-button>
  </div>
</template>

<script>
import FuncButton from '../function-components/func-button'
export default {
  name: 'home',
  components: {
    FuncButton
  },
  methods: {
    handleClick () {
      alert('你點到我了')
    }
  }
}
</script>
// func-button組件
export default {
  functional: true,
  // 組件的屬性集成在data裏, 爲了簡便,我們可以用 data 來替換下面的 props, listeners等
  // 寫成 h('button', data, ['hello', ...children])
  render (h, { props, listeners, children }) {
    return h(
      'button',
      {
        attrs: props,
        on: {
          click: listeners.click
        }
      },
      children
    )
  }
}

圖片描述

render 函數應用2: JSX 語法

平時開發還是多用temlate因爲直觀簡潔,各種指令用着很方便,但是經常寫template有時會覺得代碼看着很冗餘,如果想自己控制渲染邏輯比如循,判斷等等時我們就可以考慮使用JSX
關於 JSX 的使用參考

<script>
export default {
  name: 'h-title',
  props: {
    id: {
      type: Number,
      default: 1
    }
  },
  render () {
    const hText = `<h${this.id}>${this.$slots.default[0].text}</h${this.id}>`
    return <div domPropsInnerHTML={hText}></div>
  }
}
</script>

render 函數應用 3: 定製化組件

// List.vue 組件
<template>
    <div>
        <template v-for="(item, index) in data">
            <li :key="index" v-if="!render">{{ item }}</li>
            <ListItem
                v-else
                :key="`a${index}`"
                :render="render"
                :item="item"
            ></ListItem>
        </template>
    </div>
</template>

<script>
import ListItem from "./list-item"

export default {
    components: {
        ListItem
    },
    props: {
    // 支持自定義渲染
        render: {
            type: Function
        },
        data: {
            type: Array,
            default: () => []
        }
    }
};
</script>

// list-item.js
export default {
    props: {
        render: {
            type: Function
        },
        item: {
            type: String
        }

    },
    render(h) { // createElement  
        return this.render(h, this.item)
    }
}
// 父組件中
<List :data="['香蕉','蘋果','橘子']" :render="render"></List>
// 可以渲染你想要的標籤和內容
render (h, data) { 
      return <span>{data}</span>
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章