vue3组合式API学习记录

162 阅读6分钟

setup()函数写法

这种写法需要通过return{...}template需要的数据进行暴露

<script lang="ts">
  import {defineComponent,ref} from 'vue'
  // 可以帮助我们校验vue组件内部的ts语法代码
  export default defineComponent({ 
    setup(){
      // refImpl对象  count.value才可以获取ref里面的值  Object.defineProperty
      let count = ref(100)  
      const changeCount = ()=>{
        count.value++ 
      }
      return {
        count,
        changeCount
      }
    }
  })
</script>

setup标签写法

这种写法不需要return

<script setup lang='ts'>
  import { ref,reactive } from 'vue'
</script>

ref全家桶

ref

接受一个内部值并返回一个响应式且可变的ref对象.ref对象仅有一个.value property,指向该内部值.

// 基本数据类型/复杂数据类型可以通过ref进行包裹变成refImpl对象
const num:number = ref(0)

// ref通过xxx.value来获取num的值
console.log(num.value) // 0 

注意被ref包装之后需要.value 来进行赋值

isRef

判断一个对象是否是ref对象

import { ref, Ref,isRef } from 'vue'
  let message: Ref<string | number> = ref("我是message")
  let notRef:number = 123
  const changeMsg = () => {
    message.value = "change msg"
    console.log(isRef(message)); //true
    console.log(isRef(notRef)); //false

  }

shallowRef

创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的

修改其属性是非响应式的这样是不会改变的

<template>
  <div>
    <button @click="changeMsg">change</button>
    <div>{{ message }}</div>
  </div>
</template>



<script setup lang="ts">
  import { Ref, shallowRef } from 'vue'
  type Obj = {
    name: string
  }
  let message: Ref<Obj> = shallowRef({
    name: "小满"
  })

  const changeMsg = () => {
    message.value.name = '大满'
  }
</script>


<style>
</style>

这样是可以被监听到的修改value

import { Ref, shallowRef } from 'vue'
  type Obj = {
    name: string
  }
  let message: Ref<Obj> = shallowRef({
    name: "小满"
  })

  const changeMsg = () => {
    message.value = { name: "大满" }
  }

triggerRef

强制更新页面DOM

<template>
  <div>
    <button @click="changeMsg">change</button>
    <div>{{ message }}</div>
  </div>
</template>

<script setup lang="ts">
  import { Ref, shallowRef,triggerRef } from 'vue'
  type Obj = {
    name: string
  }
  let message: Ref<Obj> = shallowRef({
    name: "小满"
  })

  const changeMsg = () => {
    message.value.name = '大满'
    triggerRef(message)
  }
</script> 
<style>
</style>

customeRef

自定义ref

customRef 是个工厂函数要求我们返回一个对象 并且实现 get 和 set 适合去做防抖之类的

<template>
 
  <div ref="div">小满Ref</div>
  <hr>
  <div>
    {{ name }}
  </div>
  <hr>
  <button @click="change">修改 customRef</button>
 
</template>
 
<script setup lang='ts'>
import { ref, reactive, onMounted, shallowRef, customRef } from 'vue'
 
function myRef<T = any>(value: T) {
  let timer:any;
  return customRef((track, trigger) => {
    return {
      get() {
        track()
        return value
      },
      set(newVal) {
        clearTimeout(timer)
        timer =  setTimeout(() => {
          console.log('触发了set')
          value = newVal
          trigger()
        },500)
      }
    }
  })
}
 
 
const name = myRef<string>('小满')
 
 
const change = () => {
  name.value = '大满'
}
 
</script>
<style scoped>
</style>

reactive全家桶

reactive

用来绑定复杂的数据类型 例如 对象 数组,不能绑定基本类型数据

你如果用ref去绑定对象 或者 数组 等复杂的数据类型 我们看源码里面其实也是 去调用reactive

使用reactive 去修改值无须.value

import { reactive } from 'vue'
let person = reactive({
   name:"张三"
})
person.name = "李四"

readonly

拷贝一份proxy对象将其设置为只读

import { reactive ,readonly} from 'vue'
  const person = reactive({count:1})
  const copy = readonly(person)

  //person.count++

  copy.count++

shallowReactive

只能对浅层的数据 如果是深层的数据只会改变值 不会改变视图

<template>
  <div>
    <div>{{ state }}</div>
    <button @click="change1">test1</button>
    <button @click="change2">test2</button>
  </div>
</template>
 
 
 
