摘要:一語法轉換到語法從轉換到會用到,所以先熟悉下到的轉換。對于庫作者而言,凍結對象可防止有人修改庫的核心對象。
一、JSX語法轉換到Js語法
從 JSX 轉換到 JS 會用到React.createElement(),所以先熟悉下 JSX 到 JS 的轉換。
這邊是 JSX 語法:
this is spanOne this is spanTwo
這邊是轉化成的 js 語法:
React.createElement( "div", { id: "one", class: "two" }, React.createElement( "span", { id: "spanOne" }, "this is spanOne"), React.createElement("span", { id: "spanTwo" }, "this is spanTwo") );
React.createElement("標簽名","Object,包含div的props","children子節點1","children子節點2","...")
這邊是 JSX 語法:
function Div(){ }this is spanOne this is spanTwo
這邊是轉化成的 js 語法:
React.createElement(Div, {} , xxx );
如果標簽名大寫,則表示組件 Div(也就是function),小寫表示 html 的標簽 也就是說:自定義的組件必須大寫字母開頭 二、React.createElement() 作用: 源碼: 解析: (1)hasValidRef() 作用: 源碼: ① 注意:__DEV__表示測試環境,是供React內部測試的,可以不看,我簡單地解釋了下 ② 在jQuery中fn.call(xxx,a1,a2,...)或fn.apply(xxx,array)的更大作用是綁定this ③ Object.getOwnPropertyDescriptor()的作用是返回某個對象屬性的描述對象( descriptor ) 比如: 關于Object.getOwnPropertyDescriptor()和Object.getOwnPropertyDescriptors()的區別,請看:https://blog.csdn.net/qq_30100043/article/details/53424963 (2)hasValidKey 作用: 源碼: (3)雖然React.createElement()只傳三個參數,但從第三個參數開始,利用arguments來獲取剩下的參數 (4)Object.freeze() 對于 JS 庫作者而言,凍結對象可防止有人修改庫的核心對象。 關于 JS 凍結對象的方法,請看:JS紅皮書解讀之防篡改對象 (5)最后是 return 了ReactElement()方法,注意props中的children屬性就是React組件的children react組件的children屬性不會被覆蓋: 子組件: 結果: 三、ReactElement() 作用: 源碼: 解析: (2)__DEV__注釋中有提到WeakMap, 想更詳細地了解的話,可以參考下這篇文章: 關于垃圾回收機制,請看:淺談下垃圾回收機制(1) (3)該方法比較簡單,就是初始化了一個對象,并將其標記為React.Element對象($$typeof=REACT_ELEMENT_TYPE) (完) 文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。 轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105632.html
源碼地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.js
創建React.Element,示例請看一、JSX語法轉換到Js語法//注意:react只寫了3個參數,實際上,從第三個參數往后都是children
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
//賦給標簽的props不為空時
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
//防止是Number
key = "" + config.key;
}
//__self、__source 暫時不知道是干啥用的屬性
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// Remaining properties are added to a new props object
for (propName in config) {
//如果config中的屬性不是標簽原生屬性,則放入props對象中
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
//子元素數量
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
//依次將children push進array中
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
//如果是development環境的話
if (__DEV__) {
//凍結array
//未在微信發表
//https://www.jianshu.com/p/91e5dc520c0d?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends&from=singlemessage&isappinstalled=0
if (Object.freeze) {
Object.freeze(childArray);
}
}
//開發中寫的this.props.children就是子元素的集合
props.children = childArray;
}
// Resolve default props
//為傳入的props設置默認值,比如:
//class Comp extends React.Component{
// static defaultProps = {
// aaa: "one",
// bbb: () => {},
// ccc: {},
// };
//
// }
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
//如果props數組中未設值,則設置默認值(注意:null也算設置了值)
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (__DEV__) {
//一旦ref或key存在
if (key || ref) {
//如果type是組件的話,賦值displayName
const displayName =
typeof type === "function"
? type.displayName || type.name || "Unknown"
: type;
//可不看
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(
type, //"div"
key, //null
ref, //null
self, //null
source, //null
ReactCurrentOwner.current, //null或Fiber
props, //自定義的屬性、方法,注意:props.children=childArray
);
}
判斷是否設置了ref的屬性,true有,false沒有//判斷是否設置了ref的屬性,true有,false沒有
function hasValidRef(config) {
//如果是development環境的話
if (__DEV__) {
//如果config中存在ref屬性的話
//在jQuery中 .call/.apply的更大作用是綁定this
if (hasOwnProperty.call(config, "ref")) {
//Object.getOwnPropertyDescriptor() es5
//Object.getOwnPropertyDescriptors() es6
//https://blog.csdn.net/qq_30100043/article/details/53424963
//返回對象config的屬性ref 的get對象
const getter = Object.getOwnPropertyDescriptor(config, "ref").get;
//如果isReactWarning,則忽略ref屬性,返回false
if (getter && getter.isReactWarning) {
return false;
}
}
}
//
return config.ref !== undefined;
}
var obj = { p: "a" };
Object.getOwnPropertyDescriptor(obj, "p")
//返回
// Object { value: "a",
// writable: true,
// enumerable: true,
// configurable: true
}
判斷是否設置了key,同hasValidRef,不解釋了function hasValidKey(config) {
if (__DEV__) {
if (hasOwnProperty.call(config, "key")) {
const getter = Object.getOwnPropertyDescriptor(config, "key").get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.key !== undefined;
}
使用Object.freeze()凍結的對象是最嚴格的防篡改級別,既不可擴展,也是密封的,不可修改屬性。
父組件:return(
console.log(this.props)
源碼地址:https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react/src/ReactElement.js
通過工廠模式創建React.Element對象,你打印一個React組件的話,會是下面這個樣子:/**
* Factory method to create a new React element. This no longer adheres to
* the class pattern, so do not use new to call it. Also, no instanceof check
* will work. Instead test $$typeof field against Symbol.for("react.element") to check
* if something is a React Element.
*
* @param {*} type
* @param {*} props
* @param {*} key
* @param {string|object} ref
* @param {*} owner
* @param {*} self A *temporary* helper to detect places where `this` is
* different from the `owner` when React.createElement is called, so that we
* can warn. We want to get rid of owner and replace string `ref`s with arrow
* functions, and as long as `this` and owner are the same, there will be no
* change in behavior.
* @param {*} source An annotation object (added by a transpiler or otherwise)
* indicating filename, line number, and/or other information.
* @internal
*/
// type, //"div"
// key, //null
// ref, //null
// self, //null
// source, //null
// ReactCurrentOwner.current, //null或Fiber
// props, //自定義的屬性、方法,注意:props.children=childArray
const ReactElement = function(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
//標識element的類型
//因為jsx都是通過createElement創建的,所以ReactElement的類型固定:為REACT_ELEMENT_TYPE
//重要!因為react最終渲染到DOM上時,需要判斷$$typeof===REACT_ELEMENT_TYPE
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
//設置元素的內置屬性
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
//記錄創建react.element的組件(this?)
_owner: owner,
};
if (__DEV__) {
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
//驗證flag是不固定的.我們將其放置在一個store上,從而能凍結整個object
//這樣一旦它們被用在開發環境時,用WeakMap代替
//WeakMap
// http://es6.ruanyifeng.com/#docs/set-map
element._store = {};
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
//方便測試用
Object.defineProperty(element._store, "validated", {
configurable: false,
enumerable: false,
writable: true,
value: false,
});
// self and source are DEV only properties.
Object.defineProperty(element, "_self", {
configurable: false,
enumerable: false,
writable: false,
value: self,
});
// Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, "_source", {
configurable: false,
enumerable: false,
writable: false,
value: source,
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};
(1)通過$$typeof確保是React.Element類型,從而渲染到真正的DOM樹上
簡單說下WeakMap的作用:
你往WeakMap上的對象 a 添加數據,對象 b 引用 對象 a,之后對象 b 不引用 對象 a,a 就被垃圾回收,不用WeakMap的話,即使對象 b 以后不引用對象 a了,a 也不會被垃圾回收,因為強引用是不會觸發垃圾回收機制的,需要手動刪除,很麻煩。
http://es6.ruanyifeng.com/#docs/set-map
摘要:在源碼添加的注釋在。解析的主要調用如下當然在下還有些其他的調用。此外,凍結一個對象后該對象的原型也不能被修改。方法封閉一個對象,阻止添加新屬性并將所有現有屬性標記為不可配置。 前言 ReactElement并不像之前所談的PureComponent和Component那樣被頻繁的顯示使用,但我估計他應該是在react暴露出的api中被調用最為頻繁的,關于此看完后面便知。ReactEle...
摘要:調用棧是這樣的這里生成的我們將其命名為,它將作為參數傳入到。整個的調用棧是這樣的組件間的層級結構是這樣的到此為止,頂層對象已經構造完畢,下一步就是調用來自的方法,進行頁面的渲染了。通過表達的結構最終會轉化為一個純對象,用于下一步的渲染。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言...
摘要:僅針對數據屬性描述有效獲取該屬性的訪問器函數。當且僅當指定對象的屬性可以被枚舉出時,為。凍結及其對象主要目的是為提高測試環境下效率,將的一些屬性配置為不可枚舉,進行遍歷的時候跳過這些屬性。 React系列 React系列 --- 簡單模擬語法(一)React系列 --- Jsx, 合成事件與Refs(二)React系列 --- virtualdom diff算法實現分析(三)React...
摘要:的創建組件,其實根源還是調用了編譯之后一般寫法建議用來進行源碼的跟蹤鏈接從源碼角度來看創建一個組件的過程中發生了什么。 https://github.com/jimwmg/Rea... 1 React.createClass( ) var HelloWorld = React.createClass({ render : function(){ return ...
摘要:判斷是否是有效的元素。主要和同構相關。是真實的模擬,真實是由真實的元素構成,也是由虛擬的元素構成。當這些對象上的數據發生變化時,通過打把變化同步到真實的上去。原創新書移動前端高效開發實戰已在亞馬遜京東當當開售。 作者: 阿希 (滬江Web前端開發工程師)本文原創,轉載請注明作者及出處。 了解 React 的人幾乎都聽過說 Virtual DOM,甚至不了解 React 的人也聽過 Vi...
閱讀 3514·2023-04-25 17:35
閱讀 2587·2021-11-24 09:39
閱讀 2525·2021-10-18 13:32
閱讀 3409·2021-10-11 10:58
閱讀 1630·2021-09-26 09:55
閱讀 6134·2021-09-22 15:47
閱讀 959·2021-08-26 14:15
閱讀 3466·2019-08-30 15:55