【Vue实战教程】之 Vue3 新特性详解

news2024/12/22 8:56:59

1 为什么要用Vue3

在学习Vue3的新特性之前,我们先来看一下Vue3设计的目的是什么,为什么要对Vue2做出很大的改变,以及Vue3到底解决了什么问题。像Vue这样全球闻名的前端框架,在任何一次改动时,设计者都是经过深思熟虑的权衡,所以,Vue3的出现肯定是解决了某些棘手的问题。下面介绍一下Vue2中遇到的问题。

1.1 Vue2对复杂功能的处理不友好

使用Vue2开发项目的过程中,随着更加复杂业务逻辑的增加,复杂组件的代码变得难以维护。尤其是一个开发人员从别的开发人员手中接过一个新项目时,这个问题更突出。究其根本原因,Vue2中的API是通过选项来组织代码,但是大部分情况下,通过逻辑来组织代码会更有意义。Vue2中缺少多个组件之间提取和复用逻辑的机制,现有的重用机制都有很多缺点。

假设我们有一个需求,要对所有的视频进行分类管理,点击不同的学科分类,可以查看不同的视频列表,如图1所示。针对这个需求,可以设置一个filter过滤功能,根据用户点击的学科分类,展示不同内容。
在这里插入图片描述
图 1 视频分类效果

在上面的案例中,用户点击不同的学科分类,展示的视频列表如果超出了当前页面展示的数量,例如,每个分页下展示40个视频,超出范围的要使用分页显示,效果如图2所示。
在这里插入图片描述
图 2 视频列表分页效果

上面的案例,如果使用Vue2.0开发,示例代码如下:

export default {
    data() {
        return {
            filter: {}, //处理过滤功能
            pagination: {} //处理分页功能
        }
    },
    methods: {
        filterMethod: () => {},  //处理过滤功能
        paginationMethod: () => {} //处理分页功能
    },
    computed: {
        ...
    }
}

通过上面代码可以看到,在data中已经做了filter过滤和pagination分页的相关处理,但是在下面的methods和computed中还需要继续做相关的处理,这些功能会分散到好几个部分。如果仅仅是这两个处理功能,项目的逻辑看上去还不是特别复杂,但是如果再增加搜索、收藏、排序等功能,随着功能复杂度的上升,带来的问题也愈加明显。

1.2 Vue2中mixin存在缺陷

在上一小节中提到的案例,Vue2也给出了解决方案,那就是使用mixin混入的方式。对上面案例中遇到的问题,我们可以使用mixin重写编写代码,示例代码如下:

const filterMixin = {
	data() {
		return {}
	},
	methods: {}
}

const  paginationMixin = {
	data() {
		return {}
	},
	methods: {}
}

export default {
	mixins: [filterMixin, paginationMixin]
}

在上面代码中,创建了两个mixin混入对象,filterMixin和paginationMixin,然后在Vue组件对象中使用mixins选项属性引入这两个对象。虽然这样可以暂时解决一些按逻辑分类的问题,但是这样做也会带来一些其他问题。
首先是会产生多个混入对象的属性和方法名称的命名冲突,其次是mixin对象所暴露的变量有什么作用,第三是把混入对象中的逻辑复用到其他的组件中,还会出现一些不可预知的问题。

1.3 Vue2对TypeScript的支持有限

Vue框架的开发者都清楚,Vue2对TypeScript的支持并不友好,这是因为在Vue中是依赖this上下文对象向外暴露属性,但是在组件中的this与普通的JavaScript中的Object对象处理的方式不同。其实,在Vue2设计时就没有考虑对TypeScript的集成和强制类型的相关问题,所以才导致在Vue2中使用TypeScript有很多阻碍。

2 Vue3.0简介

众所周知,前端技术一直更新的很快,特别是前端框架,更新速度更是极快的。在2020年4月21日晚上,Vue的作者尤雨溪在B站上直播分享了Vue3.0 Beta最新进展,直到9月19日,Vue3.0正式版才发布。这个耗时两年,历经99为代码贡献者,2600多次代码提交的大版本更新终于和众多开发者见面了。
为了减少前端开发者的学习成本,Vue2的大部分特性都完全地保留到Vue3中,你可以像使用Vue2一样,原封不动地使用Vue3,这是遵循了渐进式的准则。如果你是一个保守派,就想使用Vue2的写法,也是完全没有问题的。
Vue3增加了以下的新特性。

2.1 Vue3在性能上有很大提升

没有哪一位开发者不想要更快、更轻的框架,Vue3给开发者带来了极致的开发体验。整个Vue3的代码库被重新编写成了一系列独立的,并且实现了不同功能的模块。据官方介绍,Vue3的代码打包大小减少了41%,初次渲染速度提升55%,更新效率提升33%,内存使用率减少54%。这些数据都得益于Vue3中重构了虚拟DOM的写法,提升渲染速度。

2.2 Vue3推出了新的API

