±à¼ÍƼö: |
±¾ÎÄÖØµã½²½âÁËÒ»¸ö»ù±¾µÄÀý×Ó£¬Í¨¹ý³õʼ»¯½×¶Î¡¢ÒÀÀµÊÕ¼¯½×¶Î¡¢ÏìÓ¦½×¶ÎÕ⼸¸ö·½ÃæÀ´½éÉÜÁË¡£
±¾ÎÄÀ´×ÔÓÚÍøÂç,ÓÉ»ðÁú¹ûÈí¼þAnna±à¼ÍƼö¡£ |
|
ÕýÎÄ
Ëæ×Å Vue 3.0 Pre Alpha °æ±¾µÄ¹«²¼£¬ÎÒÃǵÃÒÔÒ»¿úÆäÔ´ÂëµÄʵÏÖ¡£Vue ×îÇÉÃîµÄÌØÐÔÖ®Ò»ÊÇÆäÏìӦʽϵͳ£¬¶øÎÒÃÇÒ²Äܹ»ÔÚ²Ö¿âµÄ
packages/reactivity Ä£¿éÏÂÕÒµ½¶ÔÓ¦µÄʵÏÖ¡£
ËäȻԴÂëµÄ´úÂëÁ¿²»¶à£¬ÍøÉϵķÖÎöÎÄÕÂÒ²ÓÐÒ»¶Ñ£¬µ«ÊÇÒªÏëÇåÎúµØÀí½âÏìӦʽÔÀíµÄ¾ßÌåʵÏÖ¹ý³Ì£¬»¹ÊÇͦ·ÑÄÔ½îµÄÊÂÇé¡£¾¹ýÒ»ÌìµÄÑо¿ºÍÕûÀí£¬ÎÒ°ÑÆäÏìӦʽϵͳµÄÔÀí×ܽá³ÉÁËÒ»ÕÅͼ£¬¶ø±¾ÎÄÒ²½«Î§ÈÆÕâÕÅͼȥ½²Êö¾ßÌåµÄʵÏÖ¹ý³Ì¡£

ÎÄÕÂÉæ¼°µ½µÄ´úÂëÎÒÒ²ÒѾÉÏ´«µ½²Ö¿â£¬½áºÏ´úÂëÔĶÁ±¾ÎÄ»á¸üΪÁ÷³©Å¶£¡
Ò»¸ö»ù±¾µÄÀý×Ó
Vue 3.0 µÄÏìӦʽϵͳÊǶÀÁ¢µÄÄ£¿é£¬¿ÉÒÔÍêÈ«ÍÑÀë Vue ¶øÊ¹Óã¬ËùÒÔÎÒÃÇÔÚ clone ÁËÔ´ÂëÏÂÀ´ÒԺ󣬿ÉÒÔÖ±½ÓÔÚ
packages/reactivity Ä£¿éϵ÷ÊÔ¡£
1.ÔÚÏîÄ¿¸ùĿ¼ÔËÐÐ yarn dev reactivity£¬È»ºó½øÈë
packages/reactivity Ŀ¼ÕÒµ½²ú³öµÄ dist/reactivity.global.js
Îļþ¡£
2.н¨Ò»¸ö index.html£¬Ð´ÈëÈçÏ´úÂ룺
<script src="./dist/reactivity.global.js"></script>
<script>
const { reactive, effect } = VueObserver
const origin = {
count: 0
}
const state = reactive(origin)
const fn = () => {
const count = state.count
console.log(`set count to ${count}`)
}
effect(fn)
</script> |
1. ÔÚä¯ÀÀÆ÷´ò¿ª¸ÃÎļþ£¬ÓÚ¿ØÖÆÌ¨Ö´ÐÐ state.count++£¬±ã¿É¿´µ½Êä³ö
set count to 1¡£
ÔÚÉÏÊöµÄÀý×ÓÖУ¬ÎÒÃÇʹÓà reactive() º¯Êý°Ñ origin ¶ÔÏóת»¯³ÉÁË Proxy ¶ÔÏó
state£»Ê¹Óà effect() º¯Êý°Ñ fn() ×÷ΪÏìӦʽ»Øµ÷¡£µ± state.count ·¢Éú±ä»¯Ê±£¬±ã´¥·¢ÁË
fn()¡£½ÓÏÂÀ´ÎÒÃǽ«ÒÔÕâ¸öÀý×Ó½áºÏÉÏÎĵÄÁ÷³Ìͼ£¬À´½²½âÕâÌ×ÏìӦʽϵͳÊÇÔõôÔËÐеġ£
³õʼ»¯½×¶Î
ÔÚ³õʼ»¯½×¶Î£¬Ö÷Òª×öÁËÁ½¼þÊ¡£

