vue3详细讲解

一.简介

Vue 3是一种流行的JavaScript框架,用于构建用户界面。它是Vue.js框架的最新版本,于2020年9月正式发布。Vue 3在Vue 2的基础上进行了重大改进和增强,并提供了更好的性能、更好的开发体验和更多的功能。

以下是Vue 3的一些主要特点和改进:

  1. 响应式系统:Vue 3使用Proxy对象替代了Vue 2中基于Object.defineProperty的劫持方式。这样的改变使得Vue 3的响应式系统更强大和灵活。它能够捕获更多类型的变更,提供更好的性能,并且能够处理动态添加的属性和删除属性。

  2. 组件模型:Vue 3引入了组合式API(Composition API),作为选项式API(Options API)的补充。组合式API允许开发人员更好地组织和复用组件逻辑,通过使用函数来组织代码,而不仅仅依靠选项。这种方式提供了更灵活、组合性更强的组件开发方式。

  3. 性能优化:Vue 3采用了虚拟DOM算法的改进,通过静态提升(Static Nodes Hoisting)和基于模块的编译优化,提供了更好的性能。它具有更高的渲染速度、更小的包大小,以及更好的Tree-shaking支持,使您的应用程序更高效。

  4. Teleport组件:Vue 3引入了Teleport组件,它使得在DOM树中的任何位置渲染组件变得更容易。它可以帮助您处理跨组件层级的弹出窗口、对话框和模态框等场景。

  5. TypeScript支持:Vue 3更好地集成了TypeScript,并提供了更准确的类型推断和类型检查。这使得在Vue应用程序中使用TypeScript变得更加流畅和安全。

总体而言,Vue 3在性能、开发体验和功能方面都有明显的改进和增强。它提供了更好的响应式系统、更灵活的组件开发方式和更高的性能,使开发人员能够构建出更高效、易维护和功能丰富的应用程序。
 

二.使用

安装

1、确定node 版本
在这里插入图片描述
2、确定 npm 版本
在这里插入图片描述
3、安装 vue3.0

npm install -g @vue/cli

4、 查看 vue脚手架的版本

vue -V

5、 创建项目

vue create my-project (项目名)

6、 选中安装vue3.0

7、运行项目

npm run serve

声明变量

使用响应式变量

在Vue3中,响应式变量是通过ref()reactive()函数创建的。ref()函数用于创建单一的响应式变量,而reactive()函数则用于创建包含多个属性的响应式对象。

下面是一个例子,演示如何在setup()函数中创建一个响应式变量:

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

在上面的代码中,我们使用了ref()函数来创建一个名为count的响应式变量,并将其初始化为0。然后,在setup()函数中将这个变量返回给模板。

在模板中使用这个变量时,需要使用.value来访问变量的值:

<template>
    <div>Count: {{ count.value }}</div>
</template>
使用普通变量

除了响应式变量外,你还可以在setup()函数中声明普通变量。这些变量不会被自动监听,也就是说,当它们的值发生改变时,组件不会自动更新。

下面是一个例子,演示如何在setup()函数中声明普通变量:

export default {
     setup() {
        const name = 'John Doe';
     return {name};}
};

在上面的代码中,我们声明了一个名为name的普通变量,并将其初始化为'John Doe'。然后,在setup()函数中将这个变量返回给模板。

在模板中使用这个变量时,可以直接调用它:

<template>
    <div>Name: {{ name }}</div>
</template>
如何修改变量值。

在Vue 3中,要修改变量的值,可以使用ref函数来创建一个响应式的变量,并使用.value属性来访问和修改该变量的值。下面是一个最简单的例子:

import { ref } from 'vue'; 

// 创建一个响应式的变量 
const count = ref(0);

 // 访问变量的值 
console.log(count.value); // 输出: 0 

// 修改变量的值 
count.value = 10; // 

再次访问变量的值 
console.log(count.value); // 输出: 10

在上面的例子中,我们使用ref函数创建了一个响应式的变量count,初始值为0。通过count.value可以访问和修改该变量的值。在修改变量的值时,直接对count.value进行赋值即可。

需要注意的是,对于普通的JavaScript对象和数组,Vue 3提供了reactive函数和readonly函数,分别用于创建可变和只读的响应式对象。如果需要对复杂的对象进行修改,可以使用类似的方式来创建并修改响应式对象。


