Vue3学习笔记
频繁隐藏显示元素使用 v-show
,因为 v-if
会频繁删除 dom
v-bind
简写是冒号 :
<img v-bind:src="imageUrl" v-bind:alt="imageDescription"><!-- 简写形式(推荐) -->
<img :src="imageUrl" :alt="imageDescription">
setup
vue3
的 setup
中不能使用 this
vue2
语法可以和 vue3
语法一起使用
vue2
的语法,比如 data
可以从 vue3
语法比如 setup
读取数据,反之不行
![[Pasted image 20250803102508.png]]
vue3
的组件里可能有两个 script
标签,一个用来配置组件名,一个用来配置 setup
<script setup>
语法糖, 更简洁, 不需要 export default
,也不需要 return
返回
<script lang="ts" setup>
// 组件逻辑写在这里
</script>
ref
vue2
中,数据写在 data
中就是响应式的
ref
:可以将数据变成响应式的, 处理基本类型(字符串、数字、布尔值等), 也可以处理对象类型,需要使用 .value
修改值
模板(template
)里数据不用 .value
, 但是 script
中要
<script setup>
import { ref } from 'vue'// 基本类型
const count = ref(0)
console.log(count.value) // 正确// 对象类型
const user = ref({ name: '张三' })
console.log(user.value.name) // 正确// 函数中操作
const increment = () => {count.value++ // 必须用 .value,否则无法触发响应式更新
}
</script>
reactive
reactive
:处理对象类型 ,使其变成响应式的
1.如果需要一个基本类型的响应式数据,必须使用 ref
2.若需要一个响应式对象,层级不深,ref
、reactive
都可以; 层级较深的话,推荐使用 reactive
3.如果直接给 reactive
创建的变量赋新对象, 会失去响应式, 可以使用 Object.assign
<script>
import { reactive } from 'vue'
// state 是代理对象,依附于 { name: 'Vue' }
const state = reactive({ name: 'Vue' }) // 错误:直接赋值新对象,响应式丢失!
state = { name: 'Vue3' } ❌ // 此时 state 变成普通对象,修改不会触发视图更新
</script>
<script setup>
import { reactive } from 'vue'
const state = reactive({ name: 'Vue' }) // 正确
Object.assign(state, { name: 'Vue3' }) </script>
toRefs
作用: 让 reactive
对象的属性被解构使用后不丢失响应式
<script setup>
import { reactive } from 'vue'const state = reactive({name: 'Vue',age: 3
})// ❌ 直接解构:name 和 age 变成普通变量,修改不触发更新
const { name, age } = state// ✅ 用 toRefs 解构 ,正确!
const { name, age } = toRefs(state)const update = () => {name = 'Vue3'age = 4
}</script>
响应式对象: Vue 2 用 Object.defineProperty
实现,Vue 3 用 Proxy
实现
计算属性
计算属性(computed) : 当它的响应式依赖发生变化时,才会重新计算并返回值。如果依赖没有变化,会直接返回缓存的结果, 可以优化性能
<template><div><p>姓:{{ firstName }}</p><p>名:{{ lastName }}</p><p>全名:{{ fullName }}</p></div>
</template><script>
export default {data() {return {firstName: '张',lastName: '三'};},computed: {// 计算属性定义fullName() {return this.firstName + this.lastName;}}
};
</script>
fullName
是一个计算属性,它依赖于 firstName
和 lastName
这两个响应式数据。只有当 firstName
或 lastName
发生变化时,fullName
才会重新计算
计算属性只定义 getter
,没有定义 setter
时是只读的,
<script>
const fullName = computed(() => {return firstName.value + lastName.value;
});const trySetFullName = () => {// 赋值不生效fullName.value = '李四王五'; console.log(fullName.value);
};
</script>
watch
Vue3 中的 watch
只能监视以下四种数据:
- ref 定义的数据
- reactive 定义的数据
- 函数返回一个值
- 一个包含上述内容的数组
watch
调用后会返回一个函数(这里用 stopWatch
接收 ),调用这个函数可以停止监听:
watch
用于监听数据变化
接收两个参数:
newValue
:数据变化后的新值oldValue
:数据变化前的旧值
import { ref, watch } from 'vue';setup() {const count = ref(0);watch(count, (newVal, oldVal) => {console.log(`count 从 ${oldVal} 变成了 ${newVal}`);});return { count };
}
Vue 默认不会深度监听对象内部属性的变化,需要通过 deep: true
开启深度监听
// 监听 ref 定义的对象类型数据(person 是 ref 包裹的对象)
watch(person, (newValue, oldValue) => {// 数据变化时,打印新旧值console.log('person变化了', newValue, oldValue)
}, { deep: true }) // 第三个参数:配置对象,开启深度监听
开启后,不仅监听 “对象整体变换”,还监听所有属性的变化
替换了 person
所引用的整个对象, 可能导致响应式监听失效:
function changePerson() {person.value = { name: '李四', age: 90 }
}
在原对象的基础上修改属性值,而不是替换整个对象
function changePerson() {Object.assign(person, { name: '李四', age: 80 })
}
监听的属性不是对象时, 必须写成函数形式, 如果是对象可以直接写,但还是建议写成函数形式
let person = reactive({name: '张三', // name 不是对象age: 18,car: { c1: '奔驰', c2: '宝马' }
})// 必须写成函数形式 :() => person.name
watch(() => person.name, (newValue, oldValue) => {console.log('person.name变化了', newValue, oldValue)
})
watchEffect
watchEffect
: 不用手动指定监听谁,函数里用了啥响应式数据就监听啥
用 watch
的写法:
import { ref, watch } from 'vue'const count = ref(0)// 必须明确指定监听 count
watch(count, (newValue, oldValue) => {console.log('count 变化了', '新值:', newValue, '旧值:', oldValue)
})
用 watchEffect
的写法:
import { ref, watchEffect } from 'vue'const count = ref(0)// 无需明确指定监听谁,函数里用到 count,就自动监听 count
watchEffect(() => {console.log('count 变化了,新值:', count.value)
})
ref用于标签上
<template><!-- 绑定到 DOM 元素 --><input ref="inputRef" type="text"><!-- 绑定到组件 --><ChildComponent ref="childRef" />
</template><script setup>
import { ref, onMounted } from 'vue'
import ChildComponent from './ChildComponent.vue'// 创建与模板中 ref 同名的引用变量
const inputRef = ref(null)
const childRef = ref(null)onMounted(() => {// 访问 DOM 元素inputRef.value.focus() // 调用 input 元素的 focus 方法// 访问子组件实例console.log(childRef.value) // 输出 ChildComponent 实例// 如果子组件暴露了方法,也可以直接调用// childRef.value.someMethod()
})
</script>
通信
父组件使用 defineProps
接收
<template><Child msg="Hello Vue 3" :count="100" :user="{ name: '张三', age: 20 }"requiredProp="这是必传值":customProp="5"/>
</template><script setup>
const props = defineProps({msg: String,count: Number,user: {type: Object,default: () => ({ name: '默认名称', age: 0 })},requiredProp: {type: String,required: true},customProp: {type: Number,validator: (value) => {// 值必须大于 0return value > 0}}
})</script>
路由
根据 URL 路径,渲染对应的组件,无需重新加载整个页面
同源: 两个页面的协议,域名和端口都相同
同源指的是两个 URL 的协议、域名、端口一致,反之,则是跨域
出现跨域的原因:浏览器的同源策略不允许非同源的 URL 之间进行资源交互
网页:http://www.test.com/index.html
接口:http://www.api.com/userlist
组件封装: 可以把组件封装起来,重复使用
被封装的组件称为子组件,引用子组件的称为父组件, 可以在子组件中定义函数,然后从父组件传入函数