Vue3 summary


title: Vue3 Summary
date: 2022-11-06 23:57:57
tags:

  • Vue
    categories:
  • Vue
    cover: https://cover.png
    feature: false


For the basics of Vue2, see: Vue2 Summary (Basic)_Fan223's blog

For Vue2 development, see: Vue2 Summary (Development)_Fan223's Blog

1 Introduction

  1. performance improvement

    • 41% reduction in bundle size
    • 55% faster initial render, 133% faster update render
    • 54% less memory
      ...
  2. Source code upgrade

    • Use Proxy instead of defineProperty to achieve responsiveness
    • Rewrite the implementation of virtual DOM and Tree-Shaking
  3. Embracing TypeScript
    Vue3 can better support TypeScript

  4. new features

    1. Composition API (composition API)
      • setup configuration
      • ref and reactive
      • watch与watchEffect
      • provide and inject
        ...
    2. new built-ins
      • Fragment
      • Teleport
      • Suspense
    3. other changes
      • new lifecycle hook
      • The data option should always be declared as a function
      • Remove keyCode support as a v-on modifier
        ...

2. Create using

2.1 Create with vue-cli

Official document: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

2.2 Use vite to create

Official document: https://v3.cn.vuejs.org/guide/installation.html#vite
official website of vite: https://vitejs.cn

What is vite? — A new generation of front-end build tools.

  • In the development environment, no packaging operation is required, and quick cold start is possible
  • Lightweight and fast Hot Reload (HMR)
  • True on-demand compilation, no longer waiting for the entire application to be compiled.

Comparison chart of traditional build and vite build
insert image description here
insert image description here

## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

2.3 main.js changes

// 引入的不再是Vue构造函数,引入的是一个名为 createApp 的工厂函数(不需要 new)
import {
    
     createApp } from 'vue'
import App from './App.vue'

// createApp(App).mount('#app')

// 创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
//挂载
app.mount('#app')

2.4 App.vue changes

Template structure in Vue3 components can have no root tag

<template>
	<!-- Vue3组件中的模板结构可以没有根标签 -->
	<img alt="Vue logo" src="./assets/logo.png">
	<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

2.5 Vue3 Developer Tools

insert image description here

3. Common Composition APIs

3.1 setup function

  1. A new configuration item in Vue3.0, the value is a function
  2. setup is the "stage on which all Composition APIs perform"
  3. The components used in the components: data, methods, etc., should be configured in the setup
  4. Two return values ​​of the setup function:
    1. If an object is returned, the properties and methods in the object can be used directly in the template
    2. If a rendering function is returned: you can customize the rendering content
<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>年龄:{
   
   {age}}</h1>
  <button @click="info">个人信息</button>
</template>

<script>
// import {h} from 'vue'
export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = "张三"
    let age = 18

    // 方法
    function info() {
      
      
      alert(`我叫${ 
        name}, 年龄${ 
        age}`)
    }

    // 返回一个对象(常用)
    return {
      
      
      name,
      age,
      info
    }
    //返回一个函数(渲染函数),需要导入 h
    // return ()=> h('h1','渲染')
  }
}
</script>

insert image description here

1. Try not to mix it 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

2. setup cannot be an async function, because the return value of async is no longer the return object, but wrapped by promise, and the template cannot see the properties in the return object (it can also return a Promise instance, but Suspense and cooperation of asynchronous components)

3.2 ref function

As follows, modify personal information through the function

<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>年龄:{
   
   {age}}</h1>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = "李四"
    let age = 18

    // 方法
    function changeInfo() {
      
      
      name = "张三"
      age = 20
      console.log(name + age);
    }

    return {
      
      
      name,
      age,
      changeInfo
    }
  }
}
</script>

It is found that the actual value has been modified, but the page does not respond to the change
insert image description here

At this time, you need to use the ref function to define the response data

  • Role: define a responsive data
  • grammar:const xxx = ref(initValue)
    • Create a reference object (reference object, ref object for short) that contains responsive data
    • Manipulating data in JS:xxx.value
    • Read data in the template: No need .value, directly: <div>{ {xxx}}</div>, vue3 automatically parses it
<script>
import {
    
     ref } from 'vue';

export default {
    
    
  name: 'App',
  setup() {
    
    
    // 数据
    let name = ref('李四')
    let age = ref(18)

    // 方法
    function changeInfo() {
    
    
      name.value = "张三"
      age.value = 20
      console.log(name, age);
    }

    return {
    
    
      name,
      age,
      changeInfo
    }
  }
}
</script>

The object using the ref function is already a RefImpl object (Reference Implement).
insert image description here
If it is object type data, the value of the RefImpl object is a Proxy object.

<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>年龄:{
   
   {age}}</h1>
  <h2>职业:{
   
   {job.type}}</h2>
  <h2>薪水:{
   
   {job.salary}}</h2>
  <button @click="changeInfo">改变个人信息</button>
