Close read Vue official documentation series π
Note: This post is based more on the @vue/ composition-API library.
What is the Composition-API ?
The core purpose of composition-API is to reuse code. Composition-api gives developers access to Vue’s underlying responsive system, as opposed to the traditional Options API, which handles the objects returned by data itself. The composition-API now requires the developer to manually define reactive data in setup.
The disadvantage is that the definition of reactive data is no longer simple and convenient, while the advantage is that the timing and location of the definition of reactive data are no longer strictly limited, and can be assembled more flexibly.
The Options API splits function code based on different Options (categories), such as data in function to data, method logic to Methods, and computed properties to computed.
This arrangement is clear, but as the amount of code increases, it becomes less readable and presents a challenge for reuse of component logic. For example, mixins that still use this approach to reuse their code will cause naming conflicts and unclear data sources.
Composition APIS treat a feature as a whole. The whole itself includes options such as data, methods, computed, and life-cycle, and each function is treated as an independent part.
The Composition API now allows you to write your component logic in the same way that you would write functions in traditional JavaScript. You can see that reactive data has to be declared manually, but the benefit is that these reactive objects and functions can be detached from the component. Enabling sharing and reuse across components.
Logical concerns are grouped by color, and as an added bonus, in high-volume code scenarios, there is no need to scroll around with the mouse to navigate between different options for the same functionality.
Composition-API
VS Options API
Options API | Composition-API |
---|---|
Bad for reuse | Easy code reuse, separation of concerns |
Potential naming conflicts and unclear source of data sources | Clear data source |
Context loss | Provide better context |
Limited type support | betterTypeScriptsupport |
Supported by API type | Organized by function/logic |
Organized by function/logic | Easy code reuse |
Reactive data must be in the component’sdata Defined in the |
Can be used independently of Vue components |
setup
Setup is a new component option that serves as an entry point to the composition-API, and the value is a function that is executed only once to establish a link between data and logic.
Setup execution time is between beforeCreated and created, so this cannot be accessed, and data, methods, computed, etc., cannot be accessed because they have not been resolved.
{
setup(props, context){
context.attrs; //Attributes
context.slots; //slots
context.emit; //tirgger event
context.listeners; // events
context.root; // root component instance
context.parent; // parent component isntance
context.refs; // all refs
return{}; }}Copy the code
The return value of the setup method is incorporated into the context of the template to participate in the rendering of the data.
API,
getCurrentInstance
Gets the component instance that is currently executing the Setup function. Note that getCurrentInstance can only be executed in setup or a lifecycle hook.
import {getCurrentInstance} from 'composition-api';
setup(props, ctx){
const vm = getCurrentInstace();
onMounted(() = >{
vm = getCurrentInstance();
});
}
Copy the code
ref && Ref
Define a reactive REF object with a single property named value inside.
import { ref } from 'composition-api';
setup(props, ctx){
const title = ref('this is a title! ');
setTimeout(() = >{
title.value = 'change title text';
},1000);
return {title}
}
Copy the code
Type declaration
// The type structure of the ref value
interface Ref<T>{
value:T
}
// The type structure of the ref function
function ref<T> (value:T) :Ref<T>{}
Copy the code
Specifically, we can override the generic parameters passed by default when we call the ref() method, or we can declare an assertion directly using as ref
.
Ref needs to be unpacked in the setup method, but not in the template.
isRef
Check if a value is an object of type Ref. The ref() function already has this functionality by default, and if the value accepted is already of a ref type, nothing is processed, otherwise it is converted to a ref value.
unRef
Syntactic sugar, which functions like isRef(val)? Val. Value: val.
toRef / toRefs
A corresponding REF object is mapped based on a Property on the source responsive object. The REF object still maintains a reactive link to the corresponding property on the source reactive object.
import {reactive, toRef} from 'composition-api';
setup(props, ctx){
const state = reactive({foo:1.bar:2});
// Inject a ref object from the property of the source responsive object.
const fooRef = toRef(state, 'foo');
// The reactive link to the source reactive object remains
fooRef.value = 2;
console.log(state.foo);
state.foo++;
console.log (fooRef); }Copy the code
ToRef does not report an error even if the property on the source response to be mapped does not exist. Instead, a new REF object is completely created without links.
ToRefs () is a shortcut to toRef() and is used to convert all properties on a source responsive object to a REF object.
reactive
Create reactive objects that can be deconstructed into references to multiple Ref objects using the toRefs method.
setup(props, ctx){
const userInfo = reactive({
firstName:'shen'.lastName:'guotao'
});
return{... toRefs(userInfo)} }Copy the code
Type declaration:
function reactive<T extends object> (target: T) : UnwrapNestedRefs<T>
Copy the code
This means that the generics accepted by reactive methods must inherit object and then be used as type constraints for passing parameters, whose return value is wrapped around T with UnwrapNestedRefs generics.
Note that if refs and Reactive are used together, the reactVie method can be used to redefine the ref object, which automatically expands the original value of the ref object. Of course, this does not deconstruct the original Ref object.
const foo = ref(' ');
const r = reactive({foo});
r.foo === foo.value;
Copy the code
But you cannot literally add a ref to a reactive object.
const foo = ref(' ');
const r = reactive({});
r.foo = foo; //bad
Copy the code
readonly
Take a reactive object or plain object and return a read-only proxy for them.
import { readonly, toRefs } from 'composition-api';
setup(props, ctx){
const originalUserInfo = readonly(userInfo);
// override reactive objects
userInfo = originalUserInfo ;
return {
...toRefs(userInfo)
}
}
Copy the code
isProxy
Check whether the object is a proxy created by Reactive or Readonly.
isReactive
Check whether the object is a reactive agent created by Reactive.
Note: Reactive objects wrapped by Readonly remain true.
isReadonly
Check whether the object is a read-only proxy created by ReadOnly.
toRaw
Returns the original object of the Reactive or Readonly agent. This is an “escape pod” that can be used to temporarily read data without the overhead of proxy access/trace, or to write data without triggering changes.
// The original object
const foo = {};
//readonlyFoo
const readonyFoo = readonly(foo);
//reactiveFoo
const reactiveFoo = reactive(foo);
// Get the original object again
let orignal = toRaw(reactiveFoo);
Copy the code
It is not recommended to preserve persistent references to the original object. Use with caution.
markRaw
Marks an object so that it will never be converted to a proxy. Returns the object itself.
computed
The computed property functionality provided in composition-API is the same as the computed option provided in OptionsAPI.
import {computed} from 'composition-api';
setup(props, ctx){
const fullName = computed(() = >{
return userInfo.firstName + userInfo.lastName;
});
const pass = computed(() = >{
if(userInfo.score >= 60) return 'pass';
if(userInfo.score < 60) return 'Fail'})};Copy the code
Computed has a computational cache. But when the computed property is used (in the template), then computed function is necessarily executed once, and then if computed property in computed changes, computed function is executed again, returning the value of the most recent computed property.
watchEffect && watch
watchEffect
- The side effect method is executed immediately. And it is reexecuted when the internally dependent reactive values change.
- Dependencies can be collected automatically without specifying listening properties.
- Can be achieved by
onInvalidate
Cancel listening.
import {reactive, watchEffect, toRefs} from 'composition-api';
setup(props, ctx) {
const data = reactive({
num:0.count:0});const stop = watchEffect(() = >{
// Execute immediately, output 0
// Re-execute watchEffect every 1 second when the value changes.
// Count, although updated every 2 seconds, does not trigger the current watchEffect because it is not a dependency of the current watchEffect.
console.log(data.num);
//nInvalidate(fn) the incoming callback is executed when watchEffect is restarted or stopped.
onInvalidate(() = > {
// Cancel the asynchronous API call.
apiCall.cancel()
})
});
setInterval(() = >{
data.num++;
},1000);
setInterval(() = >{
data.count++;
},2000);
return {
...toRefs(data),
onStop(){stop()}
}
}
Copy the code
Note that when a side effect function executes a function that changes the reactive data, it can cause an infinite loop.
watch
- Has lazy execution and does not execute immediately.
- To clarify which dependencies have changed state and trigger the listener to re-execute, it is possible to listen on multiple dependencies.
- The ability to obtain the value before and after the state change.
- You can stop listening manually
// Listen only on reactive objects, not on properties of reactive objects.
watch(data, (newValue, oldValue) = >{
console.log(newValue,oldValue)
})
Copy the code
Listen to multiple data sources:
import { watch, reactive } from 'vue';
export default {
setup () {
const state = reactive({
count: 0.msg: 'hello'
})
const stop = watch([() = > state.count, () = > state.msg],([count, msg], [prevCount, prevMsg]) = >{
console.log(count, msg);
console.log('-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -');
console.log(prevCount, prevMsg);
})
setTimeout(() = >{
state.count++;
state.msg = 'hello world';
},1000);
return{ state }; }};Copy the code
provide && inject
Composition-api style dependency injection:
Parent:
import { provide, ref } from 'composition-api';
setup(){
const title = ref('learn vue');
const changeTitle = () = >{ title.value = 'learn vue and typescript! ' };
provide("title", title);
return {changeTitle}
}
Copy the code
Son
import { inject } from 'composition-api';
setup(){
const title = inject('title');
setTimeout(() = >{title.value ='learn success! '},1000);
return {title}
}
Copy the code
shallowReactive
Only responsivity (shallow responsivity) of the outermost property of the object is handled, so if the outermost property changes, the view is updated, and other layer properties change, the view is not updated.
{
setup(){
const obj = {
x: {y: {z:0}}};const shallowObj = shallowReactive(obj);
shallowObj.x.y.z=1; // Updates will not be triggered
return {shallowObj}
}
}
Copy the code
shallowRef
Reactive only values are processed. For reference values, reactive is not implemented.
customRef
CustomRef is used to create a customRef that explicitly controls the dependency trace and trigger response, takes a factory function with track for the trace and trigger for the response, and returns an object with get and set attributes.
V-model with anti-shake function using custom REF:
<input v-model="text" />
Copy the code
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) = > {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() = > {
value = newValue
trigger()
}, delay)
},
}
})
}
export default {
setup() {
return {
text: useDebouncedRef('hello'),}}}Copy the code
LifeCycle Hooks
Since setup() is executed before beforeCreate, created, so:
- Not in the
setup()
Function.this
Because the component is not fully instantiated at this point. - Not in the
setup()
Function.beforeCreate
δΈcreated
Two composite life cycles.
However, the following lifecycle methods can be used:
- onBeforeMount
- onMounted
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
- onErrorCaptured
- onRenderTracked
- onRenderTriggered
import {onMounted} from 'composition-api';
setup(props, ctx){
onMounted(() = >{
console.log('mounted');
});
}
Copy the code
Best practices
ref && reactive
- To be able to use
ref
Use as much as possibleref
.ref
Because there are.value
So it’s a little bit more intuitiveref
Object. - Base type values
ref
Definition. - This parameter is recommended when the object type has multiple members
reactive
.
const n = ref(0);
const data = ref([]);
const mouse = reactive({
x:0.y:0
});
Copy the code
Ref Automatically unpacks
- The template is automatically unpacked.
watch
Listening values are automatically unpacked.- use
reactive
packagingref
Object, automatically unpack
const counter = ref(0);
const rc = reactive({
foo:1,
counter
});
rc.counter; // Unpack automatically without unpacking
Copy the code
unref
Unpacking method.
This method is useful when we are not sure whether the value received is of a Ref type, but expect the end result to be of a non-REF type
acceptRef
Parameter returns a reactive result.
function add (a: Ref<number>, b: Ref<number>) {
return computer(() = >a.value + b.value);
}
Copy the code
Compatible with non-responsive scenarios
function add (a: Ref<number> | number, b: Ref<number> | number) {
return computer(() = > unref(a) + unref(b));
}
Copy the code
isRef() && ref()
The ref function has built-in judgment, which is useful when writing indeterminate types.
isRef(foo) ? foo : ref(foo) ==== ref(foo);
Copy the code
It is more useful to return an object made of ref members
It is more useful to return an object made of ref members:
const data = {
x: ref(0),
y: ref(1),
z: ref(2)}Copy the code
When using Es6 deconstruction:
const {x, y ,z} = data;
x.value = 1;
Copy the code
Used by object references, and wrapped by Reactive ().
const rcData = reactive(data);
rcData.x = 1;
Copy the code
Automatic clearance side effects
Use the onUnmounted hook from our encapsulated use method to automatically clean up dependencies, such as event untying and dependency cleanup.
Provide/inject type security
Declare type-safe keys for provide and Inject in a shared module. For example, declare a key in a shared context.ts module.
//context.ts
import {InjectionKey} from '@vue/composition-api'
interface UserInfo {
name:string;
id:number;
}
export default const InjectionKeyUser : InjectionKey<UserInfo> = Symbol(a);Copy the code
2:
import {InjectionKeyUser} from './context';
{
setup(){
provide(InjectionKeyUser, {name:'zhangsan'.id:10001})}} {setup(){
const user = inject(InjectionKeyUser);
if(user){
console.log(user.name); }}}Copy the code
State sharing
States can be created and used independently of components. However, the most common method does not support SSR. In order to support SSR, we should share state based on provide/inject.
//context.ts
//....
export default const InjectionKeyState : InjectionKey<State> = Symbol(a);Copy the code
// useState.ts
export function createState () {
const state = { / * * / };
return {
install(app:App){ app.provide(InjectionKeyState, state); }}}export function useState () :State {
const {inject} = '@vue/composition-api';
returninject(InjectionKeyState)! ; }Copy the code
Get the DOM node through ref
<img src="demo.jpg" ref="domRef" />
Copy the code
{
setup(){
const domRef = ref(null);
onMounted(() = >{
console.log(domRef.value)
})
return {domRef}
}
}
Copy the code
mayBeRef
export type mayBeRef<T> = Ref<T> | T;
Copy the code
Safely deconstruct a Reactive object.
Using ES6 to deconstruct a reactive object defined by a Reactive () method will destroy its reactive characteristics. A good way to do this is to use toRefs() for structure.
const rc = reactive({
x:0.y:1
});
//bad
const {x, y} = rc;
isRef(x); //false
//good;
const {x, y} = toRefs(rc);
Copy the code
Props cannot be deconstructed using ES6
The methods for setup(props) are a proxy object and cannot be deconstructed directly using ES6.
Use $nextTick and so on in setup
export default {
setup(props, { root }) {
const { $nextTick } = root;
console.log($nextTick); }};Copy the code
reference
- www.yuque.com/vueconf/mkw…
- Juejin. Cn/post / 685041…
- Juejin. Cn/post / 689054…