°Ñ origin ¶ÔÏóת»¯³ÉÏìӦʽµÄ Proxy ¶ÔÏó state¡£
°Ñº¯Êý fn() ×÷Ϊһ¸öÏìӦʽµÄ effect º¯Êý¡£
Ê×ÏÈÎÒÃÇÀ´·ÖÎöµÚÒ»¼þÊ¡£
´ó¼Ò¶¼ÖªµÀ£¬Vue 3.0 ʹÓÃÁË Proxy À´´úÌæÖ®Ç°µÄ Object.defineProperty()£¬¸ÄдÁ˶ÔÏóµÄ
getter/setter£¬Íê³ÉÒÀÀµÊÕ¼¯ºÍÏìÓ¦´¥·¢¡£µ«ÊÇÔÚÕâÒ»½×¶ÎÖУ¬ÎÒÃÇÔÝʱÏȲ»¹ÜËüÊÇÈçºÎ¸Äд¶ÔÏóµÄ
getter/setter µÄ£¬Õâ¸öÔÚºóÐøµÄ¡±ÒÀÀµÊÕ¼¯½×¶Î¡°»áÏêϸ˵Ã÷¡£ÎªÁ˼òµ¥Æð¼û£¬ÎÒÃÇ¿ÉÒÔ°ÑÕⲿ·ÖµÄÄÚÈÝŨËõ³ÉÒ»¸öÖ»ÓÐÁ½ÐдúÂëµÄ
reactive() º¯Êý£º
export function
reactive(target) {
const observed = new Proxy(target, handler)
return observed
} |
ÍêÕû´úÂëÔÚ reactive.js¡£ÕâÀïµÄ handler ¾ÍÊǸÄÔì getter/setter µÄ¹Ø¼ü£¬ÎÒÃǷŵ½ºóÎĽ²½â¡£
½ÓÏÂÀ´ÎÒÃÇ·ÖÎöµÚ¶þ¼þÊ¡£
µ±Ò»¸öÆÕͨµÄº¯Êý fn() ±» effect() °ü¹üÖ®ºó£¬¾Í»á±ä³ÉÒ»¸öÏìӦʽµÄ effect º¯Êý£¬¶ø
fn() Ò²»á±»Á¢¼´Ö´ÐÐÒ»´Î¡£
ÓÉÓÚÔÚ fn() ÀïÃæÓÐÒýÓõ½ Proxy ¶ÔÏóµÄÊôÐÔ£¬ËùÒÔÕâÒ»²½»á´¥·¢¶ÔÏóµÄ getter£¬´Ó¶øÆô¶¯ÒÀÀµÊÕ¼¯¡£
³ý´ËÖ®Í⣬Õâ¸ö effect º¯ÊýÒ²»á±»Ñ¹ÈëÒ»¸öÃûΪ¡±activeReactiveEffectStack¡°£¨´Ë´¦Îª
effectStack£©µÄÕ»ÖУ¬¹©ºóÐøÒÀÀµÊÕ¼¯µÄʱºòʹÓá£
À´¿´¿´´úÂ루Íê³É´úÂëÇë¿´ effect.js£©£º
export function effect (fn) {
// ¹¹ÔìÒ»¸ö effect
const effect = function effect(...args) {
return run(effect, fn, args)
}
// Á¢¼´Ö´ÐÐÒ»´Î
effect()
return effect
}
export function run(effect, fn, args) {
if (effectStack.indexOf(effect) === -1) {
try {
// Íù³Ø×ÓÀï·ÅÈ뵱ǰ effect
effectStack.push(effect)
// Á¢¼´Ö´ÐÐÒ»±é fn()
// fn() Ö´Ðйý³Ì»áÍê³ÉÒÀÀµÊÕ¼¯£¬»áÓõ½ effect
return fn(...args)
} finally {
// Íê³ÉÒÀÀµÊÕ¼¯ºó´Ó³Ø×ÓÖÐÈÓµôÕâ¸ö effect
effectStack.pop()
}
}
} |
ÖÁ´Ë£¬³õʼ»¯½×¶ÎÒѾÍê³É¡£½ÓÏÂÀ´¾ÍÊÇÕû¸öϵͳ×î¹Ø¼üµÄÒ»²½¡ª¡ªÒÀÀµÊÕ¼¯½×¶Î¡£
ÒÀÀµÊÕ¼¯½×¶Î

