摘要:這個函數內處理了的生命周期以及和生命周期鉤子函數,調用返回實際要渲染的內容,如果內容是復合組件,仍然會調用,復合組件最終一定會返回原生組件,并且最終調用的函數生成要渲染的。
原文鏈接地址:https://github.com/Nealyang%EF%BC%9A%E7%BB%84%E4%BB%B6%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%8E%E6%B8%B2%E6%9F%93.md) 轉載請注明出處前言
戰戰兢兢寫下開篇...也感謝小蘑菇大神以及網上各路大神的博客資料參考~
閱讀源碼的方式有很多種,廣度優先法、調用棧調試法等等,此系列文章,采用基線法,顧名思義,就是以低版本為基線,逐漸了解源碼的演進過程和思路。
react最初的設計靈感來源于游戲渲染的機制:當數據變化時,界面僅僅更新變化的部分而形成新的一幀渲染。所以設計react的核心就是認為UI只是把數據通過映射關系變換成另一種形式的數據,也就是展示方式。傳統上,web架構使用模板或者HTML指令構造頁面。react則處理構建用戶界面通過將他們份極為virtual dom,當然這也是react的核心,整個react架構的設計理念也是為此展開的。
準備工作我們采用基線法去學習react源碼,所以目前基于的版本為stable-0.3,后面我們在逐步分析學習演變的版本。
clone代碼git clone https://github.com/facebook/react.git git checkout 0.3-stable
React源代碼都在src目錄中,src包含了8個目錄,其主要內容描述見下表。
目 錄 | 內容 |
---|---|
core | React 核心類 |
domUtil | Dom操作和CSS操作的相關工具類 |
environment | 當前JS執行環境的基本信息 |
event | React事件機制的核心類 |
eventPlugins | React事件機制的事件綁定插件類 |
test | 測試目錄 |
utils | 各種工具類 |
vendor | 可替換模塊存放目錄 |
我們將該版本編譯后的代碼放到example下,引入到basic/index.html中運行調試。
組件初始化 使用這里還是以basic.html中的代碼為例
回到我們說的組件初始化,抽離下上面的代碼就是:
var ExampleApplication = React.createClass({render:function(){ returnNealyang}})
熟悉react使用的人都知道,render方法不能為空,當然,createClass中我們也可以去寫一寫生命周期的鉤子函數,這里我們暫且省略,畢竟目前我們更加的關注react組建的初始化過程。
同樣,熟悉react使用方法的人也會有疑惑了,怎么實例代碼中的render最后return的是React.DOM.p(null,message)
所以到這里,就不得不說一下react的編譯階段了
編譯階段我們都知道,在js中直接編寫html代碼,或者。。。jsx語法這樣的AST,在js詞法分析階段就會拋出異常的。
對的,所以我們在編寫react代碼的時候都會借助babel去轉碼
從babel官網上寫個例子即可看出:
對呀!明明人家用的是react.createElement方法,我們怎么出現個React.DOM.p...
OK,歷史原因:
react現在版本中,使用babel-preset-react來編譯jsx,這個preset又包含了4個插件,其中transform-react-jsx負責編譯jsx,調用了React.createElement函數生成虛擬組件
在react-0.3里,編譯結果稍稍有些不同,官方給出的示例文件,使用JSXTransformer.js編譯jsx(也就是),對于native組件和composite組件編譯的方式也不一致。也就是我們看到的React.DOM.p or ReactComponsiteComponent
native組件:編譯成React.DOM.xxx(xxx如div),函數運行返回一個ReactNativeComponent實例。
composite組件:編譯成createClass返回的函數調用,函數運行返回一個ReactCompositeComponent實例
題外話,不管用什么框架,到瀏覽器這部分的,什么花里胡哨的都不復存在。我這就是js、css、html。所以我們這里的ReactCompositeComponent最終其實還是需要轉成原生元素的 。
組件創建從React.js中我們可以找到createClass的出處:
"use strict"; var ReactCompositeComponent = require("ReactCompositeComponent"); ... var React = { ... createClass: ReactCompositeComponent.createClass, ... }; module.exports = React;
createClass 代碼
var ReactCompositeComponentBase = function() {}; function mixSpecIntoComponent(Constructor, spec) { var proto = Constructor.prototype; for (var name in spec) { if (!spec.hasOwnProperty(name)) { continue; } var property = spec[name]; var specPolicy = ReactCompositeComponentInterface[name]; if (RESERVED_SPEC_KEYS.hasOwnProperty(name)) { RESERVED_SPEC_KEYS[name](Constructor, property); } else if (property && property.__reactAutoBind) { if (!proto.__reactAutoBindMap) { proto.__reactAutoBindMap = {}; } proto.__reactAutoBindMap[name] = property.__reactAutoBind; } else if (proto.hasOwnProperty(name)) { // For methods which are defined more than once, call the existing methods // before calling the new property. proto[name] = createChainedFunction(proto[name], property); } else { proto[name] = property; } } } createClass: function (spec) { var Constructor = function (initialProps, children) { this.construct(initialProps, children); }; // ReactCompositeComponentBase是React復合組件的原型函數 Constructor.prototype = new ReactCompositeComponentBase(); Constructor.prototype.constructor = Constructor; // 把消費者聲明配置spec合并到Constructor.prototype中 mixSpecIntoComponent(Constructor, spec); // 判斷合并后的結果有沒有render,如果沒有 render,拋出一個異常 invariant( Constructor.prototype.render, "createClass(...): Class specification must implement a `render` method." ); //工廠 var ConvenienceConstructor = function (props, children) { return new Constructor(props, children); }; ConvenienceConstructor.componentConstructor = Constructor; ConvenienceConstructor.originalSpec = spec; return ConvenienceConstructor; },
mixSpecIntoComponent 方法就是講spec的屬性賦值給Constructor的原型上
createClass返回一個ConvenienceConstructor構造函數,構造函數接受props、children 構造函數的靜態方法componentConstructor和originalSpec分別指向Constructor和spec。
有種類似于寄生組合式繼承的寫法,Constructor為每一個組件實例的原型(`var instance = new Constructor();
instance.construct.apply(instance, arguments);`)。Constructor原型指向ReactCompositeComponentBase,又把構造器指向Constructor自己。然后把傳入的spec合并到Constructor.prototype中。判斷合并后的結果有沒有render,如果沒有 render,拋出一個異常
其實很多人看到這估計都會很疑惑,為毛這樣搞???直接返回個構造函數不就可以了嘛。
其實react在后面做diff算法的時候,是采用組件的Constructor來判斷組件是否相同的。如此可以保證每個createClass創建出來的組件都是一個新的Constructor。
ok,那么我直接用寄生繼承呀
// 寫法1 const createClass = function(spec) { var Constructor = function (initialProps, children) { this.construct(initialProps, children); }; Constructor.prototype = new ReactCompositeComponentBase(); Constructor.prototype.constructor = Constructor; mixSpecIntoComponent(ReactCompositeComponentBase, spec) return Constructor } const Table1 = new createClass(spec)(props, children); //console.log(Table1.constructor)
為什么還需要ConvenienceConstructor呢?說實話,我也不知道,然后看了在網上查到相關信息說道:
上面寫法在大多數情況下并不會產生什么問題,但是,當團隊里的人無意中修改錯點什么,比如:
Table1.prototype.onClick = null
這樣,所有Table1實例化的組件,onClick全部為修改后的空值
我們知道,js是動態解釋型語言,函數可以運行時被隨意篡改。而靜態編譯語言在運行時期間,函數不可修改(某些靜態語言也可以修改)。所以采用這種方式防御用戶對代碼的篡改。
組件實例化既然createClass返回的是一個構造函數,那么我們就來看看他的實例化吧
/** * Base constructor for all React component. * * Subclasses that override this method should make sure to invoke * `ReactComponent.Mixin.construct.call(this, ...)`. * * @param {?object} initialProps * @param {*} children * @internal */ construct: function (initialProps, children) { this.props = initialProps || {}; if (typeof children !== "undefined") { this.props.children = children; } // Record the component responsible for creating this component. this.props[OWNER] = ReactCurrentOwner.current; // All components start unmounted. this._lifeCycleState = ComponentLifeCycle.UNMOUNTED; },
其實也就是將props、children掛載到this.props上 以及生命周期的設置。這里暫且不說,因為我也正在看。。。哇咔咔
這里的
this.props[OWNER] = ReactCurrentOwner.current;
this.props[OWNER]指的是當前組件的容器(父)組件實例
如果我們直接在basic.html中打印就直接出來的是null,但是如果像如下的方式書寫:
const Children = React.createClass({ componentDidMount = () => console.log(this.props["{owner}"]), render = () => null }) const Parent = React.createClass({ render: () =>})
這里輸出的就是Parent組件實例。
再看看ReactCurrentOwner.current的賦值就明白了
_renderValidatedComponent: function () { ReactCurrentOwner.current = this; var renderedComponent = this.render(); ReactCurrentOwner.current = null; invariant( ReactComponent.isValidComponent(renderedComponent), "%s.render(): A valid ReactComponent must be returned.", this.constructor.displayName || "ReactCompositeComponent" ); return renderedComponent; }
可以看出來,在執行render前后,分別設置了ReactCurrentOwner.current的值,這樣就能保證render函數內的子組件能賦上當前組件的實例,也就是this。
組件渲染我們先撇開事務、事件池、生命周期、diff當然也包括fiber 等,先不談,其實渲染就是將經過babel編譯后的,當然這里是JSXTransformer.js編譯后的Ojb給寫入到HTML中而已。
export default function render(vnode, parent) { let dom; if (typeof vnode === "string") { dom = document.createTextNode(vnode); // let span_dom = document.createElement("span") // span_dom.appendChild(dom); // parent.appendChild(span_dom); parent.appendChild(dom); } else if (typeof vnode.nodeName === "string") { dom = document.createElement(vnode.nodeName); setAttrs(dom, vnode.props); parent.appendChild(dom) for(let i = 0; i < vnode.children.length; i++) { render(vnode.children[i], dom) } }else if(typeof vnode.nodeName === "function"){ let innerVnode = vnode.nodeName.prototype.render(); render(innerVnode,parent) } } function setAttrs(dom, props) { const ALL_KEYS = Object.keys(props); ALL_KEYS.forEach(k =>{ const v = props[k]; // className if(k === "className"){ dom.setAttribute("class",v); return; } if(k == "style") { if(typeof v == "string") { dom.style.cssText = v } if(typeof v == "object") { for (let i in v) { dom.style[i] = v[i] } } return } if(k[0] == "o" && k[1] == "n") { const capture = (k.indexOf("Capture") != -1) dom.addEventListener(k.substring(2).toLowerCase(),v,capture) return } dom.setAttribute(k, v) }) }
是的,就這樣
OK,回到源碼~
在我們目前使用的react版本中,渲染調用的是ReactDOM.render方法,這里ReactMount.renderComponent為我們的入口方法。
ReactMount.renderComponent在react初探章節講過。如果組件渲染過,就更新組件屬性,如果組件沒有渲染過,掛載組件事件,并把虛擬組件渲染成真實組件插入container內。通常,我們很少去調用兩次renderComponent,所以大多數情況下不會更新組件屬性而是新創建dom節點并插入到container中。
ReactComponent.mountComponentIntoNode之內開啟了一個事務,事務保證渲染階段不會有任何事件觸發,并阻斷的componentDidMount事件,待執行后執行等,事務在功能一章我們會詳細講解,這里不細討論。
ReactComponent._mountComponentIntoNode這個函數調用mountComponent獲得要渲染的innerHTML,然后更新container的innerHTML。
ReactCompositeComponent.mountComponent是最主要的邏輯方法。這個函數內處理了react的生命周期以及componentWillComponent和componentDidMount生命周期鉤子函數,調用render返回實際要渲染的內容,如果內容是復合組件,仍然會調用mountComponent,復合組件最終一定會返回原生組件, 并且最終調用ReactNativeComponent的mountComponent函數生成要渲染的innerHTML。
renderComponent: function(nextComponent, container) { var prevComponent = instanceByReactRootID[getReactRootID(container)]; if (prevComponent) { var nextProps = nextComponent.props; ReactMount.scrollMonitor(container, function() { prevComponent.replaceProps(nextProps); }); return prevComponent; } ReactMount.prepareTopLevelEvents(ReactEventTopLevelCallback); var reactRootID = ReactMount.registerContainer(container); instanceByReactRootID[reactRootID] = nextComponent; nextComponent.mountComponentIntoNode(reactRootID, container); return nextComponent; },
這段代碼邏輯大概就是上面的流程圖,這里不再贅述。
mountComponentIntoNode
從debugger中,可以看出mountComponentIntoNode第一個參數其實傳入的是react分配給組件的一個唯一標識
mountComponentIntoNode: function(rootID, container) { var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); transaction.perform( this._mountComponentIntoNode, this, rootID, container, transaction ); ReactComponent.ReactReconcileTransaction.release(transaction); },
源碼中,這里跟事務扯到了關系,其實我們只要關注渲染本身,所以這里我們直接看this._mountComponentIntoNode的方法實現
_mountComponentIntoNode
_mountComponentIntoNode: function(rootID, container, transaction) { var renderStart = Date.now(); var markup = this.mountComponent(rootID, transaction); ReactMount.totalInstantiationTime += (Date.now() - renderStart); var injectionStart = Date.now(); // Asynchronously inject markup by ensuring that the container is not in // the document when settings its `innerHTML`. var parent = container.parentNode; if (parent) { var next = container.nextSibling; parent.removeChild(container); container.innerHTML = markup; if (next) { parent.insertBefore(container, next); } else { parent.appendChild(container); } } else { container.innerHTML = markup; } ReactMount.totalInjectionTime += (Date.now() - injectionStart); },
上述代碼流程大概如下:
流程的確如上,作為一個初探源碼者,我當然不關心你到底是在哪innerHTML的,我想知道你是腫么把jsx編譯后的Obj轉成HTML的哇~
ReactCompositeComponent.mountComponent
這里類變成了ReactCompositeComponent(debugger可以跟蹤每一個函數)
源碼中的this.mountComponent,為什么不是調用ReactComponent.mountComponent呢?這里主要使用了多重繼承機制(Mixin,后續講解)。
mountComponent: function(rootID, transaction) { // 掛在組件ref(等于當前組件實例)到this.refs上 ReactComponent.Mixin.mountComponent.call(this, rootID, transaction); // Unset `this._lifeCycleState` until after this method is finished. // 這是生命周期 this._lifeCycleState = ReactComponent.LifeCycle.UNMOUNTED; this._compositeLifeCycleState = CompositeLifeCycle.MOUNTING; // 組件聲明有props,執行校驗 if (this.constructor.propDeclarations) { this._assertValidProps(this.props); } // 為組件聲明時間綁定this if (this.__reactAutoBindMap) { this._bindAutoBindMethods(); } //獲取state this.state = this.getInitialState ? this.getInitialState() : null; this._pendingState = null; // 如果組件聲明componentWillMount函數,執行并把setState的結果更新到this.state上 if (this.componentWillMount) { this.componentWillMount(); // When mounting, calls to `setState` by `componentWillMount` will set // `this._pendingState` without triggering a re-render. if (this._pendingState) { this.state = this._pendingState; this._pendingState = null; } } // 如果聲明了componentDidMount,則把其加入到ReactOnDOMReady隊列中 if (this.componentDidMount) { transaction.getReactOnDOMReady().enqueue(this, this.componentDidMount); } // 調用組件聲明的render函數,并返回ReactComponent抽象類實例(ReactComponsiteComponent或 // ReactNativeComponent),調用相應的mountComponent函數 this._renderedComponent = this._renderValidatedComponent(); // Done with mounting, `setState` will now trigger UI changes. this._compositeLifeCycleState = null; this._lifeCycleState = ReactComponent.LifeCycle.MOUNTED; return this._renderedComponent.mountComponent(rootID, transaction); },
這個函數式VDom中最為重要的函數,操作也最為復雜,執行操作大概如下:
如上,很多內容跟我們這part有點超綱。當然,后面都會說道,關于react的渲染,其實我們的工作很簡單,不關于任何,在拿到render的東西后,如何解析,其實就是最后一行代碼:this._renderedComponent.mountComponent(rootID, transaction);
mountComponent: function(rootID, transaction) { ReactComponent.Mixin.mountComponent.call(this, rootID, transaction); assertValidProps(this.props); return ( this._createOpenTagMarkup() + this._createContentMarkup(transaction) + this._tagClose ); }, _createOpenTagMarkup: function() { var props = this.props; var ret = this._tagOpen; for (var propKey in props) { if (!props.hasOwnProperty(propKey)) { continue; } var propValue = props[propKey]; if (propValue == null) { continue; } if (registrationNames[propKey]) { putListener(this._rootNodeID, propKey, propValue); } else { if (propKey === STYLE) { if (propValue) { propValue = props.style = merge(props.style); } propValue = CSSPropertyOperations.createMarkupForStyles(propValue); } var markup = DOMPropertyOperations.createMarkupForProperty(propKey, propValue); if (markup) { ret += " " + markup; } } } return ret + " id="" + this._rootNodeID + "">"; }, /** * Creates markup for the content between the tags. * * @private * @param {ReactReconcileTransaction} transaction * @return {string} Content markup. */ _createContentMarkup: function(transaction) { // Intentional use of != to avoid catching zero/false. var innerHTML = this.props.dangerouslySetInnerHTML; if (innerHTML != null) { if (innerHTML.__html != null) { return innerHTML.__html; } } else { var contentToUse = this.props.content != null ? this.props.content : CONTENT_TYPES[typeof this.props.children] ? this.props.children : null; var childrenToUse = contentToUse != null ? null : this.props.children; if (contentToUse != null) { return escapeTextForBrowser(contentToUse); } else if (childrenToUse != null) { return this.mountMultiChild( flattenChildren(childrenToUse), transaction ); } } return ""; }, function ReactNativeComponent(tag, omitClose) { this._tagOpen = "<" + tag + " "; this._tagClose = omitClose ? "" : "" + tag + ">"; this.tagName = tag.toUpperCase(); }
代碼稍微多一點,但是工作目標很單一,就是為了將描述jsx的obj解析成HTML string。其實可以參照我上面直接亮出來的自己寫的代碼部分。
如上,其實我們已經完成了組件的初始化、渲染~
好吧,我們一直說的渲染的核心部分還沒有細說~~~
掛載組件ref到this.refs上,設置生命周期、狀態和rootIDmountComponent: function(rootID, transaction) { invariant( this._lifeCycleState === ComponentLifeCycle.UNMOUNTED, "mountComponent(%s, ...): Can only mount an unmounted component.", rootID ); var props = this.props; if (props.ref != null) { ReactOwner.addComponentAsRefTo(this, props.ref, props[OWNER]); } this._rootNodeID = rootID; this._lifeCycleState = ComponentLifeCycle.MOUNTED; // Effectively: return ""; },
如果組件ref屬性為空,則為組件的this.refs上掛在當前組件,也就是this,實現如下:
addComponentAsRefTo: function(component, ref, owner) { owner.attachRef(ref, component); }
attachRef: function(ref, component) { var refs = this.refs || (this.refs = {}); refs[ref] = component; },
上述代碼我刪除了相關的判斷警告。
設置組件生命狀態組件的生命狀態和生命周期鉤子函數是react的兩個概念,在react中存在兩種生命周期
主:組件生命周期:_lifeCycleState,用來校驗react組件在執行函數時狀態值是否正確
輔:復合組件生命周期:_componsiteLifeCycleState,用來保證setState流程不受其他行為影響
_lifeCycleStatevar ComponentLifeCycle = keyMirror({ /** * Mounted components have a DOM node representation and are capable of * receiving new props. */ MOUNTED: null, /** * Unmounted components are inactive and cannot receive new props. */ UNMOUNTED: null });
組件生命周期非常簡單,就枚舉了兩種,MOUNTED and UNMOUNTED
在源碼中使用其只是為了在相應的階段觸發時候校驗,并且給出錯誤提示
getDOMNode: function() { invariant( ExecutionEnvironment.canUseDOM, "getDOMNode(): The DOM is not supported in the current environment." ); invariant( this._lifeCycleState === ComponentLifeCycle.MOUNTED, "getDOMNode(): A component must be mounted to have a DOM node." ); var rootNode = this._rootNode; if (!rootNode) { rootNode = document.getElementById(this._rootNodeID); if (!rootNode) { // TODO: Log the frequency that we reach this path. rootNode = ReactMount.findReactRenderedDOMNodeSlow(this._rootNodeID); } this._rootNode = rootNode; } return rootNode; },_compositeLifeCycleState
復合組件的生命周期只在一個地方使用:setState
var CompositeLifeCycle = keyMirror({ /** * Components in the process of being mounted respond to state changes * differently. */ MOUNTING: null, /** * Components in the process of being unmounted are guarded against state * changes. */ UNMOUNTING: null, /** * Components that are mounted and receiving new props respond to state * changes differently. */ RECEIVING_PROPS: null, /** * Components that are mounted and receiving new state are guarded against * additional state changes. */ RECEIVING_STATE: null });
replaceState: function(completeState) { var compositeLifeCycleState = this._compositeLifeCycleState; invariant( this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED || compositeLifeCycleState === CompositeLifeCycle.MOUNTING, "replaceState(...): Can only update a mounted (or mounting) component." ); invariant( compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_STATE && compositeLifeCycleState !== CompositeLifeCycle.UNMOUNTING, "replaceState(...): Cannot update while unmounting component or during " + "an existing state transition (such as within `render`)." ); this._pendingState = completeState; // Do not trigger a state transition if we are in the middle of mounting or // receiving props because both of those will already be doing this. if (compositeLifeCycleState !== CompositeLifeCycle.MOUNTING && compositeLifeCycleState !== CompositeLifeCycle.RECEIVING_PROPS) { this._compositeLifeCycleState = CompositeLifeCycle.RECEIVING_STATE; var nextState = this._pendingState; this._pendingState = null; var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); transaction.perform( this._receivePropsAndState, this, this.props, nextState, transaction ); ReactComponent.ReactReconcileTransaction.release(transaction); this._compositeLifeCycleState = null; } },
setState會調用replaceState ,然后調用_receivePropsAndState來更新界面
如果組件正處在mounting的過程或者接受props的過程中,那么將state緩存在_pendingState中,并不會更新界面的值。
校驗props_assertValidProps: function(props) { var propDeclarations = this.constructor.propDeclarations; var componentName = this.constructor.displayName; for (var propName in propDeclarations) { var checkProp = propDeclarations[propName]; if (checkProp) { checkProp(props, propName, componentName); } } }
this.constructor.propDeclarations 就是組件聲明的props屬性,由于props是運行時傳入的屬性。我們可以看到聲明props的屬性值即為checkProp
結束語其實至此,關于本篇組件的初始化、渲染已經介紹完畢,由于代碼中關于太多后續章節,生命周期、props、state、對象緩沖池、事務等,所以暫時都先略過,后續學習到的時候再回頭查閱。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97093.html
摘要:異步實戰狀態管理與組件通信組件通信其他狀態管理當需要改變應用的狀態或有需要更新時,你需要觸發一個把和載荷封裝成一個。的行為是同步的。所有的狀態變化必須通過通道。前端路由實現與源碼分析設計思想應用是一個狀態機,視圖與狀態是一一對應的。 React實戰與原理筆記 概念與工具集 jsx語法糖;cli;state管理;jest單元測試; webpack-bundle-analyzer Sto...
摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數各司其職,輸入輸出都是可預測,一路下來很順暢。通過進一步觀察可以發現,預廢棄的三個生命周期函數都發生在虛擬的構建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時間準備前端招聘事項...
摘要:一般情況下,都是作為等其他子路由的上層路由,使用了,接收一個屬性,傳遞給消費子組件。創建對象,兼容老瀏覽器,其他和沒有大區別總結分為四個包,分別為,其中是瀏覽器相關,是相關,是核心也是共同部分,是一些配置相關。 這篇文章主要講的是分析 react-router 源碼,版本是 v5.x,以及 SPA 路由實現的原理。 文章首發地址 單頁面應用都用到了路由 router,目前來看實現路由有...
摘要:所謂知其然還要知其所以然本文將分析的部分源碼包括組件初始渲染的過程和組件更新的過程在這之前假設讀者已經對有一定了解知道區別了解生命周期事務批量更新大致概念等如何分析源碼代碼架構預覽首先我們找到在上的地址把版本的源碼下來觀察它的整體架構這 所謂知其然還要知其所以然. 本文將分析 React 15-stable的部分源碼, 包括組件初始渲染的過程和組件更新的過程.在這之前, 假設讀者已經:...
摘要:通過前端路由可以實現單頁應用本文首先從前端路由的原理出發,詳細介紹了前端路由原理的變遷。接著從的源碼出發,深入理解是如何實現前端路由的。執行上述的賦值后,頁面的發生改變。 ??react-router等前端路由的原理大致相同,可以實現無刷新的條件下切換顯示不同的頁面。路由的本質就是頁面的URL發生改變時,頁面的顯示結果可以根據URL的變化而變化,但是頁面不會刷新。通過前端路由可以實現...
閱讀 1699·2021-11-12 10:36
閱讀 1615·2021-11-12 10:36
閱讀 3442·2021-11-02 14:46
閱讀 3798·2019-08-30 15:56
閱讀 3534·2019-08-30 15:55
閱讀 1463·2019-08-30 15:44
閱讀 1044·2019-08-30 14:00
閱讀 2735·2019-08-29 18:41