</template>

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

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = ref('李四')
    let age = ref(18)
    let job = ref({
      
      
      type: '开发',
      salary: '20k'
    })

    // 方法
    function changeInfo() {
      
      
      job.value.type = '设计'
      job.value.salary = '25k'
      console.log(job);
      console.log(job.value);
    }

    return {
      
      
      name,
      age,
      job,
      changeInfo
    }
  }
}
</script>

insert image description here

Notice:

  1. The received data can be: basic type or object type
  2. Basic types of data: Responsiveness is still completed Object.defineProperty()by get and set
  3. Object type data: internally "help" a new function in Vue3.0 - reactive function

3.3 reactive functions

  • Role: Define an object type of responsive data (do not use it for basic types, use the ref function)
  • Syntax: const 代理对象 = reactive(源对象), receive an object (or array), and return a proxy object (the instance object of Proxy, referred to as Proxy object)
  • The reactive data defined by reactive is "deep"
  • The internal ES6-based Proxy implementation operates on the internal data of the source object through the proxy object

Modify the object type example in 3.2, and use reactive to define object type data, as follows:

<template>
  <h1>姓名:{
   
   {name}}</h1>
  <h1>姓名:{
   
   {age}}</h1>
  <h2>职业:{
   
   {job.type}}</h2>
  <h2>薪水:{
   
   {job.salary}}</h2>
  <button @click="changeInfo">改变个人信息</button>
</template>

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

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let name = ref('李四')
    let age = ref(18)
    // let job = ref({
      
      
    //   type: '开发',
    //   salary: '20k'
    // })
    let job = reactive({
      
      
      type: '开发',
      salary: '20k'
    })

    // 方法
    function changeInfo() {
      
      
      // job.value.type = '设计'
      // job.value.salary = '25k'
      job.type = '设计'
      job.salary = '25k'
      console.log(job);
    }

    return {
      
      
      name,
      age,
      job,
      changeInfo
    }
  }
}
</script>

insert image description here
At the same time, it can also respond to array types and perform deep responses

<template>
  <h1>姓名:{
   
   {person.name}}</h1>
  <h1>年龄:{
   
   {person.age}}</h1>
  <h2>职业:{
   
   {person.job.type}}</h2>
  <h2>薪水:{
   
   {person.job.salary}}</h2>
  <h1>爱好:{
   
   {person.hobby}}</h1>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>
import {
      
       reactive } from 'vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      name: '李四',
      age: 18,
      job: {
      
      
        type: '开发',
        salary: '20k'
      },
      hobby: ['看剧', '听歌']
    })

    // 方法
    function changeInfo() {
      
      
      person.name = '张三'
      person.age = 20
      person.job.type = '设计'
      person.job.salary = '25k'
      person.hobby[0] = '学习'
    }

    return {
      
      
      person,
      changeInfo
    }
  }
}
</script>

insert image description here

3.4 Responsive principle in Vue3.0

3.4.1 Responsiveness of Vue2.x

Implementation principle:

  • Object type: intercept (data hijacking) Object.defineProperty()by reading and modifying properties, but cannot respond to new additions and deletions
    Object.defineProperty(data, 'count', {
          
          
    	configurable: true, // 可配置,即可删除属性
        get () {
          
           return ... }, 
        set () {
          
           响应... }
    })
    
  • Array type: interception is achieved by rewriting a series of methods for updating the array (the method of changing the array is wrapped)

There is a problem:

  • Directly add and delete attributes, the interface will not be updated, and the corresponding functions (set, delete) need to be called
    methods: {
          
          
      addSex(){
          
          
        this.person.sex = '女'; // 不生效
        this.$set(this.person, 'sex', '女')
        Vue.set(this.person, 'sex', '女')
      },
      deleteName(){
          
          
        this.$delete(this.person, 'name', '张三')
        Vue.delete(this.person, 'name', '张三')
      }
    },
    
  • Modify the array directly through the subscript, the interface will not be updated automatically, it can also be modified by the above method, and the array can also be directly changed to modify
    methods: {
          
          
    	updateHobby(){
          
          
    		this.person.hobby[0] = '学习'; // 不生效
    		this.$set(this.person.hobby, 0, '学习')
    		Vue.delete(this.person.hobby, 0, '学习')
    		this.person.hobby.splice(0, 1, '学习')
    	}
    },
    

3.4.2 Responsiveness of Vue3.0

