一. 概述

1 为什么提出Vue3

Vue2是一个非常优秀的框架, 好上手, 学习曲线也比较平滑. 也是目前使用最广泛的框架之一

为什么尤大要重构, 推出Vue3呢?

大家有兴趣可以看一下这篇专访

总结起来就是

更适应当下的技术环境

要知道, 每个技术都是有局限性的. 某一方面的提升往往意味着另一方面的牺牲.

2 提出了哪些改进

  1. 使用TS重构

  1. 应用了JS的新特性Proxy重写响应式

  1. 性能的提升

  1. Composition API

如果一定要选出一个最大的区别, 应该是Composition API

3 Options API vs Composition API

1) Options API的代码组织形式

使用Options API实现一个功能, 需要在不同的地方编写代码

  • 状态(数据)在data中定义

  • 方法在methods中定义

  • 计算属性

  • ...

当新添加一个功能时, 代码的组织会比较零散

2) Composition API的代码组织

  1. 引入vue.js

  1. 创建页面容器

  1. 实例化对象

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 1. 引入vue库 -->
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <!-- 2. 创建页面容器 -->
    <div id="app">{{msg}}</div>

    <script>
      const app = {
        data() {
          return {
            msg: 'hello',
          }
        },
      }

      // 创建Vue实例对象, 挂载到#app指定的页面容器
      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>
  • data必须是一个函数

  • 之前Vue2中的配置项依然可以使用

练习

三. setup语法

1 setup配置项

在Vue3中, 为了向前兼容, 不影响之前的配置项

创造了一个新的配置项setup, 所有的Composition API都可以在setup中使用

:::info

setup是一个函数

  • 返回一个对象, 对象中定义的属性, 方法在模板中直接使用

  • 返回渲染函数(不常用)

:::

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      {{uname}} -- {{age}}
      <p>{{ sayHi() }}</p>
    </div>

    <script>
      const app = {
        // setup配置项(函数, 返回对象)
        // 在返回的对象中定义的属性, 方法 可以直接在模板中使用
        setup() {
          // 定义数据 data
          let uname = 'xiaoming'
          let age = 20

          // 定义方法 methods
          function sayHi() {
            console.log(`大家好, 我是${uname}, 今年${age}岁了`)
            return 123
          }

          return {
            uname,
            age,
            sayHi,
          }
        },
      }

      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>

这样写的代码会丢失响应式

:::warning

💡** 注意**

不要将vue2的语法和vue3的语法混用!!

:::

2 调试工具

这里我们安装最新的vue devtools工具, 可以同时支持vue2和vue3

通过调试工具, 我们发现定义的数据并不是响应式的.

在setup函数中, 如果要实现响应式, 需要借助

  • ref函数: 实现普通类型数据的响应式

  • reactive函数: 实现引用类型数据的响应式

3 ref函数

使用步骤

  1. 导入ref函数

  1. 使用ref函数定义数据

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">{{msg}}</div>

    <script>
      // 像ref, reactive, computed从Vue对象从导出的函数, 就是composition API
      const { ref } = Vue
      const app = {
        setup() {
          // ref函数: 实现值类型数据的响应式
          // 将值类型数据(普通数据 String, Number, Boolean, undefined, null)
          let msg = ref('hello')

          // ref函数: 接收普通类型的数据作为参数, 返回一个RefImpl对象
          console.log(msg)

          return {
            msg,
          }
        },
      }

      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>

通过devtools修改

:::info

需求

  1. 点击按钮修改姓名

:::

如何使用代码实现修改数据呢?

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 1. 引入vue库 -->
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <!-- 2. 创建页面容器 -->
    <div id="app">
      <p>姓名:{{uname}}</p>
      <button @click="handleClick">点击修改</button>
    </div>

    <script>
      const { ref } = Vue

      const app = {
        setup() {
          let uname = ref('xiaoming')

          function handleClick() {
            // uname是一个RefImpl对象. 修改其`value`属性
            uname.value = '小明'
          }

          return {
            uname,
            handleClick,
          }
        },
      }

      // 创建Vue实例对象, 挂载到#app指定的页面容器
      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>
练习

使用setup语法(Composition API)实现计数器

4 reactive函数

对于引用类型数据. 如对象, 数组

使用ref函数比较麻烦, 在访问时, 每次都需要.value

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 1. 引入vue库 -->
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <!-- 2. 创建页面容器 -->
    <div id="app">
      <p>姓名:{{stu.name}}</p>
      <button @click="handleClick">点击修改</button>
    </div>

    <script>
      const { reactive } = Vue

      const app = {
        setup() {
          const stu = reactive({
            name: 'xiaoming',
            age: 20,
          })

          function handleClick() {
            console.log(stu)
            stu.name = '小明'
          }

          return {
            stu,
            handleClick,
          }
        },
      }

      // 创建Vue实例对象, 挂载到#app指定的页面容器
      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>
区别
  • 定义的角度

  • ref: 主要用于基本数据类型

  • reactive: 主要用于引用数据类型

  • 实现的角度

  • ref: 通过Object.defineProperty的get和set来实现响应式

  • reactive: 通过Proxy来实现数据劫持, 通过reflect操作内部属性

  • 使用的角度

  • ref: 需要通过.value操作数据

  • reactive: 不需要.value, 直接操作数据

5 computed函数