<script setup lang="ts">
import { shallowReactive } from 'vue'
 
 
const obj = {
  a: 1,
  first: {
    b: 2,
    second: {
      c: 3
    }
  }
}
 
const state = shallowReactive(obj)
 
function change1() {
  state.a = 7
}
function change2() {
  state.first.b = 8
  state.first.second.c = 9
  console.log(state);
}
 
 
 
 
</script> 
 
 
<style>
</style>

to全家桶

toRef

如果原始对象是非响应式的就不会更新视图 数据是会变的,如果原始对象是响应式的是会更新视图并且改变数据

<template>
   <div>
      <button @click="change">按钮</button>
      {{state}}
   </div>
</template>
 
<script setup lang="ts">
import { reactive, toRef } from 'vue'
 
const obj = {
   foo: 1,
   bar: 1
}
 
 
const state = toRef(obj, 'bar')
// bar 转化为响应式对象
 
const change = () => {
   state.value++
   console.log(obj, state);
 
}
</script>

toRefs

可以帮我们批量创建ref对象主要是方便我们解构使用

import { reactive, toRefs } from 'vue'
const obj = reactive({
   foo: 1,
   bar: 1
})
 
let { foo, bar } = toRefs(obj)
 
foo.value++
console.log(foo, bar);

toRaw

将响应式对象转换为普通对象

import { reactive, toRaw } from 'vue'
 
const obj = reactive({
   foo: 1,
   bar: 1
})
 
 
const state = toRaw(obj)
// 响应式对象转化为普通对象
 
const change = () => {
 
   console.log(obj, state);
 
}

computed计算属性

计算属性就是当依赖的属性的值发生变化的时候,才会触发他的更改,如果依赖的值,不发生变化的时候,使用的是缓存中的属性值。

函数写法

import { computed, reactive, ref } from 'vue'
let price = ref(0)//$0
 
let m = computed<string>(()=>{
   return `$` + price.value
})
 
price.value = 500

对象写法

<template>
   <div>{{ mul }}</div>
   <div @click="mul = 100">click</div>
</template>
 
<script setup lang="ts">
import { computed, ref } from 'vue'
let price = ref<number | string>(1)//$0
let mul = computed({
   get: () => {
      return price.value
   },
   set: (value) => {
      price.value = 'set' + value
   }
})
</script>
 
<style>
</style>

watch侦听器

watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用

watch第一个参数监听源

watch第二个参数回调函数cb(newVal,oldVal)

