乐趣区

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>
    }
退出移动版