Vue3 composition api Create application instance | setup function | reactive unref ref toRefs | computer function | listener function

Vue3 composition api

Create a Vue instance

  1. Create a Vue application instance createAppmethod
  2. Mount application instance.mount()

Each Vue application createAppcreates a new Vue application instance through the function

createApp(): create an application instance

  • The parameter is the root component object, which is used to configure the root component. This component is used as the starting point for rendering when the app is mounted.
  • mountAn application instance object with methods is returned .
  • Two tasks are completed inside the function ① create appan application instance ② rewrite app.mountthe method
import {
    
     createApp } from 'vue'
import App from'./App.vue' //引入根组件
//const app = createApp({
    
    
  /* 根组件选项 */
//})
const app = createApp(App)
app.mount('#app')//将ID为app的节点挂载到Vue上

/*
vue2 写法
import Vue from 'vue'; 
new Vue({
	render: h => h(App)
}).$mount('#app')
*/

Vue.use principle

Function: by Vue.use()installing the plug-in

  • Vue.use()The method passes at least one parameter, and the parameter type must be Object or Function.
    • If it is an Object, the Object needs to define an install method.
    • If it is Function, this function is regarded as the install method.

Vue.use()Execution is equivalent to executing the install method. If the same plugin is called multiple times, the plugin will only be installed once.

1. The above app is an instance of Vue, and the usage of app.use() is equivalent to Vue.use.
2. The first parameter is the plugin itself, and the optional second parameter is the option to be passed to the plugin.

setup function

setup()It is used to cooperate with the combined API to provide users with a place to build combined logic, create responsive data, create general functions, register declaration cycle hooks and other capabilities.

  • There are two return values
    • Returns a function that will be used as the component's renderfunction
    • Returns an object that exposes the data contained in the object to the template. The properties and methods in the object can be used directly in the template.
  • The parameter is(props,context)
    • The first parameter accepts a reactive props, which propspoints to an external props. If not defined props选项, setupthe first argument in will be undifined.
      - The second parameter provides a context object context, context replaces this as the context, but contextthere are only emit, attrs, and slots.

illustrate

  • setup cannot be an async function, because the return value is not a return object, but a promise, and the template cannot see the properties in the promise object
  • In the entire life cycle, setup函数it will only be mounted once. beforeCreateExecute once before, yes this( undefinedthe timing of the setup call is executed before the props are parsed and the component instance is created), so setupthe component instance cannot be obtained.
  • This way of writing will automatically expose all top-level variables and functions to the template (template)
//返回一个模板
<template>
 {
    
    {
    
    name}}
 {
    
    {
    
    say()}}
</template>

<script>
export default {
    
    
  name: 'App',
  setup() {
    
    //这里定义的数据不是响应式数据
    //数据
    let name="ranan";
    //方法
    function say() {
    
    
      console.log(`hello${
      
      name}`);
    }
    return {
    
    name,say}
  }
}
</script>

//返回一个渲染函数
<template></template>

<script>
import {
    
    h}  from 'vue' //createElement函数
export default {
    
    
  name: 'App',
  setup() {
    
       
    //渲染函数要把h函数调用的返回值返回
    //return ()=>{return h('h1','ranan')}
    return ()=> h('h1','ranan')
  }
}
</script>

script setup

In Vue3.2 (vue2.7 is also supported), you only need to scriptadd setupthe attribute to the tag

Explanation
1. The context in which the component code runs during compilation is in setup()the function
2. No need return, it templatecan be used directly in .

//语法糖
<script setup="props, context" lang="ts">
 context.attrs
 context.slots
 context.emit 
<script>
//同时支持结构语法
<script setup="props, { emit }" lang="ts">
<script>

3. In script setup, the imported components can be used directly without componentsregistration, and the name of the current component cannot be specified, and the file name will automatically be the main one , that is, there is no need to write the name attribute.

<template>
    <HelloWorld />
</template>