在Vue2中遇到了一些问题,例如,复杂组件的代码变得越来越难以维护,缺少一种纯粹的多组件之间提取和复用逻辑的机制。虽然Vue2中也提供了相关的解决方案,但是在Vue2中对于重用机制这一部分也存在了一些弊端。所以,Vue3中设计了Composition API,这也是我们本章重点介绍和使用的Vue3的新特性。
Composition这个单词是“组合”的意思,是Vue3中新推出的一系列API的合集,主要包括了以下API。

  • ref
  • reactive
  • computed
  • watch
  • 新的生命周期函数
  • 支持自定义Hooks函数
  • Teleport
  • Suspense
  • 全局API的修改和优化

2.3 更好的TypeScript支持

如果有在Vue2中集成TypeScript的开发者应该都体会过其中的痛苦,因为Vue2在推出的时候没有把TypeScript作为一个考量范围,那么在设计Vue3的时候,设计者们就痛定思痛地考虑了这方面的问题。
Vue3的源代码全部都是使用TypeScript语法编写的,提供了非常完备的类型定义,在使用Vue3开发项目时,可以把TypeScript语法深入到各个大型项目中,让开发者更加方便的享受类型推论等一系列TypeScript的红利。同时,还可以在VSCode等编辑器中安装相关的插件,完美的使用TypeScript的各种功能。

3 Vue3.0项目搭建

3.1 Vue CLI脚手架简介

学习Vue3之前要先配置Vue3和TypeScript的开发环境,我们本章节中使用的是Vue开发团队推出的官方脚手架工具——Vue CLI。
Vue CLI是一个基于Vue.js进行快速开发的完整系统,它提供了一系列与Vue框架相关的功能,例如,启动一个本地服务器、静态校验代码格式、运行单元测试、构建生产环境等。
在安装Vue CLI脚手架工具之前,需要先检查一下Node.js的版本,推荐使用Node.js10以上的版本。在cmd命令行工具中运行命令查看Node.js的版本,命令如下:

node --version

命令运行效果如图3所示。

在这里插入图片描述
图3 查看Node.js版本

3.2 安装Vue CLI

配置Vue3和TypeScript的开发环境之前,要先安装Vue CLI脚手架工具,安装命令如下。

npm install -g @vue/cli
## OR
yarn global add @vue/cli

由于npm访问的是境外服务器,很多情况下会出现请求速度慢或请求不到服务器的问题,推荐使用 cnpm 的方式下载Vue CLI,cnpm安装的命令如下。

npm install -g cnpm --registry=https://registry.npm.taobao.org

使用cnpm安装Vue CLI,命令如下。

cnpm install -g @vue/cli

如果你之前安装过Vue CLI,建议执行上面的命令来更新版本。安装成功后需要查看一下当前的Vue CLI版本是否为4.x,命令如下。

vue --version

查看版本命令运行效果如图4所示。

在这里插入图片描述
图4 查看Vue CLI版本

本节中使用的Vue CLI的版本为4.5.11,如果你的版本与本节中的版本不一致,不会造成负面影响,只需要保证你的版本为4.5.0以上即可,因为只有4.5.0以上的版本才能创建支持最新版的Vue3的基础项目。

3.3 创建Vue3项目

Vue CLI提供了命令行和UI界面创建项目的两种方式,无论使用哪种方式,创建的流程是完全一样的,只是展示的形式不太一样而已。本节使用的是命令行的方式创建Vue3项目。
在本地硬盘中创建一个project的目录,在该目录下启动命令行工具,创建vue3-basic的项目,执行以下命令。

vue create vue3-basic

在命令行工具中输入上面的命令,按回车,会出现如图5所示的选项。

在这里插入图片描述
图5 选择创建Vue项目模式

在图5中有三种创建模式,前两种分别是使用Vue2和Vue3默认的模板创建,第三种是手动创建,通过键盘的上下键选择创建模式,本节使用第三种手动创建的模式。
如果在你的命令行中没有出现“Vue3 Preview”选项,说明现在使用的是旧版的Vue CLI脚手架工具,需要更新版本,更新版本的方法参考3.2章节中的安装Vue CLI命令。
由于前两项创建模式不支持TypeScript语法,所以在命令行中,使用上下键选择 “Manually select features”模式,按回车进入下一步操作。效果如图6所示。

在这里插入图片描述
图6 安装需要的模块

在图6中提供了一系列课插拔的支持,涵盖了Vue3项目开发中需要的各种各样的功能,充分体现了Vue CLI工具的建议式配置项目的特点。在这一步骤中同样使用上下键选择需要的功能,选中需要安装的功能后按空格键在选择和取消选择之间进行切换,选择完成后按回车键确认,然后进入下一步操作。功能选择的效果如图7所示。

在这里插入图片描述
图7 功能选择

在下一步操作中需要选择Vue的版本,选择Vue3的版本,按回车进入下一步。效果如图8所示。

在这里插入图片描述
图8 选择Vue的版本

