最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文幫你理解PReact10.5.13源碼

 更新時(shí)間:2021年04月03日 07:18:43   作者:我的小樹(shù)林  
這篇文章主要介紹了一文幫你理解PReact10.5.13源碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

React源碼看過(guò)幾次,每次都沒(méi)有堅(jiān)持下來(lái),索性學(xué)習(xí)一下PReact部分,網(wǎng)上講解源碼的不少,但是基本已經(jīng)過(guò)時(shí),所以自己來(lái)梳理下

render.js部分

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.
  // 為了支持多次在一個(gè)dom節(jié)點(diǎn)上調(diào)用render函數(shù),需要在dom節(jié)點(diǎn)上添加一個(gè)飲用,用來(lái)獲取指向上一次渲染的虛擬dom樹(shù)。
  // 這個(gè)屬性默認(rèn)是指向空的,也意味著我們第一次正在裝備一顆新的樹(shù)
  // 所以開(kāi)始時(shí)這里的oldVNode是空(不論isHydrating的值),但是如果重復(fù)在這個(gè)節(jié)點(diǎn)上調(diào)用render那oldVNode是有值的
 let oldVNode = isHydrating
  ? null
  : (replaceNode && replaceNode._children) || parentDom._children;

 // 用Fragment包裹一下vnode,同時(shí)給replaceNode和parentDom的_children賦值
  vnode = (
  (!isHydrating && replaceNode) ||
  parentDom
 )._children = createElement(Fragment, null, [vnode]);

 // List of effects that need to be called after diffing.
  // 用來(lái)放置diff之后需要進(jìn)行各種生命周期處理的Component,比如cdm、cdu;componentWillUnmount在diffChildren的unmount函數(shù)中執(zhí)行不在commitRoot時(shí)執(zhí)行
 let commitQueue = [];
 diff(
  parentDom, // 這個(gè)使用parentDom的_children屬性已經(jīng)指向[vnode]了
  // Determine the new vnode tree and store it on the DOM element on
  // our custom `_children` property.
  vnode,
  oldVNode || EMPTY_OBJ, // 舊的樹(shù)
  EMPTY_OBJ,
  parentDom.ownerSVGElement !== undefined,
    // excessDomChildren,這個(gè)參數(shù)用來(lái)做dom復(fù)用的作用
  !isHydrating && replaceNode
   ? [replaceNode]
   : oldVNode
   ? null
   : parentDom.firstChild // 如果parentDom有子節(jié)點(diǎn)就會(huì)把整個(gè)子節(jié)點(diǎn)作為待復(fù)用的節(jié)點(diǎn)使用
   ? EMPTY_ARR.slice.call(parentDom.childNodes)
   : null,
  commitQueue,
    // oldDom,在后續(xù)方法中用來(lái)做標(biāo)記插入位置使用
  !isHydrating && replaceNode
   ? replaceNode
   : oldVNode
   ? oldVNode._dom
   : parentDom.firstChild,
  isHydrating
 );

 // Flush all queued effects
  // 調(diào)用所有commitQueue中的節(jié)點(diǎn)_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中直接獲取傳值

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')
);

看一下源碼:

import { enqueueRender } from './component';

export let i = 0;

export function createContext(defaultValue, contextId) {
 contextId = '__cC' + i++; // 生成一個(gè)唯一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) { // 第一次調(diào)用時(shí)進(jìn)行一些初始化操作
    let subs = [];
    let ctx = {};
    ctx[contextId] = this;
       
       // 在diff操作用,如果判斷一個(gè)組件在Comsumer中,會(huì)調(diào)用sub進(jìn)行訂閱;
       // 同時(shí)這個(gè)節(jié)點(diǎn)后續(xù)所有diff的地方都會(huì)帶上這個(gè)context,調(diào)用sub方法進(jìn)行調(diào)用
       // context具有層級(jí)優(yōu)先級(jí),組件會(huì)先加入最近的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最終會(huì)進(jìn)入renderComponent函數(shù),進(jìn)行diff、commitRoot、updateParentDomPointers等操作
      subs.some(enqueueRender);
     }
    };

    this.sub = c => {
     subs.push(c);// 進(jìn)入訂閱數(shù)組,
     let old = c.componentWillUnmount;
     c.componentWillUnmount = () => { // 重寫(xiě)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最終返回的是一個(gè)context對(duì)象,帶著Provider和Consumer兩個(gè)函數(shù)
 // 同時(shí)Consumber函數(shù)的contextType和Provider函數(shù)的_contextRef屬性都指向context
 return (context.Provider._contextRef = context.Consumer.contextType = context);
}

所以對(duì)于Provider組件,在渲染時(shí)會(huì)判斷有沒(méi)有g(shù)etChildContext方法,如果有的話(huà)調(diào)用得到globalContext并一直向下傳遞下去

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
   );

