Vue3-选项式API
状态选项
| 功能 | 作用 | 类比打比方 | 
|---|---|---|
| data | 定义本地数据(组合式用 ref) | 自己家里的存折 | 
| props | 接收父组件传值 | 父母给的生活费 | 
| computed | 计算属性,自动依赖更新 | 动态工资单,工资随业绩变 | 
| methods | 定义函数逻辑(组合式直接写) | 工具箱里的工具 | 
| watch | 监听数据变化 | 监控摄像头,数据变动就提醒 | 
| emits | 定义并触发向父组件的事件 | 打电话通知家长 | 
| expose | 子组件主动暴露接口给父组件 | 对外开放的“公开功能按钮” | 
父组件
<template>
  <MyChild ref="childRef" msg="父传子数据" @customEvent="handleCustomEvent" />
  <button @click="callChild">父组件调用子组件方法</button>
</template>
<script setup>
import { ref } from 'vue'
import MyChild from './MyChild.vue'
const childRef = ref(null)
function handleCustomEvent(val) {
  console.log('收到子组件消息:', val)
}
function callChild() {
  childRef.value?.increment()
}
</script>
子组件
<template>
  <div>
    <h3>姓名:{{ fullName }}</h3>
    <p>年龄:{{ age }}</p>
    <button @click="increment">增长年龄</button>
    <p>父组件传来:{{ msg }}</p>
    <button @click="sendToParent">通知父组件</button>
  </div>
</template>
<script setup>
// 1. props:接收父组件传值
const props = defineProps({
  msg: {
    type: String,
    default: ''
  }
})
// 2. emits:定义向父组件触发的事件
const emit = defineEmits(['customEvent'])
// 3. data:组合式API下用ref模拟data
import { ref, computed, watch, expose } from 'vue'
const age = ref(18)
const firstName = ref('秋')
const lastName = ref('先生')
// 4. computed:计算属性
const fullName = computed(() => `${firstName.value}${lastName.value}`)
// 5. methods:组合式下直接写函数
function increment() {
  age.value++
}
function sendToParent() {
  emit('customEvent', '子组件的消息')
}
// 6. watch:监听响应式数据
watch(age, (newVal, oldVal) => {
  console.log(`年龄变化:${oldVal} → ${newVal}`)
})
// 7. expose:向父组件暴露子组件方法
expose({
  increment
})
</script>
渲染选项
| 名称 | 作用 | 类比打比方 | 
|---|---|---|
| template | 模板区域,编译为渲染函数 | HTML 布局图纸 | 
| render | 手写渲染函数,替代 template | 直接用 JavaScript 造 DOM 结构 | 
| compilerOptions | 控制模板编译规则 | 施工队的特殊工作说明书 | 
| slots | 插槽,父组件传递内容的机制 | 租房,房东给你预留的空白区域 | 
父组件
<template>
  <CustomCard>
    <template #header>
      <h3>我是头部插槽</h3>
    </template>
    <p>我是默认插槽内容</p>
    <template #footer>
      <small>底部信息</small>
    </template>
  </CustomCard>
</template>
<script setup>
import CustomCard from './CustomCard.vue'
</script>
子组件 CustomCard.vue(template 写法)
<template>
  <div class="card">
    <header>
      <slot name="header">默认头部</slot>
    </header>
    <main>
      <slot />
    </main>
    <footer>
      <slot name="footer">默认底部</slot>
    </footer>
  </div>
</template>
手写 render 写法(替代 template)
<script setup>
import { h, useSlots } from 'vue'
const slots = useSlots()
defineExpose({
  test() {
    console.log('暴露给父组件的函数')
  }
})
const render = () => {
  return h('div', { class: 'card' }, [
    h('header', slots.header ? slots.header() : '默认头部'),
    h('main', slots.default ? slots.default() : '默认内容'),
    h('footer', slots.footer ? slots.footer() : '默认底部')
  ])
}
</script>
compilerOptions
在 vue.config.js 或 vite.config.js 里:
export default {
  compilerOptions: {
    // 自定义模板标签识别
    isCustomElement: tag => tag.startsWith('x-')
  }
}
配置告诉 Vue:
遇到 x-button、x-panel这种标签,别当普通组件检查,直接原样保留,适合 Web 组件、跨框架场景。
生命周期选项
<template>
  <div>
    <h3>Vue3 生命周期演示</h3>
    <p>计数:{{ count }}</p>
    <button @click="count++">点击+1</button>
  </div>
