Vite + TS has become the standard paradigm for vuE3 project development. The new syntax experience vue Composition API is combined with Script Setup. Who knows, Vite is the next generation of building tools for development and construction. With ES6 Module + ESbuild support for native development, speed and efficiency will take off in one word
preface
Vite as a build tool, we currently understand how to use it, the tool is good enough, the default for many functions have done support (CSS Module, LESS, SCSS), and as the father of VUE especially great works, for VUE also has good support, the current utilization rate has been very high, Nuxt and other large projects are supported, combined with documentation and community, the current use is enough, there is no need to worry about problems ~, get on the bus ~
Vue3, the biggest change is the full embrace of functional programming, the composition API now makes it really easy to manage complex business code, get rid of this and this a lot of unfriendly mixins, use hooks now, Logic reuse, function module split is simply too convenient, syntax, API use is also elegant and convenient, it is worth a try
Another highlight is vue3’s excellent support for TS. Now projects can fully embrace TS writing, and combine it with Setup and the next few tools I recommend
For TS, the first thing to do is to define the type, which is different from the traditional writing JS, but this step is necessary and worthwhile, which is good for your future work or for the future of this project
For example, interfacing with a back-end interface:
In the early stage, we got the interface document, defined the corresponding TS type according to the format and type, and combined with Mock to write the interface and business logic. When using TS, we could efficiently complete the code development and avoid mistakes greatly, which greatly guaranteed the later maintenance iteration
import.meta
Using Vite as a building tool, you can obtain the corresponding method through import.meta to facilitate and fast processing of business
Environment variable acquisition
- in
package.json
Is defined as a command line ("dev:development": "vite --mode development"
) - in
.env
Is defined as a configuration file (VITE_APP_ENV = dev
)
When the. Env. development file is configured as follows:
VITE_APP_ENV = dev VITE_APP_TITLE = I am titleCopy the code
Note:
When reading variables configured in.env.development, make sure that the mode parameter of vite –mode development is development
invite.config.ts
In the
You can read the mode argument in a functional way (if you want to read configuration information in.env.development from this file, you can do so in conjunction with the Dotenv library).
import { UserConfig, ConfigEnv } from 'vite';
export default ({ command, mode }: ConfigEnv): UserConfig= > {
console.log(command, mode);
return {
// ...}}Copy the code
In the component
Console. log(‘my config env: ‘, import.meta.env) prints the following
// console.log(import.meta.env)
{
BASE_URL: "/"
DEV: false
MODE: "development"
PROD: true
SSR: false
VITE_APP_ENV: "dev"
VITE_APP_TITLE: "I am the title."
}
Copy the code
Note:
Cooperate. Env/env. Development /. Env. The production and other documents to set up the environment variables, when using variable Key should VITE_ as prefix
{
"script": {"dev": "vite --mode development"}}Copy the code
To prevent accidental leakage of env variables to clients, only variables prefixed with VITE_ are exposed to Vite code. Only import.meta.env.vite_some_key is exposed to your client source VITE_SOME_KEY, but DB_PASSWORD is not.
Batch file processing
import.meta.globEager
// Read all.ts files in the current directory
const modules = import.meta.globEager('./**/*.ts')
Copy the code
Ref and reactive
Can be used to define reactive data
ref
It is used to define basic types, which need to be read or modified through.value
Basic types: Excluding Object, including String, Number, Boolean, NULL, and undefined
The console prints the data structure RefImpl
// ref
const count = ref(0)
count.value++
console.log(count.value)
Copy the code
When defining basic types, the responsivity principle is object.defineProperty (), similar to vue2.x, which reads and modifies data via get and set
However, a REF can also define reference type data, and note that when a reference type is defined, its internal implementation is reactive
You can print data to view the structure of RefImpl and Proxy on the console
reactive
Only reference types can be defined, that is, Object, including: Object, Array, Date, function
When used, read and write directly from properties
// reactive
const state = reactive({count:0})
state.count++
console.log(state.value)
Copy the code
Reactive defaults to all properties within an object and enables deep listening
The responsiveness capability is realized by ES6 Proxy, which can add and delete the monitoring of attributes, solve the defect of defineProperty, and has good support for nested attributes, and can easily realize the responsiveness update of A.B.C.D =xx
Proxy and Reflect are both ES6 grammars. generally they are used together, so they can make hijacking updates to attributes in a safe and elegant way
summary
The tempalte template is automatically unpacked and does not need to be used in the template
Ref (obj) is reactive({value: obj}). Ref (obj) is reactive({value: obj})
As you can see, reactive is actually a hijacking of attributes
Each layer of data defined by REF and Reactive is reactive
Watch, watchEffect
Listen for changes in reactive data
watch
The basic syntax is similar to vue2, though there are a few different ways to use it
Listen for reactive data defined by ref (base type)
- The functional way to write it is
.value
, listen for a change in value
const count = ref(0);
const str = ref('abc');
// 1
// watch can omit.value
watch(count, (val, old) = > console.log({ val, old }));
// 2
watch(
() = > count.value,
(val, old) = > console.log({ val, old }),
);
// 3
watch(
() = > [count.value, str.value],
(val, old) = > console.log({ val, old }),
);
Copy the code
Listen for reactive data defined by ref (reference type)
- The thing to understand is,
ref
Defines the reference type that is used internallyreactive
The implementation, therefore, needs to be passed.value
Get the reactive object, and then do the property listening
const refState = ref({
count: 0.str: 'abc'});// 1
// => refstate. value Valid
watch(refState, (val, old) = > console.log({ val, old }));
// 2
watch(
() = > refState.value.count,
(val, old) = > console.log({ val, old }),
);
Copy the code
Listen for reactive data defined by Reactive
- You need to listen for attributes
state.count
const state = reactive({
count: 0.str: 'abc'.a: {
b: {
c: 'a-b-c',}}});// 1
// result: val, old
// watch(state, (val, old) => console.log({ val, old }));
// 2
// Result: only specified property changes are triggered
watch(
() = > state.value.a.b.c, // Only listen for specified attributes
(val, old) = > console.log({ val, old }),
);
Copy the code
watchEffect
This method automatically takes over the dependencies used inside the function and triggers the function to execute when the dependencies are updated
This function is initialized once by default
watchEffect(() = >{
if(state.count>1) {// The watchEffect function is executed once whenever count changes
// If count > 1, do the corresponding behavior}})Copy the code
Summary of Watch and watchEffect
There are many things to consider when using the Watch
Watch is more about results, and Watch Effect is more about process
WatchEffect seems easier to use in terms of usage
ShallowRef and shallowReactive
- Recursive and non-recursive listening
Both REF and Reactive are recursive listeners, that is, each layer of data is responsive. If the data volume is large, performance will be consumed. Non-recursive listeners only listen to the first layer of data.
Script setup: props, context
When using setup as
However, there are issues with the definition of props, EMIT, and getting the CTX attribute
Vue also provides three new apis for this purpose
defineProps
defineEmit
useContext
// These three apis correspond to the setup properties one by one
setup(props, { emit, ctx}){}
Copy the code
If you want to get the attributes of a child component from a parent component, you need to define the attributes to expose in the child component via defineExpose
// Child component Child
const count = ref(0)
defineExpose({
count,
});
/ / the parent component
// <Child ref="ChildRef" />
const ChildRef = ref<RefType<{ count: number} > > (0);
const count = ChildRef.value.count
Copy the code
More API see the official documentation, said very detailed, here will not repeat
Props Type Type definition problem
Aside from the basic types that vue defaults to, there are special scenarios where you need to define more complex types and use propTypes in conjunction with them
For example, define menu routing types
props: {
menuData: {
type: Array as PropType<MenuDataItem[]>,
default: () = >[],}.}Copy the code
In this case, it is difficult to meet our requirements according to the conventional Array type (only known as a data, but the shape of the data is not clear), and the primitive type writing method is difficult to accurately deduce the type definition of each attribute
Prop, REF, EMIT data communication
prop
React emphasizes single-element data streams (parent => child), similar to react, mainly used to pass parameters to child components
ref
Two usages:
-
Pointing instances of child components to Ref by reference makes it possible to get all the attributes and methods in the child component in the parent, which can be done with the defineExpose API
-
Used to get DOM elements
// When echarts is used to bind DOM nodes
// <div class="chart-box" ref="chartRef"></div>
const chartRef = ref<HTMLDivElement | null> (null);
echarts.init(unref(chartRef))
Copy the code
emit
It is primarily used by the child component to pass parameters to the parent component and communicate emit (child => parent), which is received via the event method @event
<! -- Parent component --> emit('getMessage'.'I'm the parent! ') <! -- Subcomponent --><child @event="handleMethod">
Copy the code
JSX grammar
JSX has great flexibility with the template syntax. React developers should be familiar with the JSX syntax
However, for VUE, has a unique advantage, as a template syntax itself, through the injection of instance methods, the use of instructions, can be quickly and efficiently developed, in some scenarios, JSX syntax + VUE template syntax has a completely different experience ~
$router.push(‘xx’)” v-auth=”create”>
The Table component
Take the common table component as an example. After encapsulating the reusable paging logic, we split the columns into separate columns and use JSX syntax to encapsulate them. Different configurations are made according to the use of different components, which is 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={()= >The router. Push (` / app/product/detail / ${record. Id} `)} > for details</Button>
<Divider type="vertical" />
{
record.isSelf && <Popconfirm
title="Are you sure you want to quit the network?"
onConfirm={async() = >{ const res = await fetchApi.delete(record.id); If (res) {message.success(' requested to exit network '); // Trigger list refresh? . (); }}} ><Button>delete</Button>
</Popconfirm>
}
</Space>},];return columns;
}
Copy the code
When the business of action operation column is complicated, it needs to communicate with other data frequently. We can also separate the action operation column and process it inside VUE, and then cooperate with the reencapsulation of Table component
Table Component Encapsulation
<template> <a-table :columns="columns"> <! <template #action="{record}"> <template v-for="(record) index) in getActions" :key="`${index}-${action.label}`"> <! --> < 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> <! <a v-else @click="action? .onClick(record)" :type="action.type">{{ action.label }}</a> <! <a-divider type="vertical" V-if ="index < getActions. Length-1 "/> </template> </template> </a-table> < / template > < script lang = "ts" > / / the 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>Copy the code
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; }},// ...
]
Copy the code
This is my experience in the actual use of the last project, for the improvement of development efficiency is very obvious, maintenance is also very convenient, more usage is also welcome to exchange and learn, as far as the current experience vue3 is great ~
event bus
Vue3 has removed the behavior of mounting $emit in the instance. If you want to continue using it, you can download the corresponding NPM package separately, such as mitt, which is very light and only 200 bytes
The API is similar to the usage, except that for functional creation, we need to ensure that the Emitter creation of a single operation is unique
import mitt from 'mitt'
const emitter = mitt()
export emitter
Copy the code
conclusion
In fact, this article is equivalent to my own study notes, but also to deepen my impression, I recorded some problems encountered in the process of using, hoping to bring some help to myself and everyone. As far as content is concerned, it belongs to the entry level. Currently, it does not involve deep water area. This article will be updated continuously according to the usage situation