Realization principle:

  • Through Proxy (proxy): Intercept the change of any attribute in the object, including reading and writing of attribute values, adding attributes, deleting attributes, etc.

  • Through Reflect (reflection): operate on the properties of the source object

    <body>
       <script type="text/javascript">
        let person = {
            
            
          name: '张三',
          age: 18
        }
    
        // 模拟 Vue3中实现响应式
        const p = new Proxy(person, {
            
            
          // 读取时调用
          get(target, prop) {
            
            
            console.log(target, prop);
            return target[prop]
          },
          // 修改或新增时调用
          set(target, prop, value) {
            
            
            console.log(target, prop, value);
            target[prop] = value;
          },
          // 删除时调用
          deleteProperty(target, prop) {
            
            
            console.log(target, prop);
            return delete target[prop];
          }
        })
      </script>
    </body>
    

    Operate the proxy object of the original object, and the proxy object operates the original object. The
    insert image description here
    above operations can be handed over to Reflect for execution

    new Proxy(data, {
          
          
    	// 拦截读取属性值
        get (target, prop) {
          
          
        	return Reflect.get(target, prop)
        },
        // 拦截设置属性值或添加新属性
        set (target, prop, value) {
          
          
        	return Reflect.set(target, prop, value)
        },
        // 拦截删除属性
        deleteProperty (target, prop) {
          
          
        	return Reflect.deleteProperty(target, prop)
        }
    })
    
    proxy.name = 'tom'  
    
  • Proxy and Reflect described in the MDN documentation:

<template>
  <h1>姓名:{
   
   {person.name}}</h1>
  <h1>年龄:{
   
   {person.age}}</h1>
  <h1 v-show="person.sex">性别:{
   
   {person.sex}}</h1>
  <h2>职业:{
   
   {person.job.type}}</h2>
  <h2>薪水:{
   
   {person.job.salary}}</h2>
  <button @click="changeInfo">改变个人信息</button>
</template>

<script>
import {
      
       reactive } from 'vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      name: '李四',
      age: 18,
      job: {
      
      
        type: '开发',
        salary: '20k'
      },
      hobby: ['看剧', '听歌']
    })

    // 方法
    function changeInfo() {
      
      
      person.sex = '男'
      delete person.name
      person.hobby[0] = '学习'
    }

    return {
      
      
      person,
      changeInfo
    }
  }
}
</script>

insert image description here

3.5 reactive vs ref

1. Comparison 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 a proxy object through reactive internally

2. Comparison from the perspective of principle

  • Object.defineProperty()ref implements responsiveness (data hijacking) through get and set
  • reactive implements responsiveness (data hijacking) by using Proxy, and manipulates the data inside the source object through Reflect

3. Comparison from the perspective of use

  • 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: operation data and read data: neither required.value

3.6 Two Notes on Setup

3.6.1 Timing of setup execution

Execute once before beforeCreate, this is undefined

3.6.2 Parameters of setup

  • props: The value is an object, including: App.vue component passed from outside the component, and the component internally declares the received property
    App.vue component, pass the value into Demo.vue

    <template>
      <Demo
        msg="你好"
        school=""
      />
    </template>
    
    <script>
    import Demo from './components/Demo.vue';
    
    export default {
            
            
      name: 'App',
      components: {
            
            
        Demo
      }
    }
    </script>
    

    Demo.vue component, receiving the value passed in by App.vue

    <template>
      <h1>个人信息</h1>
      <h2>姓名:{
         
         {person.name}}</h2>
      <h2>年龄:{
         
         {person.age}}</h2>
    </template>
    
    <script>
    import {
            
             reactive } from 'vue'
    
    export default {
            
            
      // eslint-disable-next-line vue/multi-word-component-names
      name: 'Demo',
      props: ['msg', 'school'],
      setup(props) {
            
            
        console.log(props);
        // 数据
        let person = reactive({
            
            
          name: '张三',
          age: 18
        })
    
        return {
            
            
          person
        }
      }
    }
    </script>
    
    <style>
    </style>
    

    insert image description here

  • 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

App.vue, pass values ​​into Demo.vue, and pass in custom events and slots at the same time

<template>
  <Demo
    msg="你好"
    school=""
    @hello="showMsg"
  >
    <template v-slot:te>
      <span>测试</span>
    </template>
  </Demo>
</template>

<script>
import Demo from './components/Demo.vue';

export default {
      
      
  name: 'App',
  setup() {
      
      
    function showMsg(value) {
      
      
      alert(`触发,参数是${ 
        value}`)
    }

    return {
      
      
      showMsg
    }
  },
  components: {
      
      
    Demo
  }
}
</script>

Demo.vue component, props receives the value passed in by App.vue, the unreceived value is in attrs, emits receives custom events, if the reception is not written, it will report a warning, but it does not affect the use, emit triggers the self in App.vue Define events. The slot is used directly

<template>
  <h1>个人信息</h1>
  <h2>姓名:{
   
   {person.name}}</h2>
  <h2>年龄:{
   
   {person.age}}</h2>
  <button @click="hello">测试触发事件</button>
  <slot name="te"></slot>
</template>

<script>
import {
      
       reactive } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  props: ['msg'],
  emits: ['hello'],
  setup(props, context) {
      
      
    console.log(props);
    console.log(context.attrs);
    console.log(context.emit);
    console.log(context.slots);
    // 数据
    let person = reactive({
      
      
      name: '张三',
      age: 18
    })
    // 方法
    function hello() {
      
      
      context.emit('hello', 666);
    }

    return {
      
      
      person,
      hello
    }
  }
}
</script>