在下一步操作的提示中,系统询问了是否需要class-style的组件来支持TypeScript。由于Vue3已经对底层代码进行了重写,不需要class也可以很方便的进行开发,而且无需额外的配置。在这一步操作中,输入英文字母“n”,然后按回车进入下一步操作。效果如图9所示。

在这里插入图片描述
图9 选择是否使用class-style

在下一步操作中提示是否需要Babel和TypeScript结合使用,Babel会自动添加polyfills,并转换JSX。因为我们创建的Vue3项目中没有使用到JSX,所以这一步仍然输入字母“n”,继续按回车进入下一步操作。效果如图10所示。
在这里插入图片描述
图10 选择是否使用Babel

下一步选择是否使用“history”路由模式,这里输入“y”,按回车继续下一步,效果如图11所示。

在这里插入图片描述
图11 选择路由模式

下一步是询问是将配置信息放到一个独立的文件中还是放到package.json文件中,这里选择的是放到package.js文件中,然后按回车继续进入下一步。效果如图12所示。

在这里插入图片描述
图12 选择配置文件的类型

最后一步是询问是否将前面步骤的选择保存为一个模板,方便在以后的项目创建中一键安装,这里选择“n”,按回车。效果如图13所示。

在这里插入图片描述
图13 是否保存模板

安装上面的操作步骤完成后,会进入安装流程,这个环境需要进行一段时间的等待。当命令行工具中显示如图14所示的效果,就表示项目创建成功。

在这里插入图片描述
图14 项目创建成功

项目创建成功后,在命令行工具中继续输入如下命令,启动本地服务器。

cd vue3-basic
npm run serve

服务器启动成功效果如图15所示。

在这里插入图片描述
图15 启动本地服务器

服务器启动成功后,在浏览器中访问 http://localhost:8080/ 访问项目,效果如图16所示。

在这里插入图片描述
图16 在浏览器中访问Vue3项目

如果你在浏览器中成功打开如图16所示的页面,Vue3的项目就搭建成功了。

4 Vue3项目的目录结构

Vue3项目的目录结构如下。

- node_modules 项目的依赖管理目录
- public 公共资源管理目录
  |-- favicon.ico 站点title的图标
|-- index.html 站点的静态网页
- src 源码管理目录
  |-- assets 静态资源管理目录
  |-- components 公共组件管理目录
  |-- router 路由管理目录
  |-- store 状态管理目录
  |-- views 视图组件管理目录
  |-- App.vue 项目根组件
  |-- main.ts 项目入口文件
  |-- shims-vue.d.ts 定义.vue类型的TypeScript配置文件
- package-lock.js 依赖管理配置文件
- package.js 依赖管理配置文件
- tsconfig.json TypeScript配置文件

Vue3的项目目录结构与Vue2的类似,唯一不同的是很多.js文件都改为了.ts文件。其中,src目录下的shims-vue.d.ts是用来定义vue类型的TypeScript配置文件。因为.vue结尾的Vue组件文件在TypeScript中是不能被直接识别的,所以需要使用该配置文件来说明.vue的类型,便于TypeScript进行解析。
Vue3中main.ts入口文件的源代码与Vue2也有很大的差别。我们先来看一下Vue2中的main.js的代码,示例代码如下:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

在Vue2中使用new关键字实例化Vue对象,然后通过构造函数将选项属性传入到Vue实例中。而Vue3对main.js做了修改,在Vue3中使用的是.ts类型的文件编写的入口文件,示例代码如下:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

createApp(App).use(store).use(router).mount('#app')

Vue3的main.ts文件中引入了createApp函数,然后再引入App.vue组件,调用createApp函数来创建Vue实例,将所有的模块使用Vue实例对象进行调用,而不是像Vue2中直接传入到Vue对象的构造方法中,这是Vue3做的很大的改进。

5 Composition API 详解

Composition API是Vue的下一代主要版本中最常用的语法,它是一种全新的逻辑重用和代码组织的方法。在Vue2中使用的是Options API的方式构建组件,如果要向Vue组件中添加业务逻辑,需要先填充选项属性,例如data、methods、computed等。这种方式最大的缺点是,它本身并不是有效的JavaScript代码,需要先了解模板中可以访问哪些属性,然后再使用Vue的编译器将这些属性转换成可以执行的代码,这样做既消耗了性能,有无法让使用做更好的类型检查。
Composition API设计的目的通过将当前可用组件属性作为JavaScript函数暴露出来,这种机制可以基于功能的附加API灵活地组合组件逻辑,使Composition API编写的代码更易读。
下面我们来学习Composition API的语法。

5.1 setup函数

setup()函数是vue3中专门新增的方法,可以理解为Composition Api的入口。
Composition API的主要思想是,将Vue组件的选项属性定义为 setup 函数返回的JavaScript变量,而不是将组件的功能(例如state、method、computed等)定义为对象属性。
setup函数的执行时机是在beforeCreate之后,created之前。setup函数有两个参数,第一个参数是用于接收props数据。示例代码如下:

export default {
  props: {
    msg: {
      type: String,
      default: () => {}
    }
  },
  setup(props) {
  	console.log(props);
  }
}