使用步骤

  1. 导入computed函数

  1. 在setup中使用

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 1. 引入vue库 -->
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <!-- 2. 创建页面容器 -->
    <div id="app">
      姓: <input type="text" v-model="data.firstName" /> <br />
      名: <input type="text" v-model="data.lastName" /> <br />
      全名: {{fullName}}
    </div>

    <script>
      const { reactive, computed } = Vue

      const app = {
        setup() {
          const data = reactive({
            firstName: '',
            lastName: '',
          })
          const fullName = computed(() => {
            return data.firstName + data.lastName
          })

          return {
            data,
            fullName,
          }
        },
      }

      // 创建Vue实例对象, 挂载到#app指定的页面容器
      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>
优化
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- 1. 引入vue库 -->
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <!-- 2. 创建页面容器 -->
    <div id="app">
      姓: <input type="text" v-model="person.firstName" /> <br />
      名: <input type="text" v-model="person.lastName" /> <br />
      全名: {{person.fullName}}
    </div>

    <script>
      const { reactive, computed } = Vue

      const app = {
        setup() {
          const person = reactive({
            firstName: '',
            lastName: '',
          })

          person.fullName = computed(() => {
            return person.firstName + person.lastName
          })

          return {
            person,
          }
        },
      }

      // 创建Vue实例对象, 挂载到#app指定的页面容器
      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>

6 watch函数

在Vue3中, 将watch也定义成一个组合式API

使用步骤

  1. 导入watch函数

  1. 使用watch函数监听

由于定义数据有两种方式. 监听时也分两种情况

  • 监视ref定义的数据

  • 监视reactive定义的数据

1) 监视ref定义的数据

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <h3>当前计数为: {{count}}</h3>
      <button @click="count++">点击+1</button>
    </div>

    <script>
      const { ref, watch } = Vue
      const app = {
        setup() {
          const count = ref(0)

          watch(count, (newValue, oldValue) => {
            console.log('count改变了', newValue, oldValue)
          })

          return {
            count,
          }
        },
      }

      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>

2) 监视reactive定义的数据

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <h3>姓名: {{stu.name}}</h3>
      <h3>年龄: {{stu.age}}</h3>
      <button @click="stu.name = '小明'">修改姓名</button>
      <button @click="stu.age++">年龄+1</button>
    </div>

    <script>
      const { reactive, watch } = Vue
      const app = {
        setup() {
          const stu = reactive({
            name: 'xiaoming',
            age: 20,
          })

          watch(stu, (newValue, oldValue) => {
            // 拿不到旧的value值
            console.log('stu变化了', newValue, oldValue)
          })

          return {
            stu,
          }
        },
      }

      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>

对于引用类型, watch不能监视到oldValue

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <h3>姓名: {{stu.name}}</h3>
      <h3>年龄: {{stu.age}}</h3>
      <button @click="stu.name = '小明'">修改姓名</button>
      <button @click="stu.age++">年龄+1</button>
    </div>

    <script>
      const { reactive, watch } = Vue
      const app = {
        setup() {
          const stu = reactive({
            name: 'xiaoming',
            age: 20,
          })

          // 监听某个属性, 需要使用函数, 函数返回该属性
          watch(() => stu.name, (newValue, oldValue) => {
            // 拿不到旧的value值
            console.log('stu变化了', newValue, oldValue)
          })

          return {
            stu,
          }
        },
      }

      Vue.createApp(app).mount('#app')
    </script>
  </body>
</html>

7 生命周期

  1. Vue3的生命周期在调用mount('#app')后开始

  1. setup在所有生命周期函数执行前调用, 因此, 在setup()中this指向window, 没有批向当前实例

  1. setup主要完成composition API的初始化. created完成OptionsAPI的初始化

  1. 在setup中最常用的生命周期有两个: onMounted和onBeforeUnmount

示例
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="../node_modules/vue/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <h1>我是vm根实例</h1>
      <button @click="isShow = !isShow">切换显示子组件</button>
      <Demo v-if="isShow"></Demo>
    </div>

    <script>
      const { onMounted, onBeforeUnmount } = Vue

      // 创建一个Vue应用
      const app = Vue.createApp({
        data() {
          return {
            isShow: true,
          }
        },
      })

      // 注册子组件
      app.component('demo', {
        template: '<h3>我是子组件</h3>',
        setup() {
          onMounted(() => {
            console.log('demo组件挂载...')
          })
          onBeforeUnmount(() => {
            console.log('demo组件卸载...')
          })
        },
      })

      app.mount('#app')
    </script>
  </body>
</html>

四. 工程化

创建Vue3.0项目有这样两种方式

  • 使用vue-cli

  • 使用vite

1 使用vue-cli创建

vue create vue3-project

2 使用vite构建

npm create vite

使用vite初始化项目

五. hooks函数

1 什么是hooks

hooks本质上还是一种函数, 将多个Composition API封装, 实现某个特定功能

2 hooks的作用

  • 解耦

  • 复用

示例

将特定功能封装到一个单独的文件usePoint.js

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

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

  function onClick(event) {
    point.x = event.pageX
    point.y = event.pageY
    console.log(point.x, point.y)
  }

  onMounted(() => {
    window.addEventListener('click', onClick)
  })

  onBeforeUnmount(() => {
    window.removeEventListener('click', onClick)
  })

  return point
}

在需要时引入hooks函数

<template>
  <h3>子组件</h3>
  <p>x的坐标: {{ point.x }}--y的坐标: {{ point.y }}</p>
</template>

<script setup>
import usePoint from '../hook/usePoint'

const point = usePoint()
</script>

<style>
</style>
Logo

前往低代码交流专区

更多推荐