<style>
</style>

insert image description here

3.7 Computed properties and monitoring

3.7.1 computed function (computed property)

The first way of writing: consistent with the computed configuration function in Vue2.x

<script>
import {
    
     reactive, computed } from 'vue'

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  computed: {
    
    
    fullName() {
    
    
      return this.person.firstName + '-' + this.person.lastName;
    }
  },
  setup() {
    
    
    // 数据
    let person = reactive({
    
    
      firstName: '张',
      lastName: '三',
    })

    return {
    
    
      person,
    }
  }
}
</script>

The second way of writing is as follows:

<template>
  <h1>个人信息</h1>
  姓:<input
    type="text"
    v-model="person.firstName"
  /> <br>
  名:<input
    type="text"
    v-model="person.lastName"
  /> <br>
  <span>全名: {
   
   {person.fullName}} </span> <br>
  全名:<input
    type="text"
    v-model="person.fullName"
  >
</template>

<script>
import {
      
       reactive, computed } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      firstName: '张',
      lastName: '三',
    })
    // 计算属性-简写(只读,不考虑计算属性被修改的情况)
    person.fullName = computed(() => {
      
      
      return person.firstName + '-' + person.lastName;
    })

    // 计算属性-完整写法(考虑读和写)
    person.fullName = computed({
      
      
      get() {
      
      
        return person.firstName + '-' + person.lastName;
      },
      set(value) {
      
      
        let nameArr = value.split('-');
        person.firstName = nameArr[0]
        person.lastName = nameArr[1]
      }
    })

    return {
      
      
      person,
    }
  }
}
</script>

<style>
</style>

insert image description here

3.7.2 watch function (monitoring)

The first way of writing: consistent with the computed configuration function in Vue2.x

<script>
import {
    
     ref } from 'vue'

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  watch: {
    
    
    sum(newValue, oldValue) {
    
    
      console.log('sum 的值变化了', newValue, oldValue);
    }
  },
  // watch: {
    
    
  //   sum: {
    
    
  //     immediate: true, // 立即监视,一进来就会监视一下
  //     deep: true,
  //     handler(newValue, oldValue) {
    
    
  //       console.log('sum 的值变化了', newValue, oldValue);
  //     }
  //   }
  // },
  setup() {
    
    
    // 数据
    let sum = ref(0)

    return {
    
    
      sum,
    }
  }
}
</script>

insert image description here

The second way of writing is as follows:

1. Monitor the data defined by ref

<template>
  <h1>当前求和为:{
   
   {sum}} </h1>
  <button @click="sum++">点击 + 1</button>
  <hr>
  <h1>当前信息为:{
   
   {msg}} </h1>
  <button @click="msg += '!'">修改信息</button>
</template>

<script>
import {
      
       ref, watch } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    // 数据
    let sum = ref(0)
    let msg = ref('你好')

    // 情况一,监视 ref 所定义的一个响应式数据
    // watch(sum, (newValue, oldValue) => {
      
      
    //   console.log('sum 变了', newValue, oldValue);
    // }, {immediate: true, deep: true})

    // 情况二,监视 ref 所定义的多个响应式数据
    watch([sum, msg], (newValue, oldValue) => {
      
      
      console.log(newValue, oldValue);
    }, {
      
       immediate: true, deep: true })

    return {
      
      
      sum,
      msg
    }
  }
}
</script>

<style>
</style>

insert image description here

2. Monitor the data defined by reactive

<template>
  <h1>当前姓名为:{
   
   {person.name}} </h1>
  <button @click="person.name += '~'">修改姓名</button>
  <hr>
  <h1>当前年龄为:{
   
   {person.age}} </h1>
  <button @click="person.age ++">修改年龄</button>
  <hr>
  <h2>职业:{
   
   {person.job.type}} </h2>
  <h2>薪酬:{
   
   {person.job.salary}} </h2>
  <button @click="person.job.type += '!'">修改职业</button>
</template>

<script>
import {
      
       reactive, watch } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    // 数据
    let person = reactive({
      
      
      name: '张三',
      age: 18,
      job: {
      
      
        type: '开发',
        salary: '25k'
      }
    })

    // 情况一,监视 reactive 所定义的一个响应式数据的全部属性
    //      watch 监视的是 reactive 定义的响应式数据,则无法正确获得 oldValue!!
    //      若watch 监视的是 reactive 定义的响应式数据,则强制开启了深度监视(deep 配置无效)
    // watch(person, (newValue, oldValue) => {
      
      
    //   console.log('person变化', newValue, oldValue);
    // }, { deep: false })

    // 情况二:监视 reactive 所定义的一个响应式数据的某个属性
    // watch(() => person.age, (newValue, oldValue) => {
      
      
    //   console.log('person 的age 变化了', newValue, oldValue);
    // })

    // 情况三:监视 reactive 所定义的一个响应式数据的某些属性
    watch([() => person.age, () => person.name], (newValue, oldValue) => {
      
      
      console.log('person 的age 变化了', newValue, oldValue);
    })

    // 特殊情况:监视的是 reactive 定义的属性中的某个对象属性,所以 deep 配置有效
    watch(() => person.job, (newValue, oldValue) => {
      
      
      console.log('person 的age 变化了', newValue, oldValue);
    }, {
      
       deep: true })


    return {
      
      
      person
    }
  }
}
</script>

