无码人妻精一区二区三区,eeuss影院www在线观看,无码精品久久久久久人妻中字,日韩av高清在线看片

推薦新聞
自己寫個(gè)React渲染器: 以 Remax 為例(用React寫小程序)
發(fā)布者:深藍(lán)互聯(lián)
發(fā)布時(shí)間:2019-11-19
點(diǎn)擊:次

關(guān)于React的一些基本概念

創(chuàng)建一個(gè) React 自定義渲染器,你需要對(duì)React渲染的基本原理有一定的了解。所以在深入閱讀本文之前,先要確保你能夠理解以下幾個(gè)基本概念:

1. Element

我們可以通過 JSX 或者 React.createElement 來創(chuàng)建 Element,用來描述我們要?jiǎng)?chuàng)建的視圖節(jié)點(diǎn)。比如:

<button class='button button-blue'>
  <b>
    OK!
  </b>
</button>

JSX 會(huì)被轉(zhuǎn)義譯為:

React.createElement(
  "button",
  { class: 'button button-blue' },
  React.createElement("b", null, "OK!")
)

React.createElement 最終構(gòu)建出類似這樣的對(duì)象:

{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

也就是說 Element 就是一個(gè)普通的對(duì)象,描述用戶創(chuàng)建的節(jié)點(diǎn)類型、props 以及 children。這些 Elements 組合成樹,描述用戶視圖

 

2. Component

可以認(rèn)為是 Element 的類型,它有兩種類型:

  • Host Component: 宿主組件,這是由渲染的平臺(tái)提供的‘內(nèi)置’組件,例如ReactDOM 平臺(tái)下面的 DOM 節(jié)點(diǎn),如 div、span... 這些組件類型為字符串
  • Composite Component: 復(fù)合組件,這是一種用戶自定義的組件封裝單位。通常包含自定義的邏輯、狀態(tài)以及輸出 Element 樹。復(fù)合類型可以為類或函數(shù)
const DeleteAccount = () => (
  <div>
    <p>Are you sure?</p>
    <DangerButton>Yep</DangerButton>
    <Button color='blue'>Cancel</Button>
  </div>
);

 

3. Instance

當(dāng) React 開始渲染一個(gè) Element 時(shí),會(huì)根據(jù)組件類型為它創(chuàng)建一個(gè)‘實(shí)例’,例如類組件,會(huì)調(diào)用new操作符實(shí)例化。這個(gè)實(shí)例會(huì)一直引用,直到 Element 從 Element Tree 中被移除。

首次渲染: React 會(huì)實(shí)例化一個(gè) MyButton 實(shí)例,調(diào)用掛載相關(guān)的生命周期方法,并執(zhí)行 render 方法,遞歸渲染下級(jí)

render(<MyButton>foo</MyButton>, container)

 

更新: 因?yàn)榻M件類型沒有變化,React 不會(huì)再實(shí)例化,這個(gè)屬于‘節(jié)點(diǎn)更新’,React 會(huì)執(zhí)行更新相關(guān)的生命周期方法,如shouldComponentUpdate。如果需要更新則再次執(zhí)行render方法

render(<MyButton>bar</MyButton>, container)

 

卸載: 組件類型不一樣了, 原有的 MyButton 被替換. MyButton 的實(shí)例將要被銷毀,React 會(huì)執(zhí)行卸載相關(guān)的生命周期方法,如componentWillUnmount

render(<button>bar</button>, container)

 

4. Reconciler & Renderer

Reconciler 和 Renderer 的關(guān)系可以通過下圖縷清楚.

Reconciler 的職責(zé)是維護(hù) VirtualDOM 樹,內(nèi)部實(shí)現(xiàn)了 Diff/Fiber 算法,決定什么時(shí)候更新、以及要更新什么

而 Renderer 負(fù)責(zé)具體平臺(tái)的渲染工作,它會(huì)提供宿主組件、處理事件等等。例如ReactDOM就是一個(gè)渲染器,負(fù)責(zé)DOM節(jié)點(diǎn)的渲染和DOM事件處理。

 

 

 

 

5. Fiber 的兩個(gè)階段 React 使用了 Fiber 架構(gòu)之后,更新過程被分為兩個(gè)階段(Phase)

  • 協(xié)調(diào)階段(Reconciliation Phase) 這個(gè)階段 React 會(huì)找出需要更新的節(jié)點(diǎn)。這個(gè)階段是可以被打斷的,比如有優(yōu)先級(jí)更高的事件要處理時(shí)。
  • 提交階段(Commit Phase) 將上一個(gè)階段計(jì)算出來的需要處理的副作用(Effects)一次性執(zhí)行了。這個(gè)階段必須同步執(zhí)行,不能被打斷

 

如果按照render為界,可以將生命周期函數(shù)按照兩個(gè)階段進(jìn)行劃分:

  • 協(xié)調(diào)階段
    • constructor
    • componentWillMount 廢棄
    • componentWillReceiveProps 廢棄
    • static getDerivedStateFromProps
    • shouldComponentUpdate
    • componentWillUpdate 廢棄
    • render
    • getSnapshotBeforeUpdate()
  • 提交階段
    • componentDidMount
    • componentDidUpdate
    • componentWillUnmount

 

