Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
preactÔ´Âë·ÖÎö
 
×÷ÕߣºÕÅÔ½
  1859  次浏览      27
 2021-3-16 
 
±à¼­ÍƼö:
±¾ÎÄÖØµã½éÉÜÁËpreactÊÇʲô?ÐéÄâDOM¡¢×é¼þ¡¢diffËã·¨¡¢Òì²½setStateµÈÏà¹ØÄÚÈÝ¡£
±¾ÎÄÀ´×ÔÓÚ˼·ñ,ÓÉ»ðÁú¹ûÈí¼þAnna±à¼­ÍƼö¡£

ǰÑÔ

preactËäÈ»ÊÇreactµÄ×îСʵÏÖ, ºÜ¶àreactµÄÌØÐÔpreactÀïÒ»µã¶¼Ã»ÓÐÉÙ, ±ÈÈçcontextAPI, FragmentµÈ¡£ÎÒÃÇ·ÖÎöʱ¸ü×¢ÖØÊµÏÖ¹ý³Ì£¬»á¶ÔһЩAPIµÄʵÏÖ½øÐкöÂÔ¡£Çë¼ûÁÂ

preactÊÇʲô?

Fast 3kB React alternative with the same modern API. Components & Virtual DOM

preact¿ÉÒÔ˵ÊÇÀàreact¿ò¼ÜµÄ×îСʵÏÖ

ÐéÄâDOM

¹ØÓÚjsx

ÎÒÃÇÊ×ÏÈ¿´ÏÂpreact¹ÙÍøÉϵÄdemo¡£

import { h, render } from 'preact';
render((
<h1 id="title" >Hello, world!</h1>
), document.body);

ÆäʵÉÏÃæµÄjsx´úÂ룬±¾ÖÊÊÇÏÂÃæ´úÂëµÄÓï·¨ÌÇ

h(
'h1',
{ id: 'title' },
'Hello, world!'
)

preactÊÇÈçºÎ×öµ½µÄÄØ£¿preact±¾Éí²¢Ã»ÓÐʵÏÖÕâ¸öÓ﷨ת»»µÄ¹¦ÄÜ£¬preactÊÇÒÀÀµtransform-react-jsxµÄbabel²å¼þ×öµ½µÄ¡£

createElement

Ç°ÃæÎÒÃÇ¿´µ½ÁËjsxµÄ´úÂë»á±»×ª»»ÎªÓÃhº¯Êý°ü¹üµÄ´úÂë, ÎÒÃǽÓÏÂÀ´¿´ÏÂhº¯ÊýÊÇÈçºÎʵÏֵġ£createElementº¯ÊýλÓÚcreate-element.jsÕâ¸öÎļþÖС£

ÎļþÖÐÖ÷ҪΪ3¸öº¯Êý, createElementºÍcreateVNode, ÒÔ¼°coerceToVNode¡£

createElementºÍcreateVNodeÊÇÒ»¶ÔµÄ, createElement»á½«children¹ÒÔØµ½VNodeµÄpropsÖС£¼Èprops.childrenµÄÊý×éÖС£createVNodeÔò»á½«¸ù¾ÝÕâЩ²ÎÊý·µ»ØÒ»¸ö¶ÔÏó, Õâ¸ö¶ÔÏó¾ÍÊÇÐéÄâDOM¡£

ÔÚcreateElementÖÐÎÒÃÇ»¹¿ÉÒÔ¿´µ½¶ÔdefaultPropsµÄ´¦Àí, ¶ødefaultProps¿ÉÒÔΪÎÒÃÇÉèÖÃpropsµÄĬÈϵijõʼֵ¡£


export function createElement (type, props, children) {
if (props==null) props = {};
if (arguments.length>3) {
children = [children];
for (let i=3; i<arguments.length; i++) {
children.push(arguments[i]);
}
}

if (children!=null) {
props.children = children;
}
if (type!=null && type.defaultProps!=null) {
for (let i in type.defaultProps) {
if (props[i]===undefined) props[i] = type.defaultProps[i];
}
}
let ref = props.ref;
if (ref) delete props.ref;
let key = props.key;
if (key) delete props.key;
return createVNode (type, props, null, key, ref);
}
export function createVNode (type, props, text, key, ref) {
const vnode = {
type,
props,
text,
key,
ref,
_children: null,
_dom: null,
_lastDomChild: null,
_component: null
};
return vnode;
}

¶øcoerceToVNodeº¯ÊýµÄ×÷ÓÃÔòÊǽ«Ò»Ð©Ã»ÓÐtypeÀàÐ͵Ľڵ㡣±ÈÈçÒ»¶Î×Ö·û´®, Ò»¸öÊý×ÖÇ¿ÖÆ×ª»»ÎªVNode½Úµã, ÕâЩ½ÚµãµÄtypeֵΪnull, textÊôÐÔÖб£ÁôÁË×Ö·û´®ºÍÊý×ÖµÄÖµ¡£