<script setup>
import HelloWorld from "./components/HelloWorld.vue";
</script>

script setup specifies the name of the current component

Method 1: Modify the file name, file name = component name
Method 2: Create a script tag at the same level as script setup, and throw name in the tag

<script setup lang="ts">
</script>

<script lang="ts">
export default {
    
    
  name: 'app-viewer'
}

Method 3: Use the plug-in unplugin-vue-define-options
plug-in installation method

responsive data

  • ref()make data responsive
  • shallowRef()It only handles the responsive processing of basic data types, not the responsive processing of objects.
  • reactive()The deep-level object responsiveness defined is based on the internal Proxyimplementation, and the internal data of the source object is manipulated through the proxy object.
  • shallowReactive()Create a shallow response object (one layer response)

ref function data hijack defineProperty

ref()Make the data responsive, wrap the value into RefImpla reference object ( Refobject) and return

Create an object, set the value attribute of the object as the incoming parameter, then perform responsive processing on the object, and finally return the responsive object

  • The principle of ref responsiveness is to use data hijacking (first layer) : definePropertyin use getter,setter
  • The acquisition of data uses 属性名.valueacquisition, but the attribute name can be used directly in the template (automatically unwrapped), and the template will automatically属性名.value
<script setup lang="ts">
import {
    
    ref} from 'vue'
let count = ref(0) //生成一个0的响应式数据
console.log(count.value) //RefImpl{value:0....}
console.log(count) //0
count=10 //对0重新赋值,现在count不再是响应式数据
</script>

<template>
   <div>{
    
    {
    
    count }}</div>
</template>

insert image description here

The parameter of the ref function is an object

const 返回值obj = ref(参数对象)The return value obj itself is still a RefImpl object, but obj.valuewhat is returned is Proxya proxy object for the parameter object, and the source object is indirectly manipulated through the operation of the proxy object.

<script setup lang="ts">
import {
    
    ref} from 'vue'
/*
count是响应式的,name也是响应式的
const tmp = {name:ref("ranran")] name是响应式的,tmp不是响应式的
*/
const count = ref({
    
    name:"ranran"}) 
console.log(count) //返回一个RefImpl对象,对象的value值为传入的参数{value:{name:"ranran"}}
console.log(count.value) //Proxy(Object) {name: 'ranran'}
console.log(count.name) //注意这个是访问不到的,因为ref函数是创建一个对象,对象的value属性设置为传入的参数,然后对对象做响应式处理,最后返回响应式对象
</script>
<template>
   <div>{
    
    {
    
    count }}</div>
</template>

Details of automatic unpacking

templateAutomatic unpacking problem in tags: automatic unpacking only unpacks top-level attributes顶层属性.value.xxx

const  obj = ref({
    
    
  name:"ranran"
})
const obj2 = {
    
    
  name:ref("biubiu")
}
</script>

<template>
  <div>
	//自动解包obj.value.name
    <div>{
    
    {
    
    obj.name }}</div>
    //obj2不是响应式数据,所以没有自动解包。错误写法
    <div>{
    
    {
    
    obj2.name }}</div>
    //正确写法
    <div>{
    
    {
    
    obj2.name.value}}</div>
  </div>
 
</template>

The source code of the ref function

function ref(value) {
    
    
    return createRef(value, false); //创建ref对象,第一个参数时需要响应的数据,第二个参数是是否浅拷贝
}

function createRef(rawValue, shallow) {
    
     
    if (isRef(rawValue)) {
    
     
        return rawValue;
    }
    return new RefImpl(rawValue, shallow);
}

class RefImpl {
    
    
    constructor(value, _shallow) {
    
    
        this._shallow = _shallow;
        this.dep = undefined;
        this.__v_isRef = true;
        this._rawValue = _shallow ? value : toRaw(value);
        this._value = _shallow ? value : convert(value);
    }
    get value() {
    
    
        trackRefValue(this);
        return this._value;
    }
    set value(newVal) {
    
    
        newVal = this._shallow ? newVal : toRaw(newVal);
        if (hasChanged(newVal, this._rawValue)) {
    
    
            this._rawValue = newVal;
            this._value = this._shallow ? newVal : convert(newVal);
            triggerRefValue(this, newVal);
        }
    }
}