Õâ¸ö½×¶ÎµÄ´¥·¢Ê±»ú£¬¾ÍÊÇÔÚ effect ±»Á¢¼´Ö´ÐУ¬ÆäÄÚ²¿µÄ fn() ´¥·¢ÁË Proxy ¶ÔÏóµÄ
getter µÄʱºò¡£¼òµ¥À´Ëµ£¬Ö»ÒªÖ´Ðе½ÀàËÆ state.count µÄÓï¾ä£¬¾Í»á´¥·¢ state
µÄ getter¡£
ÒÀÀµÊÕ¼¯½×¶Î×îÖØÒªµÄÄ¿µÄ£¬¾ÍÊǽ¨Á¢Ò»·Ý¡±ÒÀÀµÊÕ¼¯±í¡°£¬Ò²¾ÍÊÇͼʾµÄ¡±targetMap"¡£targetMap
ÊÇÒ»¸ö WeakMap£¬Æä key ÖµÊÇ~~µ±Ç°µÄ Proxy ¶ÔÏó state~~´úÀíǰµÄ¶ÔÏóorigin£¬¶ø
value ÔòÊǸöÔÏóËù¶ÔÓ¦µÄ depsMap¡£
depsMap ÊÇÒ»¸ö Map£¬key ֵΪ´¥·¢ getter ʱµÄÊôÐÔÖµ£¨´Ë´¦Îª count£©£¬¶ø
value ÔòÊÇ´¥·¢¹ý¸ÃÊôÐÔÖµËù¶ÔÓ¦µÄ¸÷¸ö effect¡£
»¹ÊÇÓеãÈÆ£¿ÄÇôÎÒÃÇÔپٸöÀý×Ó¡£¼ÙÉèÓиö Proxy ¶ÔÏóºÍ effect
ÈçÏ£º
const state = reactive({
count: 0,
age: 18
})
const effect1 = effect(() => {
console.log('effect1: ' + state.count)
})
const effect2 = effect(() => {
console.log('effect2: ' + state.age)
})
const effect3 = effect(() => {
console.log('effect3: ' + state.count, state.age)
}) |
ÄÇôÕâÀïµÄ targetMap Ó¦¸ÃΪÕâ¸öÑù×Ó£º

ÕâÑù£¬{ target -> key -> dep }
µÄ¶ÔÓ¦¹ØÏµ¾Í½¨Á¢ÆðÀ´ÁË£¬ÒÀÀµÊÕ¼¯Ò²¾ÍÍê³ÉÁË¡£´úÂëÈçÏ£º
export function
track (target, operationType, key) {
const effect = effectStack[effectStack.length
- 1]
if (effect) {
let depsMap = targetMap.get(target)
if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (dep === void 0) {
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(effect)) {
dep.add(effect)
}
}
} |
ŪÃ÷°×ÒÀÀµÊÕ¼¯±í targetMap ÊǷdz£ÖØÒªµÄ£¬ÒòΪÕâÊÇÕû¸öÏìӦʽϵͳºËÐÄÖеĺËÐÄ¡£
ÏìÓ¦½×¶Î
»Ø¹ËÉÏÒ»Õ½ڵÄÀý×Ó£¬ÎÒÃǵõ½ÁËÒ»¸ö { count: 0, age: 18 } µÄ Proxy£¬²¢¹¹ÔìÁËÈý¸ö
effect¡£ÔÚ¿ØÖÆÌ¨ÉÏ¿´¿´Ð§¹û£º