export function coerceToVNode (possibleVNode) {
if (possibleVNode == null || typeof possibleVNode === 'boolean') return null;
if (typeof possibleVNode === 'string' || typeof possibleVNode === 'number') {
return createVNode (null, null, possibleVNode, null, null);
}
if (Array.isArray(possibleVNode)) {
return createElement(Fragment, null, possibleVNode);
}
if (possibleVNode._dom!=null) {
return createVNode (possibleVNode.type, possibleVNode.props, possibleVNode.text, possibleVNode.key, null);
}
return possibleVNode;
}

µ½ÕâÀïcreate-elementµÄÕâ¸öÄ£¿éÎÒÃǾͽéÉÜÍêÁË¡£ÕâÊÇÒ»¸ö·Ç³£¼òµ¥µÄÄ£¿é, ×öµÄ¹¦ÄܾÍÊǸù¾Ý¶ÔÓ¦µÄjsx->ÐéÄâDOM¡£ÎÒÃÇÕâÀﻹûÓÐÉæ¼°ÈçºÎäÖȾ³öÕæÕýµÄDOM½Úµã, ÕâÊÇÒòΪpreactÖÐäÖȾµÄ¹ý³ÌÊÇÖ±½ÓÔÚdiffËã·¨ÖÐʵÏÖ£¬Ò»±ß±È¶ÔÒ»±ß¸ú¸üÐÂÕæÊµµÄdom¡£

×é¼þ

preactÖÐÓÐÒ»¸öͨÓÃComponentÀà, ×é¼þµÄʵÏÖÐèÒª¼Ì³ÐÕâ¸öͨÓõÄComponentÀà¡£ÎÒÃÇÀ´¿´ÏÂpreactÖÐComponentÀàÊÇÈçºÎʵÏֵġ£ËüλÓÚcomponent.jsÎļþ??ÖС£

ÎÒÃÇÊ×ÏÈ¿´ÏÂComponentÀàµÄ¹¹Ô캯Êý£¬·Ç³£µÄ¼òµ¥¡£Ö»ÓÐÁ½¸öÊôÐÔprops, context¡£ÒòΪͨÓõÄComponentÀàʵÏÖÁËpropsÊôÐÔ£¬ËùÒÔÎÒÃǵÄ×é¼þÀàÔڼ̳ÐComponentÀàºó£¬ÐèÒªÏÔʽµÄʹÓÃsuper×÷Ϊº¯Êýµ÷Ó㬲¢½«props´«Èë¡£

export function Component(props, context) {
this.props = props
this.context = context
}

ComponentÀàÖÐʵÏÖÁËsetState·½·¨, forceUpdate·½·¨£¬render·½·¨£¬ÒÔ¼°ÆäËûµÄһЩ¸¨Öúº¯Êý¡£forceUpdateÉæ¼°µ½ÁËsetStateµÄÒì²½¸üÐÂ, ÎÒÃǽ«ÔÚsetStateÒ»½ÚÖÐרÃŽéÉÜ¡£ÕâÀïÔݲ»×ö½éÉÜ¡£ÎÒÃǽÓÏÂÀ´¿´¿´setStateµÄʵÏÖ¡£

Component.prototype.setState = function(update, callback) {
let s = (this._nextState!==this.state && this._nextState) || (this._nextState = assign({}, this.state));
if (typeof update!=='function' || (update = update(s, this.props))) {
assign(s, update);
}
if (update==null) return;
if (this._vnode) {
if (callback) this._renderCallbacks.push(callback);
enqueueRender (this);
}
};
// src/util.js
export function assign(obj, props) {
for (let i in props) obj[i] = props[i];
return obj;
}

ÔÚpreactµÄsetState·½·¨, ͬreactÒ»ÑùÖ§³Öº¯Êý»òÕßObjectÁ½ÖÖ·½Ê½¸üÐÂstate, ²¢ÇÒÖ§³ÖsetStateµÄ»Øµ÷¡£ÎÒÃÇÕâÀï¿´µ½ÁËÁ½¸ö¸ö˽ÓÐÊôÐÔ_nextState, _renderCallbacks¡£_renderCallbacksÔòÊÇ´æ´¢ÁËsetState»Øµ÷µÄ¶ÓÁС£

_nextStateÀï´æ´¢ÁË×îеÄstate, ΪʲôÎÒÃDz»È¥Ö±½Ó¸üÐÂstateÄØ£¿ÒòΪÎÒÃÇҪʵÏÖÉúÃüÖÜÆÚ, ±ÈÈçgetDerivedStateFromPropsÉúÃüÖÜÆÚÖÐ×é¼þµÄstate²¢Ã»ÓиüÐÂÄØ¡£ÎÒÃÇÐèҪʹÓÃ_nextState´æ´¢×îеÄstate??¡£enqueueRenderº¯ÊýÉæ¼°µ½ÁËstateµÄÒì²½¸üÐÂ, ÎÒÃÇÔÚ±¾½ÚÏȲ»½éÉÜ¡£

// src/component.js
export function Fragment() { }
Component.prototype.render = Fragment;

