Vue3-02-setup()/computed()/watch()

7/10/2021 Vue3

# 1. setup()

setup()是在组件created()之前执行的,所以在setup()内部无法获取this

  • 参数一:props

组件接收的props数据可以在setup()函数内访问到

export default {
  props: { title: String },
  setup(props) { console.log(props.title) }
}
1
2
3
4

props 具有响应性,不能使用 ES6 解构它,这会使其失去响应性。如果需要解构,可以使用 toRefs()

const { title } = toRefs(props)
console.log(title.value)

// 如果 title 是一个可选的属性,需使用 toRef()
import { toRef } from 'vue'
setup(props) {
	const title = toRef(props, 'title')
	console.log(title.value)
}
1
2
3
4
5
6
7
8
9
  • 参数二:context:

context 是一个上下文对象,可以通过 context 来访问 Vue 的实力 this。 context 内部包括三个属性:attrsslotsemit

export default {
  setup(props, { attrs, slots, emit }) {  ... }
}
1
2
3

context 是一个常规的 js 对象,它不具有响应性可以直接对它解构。

但 attrs 和 slots 是有状态的对象,当组件本身更新时它们也会随之更新,避免解构

# 2. computed()

  • computed()创建只读计算属性
const count = ref(1)
// 创建一个只读性计算属性,依赖于count值
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // error 该计算属性为只读属性,不可写
1
2
3
4
5
  • computed()创建可读写计算属性
// 注意使用的时候引入computed
import { ref, computed} from 'vue'
export default {
  name: 'Test',
  setup(){ 
    const baby = ref('嘎嘎嘎')  // 定义一个响应式数据
    const age = ref(28)
    // computed传入回调函数
    const areYouSureYouAreABaby = computed(() => {return `${baby.value}`})
    // set和get方式
    const setAge= computed({
      get() { return age.value + 10 },
      set(v) {  age.value = v - 10 }
    })
   // 对比vue2.0
   /*
   computed: {
      areYouSureYouAreABaby (){ return `${baby.value}` },
      setAge:{
        get(){ return age + 10 },
        set(v) { age = v - 10  }
      }
    }
  */
    return { baby, age, areYouSureYouAreABaby }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 3. watch()

watch()监听多个数据源,第一个参数中,要监听的数据源以数组的形式传入。

import { ref, watch } from 'vue' 
const count = ref(0) 
const count2 = ref(1) 
watch([count,count2],([newCount,newCount2],[oldCount,oldCount2])=>{})
//还有第二种写法
watch([count, count2], (newValue, oldVlaue) => {
    console.log(newValue)  //[newCount, newCount2]
    console.log(oldValue)  //[oldCount, oldCount2]
})
1
2
3
4
5
6
7
8
9

watch 有三个参数:第一个是个getter(所谓getter写法就是你要写个getter函数, 第二个是个回调函数,第三个是个options(这个参数是放vue2.0的deep或者immediate等可选项)

import { ref, watch, watchEffect } from 'vue' // 使用需要引入watch
export default {
  name: 'Test',
  setup(){
    const baby = ref('嘎嘎嘎')
    const arr = ref(['翠花', '小红'])

    // 监听一个值的情况,有两种方式 ||||  第一种:直接放ref
    watch(baby, () => `${baby.value}`)
    // 第二种:放ref的value值
    watch(() => baby.value, () => `${baby.value}`)
  
    // 监听多个值的时候 ,第一个参数是个数组,里面放监听的元素
    watch([baby, arr], (v, o) => { 
     // 这里的v,o也是数组,所以你取值的时候v[0],v[1]拿到第几个元素的变化
     ...
    }, { deep: true, immediate: true })
 
    // 或者写成
    watch([baby,arr], ([baby, arr], [prebaby,prearr]) => { ... })
    // 对比vue2.0
    /*
    watch: {
      baby(v, o) {},
      arr: {
        handle(v,o) {},
        deep: true,
        immediate: true,
        flush: 'pre' // 这个默认有三个参数,'pre'| 'post' | 'sync',默认‘pre’组件更新前运行,'post'组件渲染完毕后执行,一般用于你需要去访问$ref的时候可以用这个,'sync'是一旦你的值改变你需要同步执行回调的时候用这个
      }
    }
    */
    return {baby,areYouSureYouAreABaby,data }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# vue3.0 watchEffect 用法

import { defineComponent, ref, reactive, toRefs, watchEffect } from"vue";  
exportdefault defineComponent({  
 setup() {  
   const state = reactive({nickname: "xiaofan", age: 20});  
   let year = ref(0)  
   setInterval(() =>{ state.age++; year.value++ },1000)  
   watchEffect(() => { console.log(state); console.log(year); });  
   return { ...toRefs(state) }
 },  
});
1
2
3
4
5
6
7
8
9
10

执行结果首先打印一次state和year值;然后每隔一秒,打印state和year值

watchEffect会自动收集依赖, 只要指定一个回调函数。在组件初始化时,会先执行一次来收集依赖,然后当收集到的依赖中数据发生变化时,就会再次执行回调函数。

  1. watchEffect 不需要手动传入依赖
  2. watchEffect 会先执行一次用来自动收集依赖
  3. watchEffect 无法获取到变化前的值, 只能获取变化后的值
  //  watchEffect 有两个参数,一个是副作用函数(就是外部的数据对这个函数产生影响的,通俗点说就是在这个函数内部使用了外面的变量等),一个是options()
//  在vue2.0中,我们一般在created里添加一些监听事件,比如你的$bus的一些事件监听,在setup中就可以在这个里面写
watchEffect((onInvalidate) => {
   // 这里的变化就相当于依赖了age.value,如果age变化了就会触发这个监听
   // 刚刚创建组件的时候会立即执行这个 
   const _age= `her age is ${age.value}`
   console.log(_age)
   //有时候你需要在这里挂载一些监听事件
   const handerClick = ()=>{}
   document.addEventlistener('click', handerClick)
   // 在vue2.0我们需要在destroy的时候remove它,这里提供了一个方法onInvalidate回调解决remove的问题
   onInvalidate(()=>{
       /*
        执行时机:  在副作用即将重新执行时,就是在每次执行这个watchEffect回调的时候会先执行这个,
        如果在setup()或生命周期钩子函数中使用watchEffect, 则在卸载组件时执行此函数。
       */
       document.removeEventListener('click',handerClick )
    })  
})
// 这个也是支持async,await的
const data = ref(null)
watchEffect(async onInvalidate => {
 // 假设个接口获取数据的
  data.value = await fetchData()
  onInvalidate(() => {...})
})
// 再来理解options:这里有三个参数flush,onTrigger,onTrack
watchEffect(onInvalidate => { onInvalidate(() => {...})}, {
  flush: 'pre',      // 跟watch一样,默认pre,组件更新前去调用
  onTrigger(e) {},  // 依赖项变化时候触发这个即依赖项的set触发的时候
  onTrack(e) {}    // 依赖项被调用的时候触发这个即依赖项的get触发的时候
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32