文章目录
前言
转载:https://blog.csdn.net/weixin_47521346/article/details/109185232?spm=1001.2014.3001.5501
一、Vue2 与 Vue3 的差异
下面讲述Vue3破坏性变更的地方
1.全局API
全局api已迁移至 createApp()创建的实例下:
2.x全局API | 3.x实例API(app) |
---|---|
Vue.config.production | 已经删除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
1.use
const app = createApp(MyApp)
app.use(VueRouter)
2.component & directive
代码如下(示例):
const app = createApp(MyApp)
app.component('button-counter', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
app.directive('focus', {
mounted: el => el.focus()
})
app.mount('#app')
3.provide & inject
代码如下(示例):
// 在入口文件
app.provide('guide', 'Vue 3 Guide')
// 在子组件中
export default {
inject: {
book: {
from: 'guide'
}
},
template: `<div>{{ book }}</div>`
}
组件中使用 可参考组合式api中https://v3.cn.vuejs.org/guide/composition-api-provide-inject.html#%E4%BF%AE%E6%94%B9%E5%93%8D%E5%BA%94%E5%BC%8F-property
例如:有时我们需要在注入数据的组件内部更新 inject 的数据。在这种情况下,我们建议 provide 一个方法来负责改变响应式 property
代码如下(示例):
<!-- src/components/MyMap.vue -->
<template>
<MyMarker />
</template>
<script>
import { provide, reactive, ref } from 'vue'
import MyMarker from './MyMarker.vue'
export default {
components: {
MyMarker
},
setup() {
const location = ref('North Pole')
const geolocation = reactive({
longitude: 90,
latitude: 135
})
const updateLocation = () => {
location.value = 'South Pole'
}
provide('location', location)
provide('geolocation', geolocation)
provide('updateLocation', updateLocation)
}
}
</script>
<!-- src/components/MyMarker.vue -->
<script>
import { inject } from 'vue'
export default {
setup() {
const userLocation = inject('location', 'The Universe')
const userGeolocation = inject('geolocation')
const updateUserLocation = inject('updateLocation')
return {
userLocation,
userGeolocation,
updateUserLocation
}
}
}
</script>
后续更新 详情请看转载
二、Composition API
reactive
reactive 基本等价于2.x中的Vue.observable(),返回一个响应式对象,就像2.x中定义在data选项里的数据一样,最终都会被转换成响应式对象。基于 ES2015 的 Proxy 实现。
import { reactive } from 'vue'
// state 现在是一个响应式的状态
const state = reactive({
count: 0,
})
ref
接受一个参数值并返回一个响应式且可改变的 ref 对象。ref 对象拥有一个指向内部值的单一属性.value
const count = ref(0) // 相当于返回{value:0}
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
ref 适合基础类型值
reactive 适合对象类型的值
把变量全塞对象里直接用reactive,对象解构的时候,数据会丢失响应式特性
const pos = reactive({
x: 0,
y: 0,
})
function updatePosition(e) {
// 解构对象,导致响应式丢失,相当于重新将值赋给了一个变量,之后的更改不会改变原属性的值
let {x,y} = pos
x = e.pageX
y = e.pageY
}
正因为此,官方提供了toRefs与toRef的函数,来将一个响应式对象的基础类型属性值转换为ref对象
const state = reactive({
foo: 1,
bar: 2,
})
const fooRef = toRef(state, 'foo') // 转换单个的foo属性为ref对象
fooRef.value++
console.log(state.foo) // 2
state.foo++
console.log(fooRef.value) // 3
const state = reactive({
foo: 1,
bar: 2,
})
const stateAsRefs = toRefs(state) // 转换state对象的所有属性为ref对象
/*
stateAsRefs 的类型如下:
{
foo: Ref<number>,
bar: Ref<number>
}
*/
vue3中reacitve函数如何声明一个响应式数组,如以下案例
<template>
<div>
<div v-for="item in arr.list" :key="item">
{{item}}
</div>
<button @click="change">change</button>
</div>
</template>
<script>
import { defineComponent, reactive,ref } from 'vue';
export default defineComponent({
setup(props,context) {
let arr = reactive({
list:[]
})
function change(){
console.log("change...");
let newArr = [1,2,3]
arr.list = newArr
}
return{
arr,
change
}
},
});
</script>
<template>
<div>
名字:{{ name }}
</div>
<!-- toRefs 用于将一个 reactive 对象转化为属性全部为 ref 对象的普通对象 -->
</template>
<script lang="tsx" src="./script"></script>
<style lang="less" src="./index.less" scoped></style>
import { computed, defineComponent, reactive, ref, toRef, toRefs } from 'vue'
import { copyValueObject } from '@/utils/util.ts'
export default defineComponent({
setup(props, { emit }) {
let testObj = reactive({
name: '咳咳咳',
age: 56,
})
setTimeout(() => {
testObj = copyValueObject(testObj, { name: 'jajjaj', age: 50 })
}, 500)
return {
...toRefs(testObj),
}
},
})
utils.js
export const copyValueObject = (object: any, valueObject: any) => {
for (const key in object) {
if (
valueObject[key] ||
valueObject[key] === 0 ||
valueObject[key] === false
) {
object[key] = valueObject[key]
}
}
return object
}
watchEffect
预期接收一个含有副作用的函数,仅当该过程中用到的响应式状态发生改变时,会重新执行该函数。
import { reactive, watchEffect } from 'vue'
const state = reactive({
count: 0,
})
onMounted(()=>{
// 立即执行一次,之后会在state.count发生改变的时候执行,组件卸载的时候销毁
watchEffect(() => {
document.body.innerHTML = `count is ${state.count}`
})
})
watch和watchEffect比较
watchEffect和computed比较像,都是通过执行副作用函数获取要监听的数据源,而watch是通过第一参数获取要监听的数据源
watch的参数有3个(数据源,副作用函数,配置),watchEffect只有两个(副作用函数,配置)
watch的副作用函数接收的参数比watchEffect多两个(新旧值)
deep和immediate只对watch有用
① watch可以访问新值和旧值,watchEffect不能
② watchEffect有副作用,DOM挂载或者更新之前就会触发****通过 flush:post可以避免副作用,在DOM更新后运行副作用,确保模板引用与DOM保持同步,并引入正确的元素在执行数据请求时,副作用函数往往是一个异步函数
// 同步的方式
const stop = watchEffect(() => {
/* ... */
})
// 之后
stop()
// 如果是回调里有异步,可以用回调的参数onInvalidate去取消监听
const data = ref(0)
watchEffect((onInvalidate) => { // 立即执行,其后data改变,组件更新后执行
console.log(data.value)
const timer = setInterval(()=>{
data.value ++
},1000)
// 第一次初始化时候不执行该回调,仅注册回调,data改变时以及停止侦听时,会触发该回调
onInvalidate(() => {
// 取消定时器
clearInterval(timer)
})
})
// output: 0 1
onInvalidate 触发时机
副作用即将重新执行时(也就是追踪的依赖发生改变时)
侦听器被停止时(如果在 setup() 或 生命周期钩子函数中使用了 watchEffect, 则在卸载组件时)
三、高级响应式系统API
1.customRef
customRef 用于自定义一个 ref,可以显式地控制依赖追踪和触发响应。
<template>
<input v-model="text" />
</template>
<script>
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track() // 调用track收集依赖
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger() // 调用trigger,触发响应
}, delay)
},
}
})
}
export default {
setup() {
return {
text: useDebouncedRef('hello'),
}
},
}
</script>
markRaw
显式标记一个对象为“永远不会转为响应式代理”,函数返回这个对象本身。作用有点类似Object.freeze, 去除响应式。
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false
// 如果被 markRaw 标记了,即使在响应式对象中作属性,也依然不是响应式的
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false
shallowReactive
与reactive类似,唯一的区别就是只创建“浅代理”,嵌套对象不会变成响应式
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
})
// 变更 state 的自有属性是响应式的
state.foo++
// ...但不会深层代理
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
shallowReadonly
与readonly类似,唯一的区别就是只限制“浅只读”。嵌套对象仍然可以赋值
const state = shallowReadonly({
foo: 1,
nested: {
bar: 2,
},
})
// 变更 state 的自有属性会失败
state.foo++
// ...但是嵌套的对象是可以变更的
isReadonly(state.nested) // false
state.nested.bar++ // 嵌套属性依然可修改
shallowRef
与ref类似,唯一的区别只是“浅引用” ,只会追踪它的 .value 更改操作,但是如果赋值的是一个对象,则该对象不是可响应,并且后续的对象的属性更改均不会触发视图响应
const foo = shallowRef({})
foo.value.a = 1 // 这个a也不会响应到视图上去
isReactive(foo.value) // false
// 更改对操作会触发响应
foo.value = []
// 但上面新赋的这个对象并不会变为响应式对象,只是会同步这个值,视图上会同步显示[]
isReactive(foo.value) // false
const bar = shallowRef(0)
bar.value ++ // 1 , 这个是响应式的
toRaw
返回由 reactive 或 readonly 方法转换成响应式代理的普通对象。简单来说就是返回代理之前的原始对象。
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true