</template>
<script>
export default {
  name: 'LifeCycleDemo',
  data() {
    return {
      count: 0
    }
  },
  // ------------------- 创建阶段 -------------------
  beforeCreate() {
    console.log('beforeCreate:数据、props、methods 都还没初始化')
  },
  created() {
    console.log('created:数据、props、methods 已经初始化完毕')
  },
  // ------------------- 挂载阶段 -------------------
  beforeMount() {
    console.log('beforeMount:模板已经编译,但还没渲染到页面')
  },
  mounted() {
    console.log('mounted:组件已挂载到 DOM,真实可见')
  },
  // ------------------- 更新阶段 -------------------
  beforeUpdate() {
    console.log('beforeUpdate:响应式数据变了,DOM 更新前')
  },
  updated() {
    console.log('updated:DOM 已经更新完毕')
  },
  // ------------------- 卸载阶段 -------------------
  beforeUnmount() {
    console.log('beforeUnmount:组件即将被卸载')
  },
  unmounted() {
    console.log('unmounted:组件已完全销毁')
  },
  // ------------------- 其他钩子 -------------------
  errorCaptured(err, vm, info) {
    console.log('errorCaptured:捕获到子组件错误', err, info)
    return false // 阻止错误继续向上传播
  },
  renderTracked(e) {
    console.log('renderTracked:追踪到依赖', e)
  },
  renderTriggered(e) {
    console.log('renderTriggered:响应式依赖变了导致重新渲染', e)
  },
  activated() {
    console.log('activated:<KeepAlive> 缓存组件激活')
  },
  deactivated() {
    console.log('deactivated:<KeepAlive> 缓存组件失活')
  },
  serverPrefetch() {
    console.log('serverPrefetch:SSR 场景数据预拉取')
  }
}
</script>
组合选项
//公共 mixin:mixin.js
export default {
  data() {
    return {
      mixinMsg: '我是 mixin 里的数据'
    }
  },
  methods: {
    mixinMethod() {
      console.log('mixinMethod 被调用')
    }
  }
}
//公共基类:BaseComponent.js
export default {
  data() {
    return {
      baseMsg: '我是 extends 继承的基类数据'
    }
  },
  methods: {
    baseMethod() {
      console.log('baseMethod 被调用')
    }
  }
}
//父组件:Parent.vue
<template>
  <div>
    <h3>父组件</h3>
    <p>父组件数据:{{ msg }}</p>
    <ChildComponent />
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
  components: { ChildComponent },
  data() {
    return {
      msg: '这是父组件传下来的数据'
    }
  },
  //向下传递数据(跨多层)
  provide() {
    return {
      sharedMsg: this.msg
    }
  }
}
</script>
//子组件:ChildComponent.vue
<template>
  <div>
    <h4>子组件</h4>
    <p>inject 的数据:{{ sharedMsg }}</p>
    <p>mixin 数据:{{ mixinMsg }}</p>
    <p>extends 数据:{{ baseMsg }}</p>
    <button @click="testAll">调用所有方法</button>
  </div>
</template>
<script>
import mixin from './mixin'
import BaseComponent from './BaseComponent'
export default {
  //临时混入多个通用功能、数据、方法
  mixins: [mixin],
  //继承基础组件的功能、数据、方法
  extends: BaseComponent,
  //获取祖先提供的数据
  inject: ['sharedMsg'],
  methods: {
    testAll() {
      //使用
      this.mixinMethod()
      this.baseMethod()
    }
  }
}
</script>

其他选项
//1. 子组件 MyInput.vue
<template>
  <div>
    <p>自定义输入框:</p>
    <input v-bind="attrs" v-my-focus />
  </div>