當(dāng)渲染遇到Consumer時(shí),即遇到contextType屬性,先從Context中拿到provider,然后拿到provider的props的value值,作為組件要獲取的上下文信息。同時(shí)這時(shí)候會(huì)調(diào)用provider的sub方法,進(jìn)行訂閱,當(dāng)調(diào)用到Provider的shouldComponentUpdate中發(fā)現(xiàn)value發(fā)生變化時(shí)就會(huì)將所有的訂閱者進(jìn)入enqueueRender函數(shù)。

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

同時(shí)Provider的shouldComponentUpdate方法中用到了 ·this.props.value !== _props.value· 那么這里的this.props是哪來(lái)的?Provider中并沒(méi)有相關(guān)屬性。

主要是下面這個(gè)地方,當(dāng)判斷沒(méi)有render方法時(shí),會(huì)先用Compoent來(lái)實(shí)例化一個(gè)對(duì)象,并將render方法設(shè)置為doRender,并將constructor指向newType(當(dāng)前函數(shù)),在doRender中調(diào)用this.constructor方法

// 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;
    }
/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
 return this.constructor(props, context);
}

diff部分

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

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

只有情懷讓我繼續(xù)在這里更新

到此這篇關(guān)于一文幫你理解PReact10.5.13源碼的文章就介紹到這了,更多相關(guān)PReact10.5.13源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 微信小程序 確認(rèn)框的實(shí)現(xiàn)(附代碼)

    微信小程序 確認(rèn)框的實(shí)現(xiàn)(附代碼)

    這篇文章主要介紹了微信小程序 確認(rèn)框的實(shí)現(xiàn)(附代碼),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • js實(shí)現(xiàn)漂浮回頂部按鈕實(shí)例

    js實(shí)現(xiàn)漂浮回頂部按鈕實(shí)例

    這篇文章主要介紹了js實(shí)現(xiàn)漂浮回頂部按鈕的方法,實(shí)例分析了javascript實(shí)現(xiàn)返回頂部功能的樣式與動(dòng)態(tài)實(shí)現(xiàn)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-05-05
  • JavaScript惰性求值的一種實(shí)現(xiàn)方法示例

    JavaScript惰性求值的一種實(shí)現(xiàn)方法示例

    這篇文章主要給大家介紹了關(guān)于JavaScript惰性求值的一種實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • 微信小程序?qū)崿F(xiàn)表單驗(yàn)證

    微信小程序?qū)崿F(xiàn)表單驗(yàn)證

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)表單驗(yàn)證,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • 8個(gè)有意思的JavaScript面試題

    8個(gè)有意思的JavaScript面試題

    JavaScript 是一種有趣的語(yǔ)言,我們都喜歡它,因?yàn)樗男再|(zhì)。這篇文章主要介紹了8個(gè)有意思的JavaScript面試題 ,需要的朋友可以參考下
    2019-07-07
  • 使用js實(shí)現(xiàn)一個(gè)可編輯的select下拉列表

    使用js實(shí)現(xiàn)一個(gè)可編輯的select下拉列表

    這篇文章主要介紹了使用js實(shí)現(xiàn)一個(gè)可編輯的select下拉列表,個(gè)人感覺(jué)還不錯(cuò),需要的朋友可以參考下
    2014-02-02
  • javascript實(shí)現(xiàn)下班倒計(jì)時(shí)效果的方法(可桌面通知)

    javascript實(shí)現(xiàn)下班倒計(jì)時(shí)效果的方法(可桌面通知)

    這篇文章主要介紹了javascript實(shí)現(xiàn)下班倒計(jì)時(shí)效果的方法,涉及javascript倒計(jì)時(shí)效果及桌面提示效果的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值
    2015-07-07
  • 詳解微信小程序input標(biāo)簽正則初體驗(yàn)

    詳解微信小程序input標(biāo)簽正則初體驗(yàn)

    這篇文章主要介紹了詳解微信小程序input標(biāo)簽正則初體驗(yàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • JavaScript中清空數(shù)組的三種方式

    JavaScript中清空數(shù)組的三種方式

    本文主要介紹了JavaScript中清空數(shù)組的三種方式。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-03-03
  • input?獲取光標(biāo)位置設(shè)置光標(biāo)位置方案

    input?獲取光標(biāo)位置設(shè)置光標(biāo)位置方案

    這篇文章主要為大家介紹了input?獲取光標(biāo)位置設(shè)置光標(biāo)位置方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06

最新評(píng)論

恩平市| 淳化县| 孟州市| 华池县| 肥东县| 昭平县| 城固县| SHOW| 广河县| 万全县| 土默特左旗| 潢川县| 昆明市| 固镇县| 定日县| 安庆市| 马边| 阜新| 汉沽区| 汝南县| 邵武市| 文成县| 宁波市| 玉溪市| 渝中区| 孝昌县| 友谊县| 玉屏| 建湖县| 盐亭县| 平顶山市| 探索| 轮台县| 理塘县| 保亭| 泾源县| 湖北省| 柘荣县| 祁连县| 伊川县| 宕昌县|