vue 渲染函数生成组件、函数式组件

1、创建
	Vue.component('组件名', {
	  render: function (createElement) {	 createElement用来创建虚拟dom即VNode
		
	  	return createElement(
	  		'div',	标签名|组件名|字符串模板,或resolve('...')三种形式的一个async函数
	  			字符串模板:const Home = { template: '<p>home page</p>' }
	  		{	可选,若无可直接在此写第三个参数
			  class: {...},	`v-bind:class`相同,接受一个字符串、对象或字符串和对象组成的数组
			  style: {},	`v-bind:style`相同,接受一个字符串、对象,或对象组成的数组
			  attrs: {	普通的HTML属性
			    id: 'foo'
			  },
			  props: {	组件传参,和组件模板中props相同
			    myProp: 'bar'	默认值可直接写
			  },
			  domProps: {	和直接调用DOM相关的属性
			    innerHTML: 'baz'
			  },
			  on: {	监听事件,不再支持如`v-on:keyup.enter`这样的修饰器。
			    click: this.clickHandler
			  },
			  nativeOn: {	仅用于组件,`this.$emit`触发的事件。
			    click: this.nativeClickHandler
			  },
			  directives: [	自定义指令,无法对 `binding` 中的 `oldValue`,赋值,因为 Vue 已经自动为你进行了同步。
			    {
			      name: 'my-custom-directive',
			      value: '2',
			      expression: '1 + 1',
			      arg: 'foo',
			      modifiers: {
			        bar: true
			      }
			    }
			  ],
			  作用域插槽:即可以传递参数的插槽,具体可看第5步
			  scopedSlots: {格式为:{ name: props => VNode | Array<VNode> }
			    default: props => createElement('span', props.text) 对应:<span>...</span>
			    具名插槽名称: props => [createElement('span', props.text),...]
			  },
			  slot: 'name-of-slot',	如果组件是其它组件的子组件,需为插槽指定名称
			  key: 'myKey',	其它特殊顶层property
			  ref: 'myRef',	
			  refInFor: true	其它特殊顶层property,相同ref名,$refs.myRef会变成一个数组。
	  		},
	  		[ 标签中的子内容
	  		
	  			'可以使用字符串来生成文本虚拟节点',
	  			createElement('h1', '一则头条'), 	子级虚拟节点(VNodes),必须由`createElement()`构建而成,
	  		]
	  	)
	  },
	  renderError (h, err) {	当render函数遭遇错误时,提供另外一种渲染输出,其错误将会作为第二个参数传递到renderError,这个功能配合hot-reload非常实用,只在开发者环境下工作。
	   return h('pre', { style: { color: 'red' }}, err.stack)
	  }
	  其他组件配置
	  props:{...},
	  ...
	})

2、工厂函数生成多个相同VNode
	组件树中的所有VNode必须是唯一的
	错误示例:
	   var myParagraphVNode = createElement('p', 'hi')
	   return createElement('div', [
	     myParagraphVNode, myParagraphVNode
	   ])
    解决方法:使用工厂函数
    	render: function (createElement) {
		  return createElement('div',
		    Array.apply(null, { length: 20 }).map(function () {
		      return createElement('p', 'hi')
		    })
		  )
		}
		
2.5合并多个参数配置(vue3.0)
	mergeProps其返回的是一个新创建的对象,而作为参数传递的对象则不会被修改。
	可以传递不限数量的对象,后面参数的property优先。
	事件监听器、class和style,这些property的值是被合并的而不是覆盖的
	
	import { h, mergeProps } from 'vue'
	export default {
	  inheritAttrs: false,
	  render() {
	    const props = mergeProps({
	      class: 'active'	该 class 将与 $attrs 中的其他 class 合并。
	    }, this.$attrs)
	    return h('div', props)
	  }
	}

3、需要使用逻辑实现v-if、v-for、v-model等指令效果
	(1)v-if和v-for:
		<ul v-if="items.length">
		    <li v-for="item in items">{{ item.name }}</li>
		</ul>
		<p v-else>No items found.</p>
		逻辑实现:
		props: ['items'],
		render: function (createElement) {
		  if (this.items.length) {
		    return createElement('ul', this.items.map(function (item) {
		      return createElement('li', item.name)
		    }))
		  } else {
		    return createElement('p', 'No items found.')
		  }
		}
	
	(2)v-model:
		props: ['value'],
		render: function (createElement) {
		  var self = this
		  return createElement('input', {
		    domProps: {
		      value: self.value
		    },
		    on: {
		      input: function (event) {
		        self.$emit('input', event.target.value)
		      }
		    }
		  })
		}

