Vue 渲染函数 & JSX

作者: QiShare | 来源:发表于2022-12-27 16:26 被阅读0次

    Vue - template

    Vue 官方推荐使用template语法来创建应用,虽然写法像html,但Vue最终会把template解析为render函数返回虚拟DOM,这点可以在Vue Dev Tools中看到:

    template渲染流程:

    因此在某些特定情况下,我们可以直接使用render函数来实现我们的组件。

    示例

    根据接口返回的数值level,动态渲染标题组件h1~h6

    采用Vue的模板语法,实现如下:

    <template>
        <h1 v-if="level==1" class="template_class"><slot></slot></h1>
        <h2 v-if="level==2" class="template_class"><slot></slot></h2>
        <h3 v-if="level==3" class="template_class"><slot></slot></h3>
        <h4 v-if="level==4" class="template_class"><slot></slot></h4>
        <h5 v-if="level==5" class="template_class"><slot></slot></h5>
        <h5 v-if="level==6" class="template_class"><slot></slot></h5>
    </template>
    
    <script>
    export default {
        props: {
            level: Number,
        },
    }
    </script>
    

    不过实现过程有些冗余了。因此我们可以使用render函数来动态返回我们的组件。在此之前我们需要先了解下Vue中的h函数;

    Vue - Render & H

    h 函数

    h函数是Vue创建一个Vnode的函数。

    <!--模板语法-->
    <div class="className" style="color:red" @click="clicked">
         <h1>hello,world</h1>
    </div>
    

    等价于

    let vnode = h(
                    'div',     //标签名
                    {
                        class: 'className',
                        style: { color: 'red' },
                        onClick() {
                            console.log("点击事件")
                        }
                    },         ///标签属性
                    h(         ///子节点
                        'h1',
                        'hello,world'
                    ),
                  )
    

    render函数

    render函数在Vue中可以代替template标签,直接返回虚拟DOM

    接下来,我们使用render函数的方式实现示例需求,有三种写法:

    Vue 2

    <script>
    import { h } from 'vue'
    export default {
        props: {
            level: Number,
        },
        methods: {
            clicked() {
                alert('点击事件')
            }
        },
        render() {
            let tag = 'h' + this.level
            return h(
                tag,//tag type
                {
                    class: 'head_h',
                    onClick: this.clicked
                }, ///props
                this.$slots.default() //children 
            )
        }
    }
    </script>
    

    Vue 3

    <script>
    import { h } from 'vue'
    export default {
       props: {
           level: Number,
       },
       setup(props, { slots }) {
           return () => {
               let tag = 'h' + props.level
               return h(
                   tag,//tag type
                   {
                       class: 'head_h',
                       onClick: ()=> {
                           alert('点击事件')
                       }
                   }, ///props
                   slots.default() //children 
               )
           }
       }
    }
    </script>
    

    Vue 3 变体

    <script>
    import { h } from 'vue'
    export default (props, { slots }) => { 
    //或 export default function head_h(props, { slots }) {
       let tag = 'h' + props.level
       return h(
           tag,//tag type
           {
               class: 'head_h',
               onClick: () => {
                   alert('点击事件')
               }
           }, ///props
           slots.default() //children 
       )
    }
    </script>
    

    注意:setup语法糖是不能直接使用render函数的。

    <script setup> 
    //这里不能使用render函数
    </script>
    

    render函数除了返回单个vNode外,也可以返回字符串和数组,不过组件树中的vNodes必须是唯一的(同一个vNode实例,不能在组件中多次使用)。

    极简且合法的Vue组件:

    function Hello() {
     return 'hello world!'
    }
    

    小结

    render函数+h函数虽可以处理动态性较高的场景,但是遇到复杂的组件时h函数层层嵌套,各种属性对象,写起来很复杂 ~ ~;有没有简单,方便的写法呢? 说到这里,就不得不说说JSX

    Vue - Render & JSX

    JSX

    JSX 是在JavaScript 语法上的拓展,允许 HTML 代码和 JS 一起写。
    React框架的结合是比较紧密的。

    ///JSX 表达式 :单行代码
    const heading = <h1>Mozilla Developer Network</h1>; 
    ///JSX 表达式:多行代码
    const header = (
      <header>
        <h1>Mozilla Developer Network</h1>
      </header>
    );
    

    React一样,Vue会将JSX表达式,经过parcelbabel编译后,转换为创建虚拟DOM节点的函数。不同的是:ReactReact.createElement函数,而VuecreateVNode

    ///React JSX 编译后
    const header = React.createElement("header", null,
      React.createElement("h1", null, "Mozilla Developer Network")
    );
    ///Vue JSX 编译后
    const header = createVNode("header", null,
      createVNode("h1", null, "Mozilla Developer Network")
    );
    

    Vue JSX编译后的产物,在Vue Dev Tools也能看见:

    因此在Vue中要想使用JSX,则必须安装可以将JSX表达式转换为createVNode的编译器插件。

    环境

    场景一:
    Vue新项目,若要使用JSX,则在Vue项目创建时,配置支持JSX,如此Vue便会自动帮我们配置好JSX的编译插件。

    场景二:
    Vue非新建项目,若要使用JSX:

    # -D @vitejs/plugin-vue-jsx 插件 开发环境下有效
    npm install @vitejs/plugin-vue-jsx -D
    

    其次找到vite.config.js文件,配置plugin-vue-jsx插件如下:

    import vue from '@vitejs/plugin-vue'
    import vueJsx from '@vitejs/plugin-vue-jsx'
    export default defineConfig({
      plugins: [vue(),vueJsx()]
    })
    

    使用

    新建文件后缀更名.jsx,文件内容不再需要写<script>标签;同样,我们使用render函数+JSX的方式实现示例需求,有三种写法:

    Vue 2

    import HelloWorld from '../components/HelloWorld.vue'
    import './head_jsx.css'
    export const head_jsx = {
      props: {
        level: Number,
      },
      components: {
        HelloWorld
      },
      data() {
        return {};
      },
      render() {
        let tag = 'h' + this.level
        return <tag class='head_jsx' >{this.$slots.default()}</tag>
      }
    }
    

    Vue 3

    export const head_jsx = {
        props: {
            level: Number,
        },
        components:{
          HelloWorld
        },
    
        setup(props, { slots }) {
            return () => {
                let tag = 'h' + props.level
                return <tag class='head_jsx' >{slots.default()}</tag>
            }
        }
    }
    

    Vue 3 变体

    export const head_jsx = (props, { slots }) => {
        let tag = 'h' + props.level
        return  <tag class='head_jsx'>{slots.default()}</tag>
    }
    

    小结

    基于上述写法的Vue JSX组件,虽然展示&交互都没有问题,但却有一个共同的问题:不支持VueHMR机制,每次都需要重新载入,于开发不便利。如何让Vue JSX组件支持HMR呢?

    defineComponent

    Vue JSX 组件支持HMR检测的必要条件有两个;

    1. 必须导出组件。
    2. 组件必须使用defineComponent根级语句调用来声明。

    defineComponent是一个函数,除了HMR检测外,也可在定义 Vue 组件时提供类型推导的辅助。

    并且代码写入时也会有提示。


    使用

    基于 Render & JSX使用部分所讲的Vue 2Vue 3的代码,外层包装defineComponent即可;Vue 3变体,defineComponent不可用。以下为Vue 3 JSX组件使用defineComponent 示例。

    export const head_jsx = defineComponent({
      props: {
        level: Number,
      },
      components: {
        HelloWorld
      },
      setup(props, { slots }) {
        return () => {
          let tag = 'h' + props.level
          return <tag class='head_jsx' >{slots.default()}</tag>
        }
      }
    })
    

    v-model

    v-model需要注意ref响应式变量,在JSX表达式中的取值

    export const inputer = {
      setup(props, { slots }) {
        let val = ref(0)
        return () => (
          <div>
            <input type='number' v-model={val.value} value={val.value}/>
            <div> 结果:{val.value} </div>
          </div>
        )
      }
    }
    

    v-slot

    JSXv-slot 应替换为 v-slots

    插槽组件定义

    export const sloter = {
      setup(props, { slots }) {
        return () => {
          return (
            <div>
              <div style={{ color: 'green' }}>
                我是默认插槽:
                {slots.default ? slots.default() : ''}
              </div>
              <div style={{ color: 'red' }}>
                我是插槽A:
                {slots.A ? slots.A() : ''}
              </div>
              <div style={{ color: 'blue' }}>
                我是插槽B:
                {slots.B ? slots.B() : ''}
              </div>
            </div>
          )
        }
      }
    }
    

    插槽组件使用

    1. template使用
        <sloter>
          <template #default>
            默认
          </template>
          <template #A>
            A 
          </template>
        </sloter>
    
    1. JSX 使用
    export const slotsUse = {
      components: {
        sloter
      },
      setup(props,) {
        const slots = {
          default: ()=>(<span>JSX下默认插槽的值</span>),
          A : ()=>(<span>JSX下A插槽的值</span>),
        }
        return () => <sloter v-slots={slots}> </sloter>
      }
    }
    

    小结

    上文简单列举了渲染函数和JSX配合基本使用,详细使用可见Vue JSX 插件文档

    Vue - Template VS Render&JSX

    template 优势:

    1. 官方推荐,模板化固定的语法,便于静态标记和分析,使得编译器能应用许多编译时的优化,提升性能。
    2. 贴近实际的 HTML,利于重用已有的HTML代码片段,便于理解和修改。

    Render & JSX优势:

    1. 处理高度动态的逻辑时,渲染函数相比于模板更加灵活。
    2. 一个文件中可以返回多个组件。
    3. 利于React开发者快速上手Vue

    参考

    https://cn.vuejs.org/guide/extras/rendering-mechanism.html

    https://cn.vuejs.org/guide/extras/render-function.html

    https://github.com/vuejs/babel-plugin-jsx

    相关文章

      网友评论

        本文标题:Vue 渲染函数 & JSX

        本文链接:https://www.haomeiwen.com/subject/vhvnqdtx.html