±à¼ÍƼö: |
ÕâÆªÎÄÕÂÖ÷Òª½éÉÜÁËVue3.0Êý¾ÝÏìӦʽÔÀíÏê½â£¬ÎÄÖÐͨ¹ýʾÀý´úÂë½éÉܵķdz£Ïêϸ£¬¶Ô´ó¼ÒµÄѧϰ»òÕß¹¤×÷¾ßÓÐÒ»¶¨µÄ²Î¿¼Ñ§Ï°¼ÛÖµ¡£
±¾ÎÄÀ´×ÔÓڽű¾Ö®¼Ò,ÓÉ»ðÁú¹ûÈí¼þAnna±à¼ÍƼö¡£ |
|
Ô¤±¸ÖªÊ¶
ES6 Proxy£¬Õû¸öÏìӦʽϵͳµÄ»ù´¡¡£
еÄcomposition-APIµÄ»ù±¾Ê¹Óã¬Ä¿Ç°»¹Ã»ÓÐÖÐÎÄÎĵµ£¬¿ÉÒÔÏÈͨ¹ýÕâ¸ö²Ö¿â(composition-api-rfc)Á˽⣬ÀïÃæÒ²ÓжÔÓ¦µÄÔÚÏßÎĵµ¡£
ÏȰÑVue3.0ÅÜÆðÀ´
ÏȰÑvue-next²Ö¿âµÄ´úÂëcloneÏÂÀ´£¬°²×°ÒÀÀµÈ»ºó¹¹½¨Ò»Ï£¬vueµÄpackageϵÄdistĿ¼ÏÂÕÒµ½¹¹½¨µÄ½Å±¾£¬ÒýÈë½Å±¾¼´¿É¡£
ÏÂÃæÒ»¸ö¼òµ¥¼ÆÊýÆ÷µÄDEMO£º
<!DOCTYPE
html>
<html lang="en">
<body> <div id='app'></div>
</body>
<script src= z"./dist/vue.global.js">
</script>
<script>
const { createApp, reactive, computed } = Vue;
const RootComponent = {
template: ` <button @click="increment">
Count is: {{ state.count }} </button>
`,
setup() {
const state = reactive({
count: 0,
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
createApp().mount(RootComponent, '#app')
</script>
</html> |
templateºÍ֮ǰһÑù£¬Í¬ÑùVue3Ò²Ö§³ÖÊÖдrenderµÄд·¨£¬templateºÍrenderͬʱ´æÔÚµÄÇé¿ö£¬ÓÅÏÈrender¡£
setupÑ¡ÏîÊÇÐÂÔöµÄÖ÷Òª±ä¶¯£¬¹ËÃû˼Ò壬setupº¯Êý»áÔÚ×é¼þ¹ÒÔØÇ°(beforeCreateºÍcreatedÉúÃüÖÜÆÚÖ®¼ä)ÔËÐÐÒ»´Î£¬ÀàËÆ×é¼þ³õʼ»¯µÄ×÷Óã¬setupÐèÒª·µ»ØÒ»¸ö¶ÔÏó»òÕߺ¯Êý¡£·µ»Ø¶ÔÏó»á±»¸³Öµ¸ø×é¼þʵÀýµÄrenderContext£¬ÔÚ×é¼þµÄÄ£°å×÷ÓÃÓò¿ÉÒÔ±»·ÃÎʵ½£¬ÀàËÆdataµÄ·µ»ØÖµ¡£·µ»Øº¯Êý»á±»µ±×öÊÇ×é¼þµÄrender¡£¾ßÌå¿ÉÒÔϸ¿´Îĵµ¡£
reactiveµÄ×÷ÓÃÊǽ«¶ÔÏó°ü×°³ÉÏìӦʽ¶ÔÏó£¬Í¨¹ýProxy´úÀíºóµÄ¶ÔÏó¡£
ÉÏÃæµÄ¼ÆÊýÆ÷µÄÀý×Ó£¬ÔÚ×é¼þµÄsetupº¯ÊýÖУ¬´´½¨ÁËÒ»¸öÏìӦʽ¶ÔÏóstate°üº¬Ò»¸öcountÊôÐÔ¡£È»ºó´´½¨ÁËÒ»¸öincrementµÝÔöµÄº¯Êý£¬×îºó½«stateºÍincrement·µ»Ø¸ø×÷ÓÃÓò£¬ÕâÑùtemplateÀïµÄbutton°´Å¥¾ÍÄÜ·ÃÎʵ½incrementº¯Êý°ó¶¨µ½µã»÷µÄ»Øµ÷£¬countÒ²ÄÜÏÔʾÔÚ°´Å¥ÉÏ¡£ÎÒÃǵã»÷°´Å¥£¬°´Å¥ÉϵÄÊýÖµ¾ÍÄܸú×ŵÝÔö¡£
ÏÂÃæÇÐÈëÕýÌ⣬ÎÒÃǾÍÀ´Ì½¾¿Ï°´Å¥ÉÏcountÖµ¸ú×ÅÏìӦʽ¸üеÄÔÀí
Êý¾Ý½á¹¹
Ê×ÏÈÁÐÒ»ÏÂÖ÷ÒªµÄһЩÊý¾Ý½á¹¹£¬ÏÈÁÐÔÚÕâÀºóÃæÌáµ½¿ÉÒÔ·»ØÀ´¿´¿´¡£
ReactiveEffect Ò»¸öFunction¶ÔÏó£¬ÓÃÓÚÖ´ÐÐ×é¼þµÄ¹ÒÔØºÍ¸üС£
interface ReactiveEffect
{
(): any
isEffect: true
active: boolean
raw: Function // ¾ßÌåÖ´Ðеĺ¯Êý
deps: Array<Dep>
computed: boolean
scheduler: (run: Function) => void
onTrack: (event: DebuggerEvent) => void
onTrigger: ( event: DebuggerEvent) => void
onStop: () => void
} |
targetMap ÀàËÆ {target -> key ->
dep}µÄÒ»¸öMap½á¹¹£¬ÓÃÓÚ»º´æËùÓÐÏìӦʽ¶ÔÏóºÍÒÀÀµÊÕ¼¯¡£
export type Dep
= Set<ReactiveEffect>
export type KeyToDepMap = Map<string | symbol,
Dep>
export const targetMap: WeakMap<any, KeyToDepMap>
= new WeakMap() |
Proxy´úÀíÀ¹½Ø
reactiveº¯ÊýÖ´ÐУ¬»á½«´«ÈëµÄtarget¶ÔÏóͨ¹ýProxy°ü×°£¬À¹½ØËüµÄget£¬setµÈ£¬²¢½«´úÀíµÄtarget»º´æµ½targetMap£¬targetMap.set(target,
new Map())¡£
´úÀíµÄgetµÄʱºò»áµ÷ÓÃÒ»¸ötrackº¯Êý£¬¶øset»áµ÷ÓÃÒ»¸ötrigerº¯Êý¡£·Ö±ð¶ÔÓ¦ÒÀÀµÊÕ¼¯ºÍ´¥·¢¸üС£
// Proxy get
¼ò»¯
function get (target: any, key: string | symbol,
receiver: any) {
// ͨ¹ýkeyÄõ½Ôʼֵres
const res = Reflect.get (target, key, receiver)
// ¹ýÂ˲»ÐèÒª´úÀíµÄÇé¿ö
// ...
// ÒÀÀµÊÕ¼¯
track(target, OperationTypes.GET, key)
// Èç¹ûÈ¡µ½µÄÖµÊǸö¶ÔÏ󣬽«¶ÔÏóÔÙ´úÀí°ü×°Ò»ÏÂ
// ProxyÖ»ÄÜ´úÀí¶ÔÏóµÚÒ»²ã¼¶
return isObject(res) reactive(res) : res
}
// Proxy set ¼ò»¯
function set(
target: any,
key: string | symbol,
value: any,
receiver: any
): boolean {
// һЩ²»ÐèÒª´úÀíÉèÖõij¡¾°
// ...
// ÉèÖÃÔʼ¶ÔÏóµÄÖµ
const result = Reflect.set (target, key, value,
receiver)
// ±ÜÃâÖØ¸´triggerµÄÂß¼
// ...
// ´¥·¢Í¨Öª¸üÐÂ
trigger(target, '¸üеÄÀàÐÍ, ÐÂÔökey»ò¸üÐÂkey', key)
return result
} |
ÒÀÀµÊÕ¼¯ºÍ´¥·¢¸üÐÂ
×é¼þÔÚrender½×¶Î£¬ÊÓͼ»á¶ÁÈ¡Êý¾Ý¶ÔÏóÉϵÄÖµ½øÐÐäÖȾ£¬´Ëʱ±ã´¥·¢ÁËProxyµÄget£¬ÓÉ´Ë´¥·¢¶ÔÓ¦µÄtrackº¯Êý£¬¼Ç¼ÏÂÁ˶ÔÓ¦µÄReactiveEffect£¬Ò²¾ÍÊdz£ËµµÄÒÀÀµÊÕ¼¯¡£
ReactiveEffectÆäʵ¾Í¿ÉÒÔ¿´×÷ÊÇ×é¼þµÄ¸üУ¨mountÊÇÌØÊâµÄupdate£©£¬Êý¾ÝµÄ±ä¸ü´¥·¢trigger£¬trigger±éÀúµ÷ÓÃtrackÊÕ¼¯µÄ¶ÔÓ¦µÄÊý¾ÝµÄReactiveEffect£¬Ò²¾ÍÊǶÔÓ¦ÓйØÁªµÄ×é¼þµÄ¸üС£
trigger´¥·¢µÄ×é¼þµÄ¸üУ¬ÔÚrender½×¶ÎÓÖ´¥·¢ÁËÐÂÒ»ÂÖµÄtrackÒÀÀµÊÕ¼¯£¬¸üÐÂÒÀÀµ¡£
// ¼ò»¯µÄ track
function track(
target: any,
type: OperationTypes,
key?: string | symbol
) {
// Ö»ÓÐÔÚÒÀÀµÊÕ¼¯½×¶Î²Å½øÐÐÒÀÀµÊÕ¼¯
// ³ýÁËrender£¬ÆäËû³¡¾°Ò²¿ÉÄܻᴥ·¢ProxyµÄget£¬µ«²»ÐèÒª½øÐÐÒÀÀµÊÕ¼¯
// activeReactiveEffectStackÕ»¶¥°ü×°Á˵±Ç°renderµÄ×é¼þµÄmountºÍupdateµÄÂß¼
const effect = activeReactiveEffectStack [activeReactiveEffectStack.length
- 1]
// Èç¹ûeffectΪ¿Õ£¬ËµÃ÷µ±Ç°²»ÔÚrender½×¶Î
if (effect) {
// ...
// =====>³õʼ»¯¶ÔÓ¦{target -> key -> dep}µÄ½á¹¹
let depsMap = targetMap.get(target)
if (depsMap === void 0) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get (key as string | symbol)
if (!dep) {
depsMap.set (key as string | symbol, (dep = new
Set()))
}
// <=====³õʼ»¯¶ÔÓ¦{target -> key -> dep}µÄ½á¹¹
// ÒÀÀµÁбíÀïÈç¹ûûÓУ¬add
if (!dep.has(effect)) {
// ÕâÀォeffect×÷ΪÒÀÀµ£¬»º´æµ½ÒÀÀµÁбí
dep.add(effect)
effect.deps.push(dep)
}
}
}
// ¼ò»¯µÄtrigger
function trigger(
target: any,
type: OperationTypes,
key?: string | symbol,
extraInfo?: any
) {
// »ñÈ¡¶ÔÓ¦targetÔÚtrack¹ý³ÌÖлº´æµÄÒÀÀµ
const depsMap = targetMap.get(target)
const effects: Set<ReactiveEffect> = new
Set()
// Ê¡ÂÔ·ÖÀàÂß¼
depsMap.forEach(dep => {
// ½«effect·ÖÀà¹ýÂËÌí¼Óµ½effects
})
const run = (effect: ReactiveEffect) => {
// ÓиöÒì²½µ÷¶ÈµÄ¹ý³Ì£¬nextTick
scheduleRun (effect, target, type, key, extraInfo)
}
effects.forEach(run)
} |
´óÖÂÁ÷³Ì£º

×ܽá
ÏÖÔڵĴúÂëÖ»ÓÐÐÂÌØÐÔµÄʵÏÖ£¬¶øÇÒES6+TSµÄ×éºÏ¿É¶ÁÐÔ´ó´óÌá¸ß£¬±à¼Æ÷Ö§³ÖÒ²ºÜºÃ£¬ËùÒÔÏà¶Ô»áºÃ¶ÁºÜ¶à¡£ÕâÀïÖ»ÊǼòµ¥µÄÀíÁËÒ»ÏÂvue
3.0 reactiveµÄÕûÌåÁ÷³Ì£¬Ï¸½Ú»¹ÓÐºÜ¶àµØ·½ÖµµÃѧϰ£¬¼ÌÐø¼ÓÓÍ¡£
ÒÔÉϾÍÊDZ¾ÎĵÄÈ«²¿ÄÚÈÝ£¬Ï£Íû¶Ô´ó¼ÒµÄѧϰÓÐËù°ïÖú£¬Ò²Ï£Íû´ó¼Ò¶à¶àÖ§³Ö½Å±¾Ö®¼Ò¡£ |