setup函数的第二个参数是一个上下文对象,这个上下文对象大致包含了一些属性,示例代码如下:

const MyComponent = {
  setup(props, context) {
    context.attrs
    context.slots
    context.parent
    context.root
    context.emit
    context.refs
  }
}

这里需要注意,在setup函数中是无法访问this的。

5.2 reactive函数

reactive是用来创建一个响应式对象,等价于2.x的Vue.observable,示例代码如下:

<template>
    <div>
        <p @click="incment()">
            click Me!
        </p>
        <p>
            一:{{ state.count }} 二: {{ state.addCount }}
        </p>
    </div>
</template>

<script>
import { reactive } from 'vue';
export default {
    setup () {
        const state = reactive({//创建响应式数据
            count: 0,
            addCount: 0
        });

        function incment () {
            state.count++;
            state.addCount = state.count * 2;
        }
				
        return {
            state,
            incment
        };
    }
};
</script>

5.3 ref函数

ref函数用来给定的值创建一个响应式的数据对象,ref函数的返回值是一个对象,这个对象上只包含一个.value属性。示例代码如下:

import { ref } from 'vue';
export default {
    setup () {
        const valueNumber = ref(0);
        const valueString = ref('hello world!');
        const valueBoolean = ref(true);
        const valueNull = ref(null);
        const valueUndefined = ref(undefined);

        return {
            valueNumber,
            valueString,
            valueBoolean,
            valueNull,
            valueUndefined
        };
    }
};

使用ref函数定义的响应式属性,在template中访问的方法和Vue2一样,可以直接使用模板语法的形式访问,示例代码如下:

import { ref } from 'vue';
export default {
    setup () {
        const value = ref(1);

        return {
           value,
           msg: 'hello world!'
        };
    }
};

在template使用模板语法直接访问响应式属性,示例代码如下:

<template>
    <p>
        {{ value }} {{ msg }}
    </p>
</template>

下面来对比一下Vue3中的ref函数与Vue2中data的区别。先使用Vue2的语法编写一个计算器的案例,示例代码如下:

//Counter.vue
export default {
  data: () => ({
    count: 0
  }),
  methods: {
    increment() {
      this.count++;
    }
  },
  computed: {
    double () {
      return this.count * 2;
    }
  }
}

下面再使用Composition API定义一个完全相同功能的组件,示例代码如下:

// Counter.vue
import { ref, computed } from "vue";

export default {
  setup() {
    const count = ref(0);
    const double = computed(() => count * 2)
    function increment() {
      count.value++;
    }
    return {
      count,
      double,
      increment
    }
  }
}

在上面的示例中,使用Composition API提供的ref函数定义了一个响应式变量,其作用与Vue2的data变量几乎相同。在Vue3的示例代码中,increment方法是一个普通的JavaScript函数,需要更改子属性count的value才能更改响应式变量,这是因为使用ref函数创建的响应式变量必须是对象,以便于在传递的时候保持一致。
Composition API提供了更方便的逻辑提取方式,我们还以上面的代码为例,使用Composition提取Counter.vue组件的代码,创建useCounter.js文件,示例代码如下:

//useCounter.js
import { ref, computed } from "vue";

export default function () {
  const count = ref(0);
  const double = computed(() => count * 2)
  function increment() {
    count.value++;
  }
  return {
    count,
    double,
    increment
  }
}

如果要在其他组件中使用该函数,只需要将模块导入组件文件并调用它即可,导入的模块是一个函数,该函数将返回我们定义的变量,然后可以从setup函数中返回它们。示例代码如下:

// MyComponent.js
import useCounter from "./useCounter.js";

export default {
  setup() {
    const { count, double, increment } = useCounter();
    return {
      count,
      double,
      increment
    }
  }
}

这种操作还可以解决Vue2中mixins命名冲突的问题,示例代码如下:

export default {
  setup () {
    const { someVar1, someMethod1 } = useCompFunction1();
    const { someVar2, someMethod2 } = useCompFunction2();
    return {
      someVar1,
      someMethod1,
      someVar2,
      someMethod2
    }
  }
}

Composition API也提供了一些其他的ref辅助操作的函数。

isRef

用来判断某个值是否为ref创建出来的对象,在需要展开某个值可能是ref()创建出来的对象时使用。示例代码如下:

import { ref, isRef } from 'vue';
export default {
    setup () {
        const count = ref(1);
        const unwrappend = isRef(count) ? count.value : count;

        return {
           count,
           unwrappend
        };
    }
};

toRefs

torefs函数可以将reactive()创建出来的响应式对象转换为普通的对象,只不过这个对象上的每个属性节点都是ref()类型的响应式数据。示例代码如下:

<template>
    <p>
  			<!-- 可以不通过state.value去获取每个属性 -->
        {{ count }} {{ value }}
    </p>
</template>

<script>
import { ref, reactive, toRefs } from 'vue';
export default {
    setup () {
        const state = reactive({
            count: 0,
            value: 'hello',
        })

        return {
           ...toRefs(state)
        };
    }
};
</script>