如何定义事件,方法

在Vue 3中,您可以使用methods选项来定义事件和方法。下面是一个最简单的例子:

<template>
  <button @click="increment">点击增加</button>
</template>

<script>
export default {
  methods: {
    increment() {
      // 在方法中可以进行相应的逻辑操作
      console.log("按钮被点击了");
    }
  }
};
</script>

在上面的例子中,我们定义了一个按钮,并通过@click来监听按钮的点击事件。事件处理函数increment定义在methods选项中。当按钮被点击时,increment方法会被调用,并在控制台上输出文本。

需要注意的是,在Vue 3中,无需再使用this关键字来访问组件的方法。方法直接定义在methods选项下,可以直接在方法内部进行逻辑操作。

当然,您可以在方法中进行更复杂的逻辑操作、数据更新、使用组合式API等。以上示例仅作为最简单的例子,展示了如何定义事件和方法。


同时,您还可以使用箭头函数来定义方法,这样可以避免上下文绑定的问题,如下所示:

<template>
  <button @click="increment">点击增加</button>
</template>

<script>
export default {
  methods: {
    increment: () => {
      console.log("按钮被点击了");
    }
  }
};
</script>

在这个例子中,increment方法使用箭头函数来定义,无需担心上下文的绑定问题,仍然可以正常响应点击事件。

这就是Vue 3中定义事件和方法的最简单示例。通过methods选项,您可以定义各种处理函数来响应用户的交互事件,以及执行其他逻辑操作。

生命周期

1、setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 methods

2、onBeforeMount() : 组件挂载到节点上之前执行的函数;

3、onMounted() : 组件挂载完成后执行的函数;

4、onBeforeUpdate(): 组件更新之前执行的函数;

5、onUpdated(): 组件更新完成之后执行的函数;

6、onBeforeUnmount(): 组件卸载之前执行的函数;

7、onUnmounted(): 组件卸载完成后执行的函数;

8、onActivated(): 被包含在 <keep-alive> 中的组件,会多出两个生命周期钩子函数,被激活时执行;

9、onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;

10、onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。

Vue2.X和Vue3.X对比

  1. vue2 -------> vue3

  2. beforeCreate --------> setup(()=>{})

  3. created --------> setup(()=>{})

  4. beforeMount --------> onBeforeMount(()=>{})

  5. mounted --------> onMounted(()=>{})

  6. beforeUpdate --------> onBeforeUpdate(()=>{})

  7. updated --------> onUpdated(()=>{})

  8. beforeDestroy --------> onBeforeUnmount(()=>{})

  9. destroyed --------> onUnmounted(()=>{})

  1. activated --------> onActivated(()=>{})

  2. deactivated --------> onDeactivated(()=>{})

  3. errorCaptured --------> onErrorCaptured(()=>{})

总结: Vue2和Vue3钩子变化不大,beforeCreate 、created  两个钩子被setup()钩子来替代。

 

main.js的变化

创建好项目后和Vue2时候一样,首先来看看整个项目的入口文件main.js。

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

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

我们可以发现,引入的不再是Vue构造函数了,而是一个名为createApp的工厂函数。试着去回想一下以前的写法,并且将生成的这三行代码进行拆分做个对比。

import { createApp } from 'vue'
import App from './App.vue'
 
// Vue 3 
const app = createApp(App)
app.mount('#app')
 
// createApp(App).mount('#app')
 
// Vue 2
const vm = new Vue({
    render: h => h(App)
})
vm.$mount('#app')

定义全局变量

vue2中可以通过Vue.prototype去操作原型,在vue3中只能通过app.config.globalProperties,当时玩的时候还以为自己写错了,修改的这些你会发现改动的东西挺多的变量

举例:定义一个全局的组件

  const app = createApp(App)
    app.component('MyDiv',MyDiv)
    app.use(store).use(router).mount('#app')

举例:定义全局

    main.js中
    app.config.globalProperties.$myVals = 'Hello, world!';
    调用方式:this.$myVals 
    这种方式不适用于setup的Composition API
    
    如果使用setup方法:
    main.js中
    app.provide('$duMy', 'abceffef');
    
    页面中:setup中
    import { inject } from 'vue';
    const kk  = inject('$duMy');
    return {kk}