<style>
</style>

insert image description here

3.7.2 watchEffect function

  • The routine of watch is: specify not only the attribute of monitoring, but also the callback of monitoring
  • The routine of watchEffect is: no need to specify which attribute to monitor, which attribute is used in the monitoring callback, then which attribute to monitor
  • watchEffect is a bit like computed:
    • But computed pays attention to the calculated value (the return value of the callback function), so the return value must be written
    • And watchEffect pays more attention to the process (the function body of the callback function), so there is no need to write the return value
<script>
import {
    
     reactive, watchEffect } from 'vue'

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
    
    
    // 数据
    let sum = ref(0)
    let person = reactive({
    
    
      name: '张三',
      age: 18,
      job: {
    
    
        type: '开发',
        salary: '25k'
      }
    })

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

    return {
    
    
      person
    }
  }
}
</script>

3.8 Life cycle

insert image description here
The lifecycle hooks in Vue2.x can continue to be used in Vue3.0, but two of them have been renamed:

  • beforeDestroy was renamed to beforeUnmount
  • destroyed changed its name to unmounted

app.vue

<template>
  <button @click="isShowDemo = !isShowDemo">显示/隐藏Demo</button>
  <Demo v-if="isShowDemo" />
</template>

<script>
import Demo from './components/Demo.vue';
import {
      
       ref } from 'vue'

export default {
      
      
  name: 'App',
  setup() {
      
      
    let isShowDemo = ref(true)

    return {
      
      
      isShowDemo
    }
  },
  components: {
      
      
    Demo
  }
}
</script>

Demo.vue

<template>
  <h1>Demo</h1>
</template>

<script>
export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  beforeCreate() {
      
      
    console.log('---beforeCreate---');
  },
  created() {
      
      
    console.log('---created---');
  },
  beforeMount() {
      
      
    console.log('---beforeMount---');
  },
  mounted() {
      
      
    console.log('---mounted---');
  },
  beforeUnmount() {
      
      
    console.log('---beforeUnmount---');
  },
  unmounted() {
      
      
    console.log('---unmounted--');
  },
}
</script>

insert image description here
insert image description here

Vue3.0 also provides lifecycle hooks in the form of Composition API, and the corresponding relationship with the hooks in Vue2.x is as follows:

  • beforeCreate ===> setup()
  • created ===> setup()
  • beforeMount ===> onBeforeMount
  • mounted ===> onMounted
  • beforeUpdate ===> onBeforeUpdate
  • updated ===> onUpdated
  • beforeUnmount ==> onBeforeUnmount
  • unmounted ===> onUnmounted
<template>
  <h1>Demo</h1>
</template>

<script>
import {
      
       onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    console.log('---setup()---');

    onBeforeMount(() => {
      
      
      console.log('---onBeforeMount---')
    })
    onMounted(() => {
      
      
      console.log('---onMounted---');
    })
    onBeforeUpdate(() => {
      
      
      console.log('---onBeforeUpdate---');
    })
    onUpdated(() => {
      
      
      console.log('---onUpdated---');
    })
    onBeforeUnmount(() => {
      
      
      console.log('---onBeforeUnmount---');
    })
    onUnmounted(() => {
      
      
      console.log('---onUnmounted---');
    })
  }
}
</script>

insert image description here
insert image description here

3.9 Custom hook function

The hook is essentially a function that encapsulates the Composition API used in the setup function. Similar to mixins in Vue2.x. The code can be reused to make the logic in the setup clearer and easier to understand

As follows, get the current mouse position

<template>
  <h1>当前点击时鼠标的坐标为:X:{
   
   {point.x}},y:{
   
   {point.y}} </h1>
</template>

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

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    let point = reactive({
      
      
      x: 0,
      y: 0
    })

    onMounted(() => {
      
      
      window.addEventListener('click', (event) => {
      
      
        point.x = event.pageX
        point.y = event.pageY
      })
    })

    return {
      
      
      point
    }
  }
}
</script>

insert image description here
At this time, a click event is added to the window. As long as the page is clicked, the mouse position of the page will be obtained for the point. If the component is uninstalled, it will still be triggered.
insert image description here
The click event can be removed after the component is uninstalled. When removing an event, you need to pass in which event to remove, so write the click event as a separate function

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

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
    
    
    let point = reactive({
    
    
      x: 0,
      y: 0
    })

    function savePoint(event) {
    
    
      point.x = event.pageX
      point.y = event.pageY

      console.log(point.x, point.y);
    }

    onMounted(() => {
    
    
      window.addEventListener('click', savePoint)
    })
    onBeforeUnmount(() => {
    
    
      window.removeEventListener('click', savePoint)
    })

    return {
    
    
      point
    }
  }
}
</script>