Ч¹û·ûºÏÔ¤ÆÚ£¬ÄÇôËüÊÇÔõôʵÏÖµÄÄØ£¿Ê×ÏÈÀ´¿´¿´Õâ¸ö½×¶ÎµÄÔÀíͼ£º

µ±Ð޸ĶÔÏóµÄij¸öÊôÐÔÖµµÄʱºò£¬»á´¥·¢¶ÔÓ¦µÄ setter¡£
setter ÀïÃæµÄ trigger() º¯Êý»á´ÓÒÀÀµÊÕ¼¯±íÀïÕÒµ½µ±Ç°ÊôÐÔ¶ÔÓ¦µÄ¸÷¸ö dep£¬È»ºó°ÑËüÃÇÍÆÈëµ½
effects ºÍ computedEffects£¨¼ÆËãÊôÐÔ£© ¶ÓÁÐÖУ¬×îºóͨ¹ý scheduleRun()
°¤¸öÖ´ÐÐÀïÃæµÄ effect¡£
ÓÉÓÚÒѾ½¨Á¢ÁËÒÀÀµÊÕ¼¯±í£¬ËùÒÔÒªÕÒµ½ÊôÐÔËù¶ÔÓ¦µÄ dep Ò²¾ÍÇá¶øÒ×¾ÙÁË£¬¿ÉÒÔ¿´¿´¾ßÌåµÄ´úÂëʵÏÖ£º
export function trigger (target, operationType,
key) {
// È¡µÃ¶ÔÓ¦µÄ depsMap
const depsMap = targetMap.get(target)
if (depsMap === void 0) {
return
}
// È¡µÃ¶ÔÓ¦µÄ¸÷¸ö dep
const effects = new Set()
if (key !== void 0) {
const dep = depsMap.get(key)
dep && dep.forEach(effect => {
effects.add(effect)
})
}
// ¼ò»¯°æ scheduleRun£¬°¤¸öÖ´ÐÐ effect
effects.forEach(effect => {
effect()
})
} |
ÕâÀïµÄ´úÂëûÓд¦ÀíÖîÈçÊý×éµÄ length ±»Ð޸ĵÄÒ»Ð©ÌØÊâÇé¿ö£¬¸ÐÐËȤµÄ¶ÁÕß¿ÉÒԲ鿴 vue-next
¶ÔÓ¦µÄÔ´Â룬»òÕßÕâÆªÎÄÕ£¬¿´¿´ÕâЩÇé¿ö¶¼ÊÇÔõô´¦ÀíµÄ¡£
ÖÁ´Ë£¬ÏìӦʽ½×¶ÎÍê³É¡£
×ܽá
ÔĶÁÔ´ÂëµÄ¹ý³Ì³äÂúÁËÌôÕ½ÐÔ£¬µ«Í¬Ê±Ò²³£³£±» Vue µÄһЩʵÏÖ˼·¸ø¾ªÑÞµ½£¬ÊÕ»ñÁ¼¶à¡£±¾Îİ´ÕÕÏìӦʽϵͳµÄÔËÐйý³Ì£¬»®·ÖÁË¡±³õʼ»¯¡°£¬¡±ÒÀÀµÊÕ¼¯¡°ºÍ¡±ÏìӦʽ¡°Èý¸ö½×¶Î£¬·Ö±ð²ûÊöÁ˸÷¸ö½×¶ÎËù×öµÄÊÂÇ飬Ӧ¸ÃÄܹ»½ÏºÃµØ°ïÖú¶ÁÕßÀí½âÆäºËÐÄ˼·¡£×îºó¸½ÉÏÎÄÕÂʵÀý´úÂëµÄ²Ö¿â
µØÖ·.
|