»ùÀàµÄrender·½·¨±¾ÉíÊÇÒ»¸ö¿Õº¯Êý, ÐèÒª¼Ì³ÐµÄ×ÓÀà×Ô¼º¾ßÌåʵÏÖ¡£

component.jsµÄÄ£¿éµÄ²¿·ÖÄÚÈÝ£¬ÎÒÃÇÒѾ­½éÉÜÍê³ÉÁË, ͬÑù²»ÊǺܸ´ÔÓ¡£component.jsµÄÄ£¿éµÄÆäËûµÄÄÚÈÝÒòÎªÉæ¼°ÁËsetStateÒì²½¸üжÓÁУ¬ËùÒÔÎÒÃǽ«ÔÚsetStateÒ»½ÚÖС£»Ø¹ýÍ·À´½éÉÜËü¡£

diffËã·¨

ps:ÎÒÃÇÖ»ÐèÒª±È½Ïͬ¼¶µÄ½Úµã(ÏàͬÑÕÉ«¿òÄÚµÄ), Èç¹ûÁ½¸ö½Úµãtype²»Ò»ÖÂ, ÎÒÃÇ»áÏú»Ùµ±Ç°µÄ½Úµã¡£²»½øÐбȽÏ×Ó½ÚµãµÄ²Ù×÷¡£

ÔÚpreactÖÐdiffËã·¨ÒÔ¼°ÕæÊµdomµÄ¸üкÍäÖȾÊÇÔÓôÛÔÚÒ»ÆðµÄ¡£ËùÒÔ±¾½ÚÄÚÈÝ»á±È½Ï¶à¡£

preact»á´æ´¢ÉÏÒ»´ÎµÄäÖȾµÄVNode(´æ´¢ÔÚ_prevVNodeµÄ˽ÓÐÊôÐÔÉÏ)¡£¶ø±¾´ÎäÖȾ¹ý³ÌÖÐÎÒÃÇ»á±È½Ï±¾´ÎµÄVNodeÉÏǰһ´ÎµÄ_prevVNode¡£ÅжÏÊÇ·ñÐèÒªÉú³ÉеÄDom, Ð¶ÔØDomµÄ²Ù×÷, ¸üÐÂÕæÊµdomµÄ²Ù×÷(ÎÒÃǽ«VNode¶ÔÓ¦µÄÕæÊµµÄdom´æ´¢ÔÚVNodeµÄ˽ÓÐÊôÐÔ_dom, ¿ÉÒÔʵÏÖÔÚdiffµÄ¹ý³ÌÖиüÐÂdomµÄ²Ù×÷)¡£

render

¶Ô±ÈÎı¾½Úµã

ÎÒÃÇÊ×ÏÈ»ØÒäÒ»ÏÂÎı¾½ÚµãµÄVNodeµÄ½á¹¹ÊÇÔõôÑùµÄ

// Îı¾½ÚµãVNode
{
type: null,
props: null,
text: 'ÄãµÄÎı¾'
_dom: TextNode
}

ÎÒÃÇÊ×ÏȽøÈëdiff·½·¨¡£diff·½·¨Öлá¶ÔVNodeÀàÐͽøÐÐÅжÏ, Èç¹û²»ÊÇfunctionÀàÐÍ(×é¼þÀàÐÍ), ºÍFragmentÀàÐÍ¡£ÎÒÃǵĻáµ÷ÓÃdiffElementNodesº¯Êý¡£

// src/diff/index.js
// func diff
// ²ÎÊýºÜ¶à, ÎÒÃÇÀ´ËµÏ¼¸¸ö²ÎÊýµÄ¾ßÌ庬Òå
// domΪVNode¶ÔÓ¦µÄÕæÊµµÄDom½Úµã
// newVNodeеÄVNode
// oldVNode¾ÉµÄVNode
// mounts´æ´¢¹ÒÔØ×é¼þµÄÁбí
dom = diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent)

Èç¹û´Ëʱdom»¹Ã»Óд´½¨¡£³õ´ÎäÖȾ, ÄÇôÎÒÃǸù¾ÝVNodeÀàÐÍ´´½¨¶ÔÓ¦µÄÕæÊµdom½Úµã¡£Îı¾ÀàÐÍ»áʹÓÃcreateTextNode´´½¨Îı¾½Úµã¡£

½ÓÏÂÀ´ÎÒÃÇ»á±êǩ֮ǰVNodeµÄtextµÄÄÚÈÝ, Èç¹ûоɲ»ÏàµÈ¡£ÎÒÃǽ«ÐÂVNodeµÄtextÊôÐÔ£¬¸³Öµ¸ødom½Úµã¡£Íê³É¶ÔdomµÄ¸üвÙ×÷¡£

// src/diff/index.js
// func diffElementNodes
if (dom==null) {
dom = newVNode.type===null ? document.createTextNode(newVNode.text) : isSvg ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type) : document.createElement(newVNode.type);
excessDomChildren = null;
}
newVNode._dom = dom;
if (newVNode.type===null) {
if ((d===null || dom===d) && newVNode.text!==oldVNode.text) {
dom.data = newVNode.text;
}
}

