Vue3初探
花了大量时间看完了Vue2的核心源码,是时候开始全面接触Vue3了。我们知道Vue3新增了很多特性,Composition Api
,Teleport
,Fragments
,Emits Component Option
,createRender
Api用于创建自定义渲染器。今天我们就来初步探讨一下Componsition Api
。
Composition API
Composition Api
字面意思是组合API,它是为实现基于函数的逻辑复用机制而产生的。为了开始使用Composition Api
首先需要一个可以实际使用它的地方—-setup
基本使用
<div id="app">
<h1>Composition API</h1>
<div>count: {{ state.count }}</div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const { createApp, reactive, computed } = Vue;
const App = {
beforeCreate() {
console.log('beforeCreate');
},
setup() {
console.log('setup');
const state = reactive({
count: 0,
doubleCount: computed(() => state.count * 2)
})
return { state }
},
created() {
console.log('created');
},
}
createApp(App).mount('#app')
</script>
复制代码
通过createApp
创建组件实例,setup
是一个新的组件选项,它是组件内使用Composition API
的入口。Vue
对外暴露了reactive
,这个属性是处理响应式的,接受一个对象,返回一个响应式的代理对象。最后返回的对象将和渲染函数上下文合并。很多文章说setup
的执行时间在beforeCreate
和created
之间,经过检验,setup
的执行时间实际是在beforeCreate
和created
之前。
计算属性
<div>doubleCount: {{state.doubleCount}}</div>
复制代码
const { computed } = Vue;
setup() {
const state = reactive({
count: 0,
doubleCount: computed(() => state.count * 2)
})
return {
state,
}
}
复制代码
给computed
函数传递一个参数,返回一个只读的响应式数据。
事件处理
<div @click="add">count: {{ state.count }}</div>
复制代码
const App = {
setup() {
// setup中声明⼀个add函数
function add() {
state.count++
}
// 传⼊渲染函数上下⽂
return { state, add }
}
}
复制代码
事件的定义和options api
还是有很大的区别的,只需要在setup中定义即可,并且需要return
侦听器:watch()
const { watch } = Vue;
const App = {
setup() {
// state.count变化cb会执⾏
watch(() => state.count, (val, oldval) => {
console.log('count变了:' + val);
})
}
}
复制代码
引用对象:单值响应化 ref
<div>counter: {{ counter }}</div>
复制代码
const { ref } = Vue;
const App = {
setup() {
// 返回响应式的Ref对象
const counter = ref(1)
setTimeout(() => {
// 要修改对象的value
counter.value++
}, 1000);
// 添加counter
return { state, add, counter }
}
}
复制代码
体现可维护性
<div>
<h1>逻辑组合</h1>
<div id="app"></div>
</div>
<script>
const { createApp, reactive, onMounted, onUnmounted, toRefs } = Vue;
// ⿏标位置侦听
function useMouse() {
// 数据响应化
const state = reactive({
x: 0,
y: 0
})
const update = e => {
state.x = e.pageX
state.y = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
// 转换所有key为响应式数据
return toRefs(state)
}
// 事件监测
function useTime() {
const state = reactive({
time: new Date()
})
onMounted(() => {
setInterval(() => {
state.time = new Date()
}, 1000)
})
return toRefs(state)
}
// 逻辑组合
const MyComp = {
template: `
<div>x: {{ x }} y: {{ y }}</div>
<p>time: {{time}}</p>
`,
setup() {
// 使⽤⿏标逻辑
const { x, y } = useMouse()
// 使⽤时间逻辑
const { time } = useTime()
// 返回使⽤
return { x, y, time }
}
}
createApp(MyComp).mount('#app')
</script>
复制代码
从上面的代码可以看出我们把useMouse
,useTime
函数根据我们需要的业务模块做出了抽离,最后组合到setup
中。如果使用options api
,则我们的代码量如果过多,业务逻辑变得特别复杂,我们的响应式数据还是需要在data
中定义,事件需要在methods
中声明,这样就会需要“上下横跳”的现象。此外,这种情况是的理解和维护变得更加的困难。而Composition Api
却能够很好的解决这个问题。
响应式
我们知道vue2
是通过Object.defineProperty
来实现对数据的拦截。但是这个方法是有缺点的。
- 不能监听数组的变化
- 必须遍历对象的每个属性
- 必须深层遍历嵌套的对象
- 新加或删除属性无法监听
vue2.0
对数组做了处理,重写了数组的7个方法。
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})
复制代码
Vue3.0
则使用了ES6
的Proxy
特性来很好的解决了这些问题。
function reactive(obj) {
if (typeof obj !== 'object' && obj != null) {
return obj
}
const observed = new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
console.log(`获取${key}:${res}`)
return res
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
console.log(`设置${key}:${value}`)
return res
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key)
console.log(`删除${key}:${res}`)
return res
}
})
return observed
}
const state = reactive({
foo: 'foo'
})
state.foo // yes
state.foo = 'fooooooo' // yes
state.dong = 'dong' // yes
delete state.dong // yes
复制代码
嵌套对象响应式
对于上面的处理方式嵌套的对象不能响应
const state = reactive({
bar: { a: 1 }
})
// 设置嵌套对象属性
state.bar.a = 10 // no
复制代码
通过递归的方式处理
function isObject(val) {
if(!val && typeof val === 'object') {
return true
}
}
function reactive(obj) {
if (typeof obj !== 'object' && obj != null) {
return obj
}
const observed = new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
console.log(`获取${key}:${res}`)
return isObject(res) ? reactive(res) : res
},
···
···
})
return observed
}
复制代码
以上是对Vue3.0
的一个初步了解,后续会对Vue3.0
进行全面的学习,目前还只是了解了皮毛,后面会对vue3.0
的源码进行解刨,也希望可以通过写文章的形式督促学习。
欢迎留言~~~