Vue3造轮子(一)-Switch组件

前言

UI造轮子Vue2版官网初步部署上线了,决定暂时告一段落,先学习Vue3的造轮子课。这个阶段的学习总结以Vue3新特性为主,以及这其中踩到的坑,节奏依然是写轮子–>看文档–>写总结,然后会将Vue3的学习笔记做一个整体的总结。加油~(ง •_•)ง

效果预览

代码链接

提交历史

API设计

value

  • 组件内部,用value控制开关的开与关 — computed计算属性控制开关状态的样式
  • value应该是由父组件传入 — 父组件用ref声明变量,子组件用props接受值(父子通信)
  • 当父组件调用该子组件时,应该知道当前子组件的状态 — v-model实现父子双向绑定

disable loading

  • disable 禁用属性,loading,加载状态,处在该状态下开关无法进行操作
  • 样式类型的属性,因此也是使用computed计算属性控制样式

Vue3笔记

组件注册

Vue2.x

1
2
3
new Vue({
render: h => h(App)
}).$mount('#app')

Vue3.x

1
2
3
4
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

Composition API(组合式API)

为什么

总结一下就是:避免逻辑关注点过于碎片化,提高代码的可读性和可维护性。

在Vue2中,我们如果要实现一个需求,那么这些逻辑会被分散在data、methods、computed等各个Options API,而在Vue3中,我们可以将同一个需求的各个逻辑模块整合起来,放在Composition API。如果用颜色来区分各个逻辑块,那么下图可以直观地展示这种区别。

setup

  • 一个接受propscontext的函数,从setup返回的内容都将暴露给组件的其余部分

    • props: 传入组件的属性:setup 中接收的props是响应式的, 当传入新的 props 时,会及时被更新。由于是响应式的, 所以不可以使用 ES6 解构,解构会消除它的响应式
    • context:暴露attrsslotsemit这三个组件的property
  • 创建组件之前,初始化 props 之后调用执行,因此setup中无法访问组件实例this

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
      beforeCreate() {
    console.log('beforeCreate');
    },
    created() {
    console.log('created');
    },
    mounted() {
    console.log('mounted');
    },
    setup (props, context) {
    console.log('setup');
    }
    // setup
    // beforeCreate
    // created
    // mounted
  • 我刚开始使用setup的时候,也产生过疑惑,这样不是将所有的代码都塞到setup里面,让它变得非常庞大臃肿吗?官方文档给出了解决方案——将各逻辑模块分别提取到独立的组合式函数

带ref 的响应式变量 (响应式引用)

  • 在setup中直接声明的变量是非响应式的,因此需引入ref函数

  • ref接受参数并返回一个包装对象,包装对象具有 value property ,可使用该 property 访问或更改响应式变量的值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import { ref } form 'vue'

    export default {
    const visible = ref(false)
    console.log(visible) // { value: false }
    console.log(visible.value) // false

    visible.value = true
    console.log(visible.value) // true
    }
  • 为什么要返回一个包装对象?

    提供一个让我们能够在函数之间以引用的方式传递任意类型值的容器。这个容器可以在封装了逻辑的组合函数中将状态以引用的方式传回给组件。组件负责展示(追踪依赖),组合函数负责管理状态(触发更新)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    setup() {
    const valueA = useLogicA() // valueA 可能被 useLogicA() 内部的代码修改从而触发更新
    const valueB = useLogicB()
    return {
    valueA,
    valueB
    }
    }

    // 作者:摸鱼架构师
    // 链接:https://juejin.cn/post/6844904042242523144
    // 来源:掘金

toRefs

解构的同时保持props内部变量的响应式

1
2
3
4
5
import { toRefs } from 'vue'
setup(props){
const { user } = toRefs(props)
consloe.log(user.value)
}

computed属性

  • 使用从 Vue 导入的 computed 函数在 Vue 组件外部创建计算属性

    1
    2
    3
    4
    5
    6
    7
    8
    import { ref, computed } from 'vue'

    const counter = ref(0)
    const twiceTheCounter = computed(() => counter.value * 2)

    counter.value++
    console.log(counter.value) // 1
    console.log(twiceTheCounter.value) // 2
  • computed函数返回一个只读响应式引用,由一个作为 computed 的第一个参数传递的 getter 类回调输出。为了访问新创建的计算变量的 value,我们需要像使用 ref 一样使用 .value property。

v-model

使用:value和@input进行父子组件双向通信

其实v-model就是一个语法糖,我们可以先拆解出来它具体做了什么,以switch组件为例:

父组件:

1
2
3
4
5
6
7
8
9
10
<x-switch :value="checked" @input="visible = $event"></x-switch>

import { ref } from 'vue'
setup () {
const checked = ref(false)
const toggle = () => {
checked.value = !checked.value
}
return { checked, toggle }
}

子组件:

1
2
3
4
5
6
7
8
import { ref } from 'vue'

setup (props, context) {
const toggle = () => {
context.emit('input', !props.value)
}
return { toggle }
}

Vue2的v-model

  • Vue2.x中,在组件上使用 v-model 相当于绑定 value prop 和 input 事件

    1
    2
    3
    4
    5
    <x-switch v-model="checked" />

    <!-- 简写: -->

    <x-switch :value="checked" @input="checked = $event" />
  • 如果想更改绑定的属性名,或绑定多个变量,可使用.sync

    子组件:

    1
    this.$emit('update:value', newValue)

    父组件:

    1
    <x-switch :value.sync="checked" />

Vue3的v-model

  • 属性名任意,假设为 x
  • 事件名必须为 'update:x'
1
2
3
4
5
<x-switch v-model:value="checked" />

<!-- 简写: -->

<x-switch :value="checked" @update:value="checked = $event" />

总结

  • 使用 ref 创建内部数据
  • 使用 :value 和 @input 让父子组件进行交流(组件通信)
  • 使用 v-model
  • Vue 2 和 Vue 3 的区别
    • v-model:prop 代替以前的 v-model.sync
    • 新增 context.emit,与 this.$emit 作用相同