vite + vue3 + ts Usage Summary

At this stage, the use of vite + ts in the development of vue3 projects should have become a standard paradigm. The new syntax experience vue composition api is combined with script setup. Who knows? In terms of development and construction, vite, as a next-generation construction tool, must be understood by everyone. Using ES6 module + ESbuild to support local development, speed and efficiency take off, just one word - cool, I don't think it's necessary to talk about TypeScript. If you haven't got on the bus yet, hurry up~

<!-- more -->

preface

vite, as a construction tool, is good enough to know how to use. It supports many functions by default (css module, less, scss). As the father of vue, it also has good support for vue. At present, the utilization rate is also very high. Many large projects such as nuxt have been supported, combined with documents and communities, At present, it is enough, and there is no need to worry about difficult and Miscellaneous Diseases ~, get in the car~

vue3, I feel that the biggest change is the comprehensive embrace of functional programming. Combined with the composition api, it is now really easy to manage complex business code, abandon a lot of unfriendly mixin s in the past, and use the current hooks for processing. Logic reuse and function module splitting are very convenient, and the syntax and api are also elegant and convenient, It's worth a try

Another highlight is vue3's good support for ts. now the project can fully embrace the TS writing method, and then combine it with setup and several tools I recommend next. It's not too cool

For TS, you should first define types, which is different from the traditional writing of JS, but this step is very necessary and worthwhile, which is of great benefit to your next work or the future of the project

In such a scenario, interface with the backend interface:

In the early stage, we get the interface document, define the corresponding TS type according to the format and type, and write the interface and business logic in combination with Mock. When using ts, we can efficiently complete code development, greatly avoid mistakes, and provide great guarantee for later maintenance iterations

import.meta

Using vite as a build tool, you can import Meta gets the corresponding method, which is convenient and fast for business processing

Environment variable acquisition

import.meta.env

// console.log(import.meta.env)
{
    "BASE_URL": "/",
    "MODE": "development",
    "DEV": false,
    "PROD": true,
    "SSR": false
}

be careful:

coordination. env/. env. development/. env. When setting environment variables in production and other files, the variable Key should be VITE_ Prefix

{
  "script":{
    "dev": "vite --mode development"
  }
}

To prevent accidental disclosure of Env variables to clients, only vite_ Variables that are prefixes are exposed to vite's code. Vite will only be sent to your client source code_ SOME_ Key public import meta. env. VITE_SOME_KEY, but DB_PASSWORD will not.

Batch processing files

import.meta.globEager

// Read all files in the current directory ts file
const modules = import.meta.globEager('./**/*.ts')

ref and reactive

Can be used to define responsive data

ref

It is mainly used to define basic types, which need to be passed value read or modify

Basic types: remove objects, including: String, Number, boolean, null, undefined

The console print data structure is RefImpl

// ref
const count = ref(0)

count.value++
console.log(count.value)

When defining basic types, the principle of responsiveness is the same as vue2 X similar object Defineproperty(), reading and modifying data through get and set

However, ref can also define data of reference type. Note that when defining reference type, its internal implementation is through reactive

You can view the structure on the console by printing data, including RefImpl and Proxy

reactive

Only reference types can be defined, i.e. Object, including Object, Array, Date and function. Warnings will be given when defining basic types

When used, it can be read and written directly through attributes

// reactive
const state = reactive({count:0})

state.count++
console.log(state.value)

reactive handles all attributes in the object in a responsive manner by default, and can implement deep listening

This responsive capability is implemented through ES6 Proxy. It can listen for the addition and deletion of attributes, solve the defects of defineProperty, and has good support for nested attributes. It can easily realize the responsive update of a.b.c.d=xx

Both Proxy and Reflect are ES6 syntax. Generally, they are used together to update attributes safely and gracefully

Summary

The template tempalte will be unpacked automatically and is not required when used in the template value

For reference types, a simple understanding is that ref is also reactive in nature, ref(obj) is equivalent to reactive({value: obj}), and the underlying implementation of ref is reactive

It can be found that the so-called response is actually the hijacking of attributes

Each layer of data defined by ref and reactive is responsive

watch,watchEffect

Monitor for changes in responsive data

watch

The basic syntax is similar to vue2, but there are some different ways to use it here

Listen for responsive data defined by ref (basic type)

  • Functional writing requires Value, which monitors the change of a value
