Vue3 Composition API 基本使用

713 阅读3分钟

一、案例1 —— 获取鼠标位置

  • createApp :createApp 的作用是创建一个 vue 对象,里面可以传入一个选项,注意 createApp 传入选项中的 data 必须是函数形式 data(){}

  • setup: setup 是 composition-api 的入口

    1. setup 是在 props 被解析完毕,在组件实例被创建之前执行的,所以在 setup 内部无法通过 this 获取到组件实例,也就是说 setup 是在 beforeCreate 和 created 之间执行的,所以在 beforeCreate 和 created 之间的代码都可以放到 setup 函数中
    2. setup 有两个参数,第一个参数 props,第二个参数 context,context 是一个对象,有三个属性,分别是 attrs、emit、slots
    3. setup 还需要返回一个对象,可以使用在模版、methods、computed、以及所有钩子函数中
    4. 在 setup 中也可以使用组件生命周期的钩子函数,但是必须在前面加上 on
  • reactive:reactive 把一个对象转换成响应式对象,并且该对象的嵌套属性也会转换为响应式对象,返回的是一个 proxy 对象,reactive 和 Vue.observable 类似

  • toRefs: toRefs 可以把一个响应式对象的所有属性的值转换成响应式的

    1. toRefs 传入的对象必须是一个 proxy 代理对象,它内部会创建一个新的对象,然后来遍历传入代理对象的所有属性,把所有属性的值转换为响应式对象,然后挂载到新创建的对象上,并返回
    2. toRefs 内部就是为传入的代理对象的每一个属性,创建一个带有 value 属性的对象,该对象是响应式的,value 属性具有 get 和 set 方法,和 ref 方法类似
    3. 在模版中使用的时候可以把 value 省略,但是在代码中写的时候是不可以省略的

案例代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    x: {{ x }} <br>
    y: {{ y }}
  </div>
  <script type="module">
    // vue.esm-browser 是 vue3 完整版
    import { createApp, reactive, onMounted, onUnmounted, toRefs } from './node_modules/vue/dist/vue.esm-browser.js'

    // 获取鼠标位置的方法
    function useMousePosition() {
      // reactive 把一个对象转换成响应式对象,并且该对象的嵌套属性也会转换为响应式对象,返回的是一个 proxy 对象
      // reactive 和 Vue.observable 类似
      const position = reactive({
        x: 0,
        y: 0
      })

      const update = e => {
        position.x = e.pageX
        position.y = e.pageY
      }

      // 在 setup 中也可以使用组件生命周期的钩子函数,但是必须在前面加上 on
      onMounted(() => {
        window.addEventListener('mousemove', update)
      })

      onUnmounted(() => {
        window.removeEventListener('mousemove', update)
      })

      // toRefs 可以把一个响应式对象的所有属性的值转换成响应式的
      // toRefs 传入的对象必须是一个 proxy 代理对象,它内部会创建一个新的对象,然后来遍历传入代理对象的所有属性,
      // 把所有属性的值转换为响应式对象,然后挂载到新创建的对象上,并返回

      // 内部就是为传入的代理对象的每一个属性,创建一个带有 value 属性的对象,该对象是响应式的,value 属性具有 get 和 set 方法,和 ref 方法类似
      // 在模版中使用的时候可以把 value 省略,但是在代码中写的时候是不可以省略的
      return toRefs(position)
    }

    // createApp 的作用是创建一个 vue 对象,里面可以传入一个选项
    // 注意 createApp 传入选项中的 data 必须是函数形式 data(){}
    const app = createApp({
      // setup 是 composition-api 的入口,
      // setup 是在 props 被解析完毕,在组件实例被创建之前执行的,所以在 setup 内部无法通过 this 获取到组件实例
      // 也就是说 setup 是在 beforeCreate 和 created 之间执行的,所以在 beforeCreate 和 created 之间的代码都可以放到 setup 函数中
      // setup 有两个参数
      // 第一个参数 props
      // 第二个参数 context,context 是一个对象,有三个属性,分别是 attrs、emit、slots
      // setup 还需要返回一个对象,可以使用在模版、methods、computed、以及所有钩子函数中
      setup() {
        // position 被解构出来的 x,y 是没有具备响应式的,就只是两个基本类型的变量
        const { x, y } = useMousePosition()
        return {
          x,
          y
        }
      }
    })
    console.log(app)

    // 把实例挂载到 id 为 app 的 div 中
    app.mount('#app')
  </script>
</body>

</html>

