Vue 2.0 createHOC

by Thorsten

HTML

<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <h4>This is the Normal component rendered as-is:</h4>
  <base-test 
    @update="alert($event)"
    @click.native="handleNativeClick"
    :message="message"
  >
  <p>This goes into the default slot</p>
  <p slot="test">this goes into the test slot</p>
  <template slot="withScope" scope="{message}">
    <p>This is a scoped slot, it received the message: {{message}}</p>
  </template>
  </base-test>
  
  <hr>
  <h4>This is the same component wrapped in a HOC</h4>
  <hoc
    @update="alert($event)"
    @click.native="handleNativeClick"
    :message="message"
  >
    <p>This goes into the default slot</p>
    <p slot="test">this goes into the test slot</p>
    <template slot="withScope" scope="{message}">
      <p>This is a scoped slot, it received the message: {{message}}</p>
    </template>
  </hoc>
</div>

CSS

.default {
  background-color: blue;
  color: white;
}

.test {
  background-color: green;
  color: black;
}

.with-scope {
  background-color: orange;
  color: black;
}

Babel + JSX

const Component = {
	name: 'Base',
	template: 
  	`<div>
    	<p>The message from the parent: {{message}}</p>
      <button @click="$emit('update', 'hello! from the child!')">click me</button>
      <a id="test-anchor" href="#" @click.prevent>Click me, for a native event!</a>
    	<div class="default"><slot/></div>
      <div>something between the slots</div>
      <div class="test"><slot name="test" /></div>
      <div> And a scoped slot: </div>
      <div class="with-scope"><slot name="withScope" :message="message" /></div>
    </div>`,
  props: ['message']
}

const HOC = createHOC(Component, {
	created() {
  	console.log(`greetings from ${this.$options.name}`)
  },
  
  /*
   * The `createRenderFn` helper create a render function that:
   * - passes in all attrs, props and listeners that were defined on the component
   * - merge those with any attrs, props and listeners passed to this function
   * - normalize and pass on normal slots
   * - pass on scoped slots
   
  render: createRenderFn(Component, {
    props: {
    	myProp: this.$store.getters('someGetter')
    }
  })
  */
  /*
   * If you want / have to create your own render function, you can use the 
   * `normalizeSlots` helper function to prepare slots to pass them:
   render(h) {
     return h(Component, {
     	// it's up to you what to pass on in the data object
      // e.g. don't pass on attrs or props from the parent,
      // and only pass on what you define in the HOC
     }, normalizeSlots(this.$slots))
   }
  
  */
  
  
})

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!'
  },
  components: {
  	'base-test': Component,
    hoc: HOC,
  },
  methods: {
  	alert(event) { alert(event) },
  	handleNativeClick(event) {
    	if (event.target.id === 'test-anchor') {
      	alert('You clicked the link and captured a native event!')
      }
    }
  }
})

function createHOC(component, options = {}) {
	const hoc = {
  	// copy props from the wrapped component
  	props: typeof component...