Vue3-组合式API
官网
全局实例
app.provide、inject 和 runWithContext
import { inject } from 'vue'
app.provide('id', 1)
const injected = app.runWithContext(() => {
  return inject('id')
})
console.log(injected) // 1
•provide/inject 底层基于组件树的原型链传递数据;
•app.provide 提供了全局级别的依赖注入;
•runWithContext 是为了在非组件函数里“临时模拟” Vue 上下文环境,让 inject 正常工作;

app.config
app.config 是 Vue 应用实例上的全局配置对象。
✅ 整个应用下的所有组件、指令、插件都会共享这些配置;
✅ 注意组件内不能直接通过 this.config 访问,它属于应用实例级别;
✅ 正确用法是通过全局属性或者上下文间接获取。
app.config.globalProperties
//app.config定义
const app = createApp(App)
//通过 globalProperties 暴露给组件,全局属性命名推荐$开头
app.config.globalProperties.$apiBaseUrl = 'https://api.example.com'
//使用全局配置
//方法1: this.$xxx
console.log(this.$apiBaseUrl) // 在 options API 里
//方法2: proxy.$xxx
import { getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
console.log(proxy.$apiBaseUrl) // 在 setup 里
app.config.errorHandler 和 app.config.warnHandler

错误捕获:errorHandler
const app = createApp(App)
app.config.errorHandler = (err, instance, info) => {
  console.error('全局错误:', err)
  console.log('出错组件实例:', instance)
  console.log('错误信息:', info)
}
触发场景
<template>
  <button @click="throwError">报错</button>
</template>
<script setup>
function throwError() {
  throw new Error('按钮被点击,抛出异常!')
}
</script>
//全局错误:Error: 按钮被点击,抛出异常!
//出错组件实例: Proxy({...})
//错误信息:click event
警告捕获:warnHandler
app.config.warnHandler = (msg, instance, trace) => {
  console.warn('Vue 警告:', msg)
  console.log('出警组件实例:', instance)
  console.log('组件追踪信息:', trace)
}
//因为 notDefinedVar 未定义,Vue 会警告,warnHandler 捕获到。
<template>
  <div>{{ notDefinedVar }}</div>
</template>
app.config.performance性能追踪
开启或关闭性能追踪信息输出,方便开发者用浏览器性能面板分析 Vue 组件的渲染、更新时间。
const app = createApp(App)
app.config.performance = true  // 开启性能追踪
app.mount('#app')
app.config.compilerOptions编译配置
配置运行时编译器
const app = createApp(App)
//告诉 Vue:my- 开头的标签是外部自定义元素,Vue 不解析、不报错;
app.config.compilerOptions = {
  isCustomElement: tag => tag.startsWith('my-')
}
//是否移除模板中的 HTML 注释,true保留
app.config.compilerOptions.comments = true
app.config.optionMergeStrategies属性合并
自定义 Vue 组件选项(比如 data、methods、props、自定义配置项)在继承、混入、全局配置、组件扩展时的合并逻辑。
const app = createApp(App)
app.config.optionMergeStrategies.myOption = (parent, child) => {
  return (parent || '') + ' | ' + (child || '')
}
const mixin = {
  myOption: '全局的'
}
const comp = {
  mixins: [mixin],
  myOption: '局部的'
}
console.log(comp.myOption)  // 输出:'全局的 | 局部的'
app.config.idPrefix自动生成ID前缀
const app = createApp(App)
app.config.idPrefix = 'my-app-'
<div id="my-app-0"></div>
<div id="my-app-1"></div>
app.config.throwUnhandledErrorInProduction强制在生产模式下抛出未处理的错误
const app = createApp(App)
app.config.throwUnhandledErrorInProduction = true
setup()
setup() 是组件的初始化入口函数,在组件实例创建之前执行,用来声明响应式数据、函数、计算属性、生命周期等逻辑。
✅ 组合式 API 的入口;
✅ 定义响应式数据(ref、reactive);
✅ 定义方法、函数;
✅ 定义计算属性(computed);
✅ 使用生命周期钩子(onMounted、onUnmounted 等);
✅ 执行依赖注入(inject、provide);
✅ 最终返回的内容,模板和组件其他部分可以直接用。
<script>
<template>
  <div>
    <p>计数:{{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
  count.value++
}
</script>
这里 setup() 被 <script setup>语法糖隐藏了,本质上等同于:
export default {
  setup() {
    const count = ref(0)
    function increment() {
      count.value++
    }
    return { count, increment }
  }
}
setup() 返回值
•返回的对象,属性和方法都会暴露给模板和其他逻辑;
•不返回或返回空对象,模板里啥都用不了;
•<script setup> 语法糖会自动把定义的变量暴露出去。
注意事项
✅ setup() 里没有 this,组件实例还没完全生成;
✅ 可以使用 ref、reactive、computed、生命周期函数等组合式 API;
✅ 适合逻辑复用、代码组织更清晰的场景;
✅ 搭配<script setup> 语法糖,写法更简洁。
setup()访问 Props
setup 函数的第一个参数是组件的 props; 在 setup(props) 的参数里,Vue 自动把父组件传进来的 props 注入进来。
父组件:
<template>
  <Child title="你好秋先生" />
</template>
<script setup>
import Child from './Child.vue'
</script>
子组件:
<script setup>
defineProps({
  title: String
})
const props = defineProps()
console.log(props.title)  // 访问 props
</script>
或者不写 <script setup>,标准 setup() 写法:
export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}
📦 script setup 语法糖总结
•defineProps() 代替了传统 props 定义;
•直接调用 defineProps() 拿到 props 对象;
•模板里直接用,不用返回。
⚡ 注意细节
✅ props 是响应式的,但它是只读的;
✅ 可以监听 props 变化,但不能直接改:
✅ 要想基于 props 派生出可变的数据.
props.title = '新标题'  // ❌ 报错,props 是只读的
// 派生出可变的数据
const newTitle = ref(props.title)
//监听
watch(() => props.title, (newVal) => {
  console.log('父组件传入的 title 变了:', newVal)
})
setup 上下文(Context)
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)
  }
}

