At the beginning

Recently, IN learning vuE3 (using the development layer), I made some small demo projects, such as [MD-editor-v3], vue3-admin and so on. I like to use JSX syntax to write in vUE projects. Almost all projects use TS. Some writing methods were not found in Google, so I want to make a small summary.

The author has reservations about the idea of using TS in a project. Some areas will increase the amount of code, or even delay the development if you are not familiar with it. But from the point of view of developing common components and some reusable components, TS will assist in development and increase readability.

If there are errors in the content, please point them out.

Ts of the props

This problem mainly exists in projects that use TS development, and is demonstrated from the perspective of string, mixed string/ Object, and array (the default value is not written), regardless of whether JSX is used.

In the javascript development component, props type validation is written as follows

props: {
    propA: String.propB: [String.Object].propC: [Array]}Copy the code

If A can only be one of A, b, c, and b can only be CSS styles, then the object needs to be limited and c can only be A combination of A /b/ C in the data. You might use A validator. But it has no type and is not friendly to development hints.

So if I use ts instead of writing it,

import { PropType, CSSProperties } from 'vue';

props: {
  propA: String as PropType<'a' | 'b' | 'c'>,
  propB: [String.Object] as PropType<string | CSSProperties>,
  propC: [Array] as PropType<Array<'a' | 'b' | 'c'>>,
}
Copy the code

The obvious benefits are as follows:

Necessary code tips for lazy people ~

If you look at the source code for Ant-design-Vue, you’ll see that they use a library called vue-types to encapsulate types. If appropriate, you can use this method directly, portal

You don’t need to define the type of the first parameter to setup. It conflicts with the props.

Slots in JSX

This was demonstrated in vue3-admin with the card component, portal

If you want a component to support JSX better, you need to consider whether the component’s slot content is passed by props or by slots. The former is most commonly found in JSX syntax, while the latter is mostly in.vue template syntax.

Analyzing the Card component

import { SetupContext, EmitsOptions, } from 'vue';

setup(props, ctx: SetupContext<EmitsOptions>) {
    return () = > {
      // This will not track dependencies used in the slot
      const slotDefault = getSlot({ ctx });
      const slotTitle = getSlot({ props, ctx }, 'title');
      const slotFunc = getSlot({ props, ctx }, 'func');

      const cardClass = classnames('vra-card', props.border && 'vra-card-border');
      const headerClass = classnames('vra-card-header', props.headerClass);
      const bodyClass = classnames('vra-card-body', props.bodyClass);

      return (
        <div class={cardClass}>
          {slotTitle && (
            <div class={headerClass} style={props.headerStyle}>
              <div class="vra-card-title">{slotTitle}</div>
              {slotFunc && <div class="vra-card-func">{slotFunc}</div>}
            </div>
          )}
          <div class={bodyClass} style={props.bodyStyle}>
            {slotDefault}
          </div>
        </div>
      );
    };
  }
Copy the code

The getSlot method is used to automatically get the content of the slot in the source by name, either in the vUE slot way or in the JSX attribute way, code:

  
import { ComponentPublicInstance, SetupContext, EmitsOptions } from 'vue';

/** * Obtain the contents of the specified slot * method setting: Vue component v-slot has higher priority than props **@param Param0 component instance instance, SetupContext (setup takes two parameters), props *@param Name Specifies the name of the slot or props@returns VNode* /
export const getSlot = ({ instance, ctx, props = {} }: { instance? : ComponentPublicInstance; ctx? : SetupContext<EmitsOptions>; props? : any; }, name ='default'
) = > {
  consttargetSlot = instance? .$slots[name] || ctx? .slots[name];return (targetSlot ? targetSlot(instance) : ' ') || props[name];
};
Copy the code

In the card component, the annotation slot must be inside the function component (render) method. Since the slot content is manually fetched by the method, if you’ve looked at setup, it’s only executed once in its entire life cycle, regardless of whether there’s a subsequent state change, if you get the slot content in setup, then if there’s a subsequent update to the slot content, there’s no corresponding change in the card. On the other hand, if it is a render function in setup or an external render function, the render function executes each state change, ensuring that the latest slot contents are available.

Bidirectional binding in JSX

JSX can be used to use the instruction, but still do not need to do this plan, JSX can be used to replace the JS syntax. .

Native input tags, such as input, Textarea, and so on, can be bound bidirectional using vModel

import { defineComponent, reactive, watchEffect } from 'vue';

export default defineComponent({
  setup() {
    const test = reactive({
      a: 1
    });

    watchEffect(() = > {
      console.log(test.a);
    });

    return () = > <input vModel={test.a} />; }});Copy the code

Presentation:

The TSX will tell you that the vModel does not exist, and you need to define your own type. The author did not use this method, so he did not try further. The project is self-binding data

setup() {
  const val = ref(' ')
  return () = > <input value={val.value} onChang={({ target }: Event) = > (val.value = (target as HTMLTextAreaElement).value)} />
}
Copy the code

Target needs an assertion to work properly, so it looks like more code is being written. Aha!

Antd icon

This item usually appears when developing an Admin project. Configure the left side menu like this:

Ant – Design: The React UI library and vue UI library have both removed the icon from the react UI library and introduced a separate dependency to use the icon. It is convenient to load the icon on demand. However, there are only two ways to implement the icon configuration menu in the database and then display it when the route configuration is obtained.

  1. will@ant-design/icons-vueImport all of the components in the server, do an enumeration, configure the component name on the server, and match the components by the component name when rendering, like this:

The key element – file

import { WifiOutlined, WomanOutlined } from '@ant-design/icons-vue'

export default {
  WifiOutlined: <WifiOutlined />,
  WomanOutlined: <WomanOutlined />
}
Copy the code

use

const router = [{path: '/index'.meta: { title: 'home'.icon: 'WifiOutlined'}}]// render
<div>{ keyElement[routerItem.meta.icon] }</div>
Copy the code

It’s not scientific. There are too many ICONS. The following method is optimized.

  1. The principle is similar, it will import all, but not manually import:
import { h } from 'vue';
// Import all components
import * as Icon from '@ant-design/icons-vue';

// The obtained configuration
const router = [{path: '/index'.meta: { title: 'home'.icon: 'WifiOutlined'}}]// render
<div>{ h(Icon[router.meta.icon]) }</div>
Copy the code

Not surprisingly, using TS will prompt the following error:

(property) MenuType.iconName? : any Element implicitly has an ‘any’ type because expression of type ‘any’ can’t be used to index type…

Here we need to specify the type of meta-icon:

import Icon from '@ant-design/icons-vue/lib/icons';

export interface MenuType {
  path: string; children? :Array<MenuType>; meta? : { icon? : keyoftypeof Icon;
  }
  // Other types
  // ...
}

const router: MenuType[] = [{path: '/index'.meta: { title: 'home'.icon: 'WifiOutlined' }}]
Copy the code

Vue3’s H method is similar to React’s creatElemet method.

At the end

Temporarily write here, after the thought of update, are more basic, look at it once.