二、案例2 —— 点击按钮加加

  • ref :ref 方法可以把一个基本类型的数据转化为响应式对像,内部就是将该基本类型的值转化为一个带有 value 属性的对象,并且为这个值创建 get、set 方法
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <button @click="increase">按钮</button>
    <span>{{ count }}</span>
  </div>
  <script type="module">
    import { createApp, ref } from './node_modules/vue/dist/vue.esm-browser.js'

    function useCount() {
      // ref 方法可以把一个基本类型的数据转化为响应式对像
      const count = ref(0) // count : { value : 0 }
      return {
        count,
        increase: () => {
          count.value++
        }
      }
    }

    createApp({
      setup() {
        return {
          ...useCount()
        }
      }
    }).mount('#app')
  </script>
</body>

</html>

三、案例3 —— 未完成代办事项

  • computed 两种用法
    1. 传入一个获取值的函数,函数内部依赖响应式的数据,当依赖的数据发生变化后会重新执行该函数获取数据
    2. 传入一个响应式对象,这个对象具有 get 和 set ,返回一个不可变的响应式对象
  • computed 的目的是创建一个响应式数据,这个响应式的数据依赖于其他响应式数据,返回的是一个不可变的响应式对象,类似于使用 ref 创建的对象,只有一个 value 属性
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <button @click="push">按钮</button>
    未完成:{{ activeCount }}
  </div>
  <script type="module">
    import { createApp, reactive, computed } from './node_modules/vue/dist/vue.esm-browser.js'
    const data = [
      { text: '看书', completed: false },
      { text: '敲代码', completed: false },
      { text: '约会', completed: true }
    ]

    // computed 两种用法
    // 1、传入一个获取值的函数,函数内部依赖响应式的数据,当依赖的数据发生变化后会重新执行该函数获取数据
    // computed 函数返回的是一个不可变的响应式对象,类似于使用 ref 创建的对象,只有一个 value 属性
    // computed 的目的是创建一个响应式数据,这个响应式的数据依赖于其他响应式数据

    // 2、传入一个响应式对象,这个对象具有 get 和 set ,返回一个不可变的响应式对象
    createApp({
      setup() {
        const todos = reactive(data)

        // 返回待办事项个数
        const activeCount = computed(() => {
          return todos.filter(item => !item.completed).length
        })

        return {
          activeCount,
          push: () => {
            todos.push({
              text: '开会',
              completed: false
            })
          }
        }
      }
    }).mount('#app')
  </script>
</body>

</html>

四、案例4 —— 返回 yes or no

  • watch 三个参数:

    1. 监听的数据
    2. 监听到数据变化后执行的回调函数,该函数有两个参数,分别是新值和旧值
    3. 选项对象,deep 和 immediate
  • watch 的返回值是取消监听的函数

  • watch 的使用和过去的 this.$watch 是一样的,这是第一个参数不在是字符串,而是 ref 或者 reactive 创建的响应式对象

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <p>
      请问一个 yes/no 的问题:
      <input v-model="question">
    </p>
    <p>{{ answer }}</p>
  </div>

  <script type="module">
    import { createApp, ref, watch } from './node_modules/vue/dist/vue.esm-browser.js'

    createApp({
      setup() {
        const question = ref('')
        const answer = ref('')

        //  watch 三个参数:
        //  1、监听的数据
        //  2、监听到数据变化后执行的回调函数,该函数有两个参数,分别是新值和旧值
        //  3、选项对象,deep 和 immediate
        //  watch 的返回值是取消监听的函数

        watch(question, async (newValue, oldValue) => {
          const response = await fetch('https://www.yesno.wtf/api')
          const data = await response.json()
          answer.value = data.answer
        })

        return {
          question,
          answer
        }
      }
    }).mount('#app')
  </script>
</body>

</html>

五、案例4 —— 监听 count 变化

  • watchEffect 是 watch 函数的简化版本,也是用来监视数据的变化,内部实现和 watch 调用了同一个函数 doWatch

  • 不同的是 watchEffect 没有第二个回调函数的参数,watchEffect 接受一个函数作为参数,监听函数内部使用的响应式数据的变化,一旦变化,就执行一般该函数

  • watchEffect 也会返回了一个取消监听的函数

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <button @click="increase">increase</button>
    <button @click="stop">stop</button>
    <br>
    {{ count }}
  </div>

  <script type="module">
    import { createApp, ref, watchEffect } from './node_modules/vue/dist/vue.esm-browser.js'

    // watchEffect 是 watch 函数的简化版本,也是用来监视数据的变化,内部实现和 watch 调用了同一个函数 doWatch
    // 不同的是 watchEffect 没有第二个回调函数的参数,watchEffect 接受一个函数作为参数,监听函数内部使用的响应式数据的变化,一旦变化,就执行一般该函数
    // 也返回了一个取消监听的函数

    createApp({
      setup() {
        const count = ref(0)
        const stop = watchEffect(() => {
          // 该回调函数初始的时候会被执行一次,在 count 发生变化的时候会再次被调用
          console.log(count.value)
        })

        return {
          count,
          stop,
          increase: () => {
            count.value++
          }
        }
      }
    }).mount('#app')
  </script>
</body>

</html>