toRef

toRef函数为源响应式对象上的某个属性创建一个ref对象,二者内部操作的是同一个数据值,更新时二者是同步。但是与ref的区别是,使用toRef函数拷贝的是一份新的数据单独操作,更新时相互不影响,相当于深拷贝。当要将某个prop的ref传递给某个复合函数时,toRef很有用。示例代码如下:

import { reactive, ref, toRef } from 'vue'

export default {
    setup () {
        const m1 = reactive({
            a: 1,
            b: 2
        })

        const m2 = toRef(m1, 'a');
        const m3 = ref(m1.a);

        const update = () => {
          	// m1.a++;//m1改变时,m2也会改变
            // m2.value++; //m2改变时m1同时改变
            m3.value++; //m3改变的同时,m1不会改变
        }

        return {
            m1,
            m2,
            m3,
            update
        }
    }
}

5.4 computed计算属性

computed函数用来创建计算属性,返回值是一个ref的实例。创建只读的计算属性,示例代码如下:

import { ref, computed } from 'vue';
export default {
    setup () {
        const count = ref(0);
        const double = computed(()=> count.value + 1);//1

        double++;//Error: "double" is read-only

        return {
           count,
           double
        };
    }
};

在使用computed函数期间,传入一个包含get和set函数的对象,可以额得到一个可读可写的计算属性。示例代码如下:

// 创建一个 ref 响应式数据
const count = ref(1)

// 创建一个 computed 计算属性
const plusOne = computed({
  // 取值函数
  get: () => count.value + 1,
  // 赋值函数
  set: val => {
    count.value = val - 1
  }
})

// 为计算属性赋值的操作,会触发 set 函数
plusOne.value = 9
// 触发 set 函数后,count 的值会被更新
console.log(count.value) // 输出 8

5.5 Vue3中的响应式对象

Vue2中的data和Vue3中的ref一样,都可以返回一个响应式对象,但是Vue2中使用的是object.defineProperty()来实现响应式的,这就导致Vue2的响应式出现一些限制。对于Vue2中新增一个响应式属性就会变得很困难。
在Vue2中,无法检测property的添加或者移除,对于已经创建的实例,Vue是不允许动态添加根级别的响应式属性的。如果要动态添加响应式对象的属性,可以使用Vue.set(object, propertyName, value)方法向嵌套对象中添加响应式属性。还可以使用 vm. s e t 实例方法动态添加响应式属性,这也是全局 V u e . s e t ( ) 方法的别名。这种操作对于一个 V u e 的初学者来说,很多时候需要小心翼翼的去判断到底什么情况下需要用 set 实例方法动态添加响应式属性,这也是全局 Vue.set() 方法的别名。 这种操作对于一个Vue的初学者来说,很多时候需要小心翼翼的去判断到底什么情况下需要用 set实例方法动态添加响应式属性,这也是全局Vue.set()方法的别名。这种操作对于一个Vue的初学者来说,很多时候需要小心翼翼的去判断到底什么情况下需要用set,什么情况下可以直接触发响应式。这就对初学者带来了很多困扰。
在Vue3中,这些问题都将成为过去式。Vue3采用了ES6的一个新特性,使用Proxy来实现响应式。Proxy对象用于定义基本操作的一个自定义行为,简单来说,Proxy对象就是可以让你对一个JavaScript中一切合法对象的基本操作进行自定义,然后用自定义的操作去覆盖对象的一些基本操作。
我们可以通过下面的两段代码来学习Vue3中是如何使用Proxy进行优化的。
Vue2中的响应式处理,示例代码如下:

Object.defineProperty(data, 'count', {
    get() {},
    set() {}
})

Vue3中对于响应式的优化,示例代码如下:

new Proxy(data, {
    get(key) {},
    set(key, value) {}
})

通过上面两段代码可以看出,Proxy是在更高维度上进行一个属性拦截修改的。我们先看Vue2的代码示例。对于给定的data对象,date对象中有一个count属性,需要根据具体的count去修改set函数。所以,在Vue2中对于对象上的新增属性是无能为力的。
而Vue3中使用的是Proxy进行拦截的,这里无需知道具体的key是什么,拦截的是修改data上任意的key和读取data上任意的key的操作。所以,无论是已有的key还是新增的key都可以被拦截。
Proxy更加强大之处在于,除了getter和setter对属性的拦截外,还可以拦截更多的操作符。

5.6 生命周期的改变

在Vue3中的生命周期和Vue2中的生命周期的用法是一样的。所谓生命周期,就是在一个组件从创建到销毁的全过程,会暴露出一系列的钩子函数供开发者在对应阶段进行相关的操作。
除了Vue2中已有的一部分生命周期钩子,Vue3还增加了一些新的生命周期,可以直接导入 onXXX 一族的函数来注册生命周期钩子。示例代码如下:

import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  },
}

