国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - React - 一文幫你理解PReact10.5.13源碼

一文幫你理解PReact10.5.13源碼

2022-02-22 16:34我的小樹林 React

這篇文章主要介紹了一文幫你理解PReact10.5.13源碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

React源碼看過幾次,每次都沒有堅持下來,索性學習一下PReact部分,網上講解源碼的不少,但是基本已經過時,所以自己來梳理下

render.js部分

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import { EMPTY_OBJ, EMPTY_ARR } from './constants';
import { commitRoot, diff } from './diff/index';
import { createElement, Fragment } from './create-element';
import options from './options';
 
/**
 * Render a Preact virtual node into a DOM element
 * @param {import('./internal').ComponentChild} vnode The virtual node to render
 * @param {import('./internal').PreactElement} parentDom The DOM element to
 * render into
 * @param {import('./internal').PreactElement | object} [replaceNode] Optional: Attempt to re-use an
 * existing DOM tree rooted at `replaceNode`
 */
export function render(vnode, parentDom, replaceNode) {
 if (options._root) options._root(vnode, parentDom);
 
 // We abuse the `replaceNode` parameter in `hydrate()` to signal if we are in
 // hydration mode or not by passing the `hydrate` function instead of a DOM
 // element..
 let isHydrating = typeof replaceNode === 'function';
 
 // To be able to support calling `render()` multiple times on the same
 // DOM node, we need to obtain a reference to the previous tree. We do
 // this by assigning a new `_children` property to DOM nodes which points
 // to the last rendered tree. By default this property is not present, which
 // means that we are mounting a new tree for the first time.
  // 為了支持多次在一個dom節點上調用render函數,需要在dom節點上添加一個飲用,用來獲取指向上一次渲染的虛擬dom樹。
  // 這個屬性默認是指向空的,也意味著我們第一次正在裝備一顆新的樹
  // 所以開始時這里的oldVNode是空(不論isHydrating的值),但是如果重復在這個節點上調用render那oldVNode是有值的
 let oldVNode = isHydrating
  ? null
  : (replaceNode && replaceNode._children) || parentDom._children;
 
 // 用Fragment包裹一下vnode,同時給replaceNode和parentDom的_children賦值
  vnode = (
  (!isHydrating && replaceNode) ||
  parentDom
 )._children = createElement(Fragment, null, [vnode]);
 
 // List of effects that need to be called after diffing.
  // 用來放置diff之后需要進行各種生命周期處理的Component,比如cdm、cdu;componentWillUnmount在diffChildren的unmount函數中執行不在commitRoot時執行
 let commitQueue = [];
 diff(
  parentDom, // 這個使用parentDom的_children屬性已經指向[vnode]了
  // Determine the new vnode tree and store it on the DOM element on
  // our custom `_children` property.
  vnode,
  oldVNode || EMPTY_OBJ, // 舊的樹
  EMPTY_OBJ,
  parentDom.ownerSVGElement !== undefined,
    // excessDomChildren,這個參數用來做dom復用的作用
  !isHydrating && replaceNode
   ? [replaceNode]
   : oldVNode
   ? null
   : parentDom.firstChild // 如果parentDom有子節點就會把整個子節點作為待復用的節點使用
   ? EMPTY_ARR.slice.call(parentDom.childNodes)
   : null,
  commitQueue,
    // oldDom,在后續方法中用來做標記插入位置使用
  !isHydrating && replaceNode
   ? replaceNode
   : oldVNode
   ? oldVNode._dom
   : parentDom.firstChild,
  isHydrating
 );
 
 // Flush all queued effects
  // 調用所有commitQueue中的節點_renderCallbacks中的方法
 commitRoot(commitQueue, vnode);
}
 
/**
 * Update an existing DOM element with data from a Preact virtual node
 * @param {import('./internal').ComponentChild} vnode The virtual node to render
 * @param {import('./internal').PreactElement} parentDom The DOM element to
 * update
 */
export function hydrate(vnode, parentDom) {
 render(vnode, parentDom, hydrate);
}

create-context.js部分

Context的使用:

Provider的props中有value屬性

Consumer中直接獲取傳值

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { createContext, h, render } from 'preact';
 
const FontContext = createContext(20);
 
function Child() {
 return <FontContext.Consumer>
 {fontSize=><div style={{fontSize:fontSize}}>child</div>}
 </FontContext.Consumer>
}
function App(){
 return <Child/>
}
render(
 <FontContext.Provider value={26}>
 <App/>
 </FontContext.Provider>,
 document.getElementById('app')
);

看一下源碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { enqueueRender } from './component';
 
export let i = 0;
 
