what is it
It is Vue3
. It is a new way of writing vue
components, which realizes the React hooks
, the usage is flexible and simple, and the type inference ability is enhanced, allowing Vue
to build There is also a place for large-scale applications.
Why use
Because OptionsAPI
, developers who are satisfied with the JQ era can quickly get started because the writing method is very similar to the previous writing method.
However, the current Web applications are becoming more and more complex, the front end will do more things, using OptionsAPI will bring a lot of confusion, just use data、 computed、 methods、 watch
these, in most cases, it is understandable, because there is no mental burden It's so heavy, it's easy to understand. However, when the components become larger, the business becomes more complicated, the area of logical focus becomes longer, and the screen display is limited. I need to jump back and forth in different areas. When I add a part of the logic, I need to jump repeatedly and shuttle back and forth in the code.
Reuse
Perhaps you will say, ah pulled out the code, we have mixins
, we have extends
, we not only have a combination, as well as inheritance, however, since this is vue
to achieve, not native
, so could not even peek
, cmmand + left click will only tell you that the definition cannot be found
this.productList = this.generateProducts(productGroups);
such as this line of code, is very common, but if I tell you that generateProducts
is not in the current file, how do you search?
Believe that you will be smart, you will directly search globally. If this is a micro front-end application, your method comes from the base application, do you have to search the application or open a workspace?
Or you are familiar with the file structure, go extends
in the search base class, go mixins
array searching one by one, and finally go global install
in search, even to search Vue.prototype.xx
, data sources completely blurred, this is a black box, and finally, you Found that method, you know its logic, but it's late at night. The girlfriend is asleep.
Naming conflict
Smart you, you have solved the above problems, and at the same time, you have also learned to use mixins
, the code can be reused, and you are excited to write a mixins
export default {
data() {
return {
x: 0,
};
},
methods: {
// ...
},
computed: {
double() {
return this.x * 2;
},
},
};
Then the other one you quoted from your teammate’s mixin
also has this. The end result is that there is a problem with the program operation and the naming conflict needs to be resolved. So in order to check this issue, it was found late the next night that your girlfriend fell asleep again.
In Vue 2, mixin is the main tool for abstracting some component logic into reusable blocks. However, they have several problems:
- Mixins are prone to conflicts: because the attributes of each feature are merged into the same component, in order to avoid
property
name conflicts and debugging, you still need to understand every other feature. - Reusability is limited: we cannot
mixin
to change its logic, which reduces their flexibility in abstract logic
to sum up
Problems with object API
- The code is organized by API type, and the complexity is high and repeated horizontally
- Not conducive to reuse
- Potential naming conflict
- Context loss
Capabilities provided by the combined API
- Organized by function/logic
- Extremely easy to reuse
- Can be flexibly combined
- Provide better context support
Basic usage
In summary, it would be better if we could configure the code related to the same logical concern together. Vue has launched CompositionAPI
to help us do this.
setup
Execution mechanism
setup
are creating component instance and completedprops
executed after initialization, also in after beforeCreate hook, created before execution, can not access the option (data, comupted, methods, etc.) option,option
can usesetup
variable returned. In other words, that the use ofnuxt
, wenuxt
given thoseoptions
such as underfetch、asyncData
, can also be usedthis.xxx
access tosetup
returned xxxIt will only be executed once, and its return value is used as the connection point between data and logic.
- Without this: setup() has been called before parsing other component options
Two parameters are accepted:
- props: component passing parameters
- context: execution context, including three attribute methods:
attrs
,slots
,emit
import { toRefs } from "vue"; export default { props: { title: String, }, setup(props) { console.log(props.title); const { title } = toRefs(props); console.log(title.value); }, };
Among them, props is responsive. Of course, don't deconstruct it. Once deconstructed, the response state will be lost. If you want to deconstruct it, you need toRefs
to complete the operation safely.
And context
is a normal JavaScript
object, that is, it is not responsive, which means you can safely use ES6 deconstruction context
Ref/Reactive
Use these two functions to make the data responsive, but there is a difference between the two. If you are using a value type, you'd better use
ref
, if it is an object type, usereactive
.Which,
ref
use, need.value
, andreactive
object is automaticallyUnWrap
, without the use of.value
, however, can not be deconstructed, because it would lose the response status, if you must, then,toRefs
import { ref, reactive } from "vue"; let foo = 0; let bar = ref(0); foo = 1; bar = 1; // ts-error bar.value = 1; // 关于.value,官方说法就是可以利用它,良好的区分数据是ref的还是普通的,当然,使用起来确实烦了一点 const foo = { prop: 0 }; const bar = reactive({ prop: 0 }); foo.prop = 1; bar.prop = 1;
Of course, in template and watch , .value is not needed, it will automatically unwrap
const counter = ref(0);
watch(counter, (count) => {
console.log(count);
});
Of course, sometimes you will say that if I have a function, it can receive both responsive data and non-responsive data. How to write this? Fortunately, the official also provides a unref
operation
If you pass in a Ref, return this .value
, otherwise, return it as it is
function unref(r) {
return isRef(r) ? r.value : r;
}
- Life cycle
Option API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
Actually add on
export default {
setup() {
// mounted
onMounted(() => {
console.log("Component is mounted!");
});
},
};
Becausesetup
around thebeforeCreate
andcreated
lifecycle hooks, there is no need to explicitly define them. In other words, any code written in these hooks should besetup
function.
Provide / Inject
We can also use provide/inject in combined APIs. Both can only be called during setup() of the currently active instanceBecause they are used in
setup
, they are also more flexible and can be turned into responsive.Of course, one thing to note is that once it becomes responsive, the place to modify the corresponding value should be modified at the location provided by the data. The convention is greater than the configuration. Because you can change it inside, but that will make the data uncontrollable.
<!-- src/components/MyApp.vue --> <template> <Marker /> </template> <script> import { provide, reactive, ref } from 'vue' import MyMarker from './MyMarker.vue; export default { setup() { const location = ref("North Pole"); const geolocation = reactive({ longitude: 90, latitude: 135, }); const updateLocation = () => { location.value = "South Pole"; }; provide("location", location); provide("geolocation", geolocation); provide("updateLocation", updateLocation); } } </script> <!-- src/components/MyMarker.vue --> import { inject } from 'vue' export default { setup() { const userLocation = inject('location', 'The Universe') const userGeolocation = inject('geolocation') const updateUserLocation = inject('updateLocation') return { userLocation, userGeolocation, updateUserLocation } } }
Advantages of logical areas together
If in vue2.x, if we write a page with request loading and error views, we might write like this
<template>
<div>
<div v-if="error">failed to load</div>
<div v-else-if="loading">loading...</div>
<div v-else>hello {{ fullName }}!</div>
</div>
</template>
<script>
import { createComponent, computed } from 'vue'
export default {
data() {
// 集中式的data定义 如果有其他逻辑相关的数据就很容易混乱
return {
data: {
firstName: '',
lastName: ''
},
loading: false,
error: false,
},
},
async created() {
try {
// 管理loading
this.loading = true
// 取数据
const data = await this.$axios('/api/user')
this.data = data
} catch (e) {
// 管理error
this.error = true
} finally {
// 管理loading
this.loading = false
}
},
computed() {
// 没人知道这个fullName和哪一部分的异步请求有关 和哪一部分的data有关 除非仔细阅读
// 在组件大了以后更是如此
fullName() {
return this.data.firstName + this.data.lastName
}
}
}
</script>
Data and logic are scattered in each option
, this is just a logic, if there is more logic, more data, computed, methods? If you are a new person to take over this file, how can you quickly distinguish that method
is associated with data
So, if we use vue3, with a library called vue-request
<template>
<div>
<div v-if="error">failed to load</div>
<div v-else-if="loading">loading...</div>
<div v-else>hello {{ fullName }}!</div>
</div>
</template>
<script>
import { computed } from "vue";
import useSWR from "vue-request";
export default {
setup() {
// useRequest帮你管理好了取数、缓存、甚至标签页聚焦重新请求、甚至Suspense...
const { data, loading, error } = useRequest("/api/user", fetcher);
// 轻松的定义计算属性
const fullName = computed(() => data.firstName + data.lastName);
return { data, fullName, loading, error };
},
};
</script>
How simple and straightforward. However, if there is still a lot of logic, some people will approve, or write spaghetti code, then it depends on the powerful multiplexing operation.
Reuse
As we mixins
, the shortcomings of using 060dd341932e7e, then after using the combined API, it is much easier for us to reuse.
import { ref, onMounted, onUnmounted } from "vue";
export function useMousePosition() {
const x = ref(0);
const y = ref(0);
function update(e) {
x.value = e.pageX;
y.value = e.pageY;
}
onMounted(() => {
window.addEventListener("mousemove", update);
});
onUnmounted(() => {
window.removeEventListener("mousemove", update);
});
return { x, y };
}
This is an example of mouse movement. In the custom hook
, we returned the x,y
. After all, in fact, we only care about the values of x
and y
Or a simple example of counting down seconds. I pass in a value and return the remaining seconds.
import { ref, onMounted, onUnmounted } from "vue";
export function useCountDown(initSec = 60) {
const sec = ref(initSec);
function tiktok() {
sec.value--;
}
let timer = null;
onMounted(() => {
timer = setInterval(() => {
tiktok();
}, 1000);
});
onUnmounted(() => {
timer && clearInterval(timer);
});
return sec;
}
Use them in components
import { useMousePosition } from "./mouse";
export default {
setup() {
const { x, y } = useMousePosition();
const sec = useCountDown(100);
return { x, y, sec };
},
};
It's simply too easy. The code is also very simple, there is no evil this. Similarly, you can even use useB in useA and useC in useB
Compare React Hooks
First of all, let’s look at the useCountDown
written in the multiplexing above and write it out with react hooks
.
import { useState, useEffect } from "react";
function useCount() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount((v) => v - 1);
}, 1000);
return () => clearInterval(timer);
}, []);
return {
count,
};
}
export default function App() {
const { count } = useCount();
return <div>{count}</div>;
}
It's like right or wrong.
In fact, there are many problems with React Hook
- Do not call Hook in a loop, condition or nested function, because it is a linked list structure
- The dependency of useEffect is easy to cause mental burden. Everyone who reads this code needs to read the place where these dependencies are triggered.
- Due to closures, internally captured variables such as useEffect are all obsolete variables.
And Vue does not have the above three problems. And because the setup function is only called once, the performance is superior. Of course, the fundamental reason is because its data is responsive, and I can read the latest value directly by changing it.
VueUse
A hooks library that uses a combined API, you will definitely want to use. From the official team.
Do I still need vuex?
Because we use the combined API, you don't actually need vuex
, because I reuse it very easily, just create a reactive
it's done, right?
It's a bit similar to the recoil state management from React, which is mainly concurrent
mode.
// react
export const state = atom({
key: "testState",
default: {
foo: 1,
bar: "hello",
},
});
const [state, setState] = useRecoilState(state);
setState({
...state,
foo: 2,
});
// vue
// shared.ts
export const state = reactive({
foo: 1,
bar: "hello",
});
// a.vue
import { state } from "./shared.ts";
state.foo = 2;
// b.vue
import { state } from "./shared.ts";
console.log(state.foo); // 2
But vue
write state sharing scheme is not supported ssr
of
If you want to be compatible with ssr
, you have to write a plug-in and use the application-level provide
to do it
export const globalStateKey = Symbol();
export function providerGlobalState() {
const state = {};
return {
install(app) {
app.provide(globalStateKey, reactive(state));
},
};
}
export function useGlobalState() {
return inject(globalStateKey);
}
// main.ts
app.use(providerGlobalState());
// xx.vue
import { useGlobalState } from "";
const state = useGlobalState();
Two functions, the idea is actually quite createContext / useContext
in react hooks. react-context
Including vue-routerV4
in useRouter
and createRouter
also in a similar manner. createRouter source code location
useRouter source code location
But the program can only be vue3
use, vue2.x
in the case, could not do without vuex
, but combined to a certain extent have been solved much of the problem of the state.
How to use in Vue 2.x
Install @vue/composition-api
When migrating to Vue 3
, simply replace @vue/composition-api
vue
. There are almost no additional changes to your existing code.
Almost zero cost, because it optionsAPI
, so use it boldly.
script-setup
The latest script setup rfc proposal
Its motivation is "The main goal of the proposal is to reduce the verbosity of Composition API
used in SFC by directly exposing the context of <script setup>
The simple explanation is that if you use the setup method
<script>
import { ref, computed } from 'vue'
export default {
setup () {
const a = ref(3)
const b = computed(() => a.value + 2)
const changeA = () => { a.value = 4 }
return { a, b, changeA } // have to return everything!
}
}
</script>
And to use <script setup>
, you only need
<script setup>
import { ref, computed } from "vue";
import Foo from './Foo.vue'
import MyComponent from './MyComponent.vue'
const a = ref(3);
const b = computed(() => a.value + 2);
const changeA = () => {
a.value = 4;
};
</script>
<template>
<Foo />
<!-- kebab-case also works -->
<my-component />
<div>{{ a }}</div>
<div @click="changeA">{{ b }}</div>
</template>
It will automatically take all the things you declare, not only data, calculated properties and methods, even instructions and components can also be automatically obtained template
The advantage of this is that you don’t need to write those tedious things, your code is more streamlined, and you don’t need to write return
However, two problems I found, first, lint
problem, it is unused
, and second, I setup function
I can choose return
who, but with this, I may just be a temporary variable declarations computing, but also It will be exposed. If this boundary problem cannot be solved, I think it is only used for the use of some custom Hooks, so that I can deconstruct what I want when getting it.
Final summary
compostionAPI
appear, so usevue
write complex applications no longer become stretched- With
compostionAPI
, we can make our code better than what we wrote before (familiar with the design patterns) and write more elegantly - For a newcomer, if you start from
setup
, it will be easieroptions
- Future
vue
andreact
more like, you know,react
order to launchconcurrentmode
done a lot of groundwork. I don't know howvue
will follow up with similar strategies.
Reference article
- VueUse author Anthony Fu shares composable Vue
- The Vue3 animation that has been busy all night is very good, but it is too short
- What is so good about (Detailed comparison with React Hook)
- introduce Vue Conf 21 conference: especially mentioned script setup syntax!
- Vue CompositionAPI trap
- Vue Conf 21 script setup
- Vue2.x vueuse demo
- Vueuse document
- Vue-Demi help you automatically introduce vue2 or vue3 library, developing plug-ins necessary
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。