理解vue中render函数

4,564 阅读3分钟

参考 segmentfault.com/a/119000001…
更多详情查看官网:cn.vuejs.org/v2/guide/re…

父组件

<template>
  <div>
    <child :my-data="items" ></child>
  </div>
</template>

<script>
import child from './child'
export default {
  data () {
    return {
      items: [1, 2, 3, 4]
    }
  }
}
</script>

一. 基本用法

子组件 child.js

export default {
  props: {
    myData: Array
  },
  render: function (createElement) {
    return createElement('h3', this.myData)
  }
}

注: 第一个标签参数为必填项 类型可以为 {String | Object | Function}

// Object
return createElement({
    template: '<div></div>'//组件选项对象
});
// Function
var func = function() {
    return {template: '<div></div>'}
};
return createElement(func());

二. 添加样式、方法

export default {
  props: {
    myData: Array
  },
  render: function (createElement) {
   return createElement('h3', {
      'class': {
        foo: true,
        bar: false
      },
      style: {
        color: 'red',
        fontSize: '18px'
      },
      attrs: {
        id: 'my-data'
      },
      on: {
        click: this.handleClick
      }
      domProps: {
        innerHTML: this.myData
      }
   })
  },
  methods: {
    handleClick: function() {
        console.log(' I am data! ');
    }
  }
}

或者也可以把内容放在外面

return createElement('h3', {
    'class': {
      foo: true
    },
},this.myData)}

三.添加子元素

  return createElement('div', [ // 由createElement函数构建而成的数组
    createElement('h1', '主标'), // createElement函数返回VNode对象,VNodes必须唯一
    createElement('h2', '副标')
  ])
  // var childNode = createElement('h1', '标题');
  // return createElement('div', [
  //        childNode, childNode  //VNodes必须唯一,渲染失败
  //    ]);
  // }

四. 数组循环输出

  const items = [1,2,3,4]
  return createElement('div',
    items.map(item=> {
        return createElement('h3', item)
    })
  )

在模板中使用的v-if/v-else同理

render: function (createElement) {
  const items = [1,2,3,4]
  if (items.length) {
    return createElement('ul', items.map((item)=> {
      return createElement('li', item)
    }))
  } else {
    return createElement('p', 'No items found.')
  }
}

五. this.$slots用法

父组件

<template>
  <div>
    <child>
      <h1 slot="header"><span>About Me</span></h1>
      <p>Here is some page content</p>
      <p slot="footer">Copyright 2016 Evan You</p>
      <p>If I have some content down here</p>
    </child>
  </div>
</template>

<script>
import child from './child'
</script>

子组件 child.js

  const {header, footer} = this.$slots
  const body =this.$slots.default
  return createElement('div', [
    createElement('header', header),
    createElement('main', body),
    createElement('footer', footer)
  ])

六. v-model用法

父组件

<template>
  <div>
    <child :name="name"  @input="val=>name=val"></child>
    <div>你的名字是:{{name}}</div>
  </div>
</template>

<script>
import child from './child'
export default {
  data () {
    return {
      name: ''
    }
  }
}
</script>

子组件 child.js

export default {
  props: {
    name: String
  },
  render: function (createElement) {
    return createElement('input', {
      domProps: {
        value: this.name
      },
      on: {
        input: event=> {
          self.$emit('input', event.target.value)
        }
      }
    })
  }
}

七.作用域插槽(获取子组件数据)

父组件

<template>
    <child>
      <template scope="props">
        <span>{{props.text}}</span>
      </template>
    </child>
</template>

子组件 child.js

export default {
  render: function (createElement) {
    return createElement('div', 
      this.$scopedSlots.default({
        text: 'hello world!'
      })
    )
  },
}

八. 子组件之间传递作用域插槽

子组件 child.js

import Vue from 'vue'
export default {
  render: function (createElement) {
    return createElement('div', [
      createElement('children', {
        scopedSlots: {
          default: function (props) {
            return [
              createElement('span', '来自父组件'),
              createElement('span', props.text)
            ]
          }
        }
      })
    ])
  }
}
Vue.component('children', {
  render: function (createElement) {
    return createElement('b',
    this.$scopedSlots.default({
      text: '我是子组件'
    }))
  }
})

九. 函数化组件

父组件

<template>
  <div>
    <child :data="data"></child>
  </div>
</template>

<script>
import child from './child'
export default {
  data () {
    return {
      data: {
          type:'',
          content:''
      }
    }
  },
  components: {child},
  methods: {
    change: function(type) {
      this.data.type=type
      const content = () => {
        switch (type) {
          case 'img':
            return 'https://raw.githubusercontent.com/iview/iview/master/assets/logo.png'
          case 'video':
            return 'http://vjs.zencdn.net/v/oceans.mp4'
          default:
            return '这是一段纯文本'
        }
      }
      this.data.content=content()
    }
  },
  mounted: function () {
    this.change('img')
  }
}
</script>

子组件 child.js

// 图片组件
var ImgItem = {
  props: ['data'],
  render: function (createElement) {
    return createElement('div', [
      createElement('p', '图片组件'),
      createElement('img', {
        attrs: {
          src: this.data.content
        }
      })
    ])
  }
}
// 视频组件
var VideoItem = {
  props: ['data'],
  render: function (createElement) {
    return createElement('div', [
      createElement('p', '视频组件'),
      createElement('video', {
        attrs: {
          src: this.data.content,
          controls: 'controls',
          autoplay: 'autoplay'
        }
      })
    ])
  }
}
// 纯文本组件 
var TextItem = {
  props: ['data'],
  render: function (createElement) {
    return createElement('div', [
      createElement('p', '纯文本组件'),
      createElement('p', this.data.content)
    ])
  }
}
export default {
  functional: true,
  render: function (createElement, context) {
    function getComponent () {
      var data = context.props.data
      switch (data.type) {
        case 'img':
          return ImgItem
        case 'video':
          return VideoItem
        default:
          return TextItem
      }
    }
    return createElement(
      getComponent(),
      context.data
      // context.children
    )
  }
  //在 2.3.0 或以上的版本中,你可以省略 props选项,所有组件上的特性都会被自动解析为 props。
  // props:{
  //    data:Object
  //}, 
}

注: 在添加 functional: true 之后,锚点标题组件的 render 函数之间简单更新增加 context 参数,this.$slots.default 更新为 context.children,之后this.data 更新为 context.props.data

组件需要的一切都是通过上下文传递,包括:

  • props:提供所有 prop 的对象
  • children: VNode 子节点的数组
  • slots: 返回所有插槽的对象的函数
  • data:传递给组件的数据对象,作为 createElement 的第二个参数传入组件
  • parent:对父组件的引用
  • listeners: (2.3.0+) 一个包含了所有在父组件上注册的事件侦听器的对象。这只是一个指向 data.on 的别名。
  • injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。