Vue3的生命周期钩子函数只能在 setup() 期间同步使用,因为它们依赖于内部的全局状态来定位当前组件实例,不在当前组件下调用这些函数会抛出一个错误。组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。
Vue3中与Vue2的生命周期相对应的组合式API如下。

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

Vue2生命周期的初创期钩子beforeCreate和created,在Vue3中使用setup()替代了。除了和 2.x 生命周期等效项之外,组合式 API 还提供了以下调试钩子函数。

  • onRenderTracked
  • onRenderTriggered

两个钩子函数都接收一个 DebuggerEvent,与 watchEffect 参数选项中的 onTrack 和 onTrigger 类似。示例代码如下:

export default {
  onRenderTriggered(e) {
    debugger
    // 检查哪个依赖性导致组件重新渲染
  },
}

5.7 watch侦测变化

watch函数用来监视某些数据项的变化,从而触发某些特定的操作,看下面这个案例,会实时监听count值的变化。示例代码如下:

import { ref, watch } from 'vue';
export default {
    setup () {
        const count = ref(1);

        watch(()=>{
            console.log(count.value, 'value');
        })

        setInterval(()=>{
            count.value++;
        },1000);
        return {
           count,
        };
    }
};

watch还可以监听指定的数据源,例如监听reactive的数据变化。示例代码如下:

import { watch, reactive } from 'vue';
export default {
    setup () {
        const state = reactive({
            count: 0
        })

        watch(()=>state.count,(count, prevCount)=>{
            console.log(count, prevCount);//变化后的值 变化前的值
        })

        setInterval(()=>{
            state.count++;
        },1000);

        return {
           state
        };
    }
};

watch用于监听ref类型的数据变化,示例代码如下:

import { ref, watch } from 'vue';
export default {
    setup () {
       const count = ref(0);

        watch(count,(count, prevCount)=>{
            console.log(count, prevCount);//变化后的值 变化前的值
        })

        setInterval(()=>{
            count.value++;
        },1000);

        return {
           count
        };
    }
};

在setup()函数内创建的watch监视,会在当前组件被销毁的时候自动停止。如果想要明确的停止某个监视,可以调用watch()函数的返回值即可。示例代码如下:

// 创建监视,并得到 停止函数
const stop = watch(() => {
  /* ... */
})

// 调用停止函数,清除对应的监视
stop()

5.8 Vue3更好的支持TypeScript

Vue2依赖于this上下文对象向外暴露属性,但是在设计Vue2的API时,并没有考虑到与TypeScript集成。如果在Vue2中想要使用TypeScript语法的话,需要使用vue class或者是vue extends的方式来集成对TypeScript的支持。到了Vue3,Vue官方团队推出了一个新的方式定义component,这个方式被称为 definedComponent。示例代码如下:

import { defineComponent } from 'vue';

export default defineComponent({
  setup(){
    function demo(str: String){
      console.log(str)
    }

    return {
      demo
    }
  }
});

5.9 Teleport传送门

Teleport 是一种能够将我们的模板移动到 DOM 中 Vue app 之外的其他位置的技术。例如,在项目中,像modals、toast等这样的元素,很多情况下,需要将它完全的和Vue应用的DOM完全剥离,这样会更加便于项目的管理。如果类似于modals、toast这样的注解嵌套在Vue的某个组件内部时,那么处理嵌套组件的定位、z-index和样式就会变得很困难。
Teleport就很好的解决了这一类问题。下面用一个例子来说明Teleport的用法。
在index.html中添加一个div元素,并指定其id属性值。示例代码如下:

<div id="app"></div>
<div id="teleport-target"></div>

在HelloWorld.vue文件中,添加teleport的组件代码,teleport组件上的to属性要和index.html新增的div的id选择器保持一致。示例代码如下:

  <button @click="showToast" class="btn">打开 toast</button>
  <!-- to 属性就是目标位置 -->
  <teleport to="#teleport-target">
    <div v-if="visible" class="toast-wrap">
      <div class="toast-msg">我是一个 Toast 文案</div>
    </div>
  </teleport>

在HelloWorld.vue文件中添加script脚本,示例代码如下:

import { ref } from 'vue';
export default {
  setup() {
    // toast 的封装
    const visible = ref(false);
    let timer;
    const showToast = () => {
      visible.value = true;
      clearTimeout(timer);
      timer = setTimeout(() => {
        visible.value = false;
      }, 2000);
    }
    return {
      visible,
      showToast
    }
  }
}

在上面的示例中,使用teleport组件,通过to属性指定该组件渲染的位置与

同级,但是teleport的状态visible又是完全由内部Vue组件控制的。

5.10 Suspense异步请求

Vue3新增了Suspense组件,可以允许应用程序在等待异步组件时渲染一些后备内容,帮助开发者创建一个平滑的用户体验。Suspense组件非常容易理解,也不需要任何额外的导入。
例如,有一个异步的ArticleInfo.vue的组件,其中setup方法是异步的,用于返回加载用户的数据。示例代码如下:

