聊一聊 Vue3 基础知识

本文详细介绍了Vue3.0中的setup()函数作为应用入口的作用,以及响应式系统中的ref(),computed(),watchEffect(),watch()等核心概念,包括props和context的使用,TypeScript的支持,以及深度响应式与浅层响应式的区别。
摘要由CSDN通过智能技术生成

应用入口: 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,声明其依赖跟踪和更新触发的方式

  • 25
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值