unref()

If the argument is a ref, returns the internal value, otherwise returns the argument itself. This is val = isRef(val) ? val.value : vala syntactic sugar function for .

Usage scenario: when a value may be ref or a value other than ref

reactive function data proxy Proxy

const 代理对象 = reactive(源对象): Define a responsive proxy for an object , accept an object (array), and return a Proxy instance object.

  • reactive()The defined responsive data is deep-level , internally based on Proxyimplementation, and the internal data of the source object is manipulated through the proxy object.
  • shallowReactive()Create a shallow response object (one layer response)
<script setup lang="ts">
import {
    
    reactive} from 'vue'
const stu = reactive({
    
    name:"ranran"})
console.log(stu)
/*
{
    "name": "ranran"
}
*/
</script>

toRef(obj,prop) creates a ref object

Function: toRef(响应式对象,该对象的属性)create one ref对象, 该ref对象whose valuevalue points to a property in the parameter object (if the value of the ref object changes, the property in the parameter object will also change, and vice versa)
Description: The essence is a reference, which is associated with the original
data Scenario: To provide a property in the responsive object for external use alone

illustrate

  • For the properties of a responsive object , if it is used for a normal object (non-responsive object), the returned result is not responsive.
  • toRef返回值and 对象 ObjectBoth maintain a reference relationship (one changes and the other changes)

insert image description here

toRefs(obj) creates multiple ref objects

toRefs(obj): Returns an ordinary object consistent with the parameter, except that the value of the attribute becomes a ref object

illustrate

  • toRefsDoes not take effect for newly added attributes
  • For all properties of the entire object, the goal is to convert the reactive object (reactive package) into a normal object.
 return{
    
    
 	...toRefs(person)
 }

1. refThe essence is copying, modifying the responsive data will not affect the original data, the
toRef、toRefsessence of the view will be updated is reference, modifying the responsive data will affect the original data, the view will not be updated
2. toRef、toRefsDo not create responsive, but It is a continuation response.

Side effect function effect()

When a piece of data changes, effectthe function is automatically re-executed, and we call the data reactive. (It will be executed automatically when the responsive data changes effect()) The execution of
side-effect functions effect()will directly or indirectly affect the execution of other functions.

//假设obj是一个响应式数据
const obj = {
    
    text:'xxx'}
function effect(){
    
    //effect函数
	console.log(obj.text);
}
//修改响应式数据
obj.text = 'yyy'
//手动模拟
effect()//响应式数据发生变化,自动执行effect()

reactive responsive principle

In essence, the reading and writing of data objects is proxied through Proxy

  • When we access the proxy object, the getter will be triggered to perform dependency collection
    • track(): track()Collect dependencies, each property of the object has its own , collect depall the properties that depend on this property , and put them in.effect函数dep
  • When the data is modified, the setter dispatch notification will be triggered.
    • trigger(): trigger()Notification dependencies. After the dependencies are collected, as soon as the variable changes, all executions that depend on the variable in trigger()the notification will be executed to realize the update of the dependent variable.depeffect()
  1. Each attribute of the object has its own dep, and all the attributes that depend on this attribute are effect函数collected and placed depin the .
  2. Each object will create a Mapcorresponding relationship between each attribute and dep, the key is the attribute name, and the value is the dep of the attribute (using Set to store).
  3. Use WeakMapto store multiple objectsMap

insert image description here

  1. Proxy-ReflectImplement automatic collection and trigger dependenciestrack-trigger函数