4、事件和按键修饰符
	on:{
	  '!click': this.doThisInCapturingMode,
	  '~keyup': this.doThisOnce,
	  '~!mouseover': this.doThisOnceInCapturingMode
		
	  修饰符转换:
		.passive:&
		.capture:!
		.once:~
		.capture.once 或.once.capture: ~!
		
	  其他修饰符需要使用原生实现:
	  	.stop 		event.stopPropagation()
		.prevent 	event.preventDefault()
		.self 		if(event.target !== event.currentTarget) return;
		按键:
		.enter,.13 		if(event.keyCode!==13) return;
		修饰键:
		.ctrl,.alt,.shift,.meta 	if(!event.ctrlKey)return; (可将ctrlKey分别修改为altKey、shiftKey或者metaKey)
	}
	
5、使用插槽设置子节点内容

	方式一:this.$slots
		如:`<div><slot></slot></div>`
		
		render: function (createElement) {
		  return createElement('div', this.$slots.default)		default表示未命名的插槽
		}
		
	方式二:this.$scopedSlots
		也可以通过this.$scopedSlots访问作用域插槽,每个作用域插槽都是一个返回若干VNode的函数:
		如: `<div><slot :text="message"></slot></div>`
		
		props: ['message'],
		render: function (createElement) {
		  return createElement('div', [
		    this.$scopedSlots.default({
		      text: this.message
		    })
		  ])
		}
		
	方式三:scopedSlots
		如:`<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
		render: function (createElement) {
		  return createElement('div', [
		    createElement('child', {
		      scopedSlots: {
		        default: function (props) {
		          return createElement('span', props.text)
		        }
		      }
		    })
		  ])
		}
			
6、替换render函数中createElement方法为jsx语法:
	<anchored-heading :level="1">
	  <span>Hello</span> world!
	</anchored-heading>
	
	可实现成:
		import AnchoredHeading from './AnchoredHeading.vue'

		new Vue({
		  el: '#demo',
		  render: function (h) {	将h作为createElement的别名是Vue生态系统中的一个通用惯例,实际上也是JSX所要求的,从Vue的Babel插件的3.4.0版本开始,我们会在以ES2015语法声明的含有JSX的任何方法和getter中自动注入 const h = this.$createElement,这样你就可以去掉(h)参数了。
		    return (
		      <AnchoredHeading level={1}>
		        <span>Hello</span> world!
		      </AnchoredHeading>
		    )
		  }
		})
		
7、函数式组件
	无状态组件,即没有使用管理任何状态,也没有监听任何传递给它的状态,也没有使用生命周期方法,只是一个接受一些 prop 的函数
	好处:因为函数式组件只是函数,所以渲染开销也低很多。适用于只做单纯展示的组件
	
	Vue.component('my-component', {
	  functional: true,	这意味它无状态(没有响应式数据)
	  props: {
		...		
	  },
	  render: function (createElement, context) { 为了弥补缺少的实例,提供第二个参数作为上下文
	  
	    context包含如下属性:
	   
		    props:提供传递的参数
		    childre:VNode 子节点的数组
		    slots:一个函数,返回了包含所有插槽的对象,.slots()
		    scopedSlots:(2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
		    data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
		    parent:对父组件的引用
		    listeners:(2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
		    injections:(2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的 property。
		
		return createElement('button', context.data, context.children)
			通过向createElement传入context.data作为第二个参数,就把该组件上面所有的属性和事件监听器都传递下去了
			事实上这是非常透明的,以至于那些事件甚至并不要求.native 修饰符。
	  }
	})
	
	在 2.5.0 及以上版本中,如果你使用了单文件组件,那么基于模板的函数式组件可以这样声明:
	<template functional>
	</template>

代码示例:
实现效果:

<h1>	h1可替换成h2、h3等
  <a name="hello-world" href="#hello-world">
    Hello world!
  </a>
</h1>

使用渲染函数的方式:

Vue.component('anchored-heading', {
  render: function (createElement) {
    return createElement(
      'h' + this.level,   // 创建h标签名称
      [
        createElement('a', {	//创建a标签
          attrs: {	//锚点设置
            name: headingId,
            href: '#' + headingId
          }
        }, this.$slots.default)	//插槽内容
      ]
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值