¶Ô±È·ÇÎı¾DOM½Úµã

·ÇÎı¾DOM½ÚµãÖ¸µÄÊÇÄÇЩtypeΪdiv, span, h1µÄVNode½Úµã¡£ÕâЩÀàÐ͵ĽڵãÔÚdiff·½·¨ÖÐ, ÎÒÃÇÒÀ¾É»áµ÷ÓÃdiffElementNodesº¯ÊýÈ¥´¦Àí¡£

// src/diff/index.js
// func diff
dom = diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent)

½øÈëdiffElementNodes·½·¨ºó, Èç¹ûÊdzõ´ÎäÖȾÎÒÃÇ»áʹÓÃcreateElement´´½¨ÕæÊµµÄdom½Úµã¹ÒÔØµ½VNodeµÄ_domÊôÐÔÉÏ¡£

½ÓÏÂÀ´ÎÒÃÇ»á±È½ÏоÉVNodeµÄÊôÐÔprops¡£µ«ÊÇ֮ǰ»áµ÷ÓÃdiffChildren·½·¨, ¶Ôµ±Ç°µÄVNode×Ó½Úµã½øÐбȽϡ£ÎÒÃÇÕâÀïÏȲ»½øÈëdiffChildrenº¯ÊýÖС£ÎÒÃÇÖ»ÐèÒªÖªµÀÎÒÃÇÔÚ¸üе±Ç°½ÚµãÊôÐÔµÄʱºò, ÎÒÃÇÒѾ­Í¨¹ýµÝ¹éÐÎʽ, Íê³ÉÁ˶Ե±Ç°½ÚµãµÄ×Ó½ÚµãµÄ¸üвÙ×÷¡£½ÓÏÂÀ´ÎÒÃǽøÈëdiffPropsº¯ÊýÖС£

// src/diff/index.js
// func diffElementNodes
if (dom==null) {
dom = newVNode.type===null ? document.createTextNode (newVNode.text) : isSvg ? document.createElementNS ('http://www.w3.org/2000/svg', newVNode.type) : document.createElement (newVNode.type);
}
newVNode._dom = dom;
if (newVNode !== oldVNode) {
let oldProps = oldVNode.props;
let newProps = newVNode.props;
if (oldProps == null) {
oldProps = {};
}
diffChildren(dom, newVNode, oldVNode, context, newVNode.type === 'foreignObject' ? false : isSvg, excessDomChildren, mounts, ancestorComponent);
diffProps (dom, newProps, oldProps, isSvg);
}

ÔÚdiffPropsº¯ÊýÖÐÎÒÃÇ»á×öÁ½¼þÊ¡£ÉèÖÃ, ¸üÐÂÊôÐÔ¡£É¾³ýеÄpropsÖв»´æÔÚµÄÊôÐÔ¡£setPropertyÔÚpreactÖеľßÌåʵÏÖ, ÎÒÃÇÍùÏ¿´¡£

// src/diff/props.js
export function diffProps (dom, newProps, oldProps, isSvg) {
// ÉèÖûò¸üÐÂÊôÐÔÖµ
for (let i in newProps) {
if (i!=='children' && i!=='key' && (!oldProps || ((i==='value' || i==='checked') ? dom : oldProps) [i]!==newProps[i])) {
setProperty(dom, i, newProps[i], oldProps[i], isSvg);
}
}
// ɾ³ýÊôÐÔ
for (let i in oldProps) {
if (i!=='children' && i!=='key' && (!newProps || !(i in newProps))) {
setProperty (dom, i, null, oldProps[i], isSvg);
}
}
}

ÔÚsetProperty·½·¨ÖÐ, Èç¹ûvalue(еÄÊôÐÔÖµ)Ϊnull, ÎÒÃÇ»áɾ³ý¶ÔÓ¦µÄÊôÐÔ¡£Èç¹û²»Îªnull, ÎÒÃǽ«»á¸üлòÕßÉèÖÃеÄÊôÐÔ¡£Í¬Ê±»¹»á¶Ôʼþ½øÐд¦Àí, ÀýÈçonClickÊôÐÔ, ÎÒÃÇ»áʹÓÃaddEventListenerÌí¼ÓÔ­ÉúµÄclickʼþ¡£

