current position:Home>New changes in vue2 upgrade vue3

New changes in vue2 upgrade vue3

2022-11-24 22:40:02Yancy


在这里插入图片描述

vue 作者(尤大)在2022-2-7The announcement vue3 The official version as the default,vue3At present is also can be put into production in the project,vue3 + vite + TSIs the current popular configuration.This blog is mainly writing compared to avue2,vue3的新变化.

在这里插入图片描述

1、组合式API和setup语法糖

Vue3.0给我们提供了composition API,而实现composition APIThis code style is mainly using official offersetup这个函数.

<script lang="ts"> import {
       defineComponent, ref } from 'vue' import NavMenu from '@/components/nav-menu' import NavHeader from '@/components/nav-header' export default defineComponent({
       //Need to declare the introduction of the component components: {
       NavMenu, NavHeader }, //setup中props和context参数 setup(props,context) {
       const isCollapse = ref(false) const handleFoldChange = (isFold: boolean) => {
       isCollapse.value = isFold } //需要在setupExport variables and functions return {
       isCollapse, handleFoldChange } } }) </script>

可以看到,可以在setupFunction is written in logic,不需要像vue2那样在data和methodsDefine data and methods of the corresponding,当然,对于computed、watch等需要通过hooks来创建,It is also a option to type(vue2)和组合式(vue3)的区别所在,对于vue3来说,The same part of the business logic code(需要的数据,时间、监听等)可以写在一起,This is for code checking、Business logic to extract is very friendly.
在这里插入图片描述
在这里插入图片描述
vue3.2To further optimize the combinationapi:

<script setup lang="ts"> import {
       ref } from 'vue' import AccountLogin from './account-login.vue' const isRememberPassword = ref(true) const accountRef = ref<InstanceType<typeof AccountLogin>>() const activeName = ref('account') const login = () => {
       activeName.value === 'account' ? accountRef.value?.loginAction(isRememberPassword.value) : phoneRef.value?.loginAction(isRememberPassword.value) } </script>

可以看到,相比较于vue3.0,vue3.2的script标签直接使用setup属性(也就是setup语法糖),Variables and methods in your code doesn't need toreturnCan be directly in the template directly using the,And the introduction of components don't need tocomponents中声明,Direct can use.
需要考虑的问题:如何获取props和emits……

defineProps

Used to retrieve the parent component passedprops.

<template>
  <div>
    <h2>{
   {message}}</h2>
  </div>
</template>
<script lang="ts" setup> import {
      defineProps} from 'vue' defineProps({
       message:{
       type:String, default:'hahha' } }) </script>

defineEmits

When used to debug the parent component calls subcomponents definition method.

<template>
  <div>
  <button @click='sendEmit'>To the parent component send events</button>
</div>
</template>
<script lang="ts" setup> import {
      defineEmits} from 'vue' //使用defineEmits创建名称,接受一个数组 const emit = defineEmits(['sendEmit']) //Call event parameters const sendEmit = () =>{
       emit('sendEmit','传递的数据') } </script>

defineExpose

The parent component can pass in the component Settingsref属性,然后在script中声明对应变量,To directly obtain the son component instance(并不推荐).
子组件:ChildComponent.vue

<template>
  <div>
    <p>{
   { name }}</p>
  </div>
</template>


<script setup lang="ts"> import {
       ref } from "vue"; const name = ref("ChildComponent"); //暴露 defineExpose({
       name, }); </script>


<style scoped></style>

父组件:

<template>
  <ChildComponentVue ref="child"></ChildComponentVue>
</template>

<script setup lang="ts"> import ChildComponentVue from "./ChildComponent.vue"; import {
       onMounted, ref } from "vue"; let child = ref(null); //需要在onMounted中获取 It is obtainDOM实例 onMounted(() => {
       console.log(child.value); }); </script>

<style scoped></style>

其他

script setupWill the default statementasync,类似于async setup()的效果,你可以在script setup中直接使用await函数.
之前可以通过useContext从上下文中获取 slots 和 attrs.不过提案在正式通过后,废除了这个语法,被拆分成了useAttrs和useSlots.

2、响应式原理ref和reactive

vue2双向数据绑定是利用ES5的Object.defineProperty()对数据进行劫持,Combines the publish-subscribe pattern to realize.
vue3中使用ES6的ProxyAPI对数据代理.
ProxyIs a direct acting a whole object,所以相比较于Object.definePropertyHijacked a single attribute,Proxy有着先天的优势,Such as can direct the deep properties of a proxy object,Add or remove objects properties,Also can direct listening to(Object.defineProperty不可以),Direct agent array, etc.
如果要在vue3.2中定义响应式数据(数据改变,页面更新),需要借助ref和reactive来处理数据.

