应用入口: setup
setup()
函数是 .vue后缀文件的应用入口。
setup()
应返回一个对象暴露出内容给模板和组件实例使用。
setup()
内部不会绑定当前组件实例,this会是undefined。
<script>
import {ref} from 'vue'
export default {
setup(){
// ref响应式函数,返回值为响应式数据
const count = ref(0)
// this为undefined,不会绑定组件对象
console.log(this)
// 提供返回值供组件和模板使用
return {
count
}
}
mounted(){
// options API mounted bind this
console.log(this.count)
}
}
</script>
setup()
简写方式:
// script标签添加setup属性,这是setup()的语法糖,所有script的内容都会被自动包裹在setup()函数里,每次的组件编译setup都会执行
// 简写方式会自动返回被外界使用的内容,不需要显示的return
// 使用简写方式不能写 export defalut,正常情况下缺少export defalut会报错
<script setup>
import {ref} from 'vue'
// ref响应式函数,返回值为响应式数据
const count = ref(0)
// this为undefined,不会绑定组件实例对象
console.log(this)
</script>
关于script lang的说明:
<script lang="ts">
/*
* lang = "ts" 表示为TypeScript脚本,ts和js的区别是ts是js的子集,增加了类型声明和类型约束等增强功能
* ts最终会转为js,因此在ts脚本中写js或者ts都可以
* Vue为定义组件提供了 defineComponent 以提供类型推导
*/
export default defineComponent({
})
<script>
props
setup函数第一个参数props,用于接收组件传入的props,该props是响应式的每当传入新值props也会同步更新。
props
如果被解构那么解构出来的变量将失去响应式,props应采用props.xxx的方式取值。要是需要解构props并保持响应式,可以用toRefs、toRef两个工具函数对props包装再进行解构。
<script>
import {toRefs,toRef} from 'vue'
export default {
setup(props){
// 将props全部转为ref
const {title} = toRefs(props)
// 将props某个属性转为ref
const title = toRef(props,'title')
}
}
</script>
props
ts中两种推断类型方式,跟setup函数是否采用简写有关。
// setup 采用简写 props的类型需要用defineProps函数
<script setup lang="ts">
const props = defineProps({
count:Number
})
</script>
// 非setup简写,整个组件的类型推断需要使用defineComponet
<script lange="ts">
export default defineComponent({
props:{
name:String
},
setup(props){
// setup的props就是上面的props
}
})
</script>
context
setup函数第二个参数context,表示上下文对象(执行环境),暴露了一些公共属性。
context
是非响应式对象,其属性可以被解构使用,不存在props解构丢失响应式问题因本身就是非响应式。
<script>
import {h,ref} from 'vue'
export default {
setup(props,{attrs,slot,emit,expose}){
// attrs 等价 this.$attrs
// slot 等价 this.$slot
// emit 等价 this.$emit
// attrs、slot都是有状态的对象,总是随着组件更新而更新,不应该被解构使用
// attrs、slot的属性非响应式,如果需要根据attrs、slot的属性判断逻辑,那么就应该在onBeforUpdate函数编写相关逻辑
// 实现
const context = {}
context.attrs = ref({})
context.slot = ref({})
// expose函数 暴露公共属性
// 不向外界暴露属性
expose()
// 向外界暴露属性
expose({count:1})
// setup返回的是一个h渲染函数,h函数只能用当前域下声明的变量,因组件返回的是h函数,父组件无法获取组件里的状态,只能通过expose方式暴露属性
const count = ref(0)
return () => h('div',count.value)
}
}
</script>
响应式:核心
ref()
接受一个参数,返回一个响应式的可更改的ref对象,通过.value
取值或改值,模板中无需.value
写法会自动取值。
ref()
如果接受的参数是一个对象类型 (Object、Array等),ref内部会调用reactive()
转为深层次响应式对象。
<script setup>
import {ref} from 'vue'
const num = ref(0)
console.log(num.value) // 0
num.value = 1 // .value具有响应性改变其值使用凡是用到num的位置都会自动更新为新值
</script>
computed()
接受第一个参数可以是一个函数,返回一个只读的ref对象;或者接受是一个具有set、get函数的对象,返回的ref是可写的。
computed()
接受第二个参数是可选项对象包含用来调试debugger的函数。
<script setup>
import {computed, ref} from 'vue'
const num = ref(10)
// 接受一个函数,返回一个只读的ref对象
const compute = computed(() => num.value)
// 报警告 无效果 computed value is readonly
compute++
</script>
<script setup>
import {computed, ref} from 'vue'
const mun = ref(10)
// 接受一个具有set、get函数的对象,返回一个可写的ref
const compute = computed({
get() {
return mun.value
},
set(val) {
mun.value = val
}
})
compute.value++
</script>
// deugger
<script setup>
import {computed, ref} from 'vue'
const mun = ref(10)
const DebuggerOptions = {
onTrack(e) {
console.log('onTrack', e)
debugger
},
onTrigger(e) {
console.log('onTrigger', e)
debugger
}
}
const compute = computed(() => mun.value, DebuggerOptions)
</script>
reactive()
接受一个对象类型 (Object、Array等),返回一个具有深层响应代理的对象。深层响应代理是影响所有嵌套的属性,
该深层代理的对象是通过Proxy API处理,因此不等于原对象。
reactive()
接受了一个Map,该Map存储了ref,在取值时Vue无法直接通过.value
方式,需要用Map.get(‘keyName’).value方式。
reactive()
接受一个普通类型的值,返回一个ref。
<script setup>
import {reactive,ref} from 'vue'
// 接受对象类型
const mun = reactive({count: 1})
const m = mun.count
// 接受普通类型返回一个ref, 模板中可以自动解包
const mun = reactive(10)
console.log(mun.value)
// 接受Map存储的ref只能mun.get('count').value 取值,不能:mun.value
const mun = reactive(new Map([['count', ref(0)]]))
const m = mun.get('count').value
</script>
readonly()
接受一个对象或ref,返回一个只读的对象,该对象也是深层代理,深层只读。
<script setup>
import {readonly, ref} from 'vue'
const mun = readonly(ref(0))
// Set operation on key "value" failed: target is readonly. 只读的无法修改值
mun.value = 1
</script>
watchEffect()
接受第一个参数是一个函数会被立即执行,该函数的参数也是一个函数,再立即执行函数之后执行。
watchEffect()
接受第二个参数是可选项对象包含用来调试debugger的函数。
watchEffect()
该函数用来监听依赖的变化,并在依赖变更时重新执行。
watchEffect()
返回一个停止函数,用来停止监听。
<script setup>
import {watchEffect} from 'vue'
const DebuggerOptions = {
flush: 'post', // 'pre' | 'post' | 'sync' // 默认:'pre' 回调刷新机制
onTrack(e) {
console.log('onTrack', e)
debugger
},
onTrigger(e) {
console.log('onTrigger', e)
debugger
}
}
const stop = watchEffect(challback => {
challback()
}, DebuggerOptions)
stop()
</script>
watchPostEffect()
是watchEffect flush选项为:'post’的别名。
watchSyncEffect()
是watchEffect flush选项为:'sync’的别名。
watch()
监听一个或多个数据源,当数据源发生改变时调用相关函数,返回一个停止函数。
<script setup>
import {watch} from 'vue'
// 第一个参数 sources 来源:1. 函数,返回一个值,2. ref,3. 响应式对象,4. 以上类型组成的数组
// 第二个参数 处理函数,当来源的值发生改变时就会执行,该函数有三个参数:新值、旧值、回调函数,监听多个来源时,新值和旧值就是数组
// 第三个参数 可选对象 属性:immediate、deep、flush、onTrack / onTrigger
// 与watchEffect区别:明确变化源,有变化之前的新值和旧值
import {watch, ref} from 'vue'
const sources = ref(10)
const options = {
immediate: true, // 监听立即执行
deep: false, // 如果监听源是对象,就会深层监听,监听每个属性
flush: 'post', // 'pre' | 'post' | 'sync' // 默认:'pre' 回调刷新机制
onTrack(e) {
console.log('onTrack', e)
debugger
},
onTrigger(e) {
console.log('onTrigger', e)
debugger
}
}
watch(sources, (newValue, oldValue) => {
console.log(newValue)
console.log(oldValue)
},)
</script>
响应式:工具
isRef()
检查某个值是否是ref。
<script setup>
import {ref, isRef} from 'vue'
const count = ref(10)
console.log(isRef(count)) // true
</script>
unref()
参数如果是ref,则返回ref的值,否则返回参数本身。
<script setup>
import {ref, unref} from 'vue'
const count = ref(10)
console.log(unref(count)) // 返回.value -> 10
const count = {num: 10}
console.log(unref(count)) // 返回原值:{num:10}
</script>
toRef()
可以将值格式化成ref,双向 ref,会与源属性同步。
<script setup>
import {reactive, toRef} from 'vue'
const state = reactive({
foo: 1
})
const fooRef = toRef(state, 'foo')
console.log(fooRef.value)
</script>
<script setup>
// defineProps 只能在 script setup模式下使用,无需导入,用来接受传入的props同时声明类型
const {ms, toRef} = defineProps({
ms: String
})
ms = 'props' // ms是常量不可改变的,解构的props是不响应的
</script>
<script setup>
import {toRef,defineProps} from "vue";
// defineProps 只能在 script setup模式下使用,无需导入,用来接受传入的props同时声明类型
const props = defineProps({
ms: String
})
const propsRef = toRef(props, 'ms')
propsRef.value = 'hello' // Set operation on key "ms" failed: target is readonly.
</script>
toValue
类似unref,三种参数类型:1. 正常值,2. ref,3. getter函数
<script setup>
import {ref,toValue} from "vue";
1. 正常值
const count = toValue(1) // 返回参数本身
2. ref
const count = toValue(ref(1)) // 返回参数ref的值
3. getter函数
const count = toValue(()=>1) // 返回getter函数的返回值
</script>
toRefs
把响应式对象转换为普通对象,普通对象的属性与源对象属性名保持一致并相关联,同时普通对象的属性是通过 toRef创建。
<script setup>
import {toRefs,reactive} from "vue";
// 如果参数是普通对象,该对象及属性都不是响应的,在模板中无法自动解包,必须.value取值
const state = toRefs({
name:'xiao mi'
})
const state1 = reactive({
name:'xiao mi' // 响应式
})
// 当toRefs的参数是响应式对象,返回一个普通对象,同时该对象的属性与源对象的属性相关联并且保持响应式
// state是响应式对象,返回的state1是普通对象
const state1 = toRefs(state)
// 与源对象属性相关联,源对象也会改变
state1.name.value = 'hua wei'
console.log(state.name)
// state1是普通对象,额外添加age不是响应式
state1.age = 18
// toRefs好处是返回的对象可以结构同时不失去响应式
</script>
isProxy
检查对象是否由 reactive、readonly、shallowReactive、shallowReadonly创建的代理,返回Boolean
<script setup>
import {isProxy,reactive} from "vue";
const _isProxy = isProxy(reactive({name:'xiao mi'}))
console.log(_isProxy) // true
</script>
isReactive
检查对象是否由 reacteve、shallowRacteve创建的代理,返回Boobelan
<script setup>
import {isReactive,reactive} from "vue";
const _isReactive = isReactive(reactive({name:'xiao mi'}))
console.log(_isReactive) // true
</script>
isReadonly
检查对象是否只读,通过readonly和shallowReadonly创建的代理是只读的,返回Boobelan
<script setup>
import {readonly,isReadonly} from "vue";
const _readonly = readonly({name:'xiao mi'})
console.log(isReadonly(_readonly)) // true
</script>
响应式:进阶
shallowRef
ref的浅层响应式,只会把数据的第一层 .value 进行响应式处理,不会深层递归转为响应式
<script setup>
import {shallwoRef} from "vue";
const state = shallowRef({ count: 1 })
</script>
trggerRef
当一个浅层ref引用的内部发生变更使用
customRef
自定义一个ref,声明其依赖跟踪和更新触发的方式