</template>
<script>
export default {
  name: 'MyInput', // 组件名,便于调试和递归调用
  inheritAttrs: false, // 阻止未声明的属性自动挂载到根节点,但是元素使用了v-bind="attrs"手动把这些属性绑定到 <input> 上
  directives: {
    // 自定义指令:自动获取焦点
    myFocus: {
      mounted(el) {
        el.focus()
      }
    }
  },
  computed: {
    // 通过 $attrs 手动控制透传属性
    attrs() {
      return this.$attrs
    }
  }
}
</script>
//2. 父组件 App.vue
<template>
  <div>
    <h3>父组件</h3>
    //父组件给 <MyInput> 传入 placeholder 和 class
    <MyInput placeholder="请输入内容" class="custom-class" />
  </div>
</template>
<script>
import MyInput from './MyInput.vue'
export default {
  name: 'App',
  components: {
    MyInput // 局部注册子组件
  }
}
</script>

组件实例
| 属性/方法 | 作用 | 类比打比方 | 
|---|---|---|
| this.$data | 组件内部所有响应式数据 | 自己家里的账本 | 
| this.$props | 父组件传递的所有 props | 父母给的生活费 | 
| this.$el | 组件根 DOM 元素 | 自己的房子门口 | 
| this.$options | 组件所有选项配置对象 | 出生档案,包含所有定义 | 
| this.$parent | 父组件实例 | 父母对象 | 
| this.$root | 根组件实例 | 整个 Vue 应用的大管家 | 
| this.$slots | 父组件传入的插槽内容 | 留给你的装修空间 | 
| this.$refs | 通过 ref获取的 DOM 或组件实例 | 门牌号查找的结果 | 
| this.$attrs | 未被 props 接收的属性集合 | 杂七杂八外部传来的信息 | 
| this.$watch() | 手动监听数据变化 | 监控摄像头 | 
| this.$emit() | 触发自定义事件,通知父组件 | 打电话告诉家长 | 
| this.$forceUpdate() | 强制刷新视图(不建议滥用) | 手动刷新屏幕 | 
| this.$nextTick() | DOM 更新完成后执行回调 | 等装修完再检查 | 
//父组件 App.vue
<template>
  <div>
    <h3>父组件</h3>
    <ChildComponent ref="childRef" msg="父传子数据">
      <template #default>
        <p>这是插槽内容</p>
      </template>
    </ChildComponent>
    <button @click="testChild">父组件调用子组件</button>
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
  components: { ChildComponent },
  methods: {
    testChild() {
      console.log('父组件 this.$refs.childRef:', this.$refs.childRef)
      this.$refs.childRef.testExposeMethod()
    }
  }
}
</script>
//子组件 ChildComponent.vue
<template>
  <div ref="box">
    <h4>子组件</h4>
    <p>msg:{{ msg }}</p>
    <slot />
    <button @click="updateData">更新数据</button>
  </div>
</template>
<script>
export default {
  name: 'ChildComponent',
  props: {
    msg: String
  },
  data() {
    return {
      count: 0
    }
  },
  mounted() {
    console.log('--- 组件实例属性演示 ---')
    console.log('$data:', this.$data)
    console.log('$props:', this.$props)
    console.log('$el:', this.$el)
    console.log('$options:', this.$options)
    console.log('$parent:', this.$parent)
    console.log('$root:', this.$root)
    console.log('$slots:', this.$slots)
    console.log('$refs:', this.$refs)
    console.log('$attrs:', this.$attrs)
    // $watch 示例
    this.$watch('count', (newVal, oldVal) => {
      console.log(`count 变化:${oldVal} -> ${newVal}`)
    })
    // $emit 示例
    this.$emit('customEvent', '子组件触发的事件')
    // $nextTick 示例
    this.count = 100
    this.$nextTick(() => {
      console.log('DOM 已经更新完毕')
    })
  },
  methods: {
    updateData() {
      this.count++
      this.$forceUpdate()
    },
    testExposeMethod() {
      console.log('子组件暴露的方法被父组件调用了')
    }
  }
}
</script>
    如果这篇文章对你有用,可以关注本人微信公众号获取更多ヽ(^ω^)ノ  ~
 
 


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