指令与插槽

插槽的用途就和它的名字一样:有一个缺槽,我们可以插入一些东西。

插槽 slot 通常用于两个父子组件之间,最常见的应用就是我们使用一些 UI 组件库中的弹窗组件时,弹窗组件的内容是可以让我们自定义的,这就是使用了插槽的原理。

我们在项目中新建一个 child.vue 文件,用来当作子组件,它的代码也非常的简单。

<template>
  <div class="child-box">
    <p>我是子组件</p>
    <!-- 插槽 -->
    <slot></slot>
  </div>
</template>
<style>
.child-box {
  display: flex;
  flex-direction: column;
  align-items: center;
}
</style>

然后我们直接在 App.vue 引用该子组件,代码如下:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <child></child>
</template>
 
 
<script setup lang="ts">
import Child from "./child.vue";
</script>

响应式原理

实现原理:
通过Proxy(代理): 拦截对象中任意属性的变化,包括:属性值的读写,属性的增加,属性的删除等。

通过Reffect(反射): 对源对象的属性进行操作

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

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

被 Proxy 代理虚拟化的对象。它常被作为代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。

语法:

const p = new Proxy(target, handler)

参数:
target:
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler:
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

计算属性(computed)

计算属性使用

首先呢,使用 computed 需要引入。

import { computed } from 'vue'

引入这一个步骤是不可或缺的。

计算属性的定义

在Vue中,计算属性是一个函数,它会根据依赖的数据动态计算出一个新的值。计算属性的定义方式是在Vue组件的computed选项中创建一个函数。下面是一个计算属性的示例:

<template>
  <div>
    <p>{{ message }}</p>
    <p>{{ reversedMessage }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue3!'
    }
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  }
}
</script>

在上述代码中,我们定义了一个计算属性reversedMessage,它通过将message反转后返回一个新的值。

计算属性的特点

与其他数据绑定方式相比,计算属性具有以下几个特点:

  • 缓存性:计算属性会缓存依赖的数据,只有当依赖的数据发生变化时才会重新计算。如果多次访问该计算属性,Vue会直接返回缓存的结果,提高了性能。
  • 响应式:计算属性依赖的数据发生变化时,会自动重新计算,并更新绑定到该计算属性的视图。
  • 可读性:计算属性允许我们编写更清晰、可读性更高的代码,将复杂的逻辑封装在一个函数中,提高了可维护性。

使用

计算属性的读取

在Vue模板中,我们可以直接读取计算属性的值,就像读取普通的属性一样。下面是一个读取计算属性的示例:

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

<script>
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}
</script>

计算属性的设置

在特定的情况下,我们可能希望通过计算属性来实现双向绑定。在Vue3中,可以通过添加getset方法来实现计算属性的设置。下面是一个设置计算属性的示例:

<template>
  <div>
    <input v-model="fullName" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe'
    }
  },
  computed: {
    fullName: {
      get() {
        return this.firstName + ' ' + this.lastName
      },
      set(value) {
        const fullName = value.split(' ')
        this.firstName = fullName[0]
        this.lastName = fullName[1]
      }
    }
  }
}
</script>

 

应用场景 

 数据过滤与排序

计算属性可以用于对数据进行过滤和排序。例如,我们有一个包含用户信息的数组,想要根据某种条件对用户进行筛选。我们可以使用计算属性来动态计算符合条件的用户列表。下面是一个示例:

<template>
  <div>
    <ul>
      <li v-for="user in filteredUsers" :key="user.id">
        {{ user.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      users: [
        { id: 1, name: 'Alice', age: 25 },
        { id: 2, name: 'Bob', age: 30 },
        { id: 3, name: 'Charlie', age: 35 }
      ],
      ageThreshold: 30
    }
  },
  computed: {
    filteredUsers() {
      return this.users.filter(user => user.age >= this.ageThreshold)
    }
  }
}
</script>

在上述代码中,我们定义了一个计算属性filteredUsers,它根据ageThreshold筛选出年龄大于等于30的用户列表。在模板中,通过v-for指令遍历该列表并显示用户名称。

表单验证