async function getArticleInfo() { 
  // 一些异步API调用 
  return { article } 
}export default { 
  async setup () {    
var { article } = await getArticleInfo() 
    return { 
      article    
}  
}} 

再创建一个ArticlePost.vue组件,在该组件中包含ArticleInfo.vue组件。如果要在等待组件获取数据并解析时显示“正在加载…”的内容,只需要三步就可以实现Suspense。

  • 将异步组件包装在<template #default>标记中
  • 在我们的Async组件的旁边添加一个兄弟姐妹,标签为<template #fallback>
  • 将两个组件都包装在组件中

使用插槽,Suspense将渲染后备内容,直到默认内容准备就绪。然后,它将自动切换以显示我们的异步组件。示例代码如下:

<Suspense> 
  <template #default> 
    <article-info/> 
  </template> 
  <template #fallback> 
    <div>正在拼了命的加载…</div> 
  </template> 
</Suspense> 

5.11 全局API修改

Vue 2.x 有许多全局 API 和配置,这些 API 和配置可以全局改变 Vue 的行为。例如,要创建全局组件,可以使用 Vue.component 这样的 API。示例代码如下:

Vue.component('button-counter', {
  data: () => ({
    count: 0
  }),
  template: '<button @click="count++">Clicked {{ count }} times.</button>'
}) 

虽然这种声明方式很方便,但它也会导致一些问题。在Vue2中只能通过new Vue()创建根Vue实例,从同一个Vue构造函数创建的每个根实例共享相同的全局配置,因此,在测试期间,全局配置很容易意外地污染其他测试用例。
Vue3中增加了createApp这个新的全局API,调用createApp返回一个应用实例。示例代码如下:

import { createApp } from 'vue' 
const app = createApp({})

任何全局改变Vue行为的API在使用了createApp之后,都会转移到应用实例上。如下表所示,Vue2中的全局API转移到了Vue3中的实例API上面。

Vue2全局APIVue3实例API(app)
Vue.configapp.config
Vue.config.productionTipremoved
Vue.config.ignoredElementsapp.config.isCustomElement
Vue.componentapp.component
Vue.directiveapp.directive
Vue.mixinapp.mixin
Vue.useapp.use
Vue.prototypeapp.config.globalProperties

所有其他不全局改变行为的全局 API 现在被命名为 exports。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1945717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【openavis】明厨亮灶算法仓

明厨亮灶算法仓主要用于学校食堂&#xff0c;餐厅等饮食卫生安全监管场景&#xff0c;目前包含的算法如下&#xff1a; 算法类型 算法卡片 明厨亮灶算法仓 老鼠检测 垃圾桶未盖 厨师服检测 厨师帽检测 口罩检测 手套检测 动火离人 1. 算法规格介绍&#xff1a; 算…

微软CrowdStrike驱动蓝屏以及内核签名

原因 当Windows操作系统遇到严重错误导致系统崩溃时&#xff0c;屏幕显示为蓝色&#xff0c;通常伴有错误代码和信息&#xff0c;这被称为“蓝屏死机”&#xff08;Blue Screen of Death&#xff0c;简称BSOD&#xff09; https://www.thepaper.cn/newsDetail_forward_281262…

2024最新Uniapp的H5网页版添加谷歌授权验证

现在教程不少,但是自从谷歌升级验证之后,以前的老教程就失效了,现在写一个新教程以备不时之需。 由于众所周知的特殊原因,开发的时候一定注意网络环境,如果没有梯子是无法进行开发的哦~ clientID的申请方式我就不再进行赘述了,其他的教程上面都有,我这边只提供最重要的…

vue 实战 区域内小组件元素拖拽 示例

<template><div><el-button type"primary" click"showDialog true">快捷布局</el-button><el-dialog title"快捷布局配置" :visible.sync"showDialog"><el-row :gutter"20"><el-co…

超级兔子数据恢复的 9 大替代方案

对于任何计算机或智能手机用户来说&#xff0c;数据丢失都是最大的问题之一&#xff0c;因为它可能会造成严重的创伤&#xff0c;有时甚至会造成瘫痪。丢失所有重要文件、照片、视频和所有工作可能是一种可怕的经历。可悲的是&#xff0c;它发生的频率比我们想象的要多。这就是…

DNS应用以及扩展知识

&#xff08;一&#xff09;DNS正向代理 1.首先在DNS服务器上安装bind包&#xff0c;安装环境 此部分参考上一个笔记 2.修改配置文件 vim /etc/named.conf 在配置文件中加上"any;" 3.然后配置/etc/named.rfc1912.zonesw文件 添加选中部分 选中部分有一个file文…

Python番外篇:变量是盒子还是标签

引言 前面通过几十篇文章&#xff0c;大概把Python的一些比较实用的基础做了一些介绍&#xff0c;学会这些&#xff0c;基本能应付日常的小的需求开发了&#xff0c;写一些小工具&#xff0c;提高工作的处理效率。 接下来&#xff0c;准备开始进入一个新的篇章&#xff0c;也…

