• vue 2.0 options api
  • vue 3.0 composition api

composition-apiopen in new window

vue3中文-apiopen in new window

setup

  1. setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。
  2. setup 选项是一个接收 props 和 context 的函数
  • 调用时机

    创建组件实例,然后初始化 props ,紧接着就调用setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用

  • 模板中使用

    如果 setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文:

    <template>
      <div>{{ count }} {{ object.foo }}</div>
    </template>
    
    <script>
      import { ref, reactive } from 'vue'
    
      export default {
        setup() {
          const count = ref(0)
          const object = reactive({ foo: 'bar' })
    
          // 暴露给模板
          return {
            count,
            object,
          }
        },
      }
    </script>
    

    注意 setup 返回的 ref 在模板中会自动解开,不需要写 .value

setup 参数

该函数接收 props 作为其第一个参数:

export default {
  props: {
    name: String,
  },
  setup(props) {
    console.log(props.name)
  },
}

注意 props 对象是响应式的,watchEffectwatch 会观察和响应 props 的更新:

export default {
  props: {
    name: String,
  },
  setup(props) {
    watchEffect(() => {
      console.log(`name is: ` + props.name)
    })

    watch(() => props.name, (newValue) => {
          console.log(newValue)
    })
  },
}

然而不要解构 props 对象,那样会使其失去响应性:

export default {
  props: {
    name: String,
  },
  setup({ name }) {
    watchEffect(() => {
      console.log(`name is: ` + name) // Will not be reactive!
    })



  },
}


export default {
  components: { 'mbt':MyButton },
  props: {
      btnm: String
  },
  name: 'Menu',
  setup(props, ctx) {

      watch(() => props.btnm, (newValue) => {
          console.log(newValue)
      })
    }
}

在开发过程中,props 对象对用户空间代码是不可变的(用户代码尝试修改 props 时会触发警告)。

props context

  • 第二个参数提供了一个上下文对象,从原来 2.x 中 this 选择性地暴露了一些 property。

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

    attrsslots 都是内部组件实例上对应项的代理,可以确保在更新后仍然是最新值。所以可以解构,无需担心后面访问到过期的值:

    const MyComponent = {
      setup(props, { attrs }) {
        // 一个可能之后回调用的签名
        function onClick() {
          console.log(attrs.foo) // 一定是最新的引用,没有丢失响应性
        }
      },
    }
    

    出于一些原因将 props 作为第一个参数,而不是包含在上下文中:

    • 组件使用 props 的场景更多,有时候甚至只使用 props
    • props 独立出来作为第一个参数,可以让 TypeScript 对 props 单独做类型推导,不会和上下文中的其他属性相混淆。这也使得 setuprender 和其他使用了 TSX 的函数式组件的签名保持一致。

this的用法

  • getCurrentInstancethis 在 setup() 中不可用。由于 setup() 在解析 2.x 选项前被调用,setup() 中的 this 将与 2.x 选项中的 this 完全不同。同时在 setup() 和 2.x 选项中使用 this 时将造成混乱。在 setup() 中避免这种情况的另一个原因是:这对于初学者来说,混淆这两种情况的 this 是非常常见的错误:
import { getCurrentInstance } from 'vue'

setup() {
// 获取当前上下文对象
  const th = getCurrentInstance()


  function onClick() {
    this // 这里 `this` 与你期望的不一样!
  }
}
  • 类型定义

defineComponent

interface Data {
  [key: string]: unknown
}

interface SetupContext {
  attrs: Data
  slots: Slots
  emit: (event: string, ...args: unknown[]) => void
}

function setup(props: Data, context: SetupContext): Data

提示

为了获得传递给 setup() 参数的类型推断,需要使用 defineComponentopen in new window


生命周期钩子Hooks

  • 可以直接导入 onXXX 一族的函数来注册生命周期钩子:
import { onMounted, onUpdated, onUnmounted } from 'vue'

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

这些生命周期钩子注册函数只能在 setup() 期间同步使用, 因为它们依赖于内部的全局状态来定位当前组件实例(正在调用 setup() 的组件实例), 不在当前组件下调用这些函数会抛出一个错误。

组件实例上下文也是在生命周期钩子同步执行期间设置的,因此,在卸载组件时,在生命周期钩子内部同步创建的侦听器和计算状态也将自动删除。

  • 与 2.x 版本生命周期相对应的组合式 API

    • beforeCreate -> 使用 setup()
    • created -> 使用 setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeDestroy -> onBeforeUnmount
    • destroyed -> onUnmounted
    • errorCaptured -> onErrorCaptured
  • 新增的钩子函数

    除了和 2.x 生命周期等效项之外,组合式 API 还提供了以下调试钩子函数:

    • onRenderTracked
    • onRenderTriggered

    两个钩子函数都接收一个 DebuggerEvent,与 watchEffect 参数选项中的 onTrackonTrigger 类似:

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

contextParam

  • setup context
  • emit 子组件触发父组件
    • emits:['emit_Method'] 注册方法
// parent
/*
<template>
    <HelloWorld :msg='state' @plus='plusParent' />
</template>
*/
import { ref ,onMounted ,onBeforeMount} from 'vue'
import HelloWorld from '@/components/HelloWorld.vue'
export default {
  name: "App",
  components: {
    HelloWorld
  },
  setup() {
    let state = ref(0)
    const plusParent = (num) => {
      state.value += num;
    }
    return {  plusParent }
  }
}
// -------child
{{ msg }}
//  <button  @click='plus'> add</button>

export default {
  name: 'HelloWorld',
  props: {
    msg: Number
  },
  emits: ['plus'],
  setup(props, { emit }) {

    const plus = () => {
    // this.$emit() -- 2.0 
      emit('plus', 2)
    }
    return {  plus }
  }
}