import { computed, provide, ref, watch, watchEffect } from 'vue'
/* 1. ref 创建简单类型 和 复杂类型 返回一个响应式的对象
推荐:以后声明数据,统一用 ref => 统一了编码规范
注意点:
- 脚本中访问数据,需要通过 .value
- 在template中,.value不需要加 (帮我们扒了一层)
*/
const count = ref(0)
const setCount = () => {
count.value++
}
/*
- 计算属性
*/
const list = ref([1, 2, 3, 4, 5, 6])
// 定义计算属性
const computedList = computed(() => {
return list.value.filter(item => item > 2)
})
/*
- watch 监听单个数据变化
*/
const x = ref(0)
const y = ref(0)
const obj = ref({ id: 0 })
// 1. 监听单个数据变化
watch(x, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 2. 监听对象属性值
// 注意: 不能直接侦听响应式对象的属性值, 需要用一个返回该属性的 getter 函数
watch(() => obj.value.id, (count) => {
console.log(count)
})
// 3. 监听多个数据变化
watch([x, () => y.value, () => obj.value.id], ([newArray, oldArray]) => {
console.log(newArray, oldArray)
})
// 4. 深层监听器
watch(
() => obj.value.count,
(newValue, oldValue) => {
// 注意:newValue 此处和 oldValue 是相等的
// 除非 state.someObject 被整个替换了
console.log(newValue, oldValue)
},
{ deep: true, immediate: true, once: true }
)
/*
- watchEffect
特点: - 自动追踪回调函数内所有响应式依赖(无需显式指定依赖)
- 会立即执行一次.
- 只能获取当前值(无旧值)
- watchEffect也是自动深度监听
注意: 内同步修改依赖可能引发无限循环(例:count.value++)
*/
watchEffect(() => {
// 自动跟踪回调的响应式依赖, 也可监听多个依赖.
console.log(obj.value.id)
})
/* watch 和 watchEffect 的区别
watch 需显式声明监听目标属性, 强调精确控制. 适用于需要对比新旧值, 限制触发范围或延迟执行的场景.
// 显式指定监听源
watch(() => obj.value.id, (newVal, oldVal) => { ... })
watchEffect 通过自动依赖追踪简化代码,专注副作用执行。适合依赖关系复杂或需立即执行的逻辑
// 自动收集所有依赖
watchEffect(() => {
fetchData(obj.value.id, config.value.lang) // 自动追踪 obj.value.id 和 config.value.lang
})
*/
/* 6. 生命周期函数
数据请求放在 setUp 中, Dom 操作放在 onMounted 中.
setup : 就是用来替代 vue2.0 中的 beforeCreate 和 created
注意: 无法操作 Dom
onMounted 主要用于:
- 需要操作的 DOM 元素的场景
- 获取元素尺寸,位置等信息的场景
- 第三方库的初始化(需要 DOM 已经存在)
不依赖 DOM 的请求 → 优先 setup 同步执行
依赖 DOM/第三方库 → 必须 onMounted
*/
/*
- 父传子
- 在父页面中,给子组件添加属性的方式传值
- 在子组件, 通过 props 接收数据
*/
import SonCom from '@/components/son-com.vue'
const money = ref(100)
// 页面应用
< SonCom car = "宝马车" : money = "money" ></>
/** SonCom 子组件页面
- 对于props传递过来的数据,模板(template)中可以直接使用
- 在 setUp 中需要借助于 “编译器宏” 函数接收子组件传递的数据, 例如 props.car
*/
const props = defineProps({
car: {
type: String,
default() {
return ''
}
},
money: {
type: Number,
default() {
return 0
}
}
})
/**
- 9.子传父
- 在父页面, 通过 @XX 事件监听
- 在子组件内部, emit触发事件(编译器获取)
*/
import SonCom from '@/components/son-com.vue'
// 给子组件,添加属性的方式传值
<SonCom @changeMoney="changeFn" ></SonCom >
// 触发事件接收数据
const changeFn = (newMoney) => {
money.value = newMoney
}
/** SonCom 子组件页面 */
const emit = defineEmits(['changeMoney'])
// 需要 emit 触发事件
const setAction = () => {
emit('changeMoney', 5)
}
/**
- 模版引用 ref
*/
// 3.5+ 之前, 定义ref: const txtRef = ref(null)
// 3.5+ 之后, 定义 ref: const txt = useTemplateRef('txtRef')
// 上面两者的作用都是一样的,只不过是写法不一样.
// 开发过程中主要用其两个作用:
// 1.通过 ref 来实现调用父页面的标签属性
// 2.通过 ref 来实现调用子组件的方法或属性, 由于使用了 <script setup> 的组件是默认私有的,需要在子组件中通过 defineExpose 宏显式暴露:
//示例1:
// 1. 调用 ref 函数得到 ref 对象
const inp = ref(null)
// 2. 通过 ref 标识绑定 ref 对象
< input ref = "inp" type = "text" >
onMounted(() => {
// console.log(inp.value)
// inp.value.focus()
})
// 示例 2
import { useTemplateRef, onMounted } from 'vue'
import { Model } from 'echarts'
// 第一个参数必须与模板中的 ref 值匹配
const input = useTemplateRef('my-input')
onMounted(() => {
input.value.focus()
})
< input ref = "my-input" />
/**
- defineExpose 宏显示暴漏
- 使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露:
*/
const a = 1
const b = ref(2)
// 像 defineExpose 这样的编译器宏不需要导入
defineExpose({
a,
b
})
/**
为什么在定义全局变量时,有的使用 ref 或 reactive, 有的可以直接定义 let chart = null
原因:
- 需要响应式 → ref 或 reactive 例如: const array = ref([])
- 不需要响应式 → let 或 const 例如: let chart = null
总结: 为什么 Vue 3 代码中大量使用 const 的原因,
例如:
const 防止意外重新赋值 array 变量, const定义的变量完全满足日常的操作需求;
const array = ref([]);
// ✅ 允许:修改 ref 内部的值
array.value.push(1);
array.value = [1, 2, 3];
// ❌ 不允许:重新赋值 ref 变量
array = ref([1, 2, 3]); // 报错
使用 const 和 let 定义变量的区别:
使用 const - 不能重新赋值
例如:
// 使用 let - 可以重新赋值
let chartInstance = null;
chartInstance = echarts.init(dom); // ✅ 允许
chartInstance = null; // ✅ 允许清空
// 使用 const - 不能重新赋值
const chartData = ref([]);
chartData.value = [1, 2, 3]; // ✅ 允许:修改值
chartData = ref([4, 5, 6]); // ❌ 报错:不能重新赋值
*/
/**
- provide 和 inject
- 顶层组件向任意的底层组件传递数据和方法,实现 [跨层] 组件通信
*/
// 顶层
provide('key', '顶层组件中的数据')
// 底层
const message = inject('key')
console.log(message) // '顶部组件中的数据'
/**
- Vue3.3 新特性-defineOptions
- 在 Vue 3.3 中新引入了 defineOptions 宏。顾名思义,主要是用来定义 Options API 的选项。可以用 defineOptions 定义任意的选项, props, emits, expose, slots 除外(因为这些可以使用 defineXXX 来做到)
- 用于在 <script setup> 中声明组件选项(如 name、inheritAttrs)属于静态配置,不参与运行时逻辑.
*/
defineOptions({
name: '名字',
inheritAttrs: false
})
/**
- Vue3.3新特性-defineModel
- 在Vue3中,自定义组件上使用v-model, 相当于传递一个modelValue属性, 同时触发 update:modelValue 事件
- 生效需要配置 vite.config.js
*/
/**
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
script: {
defineModel: true
}
}),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
*/
<Child v-model= "isVisible">
// 相当于
<Child :modeValue="isVisible" @update:modelValue= 'isVisible=$event'>
</script>
<style lang="scss" scoped></style>