计算属性可以用于表单验证,根据不同的条件判断表单字段是否有效。例如,我们有一个登录表单,需要验证用户名和密码是否满足一定的要求。我们可以使用计算属性来动态计算验证结果,并将其绑定到表单的错误提示信息上。下面是一个示例:

<template>
  <div>
    <input v-model="username" />
    <input v-model="password" type="password" />
    <p>{{ usernameError }}</p>
    <p>{{ passwordError }}</p>
    <button @click="login">Login</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: '',
      password: '',
      usernamePattern: /^[a-zA-Z0-9]{4,16}$/,
      passwordPattern: /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/
    }
  },
  computed: {
    usernameError() {
      if (this.username === '') {
        return 'Username is required'
      } else if (!this.usernamePattern.test(this.username)) {
        return 'Invalid username format: alphanumeric, 4-16 characters'
      } else {
        return ''
      }
    },
    passwordError() {
      if (this.password === '') {
        return 'Password is required'
      } else if (!this.passwordPattern.test(this.password)) {
        return 'Invalid password format: at least 8 characters, at least 1 letter and 1 number'
      } else {
        return ''
      }
    }
  },
  methods: {
    login() {
      // Perform login logic
    }
  }
}
</script>

在上述代码中,我们定义了两个计算属性usernameErrorpasswordError,它们根据表单字段的值和正则表达式来判断是否满足要求,并返回相应的错误提示信息。在模板中,我们通过{{ usernameError }}{{ passwordError }}将错误提示信息显示出来。通过这种方式,我们可以实时根据用户输入更新错误提示,提高表单的交互性和用户体验。

总结

计算属性是Vue3中非常有用的特性之一,它能够根据依赖的数据动态计算出新的值,并具有缓存性、响应式和可读性等特点。本文介绍了计算属性的基本概念、使用方式和常见的应用场景。通过合理地应用计算属性,我们可以简化代码、提高性能和可维护性,从而更高效地开发Vue应用程序。

router(路由)

安装和配置Vue Router

安装Vue Router

安装Vue Router只需要在vue项目中打开终端,输入如下命令即可安装:

npm 方式安装

npm install vue-router@4

yarn方式安装

yarn add vue-router@4

配置Vue Router

为了便于我们后面代码维护和管理,我们一般将路由相关的代码统一放到一个文件夹中。因此,配置Vue Router的步骤如下:

  1. 在src文件夹新建一个router文件夹,在该文件夹下新建index.js文件
  2. 在index.js中引入vue-router中的createRouter 和 createWebHashHistory 方法,引入页面文件
import { createRouter,createWebHashHistory } from "vue-router";
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import List from '../views/List.vue'
import Detail from '../views/Detail.vue'
  1. 在index.js中定义一个routes数组,在里面定义路由规则
const routes = [
  {
    path:'/home',
    name:Home,
    component:Home
  },
  {
    path:'/about',
    component:About
  },
  {
    path:'/list',
    component:List
  },
  {
    path:'/detail',
    component:Detail
  },
  {
    path:'/',
    redirect:'/home'
  }
]
  1. 在index.js中使用createRouter创建路由实例,并配置路由模式和上面定义的路由规则
const router = createRouter({
  history:createWebHashHistory(),
  routes
})
  1. 在index.js的最后,使用export default 将上面创建的路由实例导出
export default router
  1. 注册路由:在main.js中导入上面创建的路由文件,并使用app.use注册路由
import router from './router'
const app = createApp(App)
app.use(router) //注册路由
app.mount('#app')
  1. 在组件中使用路由组件
    在App.vue中使用<router-view>组件来渲染要显示的组件,在Tabbar组件中使用<router-link>组件生成链接

App.vue组件中代码

<template>
  <Title></Title>
  <router-view></router-view>
  <Tabbar></Tabbar>
</template>
<script setup>
import Tabbar from './components/Tabbar.vue'; 
import Title from './components/Title.vue'; 

</script>
<style scoped>
</style>

Tabbar组件中代码

<template>
  <div>
    <router-link to="/">Home</router-link>
    <router-link to="/list">List</router-link>
    <router-link to="/about">About</router-link>
  </div>
  
</template>
<script setup>
</script>
<style  scoped>
div {
  position: fixed;
  bottom: 0;
  width: 100%;
  height: 50px;
  line-height: 50px;
  text-align: center;
  display: flex;
  justify-content: space-around;
}
</style>

