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