沒理解?那么下文讀起來對(duì)你可能比較吃力,建議閱讀一些關(guān)于React基本原理的相關(guān)文章。

 

就目前而言,React 大部分核心的工作已經(jīng)在 Reconciler 中完成,好在 React 的架構(gòu)和模塊劃分還比較清晰,React官方也暴露了一些庫,這極大簡(jiǎn)化了我們開發(fā) Renderer 的難度。開始吧!

 

自定義React渲染器

React官方暴露了一些庫供開發(fā)者來擴(kuò)展自定義渲染器:

  • react-reconciler - 這就是 React 的協(xié)調(diào)器, React 的核心所在。我們主要通過它來開發(fā)渲染器。
  • scheduler - 合作調(diào)度器的一些 API 。本文不會(huì)用到
需要注意的是,這些包還是實(shí)驗(yàn)性的,API可能不太穩(wěn)定。另外,沒有詳細(xì)的文檔,你需要查看源代碼或者其他渲染器實(shí)現(xiàn);本文以及擴(kuò)展閱讀中的文章也是很好的學(xué)習(xí)資料。

 

創(chuàng)建一個(gè)自定義渲染器只需兩步:

 

 

第一步: 實(shí)現(xiàn)宿主配置,這是react-reconciler要求宿主提供的一些適配器方法和配置項(xiàng)。這些配置項(xiàng)定義了如何創(chuàng)建節(jié)點(diǎn)實(shí)例、構(gòu)建節(jié)點(diǎn)樹、提交和更新等操作。下文會(huì)詳細(xì)介紹這些配置項(xiàng)

const Reconciler = require('react-reconciler');

const HostConfig = {
  // ... 實(shí)現(xiàn)適配器方法和配置項(xiàng)
};

 

第二步:實(shí)現(xiàn)渲染函數(shù),類似于ReactDOM.render() 方法

// 創(chuàng)建Reconciler實(shí)例, 并將HostConfig傳遞給Reconciler
const MyRenderer = Reconciler(HostConfig);

/**
 * 假設(shè)和ReactDOM一樣,接收三個(gè)參數(shù)
 * render(<MyComponent />, container, () => console.log('rendered'))
 */
export function render(element, container, callback) {
  // 創(chuàng)建根容器
  if (!container._rootContainer) {
    container._rootContainer = ReactReconcilerInst.createContainer(container, false);
  }

  // 更新根容器
  return ReactReconcilerInst.updateContainer(element, container._rootContainer, null, callback);
}