//reactive 将对象变成响应式的
function reactive(target) {
    
    
    const handler = {
    
    
        get(target, key, receiver) {
    
    
            track(receiver, key) // 访问时收集依赖
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
    
    
            Reflect.set(target, key, value, receiver)
            trigger(receiver, key) // 设值时自动通知更新
        }
    }

    return new Proxy(target, handler)
}

The comparison between reactive and ref


reactive ref
define data object type data If the basic type of data
defines an object, .value will use reactive to convert the object into a proxy object
principle Proxy implements responsive (data proxy)
through Reflect to manipulate the properties inside the source object
Realize responsiveness (data hijacking) through get and set of Object.defineProperty()
use No need to use .value The operation data needs .value, and the automatic unpacking and reading in the template does not need .value

Computed property computed function

The computed attribute is a computed attribute in Vue3 响应式(returning a ref object), which automatically updates its own value according to changes in other responsive data (does not detect updates to non-responsive data).

import {
    
    computed} from 'vue'
setup(){
    
    
	//计算属性fullName -简写内部是函数形式,只符合被读取的情况
	let fullName = computed(()=>{
    
    
		return person
	})
	//计算属性-完整写法
	let fullName = computed({
    
    
		get(){
    
    },
		set(val){
    
    }
	})
}

The principle of the computed attribute is to use a getter function and a setter function to achieve, and the dependent data of computed (data that monitors changes) needs to be responsive

A watch function that listens to responsive data

watch()Used to listen to a specific data source and perform side effects in the callback function. By default the callback is only executed when the listened source data changes.

watch(data,function(),[option])

  • Monitored Reactive Data
    • refobject
    • computed property
    • getterFunction: It can be simply understood as a function to obtain data, which is an operation that returns a value
    • reactiveThe defined attribute will deep:trueforce the open
    • Listen to arrays from multiple sources
  • The callback function for this data binding
  • Configuration parameters of vue2: support deep, immediate and flush options.

Writing review of vue2

illustrate

  • When the monitored property changes, handler(newVal,oldVal)the function is called, and it is not called during initialization (can be set)
  • Can monitor attributes in data and computed
  • The watch in Vue does not monitor the change of the internal value of the object by default (one layer)
  • Configure deep:true to detect internal value changes in objects (multi-layer)
//写法1
new Vue({
    
    
   el:'#root',
   data:{
    
    
       firstName:'ran',
       lastName:'an'
    },
    watch:{
    
    
 		isHot:{
    
    //固定配置
 		    immediate:false; //默认false,初始化时调用handler函数
 			//当监视的属性发生变化时,handler调用
			handler(newVal,oldVal){
    
    }
			}
		}
    
});
//写法2
vm.$watch('isHot',{
    
    配置信息})
//简写
watch:{
    
    
	isHot(newVal,oldVal){
    
    }
}
vm.$watch('isHot',function(newVal,oldVal){
    
    //handler函数})

Monitor computed property computed

import {
    
    ref,computed,watch} from 'vue'
const message = ref("ranran");
const newMessage = computed(() => {
    
    
  return message.value;
});
watch(newMessage, (newValue, oldValue) => {
    
    
});

Listen to the getter function

const x1 = ref(12);
const x2 = ref(13);
watch(
  () => x1.value + x2.value,//类似计算属性
  (newValue, oldValue) => {
    
    
  }
);

Listen to the properties defined by reactive

Listening reactiveto the defined properties will force deep:truedeep monitoring to be enabled (automatically create a deep listener, and the deep configuration is invalid), and any changes in the properties of the responsive object will trigger the callback function in the monitoring function.

Unable to get oldValue correctly, the values ​​of oldValue and newValue are the same. Because the object is a reference type, even if the properties inside have changed, the address of the object has not changed, and oldValue and newValue point to the same address.

import {
    
    reactive,watch} from 'vue'
let person= reactive({
    
    
	name:"ranna",
	age:18,
	job:{
    
    
		j1:{
    
    
			salary:20
		}
	}
  })
watch(person,(newValue,oldValue)=>{
    
     
})

watch cannot directly monitor the properties of the responsive object

1. watch cannot directly monitor the properties of responsive objects, which is equivalent to watchpassing a non-responsive number directly to , but watchcan only monitor responsive data.

//错误写法
const number = reactive({
    
     count: 0 });
const countAdd = () => {
    
    
  number.count++;
};
watch(number.count, (newValue, oldValue) => {
    
    
});

Solution: Use getterthe form of function, in this case you can getoldValue

watch(
  () => number.count,//返回的是基本数据类型
  (newValue, oldValue) => {
    
    
  }
);

2. Special case: Proxy is automatic depth, Object depth on demand
If we use getter()the form of returning a responsive object, the callback function of the watch will not be triggered if the property value of the responsive object changes.

//自动开启deep
watch(person.job,(newValue,oldValue)=>{
    
     })
watch(()=>person.job,(newValue,oldValue)=>{
    
    },{
    
    deep:true})//返回的是一个obj,需要手动开启deep

It is recommended getter()to listen to a certain property in the responsive object

Listen to arrays from multiple sources

watchArrays can also be monitored, provided that the array contains responsive data.

let sum = ref(0)
let msg= ref("hello")
 watch([sum,msg],(newvalue,oldValue)=>{
    
    
	//此时newvalue和oldValue都是数组,里面的顺序和第一个参数的顺序一致
})

Listen to props attribute

Listen to the entire props

<script setup>
import {
    
     ref, watch } from 'vue';
const props = defineProps({
    
    
    listData: {
    
    
        type: Array,
        default: [],
    }
});
const childList = ref([])
watch(props, (nweProps)=>{
    
    
    childList.value = nweProps.listData
})
</script>

Listen to specific properties in props

<script setup>
import {
    
     ref} from 'vue';
const appearState = ref(false);
const props = defineProps({
    
    
  show: {
    
    
    type: Boolean,
    default: () => false,
  },
});
watch(
  () => props.show,
  (newVal) => {
    
    
    appearState.value = newVal;
  },
);

Listener watchEffect()

watchEffect()It is also a listener, the first parameter is the callback function, and the second parameter is the configuration object, which will be executed when the function is initialized (from scratch). In the callback function, the response attribute is automatically monitored. When the response data in the callback function changes, the callback function will be executed immediately.

  • The routine of watch is: not only the attribute of monitoring, but also the callback of monitoring should be specified
  • The routine of watchEffect is: no need to specify which attribute to monitor, which attribute is used in the monitoring callback, and which attribute to monitor. The callback function is executed when the monitored data changes.
//一上来就会执行
watchEffect(()=>{
    
    
	
})

Watch the DOM in the callback | watchPostEffect()

Get the DOM in the callback function of the listener, and the default is to get the DOM before updating.

Method 1: Configuration options
If you want to get the updated DOM in the callback function, you only need to pass one more parameter option to the listener flush: 'post'.

watch(source, callback, {
    
    
  flush: 'post'
})
watchEffect(callback, {
    
    
  flush: 'post'
})

Method 2: watchPostEffect

watchPostEffect(() => {
    
    
  /* 在 Vue 更新后执行 */
})

Destroy the listener manually

Usually after a component is destroyed or uninstalled, the listener will also stop. However, in some special cases, the listener still exists and needs to be closed manually, otherwise it is easy to cause memory leaks.

//案例
<script setup>
import {
    
     watchEffect } from 'vue'
// 它会自动停止
watchEffect(() => {
    
    })
// 这个时候监听器没有与当前组件绑定,所以即使组件销毁了,监听器依然存在。
setTimeout(() => {
    
    
  watchEffect(() => {
    
    })
}, 100)
</script>

Solution: Use a variable to receive the return value of the listener function, and the return value is a function that closes the current listener.

const unwatch = watchEffect(() => {
    
    })
// ...当该侦听器不再需要时
unwatch()

Guess you like

Origin blog.csdn.net/qq_41370833/article/details/131704075