If another component also wants to use this function and reuse this piece of code, it can extract the data and functions related to this function to form a hook function
insert image description here

import {
    
     reactive, onMounted, onBeforeUnmount} from 'vue';

export default function() {
    
    
  let point = reactive({
    
    
    x: 0,
    y: 0
  })

  function savePoint(event) {
    
    
    point.x = event.pageX
    point.y = event.pageY

    console.log(point.x, point.y);
  }

  onMounted(() => {
    
    
    window.addEventListener('click', savePoint)
  })
  onBeforeUnmount(() => {
    
    
    window.removeEventListener('click', savePoint)
  })

  return point
}

You can import it when you use it

<template>
  <h1>当前点击时鼠标的坐标为:X:{
   
   {point.x}},y:{
   
   {point.y}} </h1>
</template>

<script>
import usePoint from '../hooks/usePoint'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    let point = usePoint()

    return {
      
      
      point
    }
  }
}
</script>

insert image description here

3.10 toRef

Creates a ref object whose value points to a property in another object.

  • grammar:const name = toRef(person, 'name')

It is used to provide a certain attribute in the responsive object to the external use separately, as follows, to provide the name, age and other attributes in the person object to the external use separately

<template>
  <h1>姓名: {
   
   {name}} </h1>
  <h1>年龄: {
   
   {age}} </h1>
  <h1>薪资: {
   
   {salary}} </h1>

  <button @click="name += '~'">修改姓名</button>
  <button @click="age ++">修改年龄</button>
  <button @click="salary ++">修改薪资</button>
</template>

<script>
import {
      
       reactive, toRef } from '@vue/reactivity'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Demo',
  setup() {
      
      
    let person = reactive({
      
      
      name: '张三',
      age: 18,
      job: {
      
      
        j1: {
      
      
          salary: 20
        }
      }
    })

    return {
      
      
      // name: ref(person, 'name')
      name: toRef(person, 'name'),
      age: toRef(person, 'age'),
      salary: toRef(person.job.j1, 'salary')
    }
  }
}
</script>

insert image description here
It has a responsive effect, and the data manipulated by toRef at this time is the data in the original person object. The modification means synchronously modifying the corresponding value in the person object.
If you directly use ref to convert, such as: ref(person.name), the manipulation is the person object The name attribute value of the object is a newly created ref object, and is no longer related to the name of the original person object.
insert image description here
toRefs has the same function as toRef, but multiple ref objects can be created in batches, that is, the entire object is thrown out. Syntax: , toRefs(person)but Can only target outer-level attributes

<h1>姓名: {
    
    {
    
    name}} </h1>
<h1>年龄: {
    
    {
    
    age}} </h1>
<h1>薪资: {
    
    {
    
    job.j1.salary}} </h1>
  
...toRefs(person)
// name: toRef(person, 'name'),
// age: toRef(person, 'age'),
// salary: toRef(person.job.j1, 'salary')

4. Other Composition APIs

4.1 shallowReactive 与 shallowRef

  • shallowReactive: Responsive (shallow responsive) that only deals with the outermost properties of the object
  • shallowRef: only handles the responsiveness of basic data types, not the responsiveness of objects
let person = shallowReactive ({
    
    
	name: '张三',
	age: 18,
	job: {
    
    
		j1: {
    
    
			salary: 20
		}
	}
})

When to use it?

  • If there is an object data, the structure is relatively deep, but only the outer attribute changes when it changes ===> shallowReactive
  • If there is an object data, the subsequent function will not modify the properties in the object, but generate a new object to replace ===> shallowRef

4.2 readonly 与 shallowReadonly

  • readonly: Make a responsive data read-only (deep read-only)
  • shallowReadonly: Make a responsive data read-only (shallow read-only)
let person = reactive ({
	name: '张三',
	age: 18,
	job: {
		j1: {
			salary: 20
		}
	}
})

person = readonly(person)

Applied when you do not want the data to be modified

4.3 toRaw and markRaw

toRaw

  • Function: convert a reactive object generated by reactive into a normal object

Usage scenario: used to read the common object corresponding to the responsive object, all operations on this common object will not cause page updates

const p = toRaw(person)

markRaw

  • Role: Mark an object so that it will never become a responsive object again

Application scenario:

  1. Some values ​​should not be set responsive, such as complex third-party libraries, etc.
  2. Skipping reactive transitions can improve performance when rendering large lists with immutable data sources
person.car = markRaw(person)

4.4 customRef

Create a custom ref, and explicitly control its dependency tracking and update trigger
. The following example achieves the anti-shake effect

<template>
  <input
    type="text"
    v-model="keyWord"
  />
  <h1> {
   
   {keyWord}} </h1>
</template>

<script>
import {
      
       customRef } from 'vue'