ref

  1. 作用:定义一个响应式的数据,Or to generate a reference implementation object
  2. 语法:const xxx = ref(initValue)
    ● 创建一个包含响应式数据的引用对象(reference对象)
    ● JS中操作数据需要:xxx.value
    ● 模板中读取数据:直接使用即可

PS:

  1. refThe received data can be a basic types of data,也可以是对象类型.
  2. 基本类型的数据:The response type is to rely onObject.defineProperty()的get和set完成的.
  3. 对象类型的数据:内部使用了reactive函数.
<template>
  <div>
    <p>{
   { msg }}</p>
    <button @click="msg = 'Hello Vue!'">Change</button>
  </div>
</template>

<script setup lang="ts"> import {
       ref } from "@vue/reactivity"; let msg = ref("Hello World!"); </script>

<style scoped></style>

reactive

  1. 作用:定义一个对象类型的响应式数据.
  2. 语法:const 代理对象 = reactive(源对象) Parameter is an object or array,返回一个代理对象(Proxy的实例对象,简称proxy对象).
  3. reactive定义的响应式数据是深层次的.
  4. 内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据.
<template>
  <div>
    <p>{
   { user.name }}</p>
    <p>{
   { user.age }}</p>
    <p>{
   { user.country }}</p>
  </div>
</template>

<script setup lang="ts"> import {
       reactive } from "@vue/reactivity"; let user = reactive({
       name: "Yancy Zhang", age: 20, country: "China", }); setTimeout(() => {
       user.age++; }, 1000); </script>

<style scoped></style>
  1. 若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,请使用 shallowReactive() 作替代.
  2. 不能通过 …state (扩展运算符)方式结构,这样会丢失响应式.
  3. 注意reactive封装的响应式对象,Don't return by means of deconstruction,这是不具有响应式的.可以通过 toRefs 处理,然后再解构返回,这样才具有响应式.
const state = reactive({
    
  foo: 1,
  bar: 2
})

// state.foo Was originally a responsive object,Was again after the assignment, it is not responsive object,Just a common basic types of value

const {
    foo, bar} = state; // 此时 foo 就等于 1 了,1 是一个值,不是一个响应式对象
foo = 6; // Changes in the view will not take effect,Because of the deconstruction foo 被重新赋值了,即 const foo = state.foo; const foo = 1;

toRef

Based on the response type object on an attribute,创建一个对应的 ref.这样创建的 ref With its source attribute in sync:Change the source attribute's value will be updated ref 的值,反之亦然.

const state = reactive({
    
  foo: 1,
  bar: 2
})

const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

toRefs

将一个响应式对象转换为一个普通对象,The ordinary objects of each attribute is pointing in the direction of the source object properties of the corresponding ref.每个单独的 ref 都是使用 toRef() 创建的.
当从组合式函数中返回响应式对象时,toRefs 相当有用.使用它,消费者组件可以解构/On the object returned without loss of responsiveness:

function useFeatureX() {
    
  const state = reactive({
    
    foo: 1,
    bar: 2
  })

  // ...基于状态的操作逻辑

  // 在返回时都转为 ref
  return toRefs(state)
}

// 可以解构而不会失去响应性,Because after the deconstruction of foo 是个 Ref 对象,If direct deconstructionstateAfter word of deconstruction is a value ‘1’
const {
     foo, bar } = useFeatureX()

3、computed和watch

我们知道,在vue2的选项式API中,可以在computed:{}和watch:{}Defined in the corresponding calculation attributes and listener.

computed: {
    
    fullName: function () {
    
      return this.firstName + ' ' + this.lastName
    }
  }
watch: {
    
  firstName: function (val) {
    
    this.fullName = val + ' ' + this.lastName
  },
  lastName: function (val) {
    
    this.fullName = this.firstName + ' ' + val
  }
}

那么在vue3的组合式API中,How to use the calculated properties and listener?

computed

我们可以直接使用computedMethods to define a computational properties:

<script setup> import {
       reactive, computed } from 'vue' const author = reactive({
       name: 'John Doe', books: [ 'Vue 2 - Advanced Guide', 'Vue 3 - Basic Guide', 'Vue 4 - The Mystery' ] }) // 一个计算属性 ref const publishedBooksMessage = computed(() => {
       return author.books.length > 0 ? 'Yes' : 'No' }) </script>

<template>
  <p>Has published books:</p>
  <span>{
   { publishedBooksMessage }}</span>
</template>

watch

同理,可以使用watchThe function definitions a listener:

<script setup> import {
       ref, watch } from 'vue' const question = ref('') const answer = ref('Questions usually contain a question mark. ;-)') // 可以直接侦听一个 ref watch(question, async (newQuestion, oldQuestion) => {
       if (newQuestion.indexOf('?') > -1) {
       answer.value = 'Thinking...' try {
       const res = await fetch('https://yesno.wtf/api') answer.value = (await res.json()).answer } catch (error) {
       answer.value = 'Error! Could not reach the API. ' + error } } }) </script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" />
  </p>
  <p>{
   { answer }}</p>