802.11 wireshark 抓包

80211 wireshark 抓包 前言配置 monitor软件配置wireshark 操作 前言 本人习惯使用 Omnipeek 抓包分析&#xff0c;所以 wireshark 的实验只讲到抓包完成。 Windows 环境采用 wireshark 抓包是比较麻烦的&#xff0c;因为支持在 Windows 环境中支持抓包的网卡并不多&#xff0…

IP协议和路由转发

文章目录 IP协议IP报头网段划分特殊的IP私有IP和公有IP IP分片 路由 IP协议 IP协议提供了一种能力&#xff0c;将数据报从A主机送到B主机&#xff0c;TCP可以保证可靠性&#xff0c;所以TCP/IP协议可以将数据可靠的从A主机送到B主机。 IP报头 4位版本号(version): 指定IP协议…

【压缩泛化】对大语言模型智能涌现的理解

AGI Maximizing Compression&#xff01; 1. 智能定义 一年时间&#xff0c;大语言模型(LLM)席卷互联网行业&#xff0c;包括自己在内&#xff0c;日常工作生活已经快离不开大模型应用了。前段时间&#xff0c;看到有赞干掉了30多人的UI团队&#xff0c;后又干掉了HRBP团队&am…

FTP、NFS、SAMBA系统服务

⼀、rsync托管xinetd 1 、为什么要进⾏服务托管 独⽴服务&#xff1a;独⽴启动脚本 ssh ftp nfs dns ... 依赖服务 : 没有独⽴的启动脚本 rsync telnet 依赖 xinetd 服务&#xff08;独⽴服务&#xff09; 2 、如何将 rsync 托管给 xinetd 服务去管理&#xff1f; 第⼀步&am…

无人机之航拍高级操作与技巧

一、直线飞行与矩形飞行练习&#xff1a;通过直线和矩形路径的练习&#xff0c;提升飞行路径控制能力。 二、航点命令和事件编程&#xff1a;学习如何设置航点命令和事件&#xff0c;使无人机能够自动执行复杂任务。 三、故障诊断与处理&#xff1a;掌握基本的故障诊断方法和…

USART串口理论知识总结

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 USART串口理论知识总结 1、通讯的串行和并行1.串口采用发送数据代码并用printf重代码 1、通讯的串行和并行 1.串口采用发送数据代码并用printf重代码 #include <stdint.h…

【C++】set的使用

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 &#x1f308;前言&#x1f308;关于set&#x1f525;容量函数emptysize &#x1f525;Modifiersinserteraseclear &#x1f525;Operationsfindcountlower_bound和upper_…

Redis从入门到超神-(五)Redis实现分布式锁原理

引言 什么是分布式锁&#xff1f; 分布式锁是分布式系统中用于控制多个进程或线程对共享资源的访问的一种机制。在分布式系统中&#xff0c;由于存在多个服务实例或节点&#xff0c;它们可能会同时尝试访问或修改同一份数据或资源。如果没有适当的同步机制&#xff0c;就可能导…

谷粒商城实战笔记-46-商品服务-API-三级分类-配置网关路由与路径重写

文章目录 一&#xff0c;准备工作1&#xff0c;新增一级菜单2&#xff0c;新增二级菜单 二&#xff0c;前端树形界面开发1&#xff0c;开发分类展示组件 三&#xff0c;远程调用接口获取商品分类数据1&#xff0c;远程调用2&#xff0c;路由配置 错误记录 本节的主要内容&#…

PT2262-IR

PT2262是一款很古老的编码芯片&#xff0c;其兼容型号有&#xff1a;SC2262&#xff0c;AD2262&#xff0c;SC2260(需改变匹配电阻)等。 依据其datasheet&#xff0c;PT2262射频模式工作原理: CODE BITS A Code Bit is the basic component of the encoded waveform, and ca…

iOS实际开发中使用数据驱动页面布局

引言 在实际的APP开发中&#xff0c;我们通常会首先根据设计团队提供的视觉设计UI来构建我们的应用页面。这些设计通常是最全面和理想化的状态&#xff0c;因为设计师并不需要考虑用户的实际操作和交互。然而&#xff0c;如果我们仅仅根据这些设计进行硬编码&#xff0c;会在应…

接入百度文心一言API教程

然后&#xff0c;编辑文章。点击AI识别摘要&#xff0c;然后保存即可 COREAIPOWER设置 暂时只支持经典编辑器.古腾堡编辑器等几个版本后支持.在比期间,你可以自己写点摘要 摘要内容 AL识别摘要 清空 若有收获&#xff0c;就点个赞吧 接入文心一言 现在百度文心一言&…

php-fpm如何配置max_children参数

前言 略 php-fpm 资源耗尽 php-fpm 的子进程耗尽的时&#xff1a; 会导致 502 出现nginx 出现错误日志 2024/07/18 20:19:10 [crit] 36390#0: *1402471 connect() to unix:/tmp/php-cgi-81.sock failed (2: No such file or directory) while connecting to upstream, cli…