33

I use Vue 3.1.1

I am using script setup in the experimental stage with single file components. Using the script setup, I understand defineProps, defineEmit, and useContext, but I don't understand how to use the render function.

<script lang="ts" setup>
import { defineProps } from 'vue'

const props = defineProps<{
    text: string
}>()

const handleClick = () => {
    console.log('click!!')
}

// Render function...

/* The template I want to create.
    <button
        class="btn btn-primary"
        type="button"
        @click="handleClick"
    >
        {{ props.text }}
    </button>
*/
</script>

4 Answers 4

31

try it.

<script lang="tsx" setup>
  import { h } from 'vue';

  const render = () => {
    return h('div', []);
  };

  const jsxNode = () => {
    return <div> text </div>;
  };
</script>
<template>
  <render />
  <jsxNode />
</template>

2
  • 1
    HMR doesn't work in this form when you change code inside render or jsxNode
    – tcpiper
    Commented Jan 18, 2022 at 13:16
  • 1
    This answer is only a special implementation, and does not involve what effect HMR will show. But whether or not HMR is effective, this method should not be used in preference, because it will make the code less readable. Moreover, the appearance of setup script is to solve the troublesomeness that setup() needs to be exported. If your component doesn't need <template>, you don't need setup script at all, you should use jsx/tsx file
    – awagege
    Commented Jan 21, 2022 at 9:31
11

You can assign the render function directly to your component instance via getCurrentInstance

Custom hook

// useRender.ts

import type { VNode } from 'vue';
import { getCurrentInstance } from 'vue';

export function useRender(render: () => Arrayable<VNode | null>): void {
    const vm = getCurrentInstance();

    if (!vm) {
        throw new Error('[useRender] must be called from inside a setup function');
    }

    /**
     * In development mode, assignment render property works fine
     * but in production SFC overwrites it with an empty function
     * because no <template> section defined.
     *
     * Filthy hack to avoid this in production.
     * https://github.com/vuejs/core/issues/4980
     */
    if (import.meta.env.DEV) {
        (vm as any).render = render;
    } else {
        Object.defineProperty(vm, 'render', {
            get: () => render,
            set: () => {},
        });
    }
}

Usage

<script setup>
import { h } from 'vue';

import { useRender } from './useRender';

useRender(() => h('span', 'Hello from script setup'));
</script>
10

Try to use the h function to create your element then render it inside the template section as follows :

<script setup lang="ts">
import { ref,h } from 'vue'

const props = defineProps<{
    text: string
}>()

const handleClick = () => {
    console.log('click!!')
}

const root=h('button',
             {type:'button',onClick:handleClick,class:'btn btn-primary'},props.text)

</script>

<template>
  <root/>             
</template>

DEMO

6
  • This does not work.
    – Raf A.
    Commented Mar 4, 2023 at 8:11
  • 1
    @RafA. can you show how have you tried it? Commented Mar 4, 2023 at 8:27
  • Exactly like the that, except it was JS and not TS. Well I should probably state that this "kind of" works, but it does not convert dynamic components. So if you write in that h() innerHTML: "<router-link>...." - it will not work.
    – Raf A.
    Commented Mar 5, 2023 at 8:17
  • Do you mean that you wrote h('router-link',...)? Commented Mar 5, 2023 at 8:20
  • For component try to import it then use it as first parameter, import {RouterLink} from 'vue-router'; and h(RouterLink,...) Commented Mar 5, 2023 at 8:30
-4

You may try an extra normal script.

<script lang="tsx" setup>
import { defineProps, defineExpose } from 'vue'

const props = defineProps<{
    text: string
}>()

const handleClick = () => {
    console.log('click!!')
}
defineExpose({
  handleClick,
  // other data,method
})
</script>

<script lang="tsx">
import { defineComponent } from 'vue'

export default defineComponent({
  render() {
    return (
      <button
        class="btn btn-primary"
        type="button"
        onClick={this.handleClick}
      >
        {{ this.text }}
      </button>
    )
  }
})
</script>

Any data, methods except the props should be exposed using defineExpose.

BTW, setup script and normal script should have the same lang attribute.

1
  • this doesn't work in production mode.
    – wxsm
    Commented Nov 21, 2021 at 14:17

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.