watch第三个参数一个options配置项是一个对象{

immediate:true //是否立即调用一次

deep:true //是否开启深度监听

监听ref对象

import { ref, watch } from 'vue'
 
let message = ref({
    nav:{
        bar:{
            name:""
        }
    }
})
 
 
watch(message, (newVal, oldVal) => {
    console.log('新的值----', newVal);
    console.log('旧的值----', oldVal);
},{
    immediate:true,
    deep:true
})

监听多个ref 注意变成数组啦

import { ref, watch ,reactive} from 'vue'
 
let message = ref('')
let message2 = ref('')
 
watch([message,message2], (newVal, oldVal) => {
    console.log('新的值----', newVal);
    console.log('旧的值----', oldVal);
})

监听reactive对象

使用reactive监听深层对象开启和不开启deep 效果一样

import { ref, watch ,reactive} from 'vue'
 
let message = reactive({
    nav:{
        bar:{
            name:""
        }
    }
})
 
 
watch(message, (newVal, oldVal) => {
    console.log('新的值----', newVal);
    console.log('旧的值----', oldVal);
})

监听reactive单一值

import { ref, watch ,reactive} from 'vue'
 
let message = reactive({
    name:"",
    name2:""
})
 
 
watch(()=>message.name, (newVal, oldVal) => {
    console.log('新的值----', newVal);
    console.log('旧的值----', oldVal);
})

watchEffect高级侦听器

立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

如果用到message 就只会监听message 就是用到几个监听几个 而且是非惰性 会默认调用一次

let message = ref<string>('')
let message2 = ref<string>('')
 watchEffect(() => {
    //console.log('message', message.value);
    console.log('message2', message2.value);
})

组件详情

生命周期

在我们使用 Vue3 组合式API 是没有 beforeCreate 和 created 这两个生命周期的

onBeforeMount()在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。

onMounted()在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问

onBeforeUpdate()数据更新时调用,发生在虚拟 DOM 打补丁之前。

onUpdated()DOM更新后,updated的方法即会调用。

onBeforeUnmount()在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。

onUnmounted()卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。

less/sass/scoped

less/sass 概述

Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言。这里呈现的是 Less 的官方文档(中文版),包含了 Less 语言以及利用 JavaScript 开发的用于将 Less 样式转换成 CSS 样式的 Less.js 工具。

因为 Less 和 CSS 非常像,因此很容易学习。而且 Less 仅对 CSS 语言增加了少许方便的扩展,这就是 Less 如此易学的原因之一。

官方文档 Less 快速入门 | Less.js 中文文档 - Less 中文网

sass 和 less 一样 都是css预处理器

官方文档 Sass教程 Sass中文文档 | Sass中文网

在vite中使用less | sass

npm install less  -D 
npm install sass -D 

在style标签注明即可

<style lang="less">
</style>

<style lang="scss">
</style>

scoped

实现组件的私有化, 当前style属性只属于当前模块.

在DOM结构中可以发现,vue通过在DOM结构以及css样式上加了唯一标记,达到样式私有化,不污染全局的作用

BEM架构

他是一种css架构 oocss 实现的一种 (面向对象css) ,BEM实际上是block、element、modifier的缩写,分别为块层、元素层、修饰符层,element UI 也使用的是这种架构

父子传递数据

defineProps

父组件通过v-bind绑定一个数据,然后子组件通过defineProps接受传过来的值,

父组件代码:

<template>
    <div class="layout">
        <Menu v-bind:data="data"  title="我是标题"></Menu>
        <div class="layout-right">
            <Header></Header>
            <Content></Content>
        </div>
    </div>
</template>
 
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
 
const data = reactive<number[]>([1, 2, 3])
</script>

子组件接受值

通过defineProps 来接受 defineProps是无须引入的直接使用即可

如果我们使用的TypeScript

可以使用传递字面量类型的纯类型语法做为参数

ts写法如下

<template>
    <div class="menu">
        菜单区域 {{ title }}
        <div>{{ data }}</div>
    </div>
</template>
 
<script setup lang="ts">
defineProps<{
    title:string,
    data:number[]
}>()
</script>

js写法如下

defineProps({
    title:{
        default:"",
        type:string
    },
    data:Array
})

withDefaults

TS 特有的默认值方式

withDefaults是个函数也是无须引入开箱即用接受一个props函数第二个参数是一个对象设置默认值

type Props = {
    title?: string,
    data?: number[]
}
withDefaults(defineProps<Props>(), {
    title: "张三",
    data: () => [1, 2, 3]
})

子组件给父组件传参

defineEmits

子组件代码:

<template>
    <div class="menu">
        <button @click="clickTap">派发给父组件</button>
    </div>
</template>
 
<script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([4, 5, 6])
 
const emit = defineEmits(['on-click'])
 
//如果用了ts可以这样两种方式
// const emit = defineEmits<{
//     (e: "on-click", name: string): void
// }>()
const clickTap = () => {
    emit('on-click', list)
}
 
</script>

我们在子组件绑定了一个click 事件 然后通过defineEmits 注册了一个自定义事件

点击click 触发 emit 去调用我们注册的事件on-click 然后传递参数list

父组件接受子组件的事件

<template>
    <div class="layout">
        <Menu @on-click="getList"></Menu>
        <div class="layout-right">
            <Header></Header>
            <Content></Content>
        </div>
    </div>
</template>
 
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
 
const data = reactive<number[]>([1, 2, 3])
 
const getList = (list: number[]) => {
    console.log(list,'父组件接受子组件');
}
</script>

我们从Menu 组件接受子组件派发的事件on-click 后面是我们自己定义的函数名称getList,会把参数返回过来

defineExpose

子组件暴露给父组件内部属性,通过defineExpose

我们从父组件获取子组件实例通过ref

 <Menu ref="refMenu"></Menu>
//这样获取是有代码提示的
<script setup lang="ts">
import MenuCom from '../xxxxxxx.vue'
//注意这儿的typeof里面放的是组件名字(MenuCom)不是ref的名字 ref的名字对应开头的变量名(refMenu)
const refMenu = ref<InstanceType<typeof MenuCom>>()
</script>

然后打印menus.value 发现没有任何属性

这时候父组件想要读到子组件的属性可以通过 defineExpose暴露

const list = reactive<number[]>([4, 5, 6])
 
defineExpose({
    list
})

这个时候父组件就可以拿到了