至此,我们就完成了路由的配置与搭建,运行程序,刷新浏览器,可以看到页面已经可以正常跳转,实现了路由功能。

虽然上面我们已经实现了一个完整的路由场景搭建,但是我们还是要对Vue Router的基础知识进行深入的了解,方便我们更好的理解和使用Vue Router。下面对Vue Router中的一些基本概念进行介绍。

Vue Router的基本概念

  1. 路由器:Vue Router 提供了一个路由器,用于管理应用程序中的路由。Vue Router 实例化一个 Vue Router 对象,注册路由规则,并以它为中心连接其他组件。

  2. 路由:路由是分发到不同组件的 URL 地址。在 Vue Router 中,路由通常是由 path 规则和相应的组件定义的。当浏览器的 URL 匹配到路由的 path 后,相应的组件将会被加载到页面中。路由的信息可以从 route 对象中获取。

  3. 路由规则:路由规则是由 path、component、name、meta、props 等属性组成的。其中,path 表示 URL 的路径,component 表示要渲染的组件,name 表示路由名称,meta 表示路由的元数据,props 表示路由 props 数据。路由规则可以注册到 Vue Router 中。

  4. 导航守卫:导航守卫是在路由跳转时执行的钩子函数,用于控制路由的访问权限、处理路由跳转前后的逻辑等。在 Vue Router 中,对于选项式 API,常用的导航守卫有 beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 等;对于使用组合 API 和 setup 函数来编写组件的,可以通过 onBeforeRouteUpdate 和 onBeforeRouteLeave 分别添加 update 和 leave 守卫。

Vue Router 的配置项介绍

我们在使用Vue Router 中的createRouter创建router对象时,其为我们提供了很多配置项,带完整配置项的示例代码如下:
 

const router = createRouter({
  history: createWebHashHistory(),
  routes: [],
  scrollBehavior: () => ({ top: 0, left: 0 }),
  linkActiveClass: 'active',
  linkExactActiveClass: 'exact-active',
  parseQuery: null,
  stringifyQuery: null,
  fallback: true
})

上面代码中各个配置项的含义如下:

  1. history:指定路由的 history 模式,目前支持 createWebHistory()和 createWebHashHistory()模式。

  2. routes:定义路由规则的数组,每一个路由规则都是一个对象,包含 pathnamecomponent 和 meta 等属性。

  3. scrollBehavior:指定路由切换时滚动行为的配置函数。该函数返回一个包含 x 和 y 属性的对象,表示页面跳转后滚动的位置。

  4. linkActiveClass:指定激活状态的链接的 class 名称,默认为 'router-link-active'

  5. linkExactActiveClass:指定精确匹配的激活状态的链接的 class 名称,默认为 'router-link-exact-active'

  6. parseQuery 和 stringifyQuery:用于配置路由的查询参数解析和序列化函数。通常情况下,我们不需要额外配置这两个函数,因为 Vue Router 已经提供了默认的实现。

  7. fallback:用于配置是否开启 HTML5 History 模式的回退机制。默认值为 true,表示当路由不匹配时,将自动回退到历史记录中的上一个路由。

上面的配置项中,我们一般只需要配置history和routes两个选项就可以了,其它选项了解即可

routes中的配置项介绍

在 Vue Router 中,路由规则的配置是通过 routes 属性来实现的。routes 属性中常用的配置如下:

  1. name:路由规则的名字。可以用于编程式导航和组件内部的路由跳转。

  2. path:路由的路径,可以包含动态参数和正则表达式。例如,/user/:id 表示用户页面,:id 是一个动态参数。

  3. redirect:路由的重定向规则。例如,{ path: '/', redirect: '/home' } 表示路由根路径的重定向。

  4. component:路由对应的组件。可以是一个普通的组件类或异步加载的组件。

  5. children:当前路由的子路由。可以是一个路由规则数组,也可以是一个函数,动态生成路由规则。

  6. meta:路由的元信息,用于描述路由的一些额外信息。例如,路由是否需要登录、权限鉴定等。

  7. components:路由对应的多个命名视图组件。

路由跳转

通过Vue Router,我们可以通过router-link组件的to方法和使用router.push函数以编程方式两种方法导航到路由。