setup暴露公共属性
export default {
  setup(props, { expose }) {
    // 让组件实例处于 “关闭状态”
    // 即不向父组件暴露任何东西
    expose()
    const publicCount = ref(0)
    const privateCount = ref(0)
    // 有选择地暴露局部状态
    expose({ count: publicCount })
  }
}
<script setup>语法糖使用defineExpose函数
<script setup>
import { ref, defineExpose } from 'vue'
// 内部状态
const publicCount = ref(0)
const privateCount = ref(0)
// 只暴露 publicCount,privateCount 完全私有
defineExpose({
  count: publicCount
})
</script>
setup函数渲染
子组件:
import { h, ref } from 'vue'
export default {
  setup(props, { expose }) {
    const count = ref(0)
    const increment = () => ++count.value
    expose({
      increment
    })
    return () => h('div', count.value)
  }
}
父组件:
<Child ref="childRef" />
<button @click="childRef.value.increment()">加一</button>
h('div', count.value) 实际等同于:
<template>
  <div>{{ count }}</div>
</template>
只不过是用 JS 的方式写模板结构。
响应式 API:核心
<template>
  <div>
    <h2>ref: {{ count }}</h2>
    <h2>computed: {{ doubleCount }}</h2>
    <h2>reactive: {{ state.name }} - {{ state.age }}</h2>
    <h2>readonly: {{ readonlyState.name }}</h2>
    <button @click="increment">增加 count</button>
    <button @click="changeName">改名</button>
  </div>