</template>

直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发.
For an object returns a response type getter 函数,Only when return different object,才会触发回调,Can add a third configuration parameters,To implement the monitoring deep attribute changes:

watch(
  () => state.someObject,
  (newValue, oldValue) => {
    
    // 注意:`newValue` 此处和 `oldValue` 是相等的
    // *除非* state.someObject 被整个替换了
  },
  {
     deep: true }
)

watchEffect

watch() 是懒执行的:仅当数据源变化时,才会执行回调.但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调.

const url = ref('https://...')
const data = ref(null)

async function fetchData() {
    
  const response = await fetch(url.value)
  data.value = await response.json()
}

// 立即获取
fetchData()
// ...再侦听 url 变化
watch(url, fetchData)

我们可以用 watchEffect函数 来简化上面的代码.watchEffect() 会立即执行一遍回调函数,如果这时函数产生了副作用,Vue 会自动追踪副作用的依赖关系,自动分析出响应源.上面的例子可以重写为:

watchEffect(async () => {
    
  const response = await fetch(url.value)
  data.value = await response.json()
})

所以相比较于watch,watchEffect只有一个参数,就是一个回调函数.

4、v-model

v-modelSuitable for bidirectional binding instructions,在vue2中,A component or tag can have only onev-model,And default attributes and events asvalue和input,So we can also be used for two-way data binding between father and son components.
vue3Summary of changes:
● 非兼容:用于自定义组件时,v-model prop 和事件默认名称已更改:
○ prop:value -> modelValue;
○ 事件:input -> update:modelValue;
● 非兼容:v-bind 的 .sync 修饰符和组件的 model 选项已移除,可在 v-model 上加一个参数代替;
● 新增:现在可以在同一个组件上使用多个 v-model 绑定;
● 新增:现在可以自定义 v-model 修饰符.
在 3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:

<ChildComponent v-model="pageTitle" />

<!-- 是以下的简写: -->
<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />

v-model参数

若需要更改 model 的名称,现在我们可以为 v-model 传递一个参数,以作为组件内 model 选项的替代(默认为modelValue):

<ChildComponent v-model:title="pageTitle" />

<!-- 是以下的简写: -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

这也可以作为 .sync 修饰符的替代,而且允许我们在自定义组件上使用多个 v-model.

<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<!-- 是以下的简写: -->

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" :content="pageContent" @update:content="pageContent = $event" />

v-model修饰符

除了像 .trim 这样的 2.x 硬编码的 v-model 修饰符外,现在 3.x 还支持自定义修饰符:

<ChildComponent v-model.capitalize="pageTitle" />
<script setup> const props = defineProps({
       modelValue: String, modelModifiers: {
       default: () => ({
      }) } }) defineEmits(['update:modelValue']) console.log(props.modelModifiers) // { capitalize: true } </script>

<template>
  <input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>

Pay attention to this component modelModifiers prop 包含了 capitalize 且其值为 true,Because it is in the template v-model Binding is used on the.
有了 modelModifiers 这个 prop,We can check it in the native event listener function value,And then decided to trigger a custom event to the parent component pass what value.

5、key

● 新增:对于 v-if/v-else/v-else-if 的各分支项 key 将不再是必须的,因为现在 Vue 会自动生成唯一的 key.
○ 非兼容:如果你手动提供 key,那么每个分支必须使用唯一的 key.你将不再能通过故意使用相同的 key 来强制重用分支.
● 非兼容: 的 key 应该设置在 标签上 (而不是设置在它的子节点上).
● 在 Vue 3.x 中,key 则应该被设置在 标签上.

6、v-if和v-for的优先级对比

两者作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级.(与vue2正好相反)

7、异步组件

在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件.

import {
     defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() => {
    
  return new Promise((resolve, reject) => {
    
    // ...从服务器获取组件
    resolve(/* 获取到的组件 */)
  })
})
// ... 像使用其他一般组件一样使用 `AsyncComp`

ES Dynamic import module also returns a Promise,所以多数情况下我们会将它和 defineAsyncComponent 搭配使用.类似 Vite 和 Webpack 这样的构建工具也支持此语法 (并且会将它们作为打包时的代码分割点),So we can also use it to import Vue 单文件组件:

import {
     defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
)

全局注册:

app.component('MyComponent', defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
))

Also can define them directly in the parent component directly:

<script setup> import {
       defineAsyncComponent } from 'vue' const AdminPage = defineAsyncComponent(() => import('./components/AdminPageComponent.vue') ) </script>

<template>
  <AdminPage />
</template>

copyright notice
author[Yancy],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/328/202211242238461996.html

Random recommended