const count = ref(0);
const str = ref('abc');

// 1. Common writing
// watch can be omitted value
watch(count, (val, old) => console.log({ val, old }));

// 2. Function writing
watch(
  () => count.value,
  (val, old) => console.log({ val, old }),
);

// 3. Array writing
watch(
  () => [count.value, str.value],
  (val, old) => console.log({ val, old }),
);

Listen for responsive data defined by ref (reference type)

  • It should be understood that ref defines the reference type, which is implemented internally using reactive. Therefore, it needs to pass value gets the responsive object, and then listens for properties
const refState = ref({
  count: 0,
  str: 'abc',
});
// 1. Common writing, invalid
// => refState. Value is valid
watch(refState, (val, old) => console.log({ val, old }));
// 2. Function writing
watch(
  () => refState.value.count,
  (val, old) => console.log({ val, old }),
);

Listen for reactive defined responsive data

  • You need to listen to state. For the attribute count
const state = reactive({
  count: 0,
  str: 'abc',
  a: {
    b: {
        c: 'a-b-c',
      },
    },
});

// 1. Common writing
// Result: the old and new values of Val and old are the same,
// watch(state, (val, old) => console.log({ val, old }));

// 2. Function writing
// Result: only when the specified attribute changes will it be triggered
watch(
  () => state.value.a.b.c, // Listen only for specified properties
  (val, old) => console.log({ val, old }),
);

watchEffect

Receive a function without setting a listening object. This method will automatically take over the dependencies used inside the function. When the dependencies are updated, the function execution will be triggered

This function initializes and executes once by default

watchEffect(()=>{

  if(state.count>1){
    // The watchEffect function executes once whenever the count changes
    // When count > 1, do the corresponding behavior
  }

})

Summary of watch and watchEffect

There are many situations to consider when using watch

watch emphasizes the result, and watchEffect emphasizes the process

In terms of usage, watchEffect seems easier to use~

shallowRef and shallowReactive

  • Recursive listening and non recursive listening

Both ref and reactive belong to recursive listening, that is, each layer of data is responsive. If the amount of data is large, it will consume performance. Non recursive listening will only listen to the first layer of data.

props and context processing method of script setup

When you use setup in the form of < script setup lang = "ts" > by default, the entire script is the function scope of setup. We don't have to return each variable and method defined one by one. We can use it directly

However, the definition of props, emit, and the acquisition of ctx attribute are not enough

vue also provides us with three new API s for this

  • defineProps
  • defineEmit
  • useContext
// These three APIs correspond to the properties of setup one by one
setup(props, { emit, ctx}){}

If you want to obtain the properties of a child component through the parent component, you need to define the properties to be exposed in the child component through defineExpose

// Subcomponent Child
const count = ref(0)
defineExpose({
  count,
});

// Parent component
// <Child ref="ChildRef" />
const ChildRef = ref<RefType<{ count: number }>>(0);
const count = ChildRef.value.count

See the official documentation for more API s, which is very detailed and will not be repeated here

props Type type definition problem

Despite the default basic types of vue, complex types need to be defined in some special scenarios, which need to be used in combination with PropType

For example, define the menu route type

props: {
    menuData: {
        type: Array as PropType<MenuDataItem[]>,
        default: () => [],
    },
}

Here, if the conventional type Array is difficult to meet our needs (only know that it is a data, but the data shape is not clear), it is difficult to accurately deduce the type definition of each attribute in the original type writing method

prop, ref, emit data communication

prop

Emphasize single data flow (parent = > child), similar to react, which is mainly used to pass parameters to child components

ref

Two uses:

  • Refer to the instance of the sub component to ref by reference, so that all the properties and methods in the sub component can be obtained from the parent component, which can be implemented in conjunction with the defineExpose API
  • Used to get DOM elements
// For example, when Binding DOM nodes with echorts
// <div class="chart-box" ref="chartRef"></div>

const chartRef = ref<HTMLDivElement | null>(null);
echarts.init(unref(chartRef))

emit

It is mainly used for the child component to pass parameters and communication emit (child = > parent) to the parent component, and the parent component receives it through the event method @ event

<!-- Parent component -->
emit('getMessage', 'I am the parent component!')

<!-- Subcomponents -->
<child @event="handleMethod">

jsx syntax