容器既是 React 組件樹掛載的目標(biāo)(例如 ReactDOM 我們通常會(huì)掛載到 #root 元素,#root 就是一個(gè)容器)、也是組件樹的 根Fiber節(jié)點(diǎn)(FiberRoot)。根節(jié)點(diǎn)是整個(gè)組件樹的入口,它將會(huì)被 Reconciler 用來保存一些信息,以及管理所有節(jié)點(diǎn)的更新和渲染。

關(guān)于 Fiber 架構(gòu)的一些細(xì)節(jié)可以看這些文章:

 

HostConfig 渲染器適配

HostConfig 支持非常多的參數(shù),完整列表可以看這里. 下面是一些自定義渲染器必須提供的參數(shù):

interface HostConfig {
  /**
   * 用于分享一些上下文信息
   */
  // 獲取根容器的上下文信息, 只在根節(jié)點(diǎn)調(diào)用一次
  getRootHostContext(rootContainerInstance: Container): HostContext;
  // 獲取子節(jié)點(diǎn)的上下文信息, 每遍歷一個(gè)節(jié)點(diǎn)都會(huì)調(diào)用一次
  getChildHostContext(parentHostContext: HostContext, type: Type, rootContainerInstance: Container): HostContext;


  /**
   * 節(jié)點(diǎn)實(shí)例的創(chuàng)建
   */
  // 普通節(jié)點(diǎn)實(shí)例創(chuàng)建,例如DOM的Element類型
  createInstance(type: Type, props: Props, rootContainerInstance: Container, hostContext: HostContext, internalInstanceHandle: OpaqueHandle,): Instance;
  // 文本節(jié)點(diǎn)的創(chuàng)建,例如DOM的Text類型
  createTextInstance(text: string, rootContainerInstance: Container, hostContext: HostContext, internalInstanceHandle: OpaqueHandle): TextInstance;
  // 決定是否要處理子節(jié)點(diǎn)/子文本節(jié)點(diǎn). 如果不想創(chuàng)建則返回true. 例如ReactDOM中使用dangerouslySetInnerHTML, 這時(shí)候子節(jié)點(diǎn)會(huì)被忽略
  shouldSetTextContent(type: Type, props: Props): boolean;

  /**
   * 節(jié)點(diǎn)樹構(gòu)建
   */
  // 如果節(jié)點(diǎn)在*未掛載*狀態(tài)下,會(huì)調(diào)用這個(gè)來添加子節(jié)點(diǎn)
  appendInitialChild(parentInstance: Instance, child: Instance | TextInstance): void;
  // **下面都是副作用(Effect),在’提交‘階段被執(zhí)行**
  // 添加子節(jié)點(diǎn)
  appendChild?(parentInstance: Instance, child: Instance | TextInstance): void;
  // 添加子節(jié)點(diǎn)到容器節(jié)點(diǎn)(根節(jié)點(diǎn))
  appendChildToContainer?(container: Container, child: Instance | TextInstance): void;
  // 插入子節(jié)點(diǎn)
  insertBefore?(parentInstance: Instance, child: Instance | TextInstance, beforeChild: Instance | TextInstance): void;
  // 插入子節(jié)點(diǎn)到容器節(jié)點(diǎn)(根節(jié)點(diǎn))
  insertInContainerBefore?(container: Container, child: Instance | TextInstance, beforeChild: Instance | TextInstance,): void;
  // 刪除子節(jié)點(diǎn)
  removeChild?(parentInstance: Instance, child: Instance | TextInstance): void;
  // 從容器節(jié)點(diǎn)(根節(jié)點(diǎn))中移除子節(jié)點(diǎn)
  removeChildFromContainer?(container: Container, child: Instance | TextInstance): void;

  /**
   * 節(jié)點(diǎn)掛載
   */
  // 在完成所有子節(jié)點(diǎn)初始化時(shí)(所有子節(jié)點(diǎn)都appendInitialChild完畢)時(shí)被調(diào)用, 如果返回true,則commitMount將會(huì)被觸發(fā)
  // ReactDOM通過這個(gè)屬性和commitMount配置實(shí)現(xiàn)表單元素的autofocus功能
  finalizeInitialChildren(parentInstance: Instance, type: Type, props: Props, rootContainerInstance: Container, hostContext: HostContext): boolean;
  // 和finalizeInitialChildren配合使用,commitRoot會(huì)在’提交‘完成后(resetAfterCommit)執(zhí)行, 也就是說組件樹渲染完畢后執(zhí)行
  commitMount?(instance: Instance, type: Type, newProps: Props, internalInstanceHandle: OpaqueHandle): void;

  /**
   * 節(jié)點(diǎn)更新
   */
  // 準(zhǔn)備節(jié)點(diǎn)更新. 如果返回空則表示不更新,這時(shí)候commitUpdate則不會(huì)被調(diào)用
  prepareUpdate(instance: Instance, type: Type, oldProps: Props, newProps: Props, rootContainerInstance: Container, hostContext: HostContext,): null | UpdatePayload;
  // **下面都是副作用(Effect),在’提交‘階段被執(zhí)行**
  // 文本節(jié)點(diǎn)提交
  commitTextUpdate?(textInstance: TextInstance, oldText: string, newText: string): void;
  // 普通節(jié)點(diǎn)提交
  commitUpdate?(instance: Instance, updatePayload: UpdatePayload, type: Type, oldProps: Props, newProps: Props, internalInstanceHandle: OpaqueHandle): void;
  // 重置普通節(jié)點(diǎn)文本內(nèi)容, 這個(gè)需要和shouldSetTextContent(返回true時(shí))配合使用,
  resetTextContent?(instance: Instance): void;

  /**
   * 提交
   */
  // 開始’提交‘之前被調(diào)用,比如這里可以保存一些狀態(tài),在’提交‘完成后恢復(fù)狀態(tài)。比如ReactDOM會(huì)保存當(dāng)前元素的焦點(diǎn)狀態(tài),在提交后恢復(fù)
  // 執(zhí)行完prepareForCommit,就會(huì)開始執(zhí)行Effects(節(jié)點(diǎn)更新)
  prepareForCommit(containerInfo: Container): void;
  // 和prepareForCommit對(duì)應(yīng),在提交完成后被執(zhí)行
  resetAfterCommit(containerInfo: Container): void;


  /**
   * 調(diào)度
   */
  // 這個(gè)函數(shù)將被Reconciler用來計(jì)算當(dāng)前時(shí)間, 比如計(jì)算任務(wù)剩余時(shí)間 
  // ReactDOM中會(huì)優(yōu)先使用Performance.now, 普通場(chǎng)景用Date.now即可
  now(): number;
  // 自定義計(jì)時(shí)器
  setTimeout(handler: (...args: any[]) => void, timeout: number): TimeoutHandle | NoTimeout;
  // 取消計(jì)時(shí)器
  clearTimeout(handle: TimeoutHandle | NoTimeout): void;
  // 表示一個(gè)空的計(jì)時(shí)器,見               

 

  • 聯(lián)系地址
    中國(guó) 深圳 龍華區(qū)龍觀西路99號(hào)順泰中晟大廈一棟14樓13A01
  • 聯(lián)系電話
    電話:13530005652 / 0755-23110995
  • 電子郵箱
    wisepu@szdbi.com
關(guān)注深藍(lán)互聯(lián)公眾號(hào)
Copyright ? 2013-2025 深藍(lán)互聯(lián) 版權(quán)所有
友情鏈接: