From Vue2 to Vue3 [1] - Composition API (Chapter 1)

Series Article Directory

content Link
From Vue2 to Vue3 [zero] Introduction to Vue3
From Vue2 to Vue3 [1] Composition API (Chapter 1)
From Vue2 to Vue3 [2] Composition API (Chapter 2)
From Vue2 to Vue3 [3] Composition API (Chapter 3)
From Vue2 to Vue3 [4] Composition API (Chapter 4)


foreword

As the latest version of the Vue.js framework, Vue3 introduces many exciting new features and improvements. Among them, the combined API is one of the most striking features of Vue3 . In this article, we'll dive into Vue3's compositional API and explore its benefits and usage during development . Whether you are a novice or an experienced Vue developer, by learning how to use the composition API, we can build complex applications more efficiently and improve code maintainability and reuse .

1. Composition API and options API

  • Vue2 uses the options API
    (1) Advantages: easy to learn and use, each code has a clear location (for example: data is placed in data, methods are placed in methods) (2) Disadvantages: similar logic, not easy to reuse, especially in large projects (3) Although optionsAPI can extract the same logic through mixins, it is not particularly easy
    to
    maintain
  • What's new in vue3 is composition API
    (1) composition API organizes code based on logical functions, and a functional api is put together
    (2) Even if the project is large and has many functions, it can quickly locate function-related apis
    (3) Greatly improves code readability and maintainability

Case mouse movement displays mouse coordinates x, y
vue2 options API traditional approach

<template>
  <div>当前鼠标位置</div>
  <div>x: {
   
   { mouse.x }}</div>
  <div>y: {
   
   { mouse.y }}</div>
  <div>当前点击次数:{
   
   { count }}</div>
  <button @click="add">点击</button>
</template>

<script>
export default {
      
      
  // vue2 中采用的是 options API
  // 常见的配置项: data created methods watch computed components
  data() {
      
      
    return {
      
      
      mouse: {
      
      
        x: 0,
        y: 0,
      },
      count: 0,
    }
  },
  mounted() {
      
      
    document.addEventListener('mousemove', this.move)
  },
  methods: {
      
      
    move(e) {
      
      
      this.mouse.x = e.pageX
      this.mouse.y = e.pageY
    },
    add() {
      
      
      this.count++
    },
  },
  destroyed() {
      
      
    document.removeEventListener('mousemove', this.move)
  },
}
</script>

Vue3 composition API combination api practice

<template>
  <div>当前鼠标位置</div>
  <div>x: {
   
   { mouse.x }}</div>
  <div>y: {
   
   { mouse.y }}</div>
  <div>当前点击次数:{
   
   { count }}</div>
  <button @click="add">点击</button>
</template>

<script>
import {
      
       onMounted, onUnmounted, reactive, ref } from 'vue'

export default {
      
      
  setup() {
      
      
    const count = ref(0)
    const add = () => {
      
      
      count.value++
    }

    const mouse = reactive({
      
      
      x: 0,
      y: 0,
    })

    const move = (e) => {
      
      
      mouse.x = e.pageX
      mouse.y = e.pageY
    }
    onMounted(() => {
      
      
      document.addEventListener('mousemove', move)
    })
    onUnmounted(() => {
      
      
      document.removeEventListener('mousemove', move)
    })
    return {
      
      
      count,
      add,
      mouse,
    }
  },
}
</script>

It can continue to be separated, and the specific code that implements the logic can be separated and encapsulated to facilitate reuse and maintenance

function useMouse() {
    
    
  const mouse = reactive({
    
    
    x: 0,
    y: 0,
  })
  const move = (e) => {
    
    
    mouse.x = e.pageX
    mouse.y = e.pageY
  }
  onMounted(() => {
    
    
    document.addEventListener('mousemove', move)
  })
  onUnmounted(() => {
    
    
    document.removeEventListener('mousemove', move)
  })
  return mouse
}

function useCount() {
    
    
  const count = ref(0)
  const add = () => {
    
    
    count.value++
  }
  return {
    
    
    count,
    add,
  }
}

What are the benefits of putting the extracted and packaged functions into the hooks folder under the src directory ? Organize code based on logical functions to facilitate reuse and maintenance

Two, setup

  • A new configuration item in Vue3.0, the value is a function, which is the starting point (stage) of the composition API
  • From the perspective of life cycle, setup will be executed before the beforeCreate hook function
  • This cannot be used in setup, this points to undefined
  • The data and functions that need to be used in the template need to be returned in the setup
  • The components used in the components: data, methods, etc., must be configured in the setup
<template>
  <div class="container">
    <h1 @click="say()">{
   
   {msg}}</h1>
  </div>
</template>

<script>
export default {
      
      
  setup () {
      
      
    console.log('setup执行了')  //setup先执行
    console.log(this)  // undefined
    // 定义数据和函数
    const msg = 'hi vue3'
    const say = () => {
      
      
      console.log(msg)
    }
	
	// 需要使用的一定要导出
    return {
      
       msg , say}
  },
  beforeCreate() {
      
      
    console.log('beforeCreate执行了') 
    console.log(this)
  }
}
</script>
  • Points to note :

    • Try not to mix with Vue2.x configuration
      • Properties and methods in setup can be accessed in Vue2.x configuration (data, methos, computed...)
      • But Vue2.x configuration (data, methos, computed...) cannot be accessed in setup
      • If there is a duplicate name, setup takes precedence
    • setup cannot be an async function , because the return value is no longer the return object, but a promise, and the template cannot see the properties in the return object (it can also return a Promise instance later, but it requires the cooperation of Suspense and asynchronous components )

Three, ref

  • Define a responsive data (either a basic type or an object type)
<template>
  <div>{
   
   { money }}</div>
  <button @click="money++">改值</button>
</template>

<script>
import {
      
       reactive, ref } from 'vue'
export default {
      
      
  setup() {
      
      
    let money = ref(100)
    money.value++
    return {
      
      
      money
    }
  }
}
</script>
  • use

    • Operation data (xxx.value)

    • Use data ({ {xxx}}})

  • Basic types of data: Reactive is still dependable Object.defineProperty()and getcomplete set.

  • Object type data: internally "rescues" a new function in Vue3.0 - reactivefunction.

Four, reactive

  • Pass in a complex data type, convert the complex type data into responsive data (return the responsive proxy of the object)
<template>
  <div>{
   
   { obj.name }}</div>
  <div>{
   
   { obj.age }}</div>
  <button @click="obj.name = 'ls'">改值</button>
</template>

<script>
import {
      
       reactive } from 'vue'

export default {
      
      
  setup () {
      
      
    // 1. setup 需要返回值, 返回的值才能在模板中使用
    // 2. 默认的普通的值不是响应式的, 需要用 reactive 函数
    const obj = reactive({
      
      
      name: 'zs',
      age: 18
    })

    return {
      
      
      obj
    }
  }
}
</script>
  • use

    • Operation data (xxx)

    • Use data ({ {xxx}}})

  • The reactive data defined by reactive is "deep"

  • The internal ES6-based Proxy implementation operates the internal data of the source object through the proxy object

Five, reactive and ref comparison

Before using these two functions, be sure to introduce

import {
    
    reactive, ref} from 'vue'
  • From the perspective of defining data:
    • ref is used to define: basic type data
    • reactive is used to define: object (or array) type data
    • Remarks: ref can also be used to define object (or array) type data , which will be automatically converted to reactivea proxy object internally
  • From the perspective of principle comparison:
    • Responsiveness (data hijacking) is realized Object.defineProperty()by ref getandset
    • reactive implements responsiveness (data hijacking) by using Proxy , and manipulates the data inside the source object through Reflect
  • From a usage point of view:
    • Data defined by ref: required for data manipulation .value, not required for direct reading in the template when reading data.value
    • Data defined by reactive: operating data and reading data: neither required.value

Six, setup notes

  • parameter of setup
    • props: the value is an object, including: the properties passed from outside the component and received by the internal declaration of the component
    • context: context object
      • attrs: The value is an object, including: attributes passed from outside the component but not declared in the props configuration, equivalent tothis.$attrs
      • slots: received slot content, equivalent tothis.$slots
      • emit: A function that distributes custom events, equivalent tothis.$emit

parent component App

<template>
	<Demo @hello="showHelloMsg" msg="你好啊" school="尚硅谷">
	    // 具名插槽
		<template v-slot:qwe>
			<span>尚硅谷</span>
		</template>
		<template v-slot:asd>
			<span>尚硅谷</span>
		</template>
	</Demo>
</template>

<script>
	import Demo from './components/Demo'
	export default {
      
      
		name: 'App',
		components:{
      
      Demo},
		setup(){
      
      
			function showHelloMsg(value){
      
      
				alert(`你好啊,你触发了hello事件,我收到的参数是:${ 
        value}`)
			}
			return {
      
      
				showHelloMsg
			}
		}
	}
</script>

Subcomponent Demo

<template>
	<h1>一个人的信息</h1>
	<h2>姓名:{
   
   {person.name}}</h2>
	<h2>年龄:{
   
   {person.age}}</h2>
	<button @click="test">测试触发一下Demo组件的Hello事件</button>
</template>

<script>
	import {
      
      reactive} from 'vue'
	export default {
      
      
		name: 'Demo',
		props:['msg','school'],
		emits:['hello'],
		setup(props,context){
      
      
			// console.log('---setup---',props)
			// console.log('---setup---',context)
			// console.log('---setup---',context.attrs) //相当与Vue2中的$attrs
			// console.log('---setup---',context.emit) //触发自定义事件的。
			console.log('---setup---',context.slots) //插槽
			//数据
			let person = reactive({
      
      
				name:'张三',
				age:18
			})

			//方法
			function test(){
      
      
				context.emit('hello',666)
			}

			//返回一个对象(常用)
			return {
      
      
				person,
				test
			}
		}
	}
</script>


Seven, script setup syntax

Starting from vue3.2, it supports setup syntax sugar.
script setup is compile-time syntactic sugar for using the compositional API in single-file components (SFCs). Compared with ordinary script syntax, it is more concise

The top-level binding will be automatically exposed to the template, so defined variables, functions and imported content (including imported sub-components) can be used directly in the template

<template>
  <div>
    <h3>根组件</h3>
    <div>点击次数:{
   
   { count }}</div>
    <button @click="add">点击修改</button>
  </div>
</template>

<script setup>
import {
      
       ref } from 'vue'

const count = ref(0)
const add = () => {
      
      
  count.value++
}

// 不再需要return {} 直接可以读取数据
</script>

Use the script setup syntax to rewrite the mouse coordinate case

<template>
  <div>当前鼠标位置</div>
  <div>x: {
   
   { mouse.x }}</div>
  <div>y: {
   
   { mouse.y }}</div>
  <div>当前点击次数:{
   
   { count }}</div>
  <button @click="add">点击</button>
</template>

<script setup>
import {
      
       onMounted, onUnmounted, reactive, ref } from 'vue'
const count = ref(0)
const add = () => {
      
      
  count.value++
}
const mouse = reactive({
      
      
  x: 0,
  y: 0,
})
const move = (e) => {
      
      
  mouse.x = e.pageX
  mouse.y = e.pageY
}
onMounted(() => {
      
      
  document.addEventListener('mousemove', move)
})
onUnmounted(() => {
      
      
  document.removeEventListener('mousemove', move)
})
</script>

8. Calculated properties and monitoring

8.1 computed

When the computed function is called, it needs to receive a processing function . In the processing function, the value of the computed property needs to be returned

<template>
  <div>我今年的年纪 <input type="text" v-model="age" /></div>
  <div>我明年的年龄 {
   
   { nextAge }}</div>
  <div>我后年的年龄 <input type="text" v-model="nextAge2" /></div>
</template>

<script setup>
import {
      
       computed, ref } from 'vue'
const age = ref(10)
// 不带set的计算属性 (简写)
const nextAge = computed(() => {
      
      
  return +age.value + 1  // 变量前的加号是转为number类型
})

// 带set的计算属性 (完整写法)
const nextAge2 = computed({
      
      
  get() {
      
      
    return +age.value + 2
  },
  set(value) {
      
      
    age.value = value - 2
  },
})
</script>

8.2 watch

watch monitoring, receiving three parameters

  • Parameter 1: The monitored data source
  • Parameter 2: callback function
  • Parameter 3: Additional configuration (such as immediate listening, deep listening)
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
    
    
	console.log('sum变化了',newValue,oldValue)
},{
    
    immediate:true})

//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
    
    
	console.log('sum或msg变化了',newValue,oldValue)
}) 

/* 情况三:监视reactive定义的响应式数据
			若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue
			若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
*/
watch(person,(newValue,oldValue)=>{
    
    
	console.log('person变化了',newValue,oldValue)
},{
    
    immediate:true,deep:false}) //此处的deep配置不再奏效

//情况四:监视reactive定义的响应式数据中的某个属性 属性值是简单数据类型
watch(()=>person.age,(newValue,oldValue)=>{
    
    
	console.log('person的age变化了',newValue,oldValue)
},{
    
    immediate:true}) 

//情况五:监视reactive定义的响应式数据中的某些属性 属性值是简单数据类型
watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{
    
    
	console.log('person的job变化了',newValue,oldValue)
},{
    
    immediate:true})

//特殊情况 监视reactive定义的响应式数据中的某些属性 属性值是引用数据类型
watch(()=>person.job,(newValue,oldValue)=>{
    
    
    console.log('person的job变化了',newValue,oldValue)
},{
    
    deep:true}) //此处由于监视的是job对象,所以deep配置有效

When the value of ref is a complex data type, in-depth monitoring is required

let person = ref({
    
    
	name:'张三',
	age:18,
	job:{
    
    
		j1:{
    
    
			salary:20
		}
	}
})
//由于监听的ref的值是一个对象 所以要开启深度侦听
watch(person,(newValue,oldValue)=>{
    
    
	console.log('person的值变化了',newValue,oldValue)
},{
    
    deep:true})

8.3 watchEffect

No need to specify which property to monitor, which property is used in the monitoring callback, then which property to monitor

//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
    
    
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回调执行了')
})

Summarize

In general, Vue3's combined API provides us with a more flexible and powerful development method. By fully understanding and applying compositional APIs, we can develop more efficient, leaner Vue applications and provide users with a better experience. Whether it is for a novice or an experienced Vue developer, mastering the composition API is an important skill that is worth learning and mastering.

Guess you like

Origin blog.csdn.net/m0_57524265/article/details/131736696