"dev": "node scripts/dev.js --sourcemap"
复制代码
<div id="app">
<h1>{{message}}</h1>
<comp></comp>
</div>
<script src="../dist/vue.global.js"></script>
<script>
// 建立实例方式变化了
const {createApp} = Vue
const app = createApp({
data() {
return {
'message': 'hello, vue3'
}
},
})
// 之前全局方法,变成实例方法
app.component('comp', {
template: '<div>comp</div>'
})
app.mount('#app')
</script>
复制代码
Composition API字面意思是组合API,它是为了实现基于函数的逻辑复用机制而产生的。html
<div id="app">
<h1>{{message}}</h1>
<p @click="add">{{count}}</p>
<p>{{doubleCount}}</p>
<p>{{num}}</p>
</div>
<script src="../dist/vue.global.js"></script>
<script>
// 引入使用的函数方法
const {createApp, reactive, computed, ref, toRefs} = Vue
const app = createApp({
// setup在beforeCreated以前执行
setup() {
// reactivity api
// message相关
const data = reactive({
message: 'hello,vue3',
})
setTimeout(() => {
data.message = 'vue3,hello'
}, 1000);
// count相关
const counter = reactive({
count: 0,
doubleCount: computed(() => counter.count * 2)
})
function add() {
counter.count++
}
// 单值响应式, ref()返回Ref对象,若是要修改它的值,访问value属性
const num = ref(0)
setInterval(() => {
num.value++
}, 1000);
return {...toRefs(data), add, ...toRefs(counter), num}
}
})
app.mount('#app')
</script>
复制代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>composition api</title>
<script src="../dist/vue.global.js"></script>
</head>
<body>
<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>
</body>
</html>
复制代码
以上可见vue
// 1.对象响应化:遍历每一个key,定义getter、setter
// 2.数组响应化:覆盖数组原型方法,额外增长通知逻辑
const originalProto = Array.prototype
const arrayProto = Object.create(originalProto)
['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach(
method => {
arrayProto[method] = function() {
originalProto[method].apply(this, arguments)
notifyUpdate()
}
})
function observe(obj) {
if (typeof obj !== 'object' || obj == null) {
return
}
// 增长数组类型判断,如果数组则覆盖其原型
if (Array.isArray(obj)) {
Object.setPrototypeOf(obj, arrayProto)
} else {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
defineReactive(obj, key, obj[key])
}
}
}
function defineReactive(obj, key, val) {
observe(val) // 解决嵌套对象问题
Object.defineProperty(obj, key, {
get() {
return val
},
set(newVal) {
if (newVal !== val) {
observe(newVal) // 新值是对象的状况 val = newVal
notifyUpdate()
}
}
})
}
function notifyUpdate() {
console.log('页面更新!')
}
复制代码
vue2响应式弊端:node
// Proxy
// 提取帮助方法
const isObject = v => v !== null && typeof v === 'object'
function reactive(obj) {
// 判断是否对象
if (!isObject(obj)) {
return obj
}
return new Proxy(obj, {
get(target, key, receiver) {
const ret = Reflect.get(target, key, receiver)
console.log('获取', key);
track(target, key)
// 若是是对象须要递归
return isObject(ret) ? reactive(ret) : ret
},
set(target, key, value, receiver) {
const ret = Reflect.set(target, key, value, receiver)
console.log('设置', key);
trigger(target, key)
return ret
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key)
console.log('删除', key);
trigger(target, key)
return ret
},
})
}
复制代码
// 大概结构以下所示
// target | depsMap
// obj | key | Dep
// k1 | effect1,effect2...
// k2 | effect3,effect4...
// {target: {key: [effect1,...]}}
复制代码
// 保存当前活动响应函数做为getter和effect之间桥梁
const effectStack = []
// effect任务:执行fn并将其入栈
function effect(fn) {
const rxEffect = function () {
// 1.捕获异常
// 2.fn入栈出栈
// 3.执行fn
// 4.执行结束,出栈
try {
effectStack.push(rxEffect)
return fn()
} finally {
effectStack.pop()
}
}
// 默认执行一次响应函数
rxEffect()
// 返回响应函数
return rxEffect
}
// 响应函数触发某个响应式数据,开始作依赖收集(映射过程)
// {target: {key: [fn1,fn2]}}
const targetMap = new WeakMap()
function track(target, key) {
// 从栈中取出响应函数
const effect = effectStack[effectStack.length - 1]
if (effect) {
// 获取target对应依赖表
let depsMap = targetMap.get(target)
if (!depsMap) {
// 首次访问不存在,建立一个
depsMap = new Map()
targetMap.set(target, depsMap)
}
// 获取key对应的响应函数集
let deps = depsMap.get(key)
if (!deps) {
deps = new Set()
depsMap.set(key, deps)
}
// 将响应函数加入到对应集合
deps.add(effect)
}
}
// 触发target.key对应响应函数,根据映射关系执行对应cb
function trigger(target, key) {
// 获取依赖表
const depsMap = targetMap.get(target)
if (depsMap) {
// 获取响应函数集合
const deps = depsMap.get(key)
if (deps) {
// 执行全部响应函数
deps.forEach(effect => effect())
}
}
}
复制代码
// state就是Proxy实例
const state = reactive({foo: 'foo', bar: {a: 1}, arr: [1, 2, 3]})
// 测试代码
// state.foo
// state.foo = 'foooooo'
// // 设置不存在属性
// state.bar = 'bar'
// state.bar.a = 10
// state.arr.push(4)
// state.arr.pop()
effect(() => {
console.log('effect', state.foo);
})
state.foo = 'fooooooooo'
复制代码