Vue3快速上手

191 阅读3分钟

setup函数

  • 从组件生命周期来看,它的执行在组件实例创建之前beforeCreate执行。
  • 这就意味着在setup函数中 this 还不是组件实例,this 此时是 undefined
  • 在模版中需要使用的数据和函数,需要在 setup 返回。
<template>
  <div class="container">
    <h1 @click="say()">{{msg}}</h1>
  </div>
</template>
<script>
export default {
  setup () {
    console.log('setup执行了')
    // 这输出undefined
    console.log(this)
    // 定义数据和函数
    const msg = 'vue3'
    const say = () => {
      console.log(msg)
    }

    return { msg , say}
  },
  beforeCreate() {
    console.log('beforeCreate执行了')
    console.log(this)
  }
}
</script>

setup的注意点

  • setup执行的时机
    • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数
    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
      • slots: 收到的插槽内容, 相当于 this.$slots
      • emit: 分发自定义事件的函数, 相当于 this.$emit

官方文档中是这样写的
cn.vuejs.org/api/composi…
传入 setup 函数的第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值:

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) } }

该上下文对象是非响应式的,可以安全地解构:

export default {
  setup(props, { attrs, slots, emit, expose }) {
    ...
  }
}

总结:  setup 组件初始化之前执行,它返回的数据和函数可在模版使用。


ref函数

  • 作用: 定义一个响应式的数据
  • 语法: const xxx = ref(initValue)
    • 创建一个包含响应式数据的引用对象
    • JS中操作数据: xxx.value
    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:
    • 接收的数据可以是:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
    • 对象类型的数据:内部 “ 求助 ”了Vue3.0中的一个新函数—— reactive函数。
<template>
 <div class="container">
   <div>{{name}}</div>
   <div>{{age}}</div>
   <button @click="updateName">修改数据</button>
 </div>
</template>
<script>
import { ref } from 'vue'
export default {
 name: 'App',
 setup () {
   // 1. name数据
   const name = ref('lin')
   console.log(name)
   const updateName = () => {
     name.value = 'chen'
   }
   // 2. age数据
   const age = ref(10)
   // ref常用定义简单数据类型的响应式数据
   // 其实也可以定义复杂数据类型的响应式数据
   // 对于数据未之的情况下 ref 是最适用的
   return {name, age, updateName}
 }
}
</script>

使用场景:

  • 当你明确知道需要的是一个响应式数据对象那么就使用 reactive 即可
  • 其他情况使用ref

reactive函数

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象
  • reactive定义的响应式数据是“深层次的”。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
<template>
  <div class="container">
    <p>{{ obj.name }}</p>
    <p>{{ obj.age }}</p>
    <h1 @click="updateName()">点击更新</h1>
  </div>
</template>
<script>
import { reactive } from '@vue/reactivity'
export default {
  setup() {
    const obj = reactive({
      name: 'Code小叔',
      age: 18
    })
    // 定义一个修改名字的方法
    const updateName = () => {
      obj.name = '我更新啦'
    }
    // 一定要记得return出去喔
    return { obj, updateName }
  }
}
</script>

reactie和ref对比

  • 从使用角度对比:
    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
    • reactive定义的数据:操作数据与读取数据:均不需要.value

toRef和toRefs

举个栗子🌰

此处toRef可以从对象里,取出任意属性,变成响应式数据,没有取出的数据则将丢失响应式,当点击按钮的话也就不会随之变化

<template>
  <button @click="change">有变化了</button>
</template>
<script setup lang="ts">
import { toRef } from 'vue'
const Preson = {
  first: 1,
  second: 2
}
const state = toRef(Preson, 'second')
const change = () => {
  state.value++
  console.log(Preson, state)
}
</script>

image.png

桥豆麻袋,相信看到这里,聪明的小朋友应该猜到toRefs怎么用了,没错不要怀疑,就是你想的那样!

toRefs会帮我们批量的生成响应式的ref对象

<template>
  <button @click="change">有变化了</button>
</template>
<script setup lang="ts">
import { reactive, toRefs } from 'vue'
const Preson = reactive({
  first: 1,
  second: 2
})
const { first, second } = toRefs(Preson)
const change = () => {
  first.value++, second.value++
  console.log(first, second)
}
</script>

watch函数

第一个参数:需要监听的源对象

第二个参数:回调函数callBack(newVal,oldVal)

第三个参数:options配置项是一个对象{ immediate:true //是否立即调用一次 deep:true //是否开启深度监听 }

const obj = reactive({
      name: 'Code小叔',
      age: 18,
      job: {
        demo1: {
          salary: 10000
        }
      }
    })
    watch(obj, (newValue, oldValue) => {
      console.log('watch触发', newValue, oldValue)
    })

监听对象中的某个值

watch(() => obj.name,(newValue, oldValue) => {
        console.log('watch触发', newValue, oldValue)
      }
    )

只监听子属性

watch(() => ({ ...obj }),(newValue, oldValue) => {
        console.log('watch触发', newValue, oldValue)
      }
    )

监听多个数据

其中有任意一个数据变化都会触发监听

const demoNumer = ref(1)
    const obj = reactive({
      name: 'Code小叔',
      age: 18
    })
watch([() => obj.age, demoNumer],([newValue, oldValue], [numNew, numOld]) => {
        console.log('watch触发', newValue, oldValue)
        console.log('demoNumer触发', numNew, numOld)
      }
    )

⭐⭐⭐⭐⭐

如果你想初始化就执行一次监听,那么就用到了{ immediate: true }

watch(obj,(newValue, oldValue) => {
        console.log('watch触发', newValue, oldValue)
      },{ immediate: true }
    )

组件传参

传值

<template>
  <div class="container">
    <HelloWorld :money="sonData"></HelloWorld>
  </div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
import { ref } from 'vue'
export default {
  setup() {
    const sonData = ref(100)
    return { sonData }
  },
  components: { HelloWorld }
}
</script>
<template>
  <div>
    <p>我是子组件</p>
    <p>{{ money }}</p>
  </div>
</template>

<script>
export default {
  props: {
    money: {
      type: Number,
      default: 0
    }
  },
  setup(props) {
    // 获取父组件中的money
    console.log(props.money)
    return {}
  }
}
</script>

传事件

<template>
  <div class="container">
    <HelloWorld :money="sonData" @change-emit="emitMoney"></HelloWorld>
  </div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
import { ref } from 'vue'
export default {
  setup() {
    const sonData = ref(100)
    const emitMoney = (newMoney) => {
      sonData.value = newMoney
    }
    return { sonData, emitMoney }
  },
  components: { HelloWorld }
}
</script>
<template>
  <div>
    <p>我是子组件</p>
    <p>{{ money }}</p>
    <button @click="changeMoney">点击修改金额</button>
  </div>
</template>

<script>
export default {
  props: {
    money: {
      type: Number,
      default: 0
    }
  },
  setup(props, { emit }) {
    // 获取父组件中的money
    // 解构emit 触发自定义事件的函数
    console.log(props.money)
    const changeMoney = () => {
      emit('change-emit', 2000)
    }
    return { changeMoney }
  }
}
</script>

provide 与 inject

<template>
  <div class="container">
    <p>{{ sonData }}</p>
    <HelloWorld></HelloWorld>
  </div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
import { provide, ref } from 'vue'
export default {
  setup() {
    const sonData = ref(100)
    const emitMoney = (newMoney) => {
      sonData.value = newMoney
    }
    // 将数据提供给后代组件
    provide('money', sonData)
    provide('changeMoney', emitMoney)
    return { sonData }
  },
  components: { HelloWorld }
}
</script>
<template>
  <div>
    <p>我是子组件</p>
    <p>{{ money }}</p>
    <button @click="emitMoney">点击修改金额</button>
  </div>
</template>

<script>
import { inject } from 'vue'
export default {
  setup() {
    const money = inject('money')
    const changeMoney = inject('changeMoney')
    const emitMoney = () => {
      changeMoney(200)
    }
    return { money, emitMoney }
  }
}
</script>