使用 router-link组件

使用router-link组件实现路由跳转,我们只需要将菜单按钮使用router-link组件包裹,并在上面使用to方法即可进行跳转,示例代码如下:

<div>
    <router-link to="/">Home</router-link>
    <router-link to="/list">List</router-link>
    <router-link to="/about">About</router-link>
</div>

使用router.push函数

使用router.push函数以编程方式实现路由跳转,我们只需要在普通按钮上绑定click事件,并在事件中调用router.push()方法即可实现跳转,示例代码如下:

<template>
  <div>
    <router-link to="/">Home</router-link>
    <router-link to="/list">List</router-link>
    <router-link to="/about">About</router-link>
    <button @click="gotoAbout"> 关于 </button>
  </div>
  
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const gotoAbout = () => {
  router.push('/about')
}
</script>

使用 router.push 方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。

事实上,当我们点击 <router-link> 时,Vue Router 内部会调用这个方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...)

hooks模式


提纯代码,可以把逻辑放到单独的文件中,甚至可以复用hooks

一、 什么是hooks

hook是钩子的意思,看到“钩子”是不是就想到了钩子函数?事实上,hooks 还真是函数的一种写法。

vue3 借鉴 react hooks 开发出了 Composition API ,所以也就意味着 Composition API 也能进行自定义封装 hooks

vue3 中的 hooks 就是函数的一种写法,就是将文件的一些单独功能的js代码进行抽离出来,放到单独的js文件中,或者说是一些可以复用的公共方法/功能。其实 hooks 和 vue2 中的 mixin 有点类似,但是相对 mixins 而言, hooks 更清楚复用功能代码的来源, 更清晰易懂。

代码示例:

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <button @click="handleClick">{{state.count}}{{state.doubble}}</button>
  </div>
</template>
<script>
    import {useCount} from "../hooks/useCount.js"
    export default{
        setup(){
            let {state, handleClick} = useCount();

            return {
                state,
                handleClick
            }
        }
    }
</script>

<!--hooks的文件useCount.js-->
import {
    reactive,
    computed
} from "vue"

export function useCount() {
    const state = reactive({
        count: 0,
        doubble: computed(() => state.count * 2)
    });

    function handleClick() {
        state.count++;
    }
    return {
        state,
        handleClick
    }
}

Teleport传送门组件

可以给弹框定位到想要的位置,比如说body 或者 某个div

代码示例:

<template>
    <div id="target">
        asdfa
    </div>
    <div>
      <button @click="showModal">打开模态框</button>
      <teleport to="#target">
        <div v-if="visible" class="modal">
          <h2>模态框标题</h2>
          <p>模态框内容</p>
        </div>
      </teleport>
    </div>
  </template>

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

  export default {
    setup() {
      const visible = ref(false);
      const showModal = () => {
        visible.value = true;
      };
      return {
        visible,
        showModal,

      };
    }
  };
  </script>

  <style>
  #target{
    width: 500px;
    height: 500px;
    background-color: palegoldenrod;
  }
  .modal {
    position: relative;
    width: 200px;
    height: 80px;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background-color: #fff;
    padding: 20px;
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  }
  </style>

附:

vue2 和vue3的大致区别

    1.源码组织方式变化:使用 TS 重写
    2.支持 compositionAPI,基于函数的 api,更灵活组织组件逻辑(Vue2 使用 options api) 
    3.响应式系统提升:Vue3 的响应式数据原理改成了 Proxy,可以监听动态新增删除属性,以及数组变化 
    4.编译优化:Vue 通过标记静态根节点来优化 Diff,Vue 则标记和提升所有静态根节点,Diff 的时候只需要去对比动态节点的内容 
    5.打包体积优化:移除了一些不常用的 api(inline-template,filter)
    6.生命周期的变化,使用 setup 替代了 beforeCreate 和 created
    7.template 支持多个根标签
    8.Vuex 状态管理,创建实例的方式,Vue2 使用 new store;Vue3 使用 createStore;
    9.Route 获取页面实例以及路由信息,Vue2 使用 this 的方式获取;而 Vue3 采用 getCurrentInstance/userRoute,userRouter 的方式来获取
    10.对 props 的使用:Vue2 通过 this 的方式使用;Vue3 则直接通过 props 使用;