// src/diff/props.js
function setProperty (dom, name, value, oldValue, isSvg) {
let v;
// ¶Ôclass´¦Àí
if (name==='class' || name==='className') name = isSvg ? 'class' : 'className';
// ¶Ôstyle´¦Àí, style´«ÈëObject»òÕß×Ö·û´®¶¼»áµÃµ½¼æÈݵĴ¦Àí
if (name==='style') {
let s = dom.style;
// Èç¹ûstyleÊÇstringÀàÐÍ
if (typeof value==='string') {
s.cssText = value;
}
else {
// Èç¹ûstyleÊÇobjectÀàÐÍ
if (typeof oldValue==='string') s.cssText = '';
else {
for (let i in oldValue) {
if (value==null || !(i in value)) s.setProperty (i.replace(CAMEL_REG, '-'), '');
}
}
for (let i in value) {
v = value[i];
if (oldValue==null || v!==oldValue[i]) {
s.setProperty(i.replace(CAMEL_REG, '-'), typeof v==='number' && IS_NON_DIMENSIONAL.test(i)===false ? (v + 'px') : v);
}
}
}
}
else if (name==='dangerouslySetInnerHTML') {
return;
}
else if (name[0]==='o' & & name[1]==='n') {
// ¶Ôʼþ´¦Àí
let useCapture = name !== (name=name.replace(/Capture$/, ''));
let nameLower = name.toLowerCase();
name = (nameLower in dom ? nameLower : name).substring(2);
if (value) {
if (!oldValue) dom.addEventListener (name, eventProxy, useCapture);
}
else {
dom.removeEventListener (name, eventProxy, useCapture);
}
(dom._listeners || (dom._listeners = {}))[name] = value;
}
else if (name!=='list' && name!=='tagName' && !isSvg && (name in dom)) {
dom[name] = value==null ? '' : value;
}
else if (value==null || value===false) {
// ɾ³ýÒÔ¼°ÎªnullµÄÊôÐÔ
if (name!== (name = name.replace(/^xlink:?/, ''))) dom.removeAttributeNS ('http://www.w3.org/1999/xlink', name.toLowerCase());
else dom.removeAttribute(name);
}
else if (typeof value!=='function') {
// ¸üлòÉèÖÃеÄÊôÐÔ
if (name!==(name = name.replace(/^xlink:?/, ''))) dom.setAttributeNS ('http://www.w3.org/1999/xlink', name.toLowerCase(), value);
else dom.setAttribute (name, value);
}
}

¶Ô±È×é¼þ

Èç¹ûVNodeÊÇ×é¼þÀàÐÍ¡£ÔÚdiffº¯ÊýÖÐ, »áÔÚ²»Í¬µÄʱ¿ÌÖ´ÐÐ×é¼þµÄÉúÃüÖÜÆÚ¡£ÔÚdiffÖÐ, Ö´ÐÐ×é¼þʵÀýµÄrenderº¯Êý¡£ÎÒÃǽ«»áÄõ½×é¼þ·µ»ØµÄVNode, È»ºóÔÙ½«VNodeÔÙÒ»´Î´øÈëdiff·½·¨ÖнøÐÐdiff±È½Ï¡£´óÖµÄÁ÷³Ì¿ÉÒÔÈçÉÏͼËùʾ¡£

// src/diff/index.js
// func diff
let c, p, isNew = false, oldProps, oldState, snapshot,
newType = newVNode.type;

let cxType = newType.contextType;
let provider = cxType && context[cxType._id];
let cctx = cxType != null ? (provider ? provider.props.value : cxType._defaultValue) : context;
if (oldVNode._component) {
c = newVNode._component = oldVNode._component;
clearProcessingException = c._processingException;
}
else {
isNew = true;
// ´´½¨×é¼þµÄʵÀý
if (newType.prototype && newType.prototype.render) {
newVNode._component = c = new newType(newVNode.props, cctx);
}
else {
newVNode._component = c = new Component(newVNode.props, cctx);
c.constructor = newType;
c.render = doRender;
}

c._ancestorComponent = ancestorComponent;
if (provider) provider.sub(c);
// ³õʼ»¯,×é¼þµÄstate, propsµÄÊôÐÔ
c.props = newVNode.props;
if (!c.state) c.state = {};
c.context = cctx;
c._context = context;
c._dirty = true;
c._renderCallbacks = [];
}
// ×é¼þµÄʵÀýÉϹÒÔØ×é¼þËù¶ÔÓ¦µÄVNode½Úµã
c._vnode = newVNode;
let s = c._nextState || c.state;
// Ö´ÐÐgetDerivedStateFromPropsÉúÃüÖÜÆÚº¯Êý, ·µ»ØÖ»»á¸üÐÂ×é¼þµÄstate
if (newType.getDerivedStateFromProps != null) {
oldState = assign({}, c.state);
if (s === c.state) s = c._nextState = assign({}, s);
assign(s, newType.getDerivedStateFromProps(newVNode.props, s));
}
if (isNew) {
// Ö´ÐÐcomponentWillMountÉúÃüÖÜÆÚ
if (newType.getDerivedStateFromProps == null && c.componentWillMount != null) c.componentWillMount();
// ½«ÐèÒªÖ´ÐÐcomponentDidMountÉúÃüÖÜÆÚµÄ×é¼þ, pushµ½mounts¶ÓÁÐÖÐ
if (c.componentDidMount != null) mounts.push(c);
}
else {
// Ö´ÐÐcomponentWillReceivePropsÉúÃüÖÜÆÚ
if (newType.getDerivedStateFromProps == null && force == null && c.componentWillReceiveProps != null) {
c.componentWillReceiveProps(newVNode.props, cctx);
s = c._nextState || c.state;
}
// Ö´ÐÐshouldComponentUpdateÉúÃüÖÜÆÚ, ²¢½«_dirtyÉèÖÃΪfalse, µ±_dirty±»ÉèÖÃΪfalseʱ, Ö´ÐеĸüвÙ×÷½«»á±»ÔÝÍ£
if (!force && c.shouldComponentUpdate != null && c.shouldComponentUpdate (newVNode.props, s, cctx) === false) {
c.props = newVNode.props;
c.state = s;
c._dirty = false;
// breakºó£¬²»ÔÚÖ´ÐÐÒÔϵĴúÂë
break outer;
}
// Ö´ÐÐcomponentWillUpdateÉúÃüÖÜÆÚ
if (c.componentWillUpdate != null) {
c.componentWillUpdate (newVNode.props, s, cctx);
}
}
oldProps = c.props;
if (!oldState) oldState = c.state;
c.context = cctx;
c.props = newVNode.props;
// ½«¸üкóµÄstateµÄs£¬¸³Óè×é¼þµÄstate
c.state = s;
// prevΪÉÏÒ»´ÎäÖȾʱ¶ÔÓ¦µÄVNode½Úµã
let prev = c._prevVNode;
// µ÷ÓÃ×é¼þµÄrender·½·¨»ñÈ¡×é¼þµÄVNode
let vnode = c._prevVNode = coerceToVNode (c.render(c.props, c.state, c.context));
c._dirty = false;
if (c.getChildContext != null) {
context = assign (assign({}, context), c.getChildContext());
}
// Ö´ÐÐgetSnapshotBeforeUpdateÉúÃüÖÜÆÚ
if (!isNew && c.getSnapshotBeforeUpdate != null) {
snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
}
// ¸üÐÂ×é¼þËù¶ÔÓ¦µÄVNode£¬·µ»Ø¶ÔÓ¦µÄdom
c.base = dom = diff(dom, parentDom, vnode, prev, context, isSvg, excessDomChildren, mounts, c, null);
if (vnode != null) {
newVNode._lastDomChild = vnode._lastDomChild;
}
c._parentDom = parentDom;

