第9期 - 迪士尼烟花

封面图来源于上海迪士尼。贪玩项目到放烟花前一刻,误打误撞到了城堡后方,从城堡背面看烟花,依旧是很美、很绚烂。

技术分享-前端框架相关-Vue3.0

Composition API (组合式 API)

setup

script setup 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,代码会被编译成组件 setup() 函数的内容。

特性

setup() 钩子是在组件中使用组合式 API 的入口,在 setup() 函数中返回的对象(返回值会暴露给模板和其他的选项式 API 钩子)会暴露给模板和组件实例。在模板中访问从 setup 返回的 ref 时会自动浅层解包,因此无须再在模板(html-template)中写 .value。setup() 自身并不含对组件实例的访问权,即在 setup() 中访问 this 会是 undefined。

组件传值

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

尽量 不使用解构的方式去访问 props 中的属性,会导致其失去组件传值的响应式特性。通过 props.xxx 的形式来使用其中的 props

context

访问上下文对象

export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)
    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)
    // 触发事件(函数,等价于 $emit)
    console.log(context.emit)
    // 暴露公共属性(函数)
    console.log(context.expose)
  }
}
//  解构 context
export default {
  setup(props, { attrs, slots, emit, expose }) {
    // 让组件实例处于 “关闭状态” 即不向父组件暴露任何东西
    expose()
    const publicCount = ref(0)
    const privateCount = ref(0)
    // 有选择地暴露局部状态
    expose({ count: publicCount })
  }
}

context 可以安全地解构,解构之后的上下文对象如果是响应式的,依然需要通过 attrs.x 或 slots.x 的形式使用其中的属性

可以返回一个渲染函数

在渲染函数中可以直接使用在同一作用域下声明的响应式状态

import { h, ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    return () => h('div', count.value)
  }
}
// 返回一个渲染函数将会阻止返回其他东西, 通过调用 expose() 解决这个问题
export default {
  setup(props, { expose }) {
    const count = ref(0)
    const increment = () => ++count.value

    expose({
      increment
    })

    return () => h('div', count.value)
  }
}

响应式 API

ref()

接受一个内部值,返回一个响应式的、可更改的 ref 对象。通过访问其内部的属性 .value来访问对象值, 所有对 .value 的操作都将被追踪,并且会触发与之相关的副作用。注意:浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式的,对于 深层次的对象 通过 reactive() 转为具有深层次响应式的对象

reactive()

响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。若要避免这种深层次的转换,请使用 shallowRef() 来替代。

shallowRef()

shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。

shallowReactive()

reactive() 的浅层作用形式。浅层响应式对象里只有根级别的属性是响应式的。只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中

computed()

创建一个只读的计算属性 ref:

const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误

可写的

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})
plusOne.value = 1
console.log(count.value) // 0

watch()

懒侦听, 仅在侦听源发生变化时才执行回调函数。 第一个参数是侦听器的源, 第二个参数是在发生变化时要调用的回调函数, 接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数(在副作用下一次重新执行前调用,可以用来清除无效的副作用), 第三个可选的参数是一个对象, 属性包括:immediate、deep、flush(调整回调函数的刷新时机)、onTrack / onTrigger(不常用)

const state = reactive({ count: 0 })
watch(
  () => state,
  (newValue, oldValue) => {
    // newValue === oldValue
  },
  { deep: true } // 强制侦听器进入深层级模式
)

// 停止侦听器
const stop = watch(source, callback)

// 当已不再需要该侦听器时:
stop()

readonly()

接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。

const original = reactive({ count: 0 })

const copy = readonly(original)

watchEffect(() => {
  // 用来做响应性追踪
  console.log(copy.count)
})

// 更改源属性会触发其依赖的侦听器
original.count++

// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!

生命周期

onMounted(): 组件挂载完成后执行 onUpdated(): 在组件因为响应式状态变更而更新其 DOM 树之后调用 onUnmounted(): 组件实例被卸载之后 onBeforeMount(): 组件被挂载之前 onBeforeUpdate(): 组件即将因为响应式状态变更而更新其 DOM 树之前 onBeforeUnmount(): 组件实例被卸载之前 onErrorCaptured(): 捕获后代组件传递的错误时

依赖注入

provide

通过传入key, 在后代组件注入新的值更新全局 key。接受两个参数:要注入的 key(字符串或symbol),和 要注入的值。

inject()

注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。 如果父组件链上多个组件对同一个 key 提供了值,那么离得更近的组件将会“覆盖”链上更远的组件所提供的值。如果没有能通过 key 匹配到值,inject() 将返回 undefined。(传入第二个可选参数,在没有匹配到 key 时使用的默认值)