export function createContext(defaultValue, contextId) {
 contextId = '__cC' + i++; // 生成一個唯一ID
 
 const context = {
  _id: contextId,
  _defaultValue: defaultValue,
  /** @type {import('./internal').FunctionComponent} */
  Consumer(props, contextValue) {
   // return props.children(
   //  context[contextId] ? context[contextId].props.value : defaultValue
   // );
   return props.children(contextValue);
  },
  /** @type {import('./internal').FunctionComponent} */
  Provider(props) {
   if (!this.getChildContext) { // 第一次調用時進行一些初始化操作
    let subs = [];
    let ctx = {};
    ctx[contextId] = this;
       
       // 在diff操作用,如果判斷一個組件在Comsumer中,會調用sub進行訂閱;
       // 同時這個節點后續所有diff的地方都會帶上這個context,調用sub方法進行調用
       // context具有層級優先級,組件會先加入最近的context中
    this.getChildContext = () => ctx;
 
    this.shouldComponentUpdate = function(_props) {
     if (this.props.value !== _props.value) {
      // I think the forced value propagation here was only needed when `options.debounceRendering` was being bypassed:
      // https://github.com/preactjs/preact/commit/4d339fb803bea09e9f198abf38ca1bf8ea4b7771#diff-54682ce380935a717e41b8bfc54737f6R358
      // In those cases though, even with the value corrected, we're double-rendering all nodes.
      // It might be better to just tell folks not to use force-sync mode.
      // Currently, using `useContext()` in a class component will overwrite its `this.context` value.
      // subs.some(c => {
      //  c.context = _props.value;
      //  enqueueRender(c);
      // });
 
      // subs.some(c => {
      //  c.context[contextId] = _props.value;
      //  enqueueRender(c);
      // });
            // enqueueRender最終會進入renderComponent函數,進行diff、commitRoot、updateParentDomPointers等操作
      subs.some(enqueueRender);
     }
    };
 
    this.sub = c => {
     subs.push(c);// 進入訂閱數組,
     let old = c.componentWillUnmount;
     c.componentWillUnmount = () => { // 重寫componentWillUnmount
      subs.splice(subs.indexOf(c), 1);
      if (old) old.call(c);
     };
    };
   }
 
   return props.children;
  }
 };
 
 // Devtools needs access to the context object when it
 // encounters a Provider. This is necessary to support
 // setting `displayName` on the context object instead
 // of on the component itself. See:
 // https://reactjs.org/docs/context.html#contextdisplayname
 // createContext最終返回的是一個context對象,帶著Provider和Consumer兩個函數
 // 同時Consumber函數的contextType和Provider函數的_contextRef屬性都指向context
 return (context.Provider._contextRef = context.Consumer.contextType = context);
}

所以對于Provider組件,在渲染時會判斷有沒有getChildContext方法,如果有的話調用得到globalContext并一直向下傳遞下去

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if (c.getChildContext != null) {
    globalContext = assign(assign({}, globalContext), c.getChildContext());
   }
 
   if (!isNew && c.getSnapshotBeforeUpdate != null) {
    snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
   }
 
   let isTopLevelFragment =
    tmp != null && tmp.type === Fragment && tmp.key == null;
   let renderResult = isTopLevelFragment ? tmp.props.children : tmp;
 
   diffChildren(
    parentDom,
    Array.isArray(renderResult) ? renderResult : [renderResult],
    newVNode,
    oldVNode,
    globalContext,
    isSvg,
    excessDomChildren,
    commitQueue,
    oldDom,
    isHydrating
   );

當渲染遇到Consumer時,即遇到contextType屬性,先從Context中拿到provider,然后拿到provider的props的value值,作為組件要獲取的上下文信息。同時這時候會調用provider的sub方法,進行訂閱,當調用到Provider的shouldComponentUpdate中發現value發生變化時就會將所有的訂閱者進入enqueueRender函數。

一文幫你理解PReact10.5.13源碼

所以源碼中,globalContext對象的每一個key指向一個Context.Provider;componentContext代表組件所在的Consumer傳遞的上下文信息即配對的Provider的props的value;

同時Provider的shouldComponentUpdate方法中用到了 ·this.props.value !== _props.value· 那么這里的this.props是哪來的?Provider中并沒有相關屬性。

主要是下面這個地方,當判斷沒有render方法時,會先用Compoent來實例化一個對象,并將render方法設置為doRender,并將constructor指向newType(當前函數),在doRender中調用this.constructor方法

?
1
2
3
4
5
6
7
8
9
10
// Instantiate the new component
    if ('prototype' in newType && newType.prototype.render) {
     // @ts-ignore The check above verifies that newType is suppose to be constructed
     newVNode._component = c = new newType(newProps, componentContext); // eslint-disable-line new-cap
    } else {
     // @ts-ignore Trust me, Component implements the interface we want
     newVNode._component = c = new Component(newProps, componentContext);
     c.constructor = newType;
     c.render = doRender;
    }
?
1
2
3
4
/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
 return this.constructor(props, context);
}

diff部分

diff部分比較復雜,整體整理了一張大圖

一文幫你理解PReact10.5.13源碼

真是不得不吐槽,博客園的編輯器bug太多了,尤其是mac上使用,比如第二次上傳代碼提交不了;賦值粘貼用不了。。。

只有情懷讓我繼續在這里更新

到此這篇關于一文幫你理解PReact10.5.13源碼的文章就介紹到這了,更多相關PReact10.5.13源碼內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/dojo-lzz/p/14612797.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 一区二区三区视频 | 在线一区二区三区做爰视频网站 | 日日操夜夜操免费视频 | 日日爱视频| 亚洲国产免费 | 日韩一区二区三区电影在线观看 | 成人福利电影 | 亚洲免费看片 | 久久久国产一区 | 欧美国产日韩精品 | 欧美久久久久 | 一区二区三区高清视频在线观看 | 免费av电影观看 | 精品一区二区三区视频 | 国产一区二区三区在线 | 天堂资源 | 欧美亚洲国产日韩 | 欧美日韩久久精品 | 精品国产黄a∨片高清在线 成人欧美 | 日产欧产va高清 | 99看片网 | 日本aⅴ毛片成人实战推荐 伊人久久在线 | 久久男人精品 | 国产精品99久久久久久动医院 | 一区二区三区av | 91社区在线高清 | 毛片免费毛片一级jjj毛片 | 亚洲一区国产视频 | 日韩欧美国产精品综合嫩v 在线视频 中文字幕 | 国产视频9999 | 综合网视频| 日本美女一区二区三区 | 亚洲欧美视频在线播放 | 欧美在线观看一区二区 | 日韩视频在线一区二区 | 免费观看黄色大片 | 国产中文字幕在线 | 亚洲福利一区二区 | 中文在线一区二区 | 午夜男人| 91久久久久久 |