ÔÚdiffº¯ÊýµÄ¶¥²¿ÓÐÕâÑùÒ»¶Î´úÂëÉÏÃæÓÐÒ»¾äÓ¢ÎÄ×¢ÊÍ(If the previous type doesn't match the new type we drop the whole subtree), Èç¹ûoldVNodeºÍnewVNodeÀàÐͲ»Í¬£¬ÎÒÃǽ«»áÐ¶ÔØÕû¸ö×ÓÊ÷¡£

if (oldVNode==null || newVNode==null || oldVNode.type!==newVNode.type) {
// Èç¹ûnewVNodeΪnull, ÎÒÃǽ«»áÐ¶ÔØÕû¸ö×é¼þ, ²¢É¾³ý¶ÔÓ¦µÄdom½Úµã
if (oldVNode!=null) unmount(oldVNode, ancestorComponent);
if (newVNode==null) return null;
dom = null;
oldVNode = EMPTY_OBJ;
}

¶Ô±È×Ó½Úµã

export function diffChildren (parentDom, newParentVNode, oldParentVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent) {
let childVNode, i, j, p, index, oldVNode, newDom,
nextDom, sibDom, focus,
childDom;
let newChildren = newParentVNode._children || toChildArray (newParentVNode.props.children, newParentVNode._children=[], coerceToVNode);
let oldChildren = oldParentVNode !=null && oldParentVNode!= EMPTY_OBJ && oldParentVNode._children || EMPTY_ARR;
let oldChildrenLength = oldChildren.length;
childDom = oldChildrenLength ? oldChildren[0] && oldChildren[0]._dom : null;

for (i=0; i<newChildren.length; i++) {
childVNode = newChildren[i] = coerceToVNode(newChildren[i]);
oldVNode = index = null;
p = oldChildren[i];

//
if (p != null && (childVNode.key==null && p.key==null ? (childVNode.type === p.type) : (childVNode.key === p.key))) {
index = i;
}
else {
for (j=0; j<oldChildrenLength; j++) {
p = oldChildren[j];
if (p!=null) {
if (childVNode.key==null && p.key==null ? (childVNode.type === p.type) : (childVNode.key === p.key)) {
index = j;
break;
}
}
}
}
if (index!=null) {
oldVNode = oldChildren[index];
oldChildren[index] = null;
}
nextDom = childDom!=null && childDom.nextSibling;

newDom = diff (oldVNode==null ? null : oldVNode._dom, parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, ancestorComponent, null);
if (childVNode!=null && newDom !=null) {
focus = document.activeElement;
if (childVNode._lastDomChild != null) {
newDom = childVNode._lastDomChild;
}
else if (excessDomChildren==oldVNode || newDom!=childDom || newDom.parentNode==null) {
outer: if (childDom==null || childDom.parentNode !==parentDom) {
parentDom.appendChild(newDom);
}
else {
sibDom = childDom;
j = 0;
while ((sibDom=sibDom.nextSibling) && j++<oldChildrenLength/2) {
if (sibDom===newDom) {
break outer;
}
}
parentDom.insertBefore (newDom, childDom);
}
}
if (focus!== document.activeElement) {
focus.focus();
}
childDom = newDom! =null ? newDom.nextSibling : nextDom;
}
}

for (i=oldChildrenLength; i--; ) {
if (oldChildren[i]!=null) {
unmount(oldChildren[i], ancestorComponent);
}
}
}

diffChildrenÊÇ×îΪ¸´ÔÓµÄÒ»²¿·ÖÄÚÈÝ¡£×ÓVNode×÷Ϊһ¸öÊý×é, Êý×éÖеÄÄÚÈÝ¿ÉÄܸıäÁË˳Ðò»òÕßÊýÄ¿, ºÜÄÑÈ·¶¨ÐµÄVNodeÒªºÍÄÇÒ»¸ö¾ÉµÄVNode±È½Ï¡£ËùÒÔpreactÖе±Ãæ¶ÔÁбíʱ£¬ÎÒÃǽ«ÒªÇóÓû§Ìṩkey, °ïÖúÎÒÃDZȽÏVNode¡£´ïµ½¸´ÓÃDomµÄÄ¿µÄ¡£

ÔÚdiffChildrenÖУ¬ÎÒÃÇ»áÊ×ÏÈͨ¹ýtoChildArrayº¯Êý½«×Ó½ÚµãÒÔÊý×éµÄÐÎʽ´æ´¢ÔÚ_childrenÊôÐÔÉÏ¡£

childDomΪµÚÒ»¸ö×Ó½ÚµãÕæÊµµÄdom(ÕâºÜÓÐÓÃ, ÎÒÃÇÔÚºóÃæ½«Í¨¹ýËüÀ´ÅжÏÊÇʹÓÃappendChild²åÈënewDom»¹ÊÇʹÓÃinsertBefore²åÈënewDom£¬»òÕßʲô¶¼²»×ö)

½ÓÏÂÀ´±éÀú_childrenÊôÐÔ¡£Èç¹ûVNodeÓÐkeyÊôÐÔ, ÔòÕÒµ½keyÓëkeyÏàµÈµÄ¾ÉµÄVNode¡£Èç¹ûûÓÐkey, ÔòÕÒµ½×î½üµÄtypeÏàµÈµÄ¾ÉµÄVNode¡£È»ºó½«oldChildren¶ÔÓ¦µÄλÖÃÉèÖÃnull, ±ÜÃâÖØ¸´µÄ²éÕÒ¡£Ê¹ÓÃdiffËã·¨¶Ô±È, оÉVNode¡£·µ»ØÐµÄdom¡£

Èç¹ûchildDomΪnull, Ôò½«ÐÂdom, appendµÄµ½¸¸DOMÖС£Èç¹ûÕÒµ½ÁËÓëеÄdomÏàµÈµÄdom(ÒýÓÃÀàÐÍ), ÎÒÃÇÔò²»×öÈκδ¦Àí(propsÒѾ­ÔÚdiffElementNodeÖиüÐÂÁË)¡£Èç¹ûÔÚchildDomµÄnextSiblingûÓÐÕÒµ½ºÍеÄdomÏàµÈµÄdom, ÎÒÃǽ«dom²åÈëchildDomµÄÇ°Ãæ¡£½ÓןüÐÂchildom¡£

±éÀúÊ£ÓàûÓÐʹÓõ½oldChildren, Ð¶ÔØÕâЩ½Úµã»òÕß×é¼þ¡£

Òì²½setState

preact³ýÁËʹÓÃdiffËã·¨¼õÉÙdom²Ù×÷ÓÅ»¯ÐÔÄÜÍâ, preact»á½«Ò»¶Îʱ¼äÄڵĶà´ÎsetStateºÏ²¢¼õÉÙ×é¼þäÖȾµÄ´ÎÊý¡£

ÎÒÃÇÊ×ÏÈÔÚsetStateÖÐ, ²¢Ã»ÓÐÖ±½Ó¸üÐÂstate, »òÕßÖ±½ÓÖØÐÂäÖȾº¯Êýº¯Êý¡£¶øÊǽ«×é¼þµÄʵÀý´øÈëµ½ÁËenqueueRenderº¯ÊýÖС£

Component.prototype.setState = function(update, callback) {
let s = (this._nextState!== this.state && this._nextState) || (this._nextState = assign({}, this.state));
if (typeof update!== 'function' || (update = update(s, this.props))) {
assign(s, update);
}
if (update==null) return;
if (this._vnode) {
if (callback) this._renderCallbacks.push (callback);
enqueueRender(this);
}
};

ÔÚenqueueRenderº¯ÊýÖÐ, ÎÒÃǽ«×é¼þpushµ½¶ÓÁÐqÖС£

ͬʱʹÓÃ_dirty¿ØÖÆ, ±ÜÃâq¶ÓÁÐÖб»pushÁËÏàͬµÄ×é¼þ¡£ÎÒÃÇÓ¦¸ÃÔڶ೤ʱ¼äÄÚÇå¿Õq¶ÓÁÐÄØ?

ÎÒÃǸÃÈçºÎ¶¨ÒåÕâôһ¶Îʱ¼äÄØ£¿±È½ÏºÃµÄ×ö·¨ÊÇʹÓÃPromise.resolve()¡£ÔÚÕâÒ»¶Îʱ¼äµÄsetState²Ù×÷¶¼»á±»pushµ½q¶ÓÁÐÖС£_nextState½«»á±»ºÏ²¢ÔÚÇå¿Õ¶ÓÁеÄʱºò£¬Ò»²¢¸üе½stateÉÏ£¬±ÜÃâÁËÖØ¸´µÄäÖȾ¡£

let q = [];
export function enqueueRender(c) {
if (!c._dirty && (c._dirty = true) && q.push(c) === 1) {
(options.debounceRendering || defer)(process);
}
}
function process() {
let p;
while ((p=q.pop())) {
if (p._dirty) p.forceUpdate(false);
}
}
const defer = typeof Promise=='function' ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;

ÔÚºêÈÎÎñÍê³Éºó£¬ÎÒÃÇÖ´ÐÐ΢ÈÎÎñPromise.resolve(), Çå¿Õq¶ÓÁУ¬Ê¹ÓÃdiff·½·¨¸üжÓÁÐÖеÄ×é¼þ¡£

Component.prototype.forceUpdate = function(callback) {
let vnode = this._vnode, dom = this._vnode._dom, parentDom = this._parentDom;
if (parentDom) {
const force = callback!==false;
let mounts = [];
dom = diff (dom, parentDom, vnode, vnode, this._context, parentDom.ownerSVGElement!==undefined, null, mounts, this._ancestorComponent, force);
if (dom!=null && dom.parentNode!==parentDom) {
parentDom.appendChild(dom);
}
commitRoot(mounts, vnode);
}
if (callback) callback();
};

½áÓï

µ½ÕâÀïÎÒÃÇÒѾ­°ÉpreactµÄÔ´Âë´óÖÂä¯ÀÀÁËÒ»±é¡£ÎÒÃǽÓÏÂÀ´¿ÉÒԲο¼preactµÄÔ´Â룬ʵÏÖ×Ô¼ºµÄreact¡£»°ËµÎÒ»¹¸øpreactµÄÏîÄ¿Ìá½»ÁËpr£¬²»¹ý»¹Ã»ÓÐmerge¡£

 

 
   
1859 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕÂ

Éî¶È½âÎö£ºÇåÀíÀôúÂë
ÈçºÎ±àд³öÓµ±§±ä»¯µÄ´úÂë
ÖØ¹¹-ʹ´úÂë¸ü¼ò½àÓÅÃÀ
ÍŶÓÏîÄ¿¿ª·¢"±àÂë¹æ·¶"ϵÁÐÎÄÕÂ
Ïà¹ØÎĵµ

ÖØ¹¹-¸ÄÉÆ¼ÈÓдúÂëµÄÉè¼Æ
Èí¼þÖØ¹¹v2
´úÂëÕû½àÖ®µÀ
¸ßÖÊÁ¿±à³Ì¹æ·¶
Ïà¹Ø¿Î³Ì

»ùÓÚHTML5¿Í»§¶Ë¡¢Web¶ËµÄÓ¦Óÿª·¢
HTML 5+CSS ¿ª·¢
ǶÈëʽC¸ßÖÊÁ¿±à³Ì
C++¸ß¼¶±à³Ì
×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]
 