In the process of using, it is found that jsx has great flexibility with template syntax. For jsx syntax, those with react development experience should feel very familiar with it, and the development experience is very similar

However, vue has unique advantages. As a template syntax, it can be developed quickly and efficiently through the injection of instance methods and the use of instructions. In some scenarios, jsx syntax + vue template syntax has a completely different experience~

For example, < div @ Click = "$router. Push ('xx ')" v-auth = "create" > < / div >

Table component

Take the more common table component as an example. After encapsulating the reusable paging logic, we split the columns single out for use, encapsulate it with jsx syntax, and make different configurations according to the use of different components, which is also more convenient for maintenance

export function columnsConfig(refresh: () => void) {
  // ...  Other business logic

  const columns: ColumnProps[] = [
    {
      title: 'IP Address and port',
      dataIndex: 'ip',
      width: 150,
      customRender: ({ record }) => `${record.ip}:${record.port}`,
    },
    {
      title: 'operation',
      key: 'action',
      width: 200,
      fixed: 'right',
      customRender: ({ record }) =>
        <Space>
          <Button type="primary" onClick={() => router.push(`/app/product/detail/${record.id}`)}>details</Button>
          <Divider type="vertical" />
          {
            record.isSelf && <Popconfirm
              title="Are you sure you want to exit the network?"
              onConfirm={async () => {
                const res = await fetchApi.delete(record.id);
                if (res) {
                  message.success(`You have applied to quit the network`);
                  // Trigger list update
                  refresh?.();
                }
              }}
            >
              <Button>delete</Button>
            </Popconfirm>
          }
        </Space>
    },
  ];

  return columns;
}

When the business of the action operation column is complex and needs to communicate with other data frequently, we can also peel the action operation column out and process it in vue, and then cooperate with the re encapsulation of the Table component

Table component encapsulation

<template>
  <a-table :columns="columns">
    <!-- Functional writing custom operation column -->
    <template #action="{ record }">
      <template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
        <!-- Bubble confirmation box -->
        <a-popconfirm
          v-if="action.enable"
          :title="action?.title"
          @confirm="action?.onConfirm(record)"
          @cancel="action?.onCancel(record)"
        >
          <a @click.prevent="() => {}" :type="action.type">{{ action.label }}</a>
        </a-popconfirm>
        <!-- Button -->
        <a v-else @click="action?.onClick(record)" :type="action.type">{{ action.label }}</a>
        <!-- Split line -->
        <a-divider type="vertical" v-if="index < getActions.length - 1" />
      </template>
    </template>
  </a-table>
</template>
<script lang="ts">
// Action action column
const getActions = computed(() => {
  return (toRaw(props.actions) || [])
    .filter((action) => hasPermission(action.auth))
    .map((action) => {
      const { popConfirm } = action;
      return {
        type: 'link',
        ...action,
        ...(popConfirm || {}),
        enable: !!popConfirm,
      };
    });
});
</script>

use

// <Table :columns="columns" :actions="tableActions"/>

export const columns = [
  // ...
  {
    title: 'operation',
    key: 'action',
    width: 120,
    slots: { customRender: 'action' },
  },
]

const tableActions = ref([
  {
    label: 'edit',
    auth: AuthEnum.user_update, // Configure button permissions
    onClick: async (row) => {
      modalState.visible = true;
      const res = await store.fetchDetail(row.id);
      if (res) formModel.value = res;
    },
  }
  // ...
]

This is my experience of actual use in the last project. It is very obvious for the improvement of development efficiency and convenient for maintenance. You are also welcome to exchange and learn more usage. For the current experience, vue3 is great~

event bus

The behavior of attaching $emit in the instance is removed from vue3. If you want to continue using, you can download the corresponding npm package separately, such as: mitt , the package is very light, only 200byte

The api is similar to the usage, except that for functional creation, you need to ensure that the emitter creation of a single operation is unique

import mitt from 'mitt'

const emitter = mitt()

export emitter

epilogue

This article is actually equivalent to your own learning notes, but also to deepen your impression. It records some problems encountered in the process of use, hoping to bring some help to yourself and everyone. As far as the content is concerned, it belongs to the entry-level use level, and the deep-water area is not involved at present. This article will be continuously updated according to the use situation

Keywords: TypeScript Vue Vue.js vite setup

Added by miro_igov on Mon, 17 Jan 2022 14:52:45 +0200