`vue3`+`ts`+`setup`Grammatical sugar implements an infinitely nested`tree`component

Posted by n-gen on Sat, 05 Mar 2022 19:57:50 +0100

Today, we use vue3+ts+setup grammar sugar to implement an infinitely nested tree component. First, we will see the effect of the implementation. The style has not been much modified.

This article is very basic, and I am mainly new to vue3. Recently, I have also started to practice using vue3 to implement a set of vue2 components when the company was developing them, and I want to see what people can gain.

setup Syntax Sugar for Vue3

The addition of setup in vue3 allows Vue to change from Options API to Composition API. The Options API causes data, methods, computed, and so on to be implemented in separate objects, making the logic too dispersed. Instead of writing separately, the Componsitions API can write the same function in the same code block in the setup. Some students may feel that all the functions of a component are written together, which will lead to all the code in one place, but will easily lead to logic confusion. In fact, there is no need to worry. vue3 can pull a piece of logic apart like react hook, and a logic is a code block, which is introduced into the component, so that the logic is clear and easy to reuse. It also replaces the mixin notation in vue2. Look at the picture below to see:

In setup syntax sugar, we don't need to use return to return the response variable, function, component, just declare it at the top level, then use it directly in the html template, see the following code:

const {dataList, levelPadL, selectIds} = defineProps({
  // Data Source List
  dataList: {
    type: Array as PropType<BaseOptons[]>,
    required: true
  },
  // Hierarchical reduction, default 10px
  levelPadL: {
    type: Number,
    default: 10
  },
  // Selected id collection
  selectIds: {
    type: Array,
    default: []
  }
})

The three props above, dataList, levelPadL, selectIdsare no longer return ed and can be used directly in templates. In addition, the difference between setup syntax sugar and setup hook function is that props and context parameters cannot be accepted, but there are more global variables, which are mainly used in defineProps,defineEmits. The former declares props that the component needs to accept, and the latter accepts events from the parent component. Here our tree component needs to accept an updated selected event. The idea here is to achieve a two-way binding of props, so props selectIds is implemented using the new syntax v-model:selectIds of vue3. See the code below:

const emits = defineEmits(['update:selectIds'])
// some change 
emits('update:selectIds', selectIds)

Use of Ts and ts in Vue3

First, for props to have a type reminder, we need to define our props type, which is essentially the type of dataList that the tree component receives, an infinitely nested array:

export interface BaseOptions {
    label: string,
    value: string | number,
    children?: BaseOptions[]
}
// Reference the type and use it
const dataList = reactive<BaseOptions[]>([{
  label: 'Unhappy Monday',
  value: '1',
  children: [
    {label: 'Unhappy 1', value: '1-1', children: [{label: 'Unhappy 1-1', value: '1-1-1'}]},
    {label: 'Unhappy 2', value: '1-2', children: [{label: 'Unhappy 2-1', value: '1-2-1'}]},
    {label: 'Happy 3', value: '1-3', children: [{label: 'Happy 1-3-1', value: '1-3-1', children: [{label: 'Happy 1-3-2', value: '1-3-2'}]}]}
  ]
}])
// Accept the type props and mark the type
defineProps({
  // Data Source List
  dataList: {
    type: Array as PropType<BaseOptions[]>,
    required: true
  }
})

You can see that generics of ts are required to define reactive s in vue3 to tag types. If the type of props is declared at runtime, when the type of props is Object or Array, the type needs to be marked with Array as PropType <BaseOptions[]>, which is more cumbersome than the general use of ts.

Component Other Parts

The component uses a two-way bound props selectIds, slightly different from vue2: v-model:selectIds="selectIds", which is more flexible. Subcomponents also modify the props through emits(update:propsName).

Template section
<ul class="treeList" :style="{'padding-left': `${levelPadL}px`}">
    <li v-for="item in dataList" class="item" :key="item.value" :class="{'hasChild': item?.children?.length}">
      <div class="label" :class="{'is-open': isOpen(item.value)}" @click="toggleItem(item)">
        <span>{{item.label}}</span>
        <span class="icon" v-if="item?.children?.length"></span>
      </div>
      <collapseTransition>
        <template v-if="isOpen(item.value)">
          <treeList v-bind="$props" :data-list="item.children"></treeList>
        </template>
      </collapseTransition>
    </li>
  </ul>

You can see the vue3 template, and finally you can use.. The optional chain new syntax is so cool that an empty pointer to an object or array will not render the entire dom. The collapseTransition animation component is based on vue2 and appears to be fully compatible.

Component method
// Expand Collapse Method
const toggleItem = (item: BaseOptions) => {
  const {value, children = []} = item
  if (children.length) {
    const findIndex = selectIds.findIndex(id => value === id)
    if (findIndex > -1) {
      selectIds.splice(findIndex, 1)
    } else {
      selectIds.push(value)
    }
    emits('update:selectIds', selectIds)
  }
}

// Determine whether to expand
const isOpen = (id: Number | String) => {
  return !!selectIds.find(item => id === item)
}

summary

The whole component is still quite simple and we often use it. This is mainly based on vue3+setup+TS to familiarize yourself with the grammar and new features. You can also use it to practice and experience vue3 to see if it is really fragrant and roll up!!!

Finally, you can focus on my personal public number and get the latest push for the first time.

Topics: Front-end Vue.js html Mini Program