export default {
      
      
  name: 'App',
  setup() {
      
      
    // 自定义一个 ref
    function myRef(value, delay) {
      
      
      let timer;

      return customRef((track, trigger) => {
      
      
        return {
      
      
          get() {
      
      
            // 通知 Vue 追踪数据变化(提前约定)
            track();
            return value;
          },
          set(newValue) {
      
      
            value = newValue;
            clearTimeout(timer);

            timer = setTimeout(() => {
      
      
              // 通知 Vue 去重新解析模板(调用一下 get() 方法)
              trigger();
            }, delay);
          }
        }
      })
    }

    // let keyWord = ref('hello') // 使用 Vue 提供的 ref
    let keyWord = myRef('hello', '500') // 使用自定义的 ref

    return {
      
      
      keyWord,
      myRef
    }
  },
}
</script>

insert image description here

4.5 provide and inject

insert image description here
Realize the communication between ancestor and descendant components . The parent component has a provide option to provide data, and the descendant components have an inject option to start using these data
as follows. The ancestor component App.vue

<template>
  <div class="app">
    <h1> App 组件(祖),{
   
   {name}}---{
   
   {price}} </h1>
    <Child />
  </div>
</template>

<script>
import {
      
       reactive, toRefs } from '@vue/reactivity'
import Child from './components/Child.vue'
import {
      
       provide } from '@vue/runtime-core'

export default {
      
      
  name: 'App',
  setup() {
      
      
    let car = reactive({
      
      
      name: '奔驰',
      price: '40w'
    })

    provide('car', car)

    return {
      
      
      ...toRefs(car)
    }
  },
  components: {
      
      
    Child
  }
}
</script>

child component, Child.vue

<template>
  <div class="child">
    <h1> Child组件(子)</h1>
    <Son />
  </div>
</template>

<script>
import Son from './Son.vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Child',
  components: {
      
      
    Son
  }
}
</script>

Son component, Son.vue

<template>
  <div class="son">
    <h1> Son组件(孙),{
   
   {car.name}}---{
   
   {car.price}}</h1>
  </div>
</template>

<script>
import {
      
       inject } from '@vue/runtime-core'
export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Son',
  setup() {
      
      
    let car = inject('car')

    return {
      
      
      car
    }
  }
}
</script>

insert image description here

4.6 Judgment of responsive data

  • isRef : checks if a value is a ref object
  • isReactive : checks if an object is a reactive proxy created by reactive
  • isReadonly : Checks if an object is a read-only proxy created by readonly
  • isProxy : checks if an object is a proxy created by a reactive or readonly method
let car = reactive({
    
    name: '奔驰', price: '40w'})
let sum = ref(0)
let car2 = readonly(car)

console.log(isReactive(car))
console.log(isRef(sum))
console.log(isReadonly(car2))
console.log(isReadonly(isProxy))

5. Advantages of the Composition API

5.1 Problems with the Options API

In the traditional Options API used by Vue2, if you add or modify a requirement, you need to modify it in data, methods, and computed respectively
insert image description here
insert image description here

5.2 Advantages of the Composition API

Our code and functions can be organized more elegantly. Let the code of related functions be organized together in a more orderly manner
insert image description here
insert image description here

6. New components

6.1 Fragment

In Vue2: Components must have a root tag. In Vue3: the component can have no root tag, and multiple tags will be included in a Fragment virtual element internally, which can reduce the tag level and memory footprint

<template>
  <h1>111</h1>
  <h1>222</h1>
</template>

insert image description here

6.2 Teleport

Able to move the component html structure to the specified position
as follows, directly opening the pop-up window will expand the height of the component and its parent
insert image description here
component Son.vue

<template>
  <div class="son">
    <h1> Son 组件(孙)</h1>
    <Dialog />
  </div>
</template>

<script>
import Dialog from './Dialog.vue'

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Son',
  components: {
      
      
    Dialog
  }
}
</script>

Component Dialog.vue

<template>
  <button @click="isShow = true"> 点击弹窗 </button>
  <div
    v-if="isShow"
    class="dialog"
  >
    <h1>弹窗内容</h1>
    <h1>弹窗内容</h1>
    <h1>弹窗内容</h1>
    <button @click="isShow = false">关闭弹窗</button>
  </div>
</template>

<script>
import {
      
       ref } from '@vue/reactivity';

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Dialog',
  setup() {
      
      
    let isShow = ref(false);

    return {
      
      
      isShow
    }
  }
}
</script>

Use Teleport to move the popup window to the body

<template>
  <button @click="isShow = true"> 点击弹窗 </button>
  <teleport to='body'>
    <div
      v-if="isShow"
      class="mask"
    >
      <div class="dialog">
        <h1>弹窗内容</h1>
        <h1>弹窗内容</h1>
        <h1>弹窗内容</h1>
        <button @click="isShow = false">关闭弹窗</button>
      </div>
    </div>
  </teleport>
</template>

<script>
import {
      
       ref } from '@vue/reactivity';

