Rendering function implementation of vue3 component development page


Preface

Component-based development is the current norm of development
This article records the development process of splitting a page into multiple different component modules, and then implementing the page based on the rendering function


1. Rendering mechanism

Since the rendering function is implemented based on the virtual DOM, let’s first briefly understand the background of the rendering function.

Virtual DOM

Virtual DOM (VDOM for short) is a programming concept, which means to "virtually" represent the UI required by the target through a data structure, save it in memory, and then synchronize the real DOM with it. This concept was pioneered by React and has since been adopted by many different frameworks, including Vue.

Virtual DOM is more of a pattern than a specific technology, so there is no standard implementation. For example, the following code is a pure JavaScript object (a "virtual node"), which represents a <div> element

const vnode = {
    
    
  type: 'div',
  props: {
    
    
    id: 'hello'
  },
  children: [
    /* 更多 vnode */
  ]
}

rendering pipeline

  • 编译: Vue templates are compiled into rendering functions: functions that return a virtual DOM tree. This step can be done ahead of time via a build step, or on the fly using a runtime compiler.

  • 挂载: The runtime renderer calls the rendering function, traverses the returned virtual DOM tree, and creates actual DOM nodes based on it. This step is performed as a reactive side effect, so it tracks all reactive dependencies used in it.

  • 更新: When a dependency changes, the side effect will be re-run, and an updated virtual DOM tree will be created. The runtime renderer traverses this new tree, compares it to the old tree, and applies the necessary updates to the real DOM.

Combined with the above, we can see that using 渲染函数 will render faster than using Vue模版 because there is one less compilation step

2. Rendering function

In most cases, Vue recommends using template syntax to create applications. However, in some use cases, we really need to use the full programming capabilities of JavaScript. This is where the rendering function comes in handy.

Basic usage

h() is short for hyperscript - meaning "JavaScript that generates HTML (Hypertext Markup Language)". The name comes from a convention formed by default in many virtual DOM implementations. A more accurate name would be createVnode(), but when you need to use the render function multiple times, a shorter name will save effort.

  • Create Vnodes
  • Vue provides a h() function for creating vnodes:
import {
    
     h } from 'vue'

const vnode = h(
  'div', // type
  {
    
     id: 'foo', class: 'bar' }, // props
  [
    /* children */
  ]
)
// 除了类型必填以外,其他的参数都是可选的
h('div')
h('div', {
    
     id: 'foo' })

// attribute 和 property 都能在 prop 中书写
// Vue 会自动将它们分配到正确的位置
h('div', {
    
     class: 'bar', innerHTML: 'hello' })

// 像 `.prop` 和 `.attr` 这样的的属性修饰符
// 可以分别通过 `.` 和 `^` 前缀来添加
h('div', {
    
     '.name': 'some-name', '^width': '100' })

// 类与样式可以像在模板中一样
// 用数组或对象的形式书写
h('div', {
    
     class: [foo, {
    
     bar }], style: {
    
     color: 'red' } })

// 事件监听器应以 onXxx 的形式书写
h('div', {
    
     onClick: () => {
    
    } })

// children 可以是一个字符串
h('div', {
    
     id: 'foo' }, 'hello')

// 没有 props 时可以省略不写
h('div', 'hello')
h('div', [h('span', 'hello')])

// children 数组可以同时包含 vnodes 与字符串
h('div', ['hello', h('span', 'hello')])

Declare rendering function

Rendering functions can be declared using the render option:

import {
    
     h } from 'vue'

export default {
    
    
  data() {
    
    
    return {
    
    
      msg: 'hello'
    }
  },
  render() {
    
    
    return h('div', this.msg)
  }
}

In addition to returning a single vnode, you can also return a string or array:

export default {
    
    
  render() {
    
    
    return 'hello world!'
  }
}
import {
    
     h } from 'vue'

export default {
    
    
  render() {
    
    
    // 用数组来返回多个根节点
    return [
      h('div'),
      h('div'),
      h('div')
    ]
  }
}

Vnodes must be unique

vnodes must be unique within the component tree.

If you really want to render multiple duplicate elements or components on the page, you can use a factory function to do this. For example, the following rendering function can perfectly render 20 identical paragraphs:

function render() {
    
    
  return h(
    'div',
    Array.from({
    
     length: 20 }).map(() => {
    
    
      return h('p', 'hi')
    })
  )
}

3. Page rendering function and component configuration

Split a page into multiple component modules and generate them sequentially in assembly form
You can use the Wrapper component to package and process multiple routing common logic

  • wrapper/index.vue
<template>
  <slot></slot>
</template>

<script lang="ts" src="./index.ts" />

<style lang="less" scoped></style>

  • user-sreach/index.vue
<template>
  <div>
    user sreach
  </div>
</template>

<style scoped lang="less"></style>
  • user-list/index.vue
<template>
  <div>
    user list
  </div>
</template>

<style scoped lang="less"></style>
  • /user/index.ts
import {
    
     ROUTER_CONFIG_MAP } from '@/const/router';
import {
    
     defineComponent, h, defineAsyncComponent, type Component } from 'vue';

const Wrapper = defineAsyncComponent(() => import('@/components-business/wrapper/index.vue'));
const UserSreach = defineAsyncComponent(() => import('@/components-business/form/user-sreach/index.vue'));
const UserList = defineAsyncComponent(() => import('@/components-business/table/user-list/index.vue'));

const componentsMap: Types.KeyMapAny<Component> = {
    
    
  UserSreach,
  UserList
};

const pageComponent = [
  'UserSreach',
  'UserList'
]
export default defineComponent({
    
    
  // 配置后可在 动态获取路由配置中设置对应值
  name: ROUTER_CONFIG_MAP.system.user.name,
  title: ROUTER_CONFIG_MAP.system.user.title,
  path: ROUTER_CONFIG_MAP.system.user.path,
  setup() {
    
    
	// return () => pageComponent.map((item: string) => componentsMap[item] ? h(componentsMap[item]) : null);
    return () => h(
      Wrapper,
      null,
      () => pageComponent.map((item: string) => componentsMap[item] ? h(componentsMap[item]) : null)
    );
  },
});
  • router/index.ts
import {
    
     createRouter, createWebHistory } from "vue-router";
const router = createRouter({
    
    
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
    
    
      path: "/",
      name: "Home",
      component: () => import('@/views/contents-pages/system/user/index'),
    },
  ],
});

export default router;

Insert image description here


Summarize

如有启发,可点赞收藏哟~

Guess you like

Origin blog.csdn.net/weiCong_Ling/article/details/134549950