×îÐÂÎÄÕÂ
ÈçºÎÉè¼Æ¸ßÀ©Õ¹µÄÔÚÏßÍøÒ³ÖÆ×÷ƽ̨
electronÈëÃÅÐĵÃ
ʹÓà Electron ¹¹½¨×ÀÃæÓ¦ÓÃ
VUE.JS×é¼þ»¯¿ª·¢Êµ¼ù
ÉîÈëÀí½âJSCore
×îпγÌ
HTML 5 + CSS3 Ô­ÀíÓ뿪·¢Ó¦ÓÃ
Webǰ¶Ë¸ß¼¶¹¤³Ìʦ±Ø±¸¼¼ÄÜʵս
Vue´óÐÍÏîÄ¿¿ª·¢ÊµÕ½
ReactÔ­ÀíÓëʵ¼ù
Vue.js½ø½×Óë°¸Àýʵ¼ù
³É¹¦°¸Àý
Öн»¼¯ÍÅ ¹¹½¨Web×Ô¶¯»¯²âÊÔ¿ò¼Ü
ijָÃûµçÐŹ«Ë¾ Vue.js½ø½×Óë°¸Àý
¹úµçÍ¨ÍøÂç¼¼Êõ HTML5+CSS3 +webǰ¶Ë¿ò
ÒÆ¶¯Í¨ÐÅ ÒÆ¶¯»¥ÁªÍøÓ¦Óÿª·¢Ô­Àí
ijµçÁ¦ÐÐ android¿ª·¢Æ½Ì¨×î¼Ñ