export default {
      
      
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Dialog',
  setup() {
      
      
    let isShow = ref(false);

    return {
      
      
      isShow
    }
  }
}
</script>

<style>
.mask {
      
      
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.5);
}
.dialog {
      
      
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  background-color: green;
  width: 300px;
  height: 300px;
}
</style>

insert image description here

6.3 Suspense

Render some extra content while waiting for asynchronous components, so that the application has a better user experience
Static introduction

<template>
  <div class="app">
    <h1> App 组件(祖)</h1>
    <Child />
  </div>
</template>

<script>
import Child from './components/Child.vue' // 静态引入

export default {
      
      
  name: 'App',
  components: {
      
      
    Child
  }
}
</script>

Slow down the network speed, the ancestor component and the descendant component come out together and
insert image description here
asynchronously import

<template>
  <div class="app">
    <h1> App 组件(祖)</h1>
    <Child />
  </div>
</template>

<script>
// import Child from './components/Child.vue' //静态引入
import {
      
       defineAsyncComponent } from '@vue/runtime-core' // 静态引入
const Child = defineAsyncComponent(() => import('./components/Child.vue')) // 异步引入

export default {
      
      
  name: 'App',
  components: {
      
      
    Child
  }
}
</script>

When the network speed is slow, the ancestor component will appear first, and then the descendant component will appear.
insert image description here
At this time, there is a problem. If the Child component is not loaded, its location is empty, and it is not known whether there is any content. Use Suspense to solve as follows:

<template>
  <div class="app">
    <h1> App 组件(祖)</h1>
    <Suspense>
      <template v-slot:default>
        <Child />
      </template>
      <template v-slot:fallback>
        <h3>稍等,加载中...</h3>
      </template>
    </Suspense>
  </div>
</template>

<script>
// import Child from './components/Child.vue' //静态引入
import {
      
       defineAsyncComponent } from '@vue/runtime-core' // 静态引入
const Child = defineAsyncComponent(() => import('./components/Child.vue')) // 异步引入

export default {
      
      
  name: 'App',
  components: {
      
      
    Child
  }
}
</script>

insert image description here
After using asynchronous import, the component is an asynchronous component, then setup()you can use async to fix an asynchronous function

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

export default {
    
    
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Child',
  async setup() {
    
    
    let sum = ref(0)

    let p = new Promise((resove, reject) => {
    
    
      setTimeout(() => {
    
    
        resove(sum)
      }, 3000);
    })

    return await p;
  }
}
</script>

In the past, the effect of waiting for the Child component to appear was achieved by slowing down the network speed. After using the asynchronous function, the normal network speed can also allow the Child component to wait until it appears
insert image description here

7. Other changes

7.1 Transfer of global API

Vue 2.x has many global APIs and configurations, such as: registering global components, registering global directives, etc.

//注册全局组件
Vue.component('MyButton', {
    
    
  data: () => ({
    
    
    count: 0
  }),
  template: '<button @click="count++">Clicked {
    
    { count }} times.</button>'
})

//注册全局指令
Vue.directive('focus', {
    
    
  inserted: el => el.focus()
}

These APIs are adjusted in Vue3.0, and the global API, namely: Vue.xxx is adjusted to the application instance (app)

2.x Global API (Vue) 3.x instance API (app)
app.config.xxxx app.config.xxxx
Vue.config.productionTip remove
Vue.component app.component
Directive.view app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

7.2 Other changes

  • The data option should always be declared as a function
    // data { } 
    data() {
          
           }
    
  • Excessive class name changes:
    • Vue2.x writing method
      .v-enter,
      .v-leave-to {
              
              
        opacity: 0;
      }
      .v-leave,
      .v-enter-to {
              
              
        opacity: 1;
      }
      
    • Vue3.x writing method
      .v-enter-from,
      .v-leave-to {
              
              
        opacity: 0;
      }
      
      .v-leave-from,
      .v-enter-to {
              
              
        opacity: 1;
      }
      
  • Remove keyCode as a v-on modifier, and no longer support config.keyCodes
    // @keyup.13
    // Vue.config.keyCodes.enter = 13
    
  • Remove the v-on.native modifier , when passing an event to a component, Vue2 will also consider the click event as a custom event, and need to add native to indicate that it is a native event. Vue3 uses emit to specify custom events, and those that are not specified are native events
    • Bind event in parent component
      <my-component
        v-on:close="handleComponentEvent"
        v-on:click="handleNativeClickEvent"
      />
      
    • Declare custom events in subcomponents
      <script>
        export default {
              
              
          emits: ['close']
        }
      </script>
      
  • Removing the filter (filter)
    Filter, while it seems convenient, requires a custom syntax that breaks the assumption that expressions inside curly braces are "just JavaScript", which not only has a learning cost, but also an implementation cost! It is recommended to replace filters with method calls or computed properties

Guess you like

Origin blog.csdn.net/ACE_U_005A/article/details/127017186