</template>
<script setup>
import { ref, computed, reactive, readonly, watchEffect, watchPostEffect, watchSyncEffect, watch, onMounted, onUnmounted } from 'vue'
/** 1. ref:基础响应式变量 */
const count = ref(0)
/** 2. computed:计算属性 */
const doubleCount = computed(() => count.value * 2)
/** 3. reactive:响应式对象 */
const state = reactive({
  name: '秋先生',
  age: 25
})
/** 4. readonly:只读包装,无法修改 */
const readonlyState = readonly(state)
/** 5. watchEffect:立即执行,依赖变化自动重新执行 */
watchEffect(() => {
  console.log('watchEffect 监听 count:', count.value)
})
/** 6. watchPostEffect:DOM 更新后执行 */
watchPostEffect(() => {
  console.log('watchPostEffect DOM 更新完,count:', count.value)
})
/** 7. watchSyncEffect:同步执行,DOM 更新前触发 */
watchSyncEffect(() => {
  console.log('watchSyncEffect 同步监听 count:', count.value)
})
/** 8. watch:精确监听指定数据 */
watch(() => state.name, (newVal, oldVal, onCleanup) => {
  console.log(`state.name 变化:${oldVal} -> ${newVal}`)
  /** 9. onWatcherCleanup:清理逻辑示例 */
  onCleanup(() => {
    console.log('上一次监听清理了')
  })
})
/** 按钮操作 */
function increment() {
  count.value++
}
function changeName() {
  state.name = '秋老师'
}
</script>
✅ ref 适合基础类型响应式;
✅ computed 计算属性,自动依赖追踪;
✅ reactive 响应式对象,直接操作;
✅ readonly 包装只读对象,防止误改;
✅ watchEffect 自动依赖追踪,立即执行;
✅ watchPostEffect 视图更新后执行;
✅ watchSyncEffect 同步立即执行;
✅ watch 精确监听 + onCleanup 释放资源。
响应式: 工具
<script setup>
import { ref, reactive, isRef, unref, toRef, toRefs, isProxy, isReactive, isReadonly, readonly, toValue } from 'vue'
// 1. ref 创建响应式基本数据
const count = ref(10)
console.log('count是ref吗?', isRef(count))  // true
// 2. reactive 创建响应式对象
const state = reactive({
  name: '秋先生',
  age: 30
})
console.log('state是Proxy吗?', isProxy(state))       // true
console.log('state是reactive吗?', isReactive(state)) // true
// 3. readonly 创建只读对象
const readOnlyState = readonly(state)
console.log('readOnlyState是只读吗?', isReadonly(readOnlyState)) // true
// 4. unref 取出ref的值,若不是ref直接返回原值
console.log('unref(count):', unref(count)) // 10
console.log('unref(100):', unref(100))     // 100
// 5. toRef 单独把对象里的属性“转ref”
const nameRef = toRef(state, 'name')
console.log('nameRef是ref吗?', isRef(nameRef)) // true
// 修改 nameRef,相当于改 state.name
nameRef.value = 'Vue大师'
console.log('state.name:', state.name)  // Vue大师
// 6. toRefs 把整个对象每个属性都转成ref
const stateRefs = toRefs(state)
console.log('stateRefs:', stateRefs)
/*
{
  name: Ref<...>,
  age: Ref<...>
}
*/
stateRefs.age.value = 99
console.log('state.age:', state.age)  // 99
// 7. toValue 取出ref值,非ref原样返回,类似 unref
console.log('toValue(count):', toValue(count)) // 10
console.log('toValue("abc"):', toValue("abc")) // abc
</script>
响应式 API:进阶
<script setup>
import { 
  ref, shallowRef, triggerRef, customRef, 
  shallowReactive, shallowReadonly, toRaw, markRaw, 
  effectScope, getCurrentScope, onScopeDispose 
} from 'vue'
// -------------------- 1. shallowRef --------------------
const shallow = shallowRef({ name: '秋先生' })
console.log('shallowRef:', shallow.value)
// 改属性不触发视图更新(浅层)
shallow.value.name = 'Vue大师'
// 必须整个对象替换,才触发更新
shallow.value = { name: '新名字' }
// -------------------- 2. triggerRef --------------------
const manual = shallowRef(0)
function forceUpdate() {
  manual.value++  // 或手动触发
  triggerRef(manual)
}
console.log('手动触发 shallowRef 更新:', manual.value)
// -------------------- 3. customRef --------------------
const debounced = customRef((track, trigger) => {
  let value = ''
  let timer
  return {
    get() {
      track() // 依赖追踪
      return value
    },
    set(newVal) {
      clearTimeout(timer)
      timer = setTimeout(() => {
        value = newVal
        trigger() // 手动通知变化
      }, 500)
    }
  }
})
debounced.value = '输入防抖示例'
console.log('debounced:', debounced.value)
// -------------------- 4. shallowReactive --------------------
const shallowObj = shallowReactive({ deep: { count: 0 } })
shallowObj.deep.count++  // 不追踪内部对象
// -------------------- 5. shallowReadonly --------------------
const readonlyObj = shallowReadonly({ msg: '你好' })
// readonlyObj.msg = '修改' // 报错
readonlyObj.msg.newProp = '不追踪内部'
// -------------------- 6. toRaw --------------------
const reactiveObj = shallowReactive({ foo: 1 })
const raw = toRaw(reactiveObj)
console.log('原始对象:', raw)
// -------------------- 7. markRaw --------------------
const pure = markRaw({ secret: '不参与响应式' })
const obj = shallowReactive({ pure })
console.log('pure是否响应式:', toRaw(obj.pure) === obj.pure)  // true
// -------------------- 8. effectScope --------------------
const scope = effectScope()
scope.run(() => {
  const count = ref(0)
  console.log('effectScope内部count:', count.value)
})
scope.stop()  // 统一停止作用域内副作用
// -------------------- 9. getCurrentScope & onScopeDispose --------------------
effectScope().run(() => {
  if (getCurrentScope()) {
    onScopeDispose(() => {
      console.log('作用域销毁,清理资源')
    })
  }
})
</script>

生命周期钩子
挂前 onBeforeMount,挂后 onMounted;
更新前 onBeforeUpdate,更新后 onUpdated;
卸载前 onBeforeUnmount,卸载后 onUnmounted;
错误捕获 onErrorCaptured,渲染追踪 onRenderTracked;
渲染触发 onRenderTriggered,激活休眠 onActivated、onDeactivated;

依赖注入
<template>
  <div>
    <h1>👑 祖先组件</h1>
    <ParentComponent />
  </div>
</template>
<script setup>
import { provide } from 'vue'
import ParentComponent from './ParentComponent.vue'
// provide:向下传递数据
provide('msg', '这是从祖先传来的数据')
provide('count', 520)
</script>
<!-- ParentComponent.vue -->
<template>
  <div style="padding-left: 20px;">
    <h2>🧑 父组件</h2>
    <ChildComponent />
  </div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
</script>
<!-- ChildComponent.vue -->
<template>
  <div style="padding-left: 40px;">
    <h3>👶 子组件</h3>
    <p>收到的 msg:{{ msg }}</p>
    <p>收到的 count:{{ count }}</p>
    <p v-if="!hasContext">⚠️ 不在有效注入上下文中</p>
  </div>
</template>
<script setup>
import { inject, hasInjectionContext } from 'vue'
// 判断是否有注入上下文
const hasContext = hasInjectionContext()
// inject:获取祖先提供的数据
const msg = inject('msg', '默认值')
const count = inject('count', 0)
</script>

辅助函数
<template>
  <div v-bind="attrs">
    <slot></slot>
    <input :id="id" v-model="modelValue" />
    <div ref="el">我是 DOM 元素</div>
  </div>
</template>
<script setup>
import { useAttrs, useSlots, useModel, useTemplateRef, useId } from 'vue'
// 获取未被 props 接收的其他属性
const attrs = useAttrs()
// 获取<template>标签里的插槽内容(具体的内容由父组件传递给子组件)
const slots = useSlots()
console.log('默认插槽是否存在:', !!slots.default)
// 组合式的 v-model
const modelValue = useModel()
// 获取模板里 ref="xxx" 标记的 DOM 或组件
const el = useTemplateRef('el')
// 生成唯一 ID
const id = useId()
</script>
 


 
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号