前言
衆所周知React Native開發中,頁面View書寫佈局採用了React 的JSX語法,而在ReactNative面試中可能會遇到有關JSX相關的面試題,今天和大家分享有關JSX的知識,爲你的面試助一臂之力。
JSX的定義
JSX到底是什麼?我們先看看[React官網](https://reactjs.org/docs/glossary.html#jsx)的定義。
JSX is a syntax extension to JavaScript. It is similar to a template
language, but it has full power of JavaScript. JSX gets compiled to
React.createElement() calls which return plain JavaScript objects
called “React elements”.
JSX是JavaScript的一種語法擴展,類似一種模板語言,但是它擁有JavaScript的全部能力。JSX被編譯成React.createElement(), React.createElement()返回一個名爲“React element”的JavaScript對象。
從以上定義可以看到JSX是作爲一種擴展存在的,那就會出現在低版本的瀏覽器或者其他開發環境中的兼容性問題。怎麼解決這個問題呢?上面定義中也提到了, JSX被編譯成React.createElement()並返回一個js對象。
JSX編譯工具-Babel
將JSX編譯成js對象就是由Babel完成的。
[Babel官網](https://babeljs.io/docs/en/)的定義:
Babel是一個工具鏈,主要用於將ECMAScript 2015+代碼轉換爲當前和舊版本瀏覽器或環境中向後兼容的JavaScript版本。
比如在ES2015中出現的箭頭函數,轉成ES5環境的支持
// Babel Input: ES2015 arrow function
[1, 2, 3].map((n) => n + 1);
// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
return n + 1;
});
類似的,Babel也可以編譯JSX語法。
Babel can convert JSX syntax! Check out our React preset to get
started. Use it together with the babel-sublime package to bring
syntax highlighting to a whole new level.
如下一段代碼是ReactNative經典的入門Hello World.
import React from 'react';
import { Text, View } from 'react-native';
const HelloWorldApp = () => {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center"
}}>
<Text>Hello, world!</Text>
</View>
)
}
export default HelloWorldApp;
我們將佈局文件使用[Babel工具]編譯一下,看看編譯後的代碼是什麼樣子的
從編譯結果可以看到,所有的JSX標籤都被編譯成了React.createElement調用了,所以我們書寫JSX其實就是在寫React.createElement()函數調用。
JSX描述小結
JSX的正確描述應該是它是JavaScript調用函數React.createElement()的語法糖。
爲什麼React使用JSX
在上面分析中我們看到通過Babel轉換JSX後其實就是React.createElement的調用,爲什麼我們不直接使用React.createElement,而要使用JSX語法糖呢?
其實從上面Babel編譯截圖上可以看到,佈局中只有兩個元素View,Text,直接寫React.createElement的代碼就很多了,如果是很多元素的嵌套,那畫面沒法想象了,首先代碼閱讀體驗不好,佈局的嵌套也非常的混亂。相比較而言JSX就顯得層次分明,嵌套關係清晰。
結論:使用JSX語法糖可以使用開發人員使用類Html的標籤創建虛擬DOM進行佈局,既提高了開發效率和體驗,也降低了學習成本。
JSX是如何映射爲虛擬DOM的
我們已經知道了JSX會通過Babel編譯成React.createElement()調用,我們看一下這個方法的源碼實現:
function createElement(type, config, children) {
var propName = void 0;
// Reserved names are extracted
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
}
if (hasValidKey(config)) {
key = '' + config.key;
}
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) {
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.
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
{
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
// Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
{
if (key || ref) {
var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
通過以上源碼我們可以看到createElement函數接受三個參數,分別是type, config, childen。
type:標識節點的類型。可以是標記名字符串(例如'div'或'span')、React組件類型(類或函數)或React片段類型。
config: 組件的所有屬性以鍵值對的形式存儲在config中,以對象的形式傳遞。
childen: 組件的嵌套內容,也就是子元素,子節點,也是以對象的形式進行傳遞。
createElement函數主要做了如下工作:
根據config,處理key, ref, self, source的賦值工作
遍歷config,刷選適合的屬性放入到props中
獲取子元素放入到children中
處理defaultProps
最終調用ReactElement來創建元素。
如何渲染到DOM
ReactElement是通過React.render()方法渲染到DOM。
下面是一個示例
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
render()
ReactDOM.render(element, container[, callback])
ReactDOM.render函數接受三個參數,第一個是代表元素或者節點,第二個container是一個真是的DOM節點,作爲一個DOM容器,用於包含渲染的元素內容。
總結
JSX只是一種語法糖,你也可以不使用JSX進行書寫頁面佈局,但是使用JSX會使頁面佈局層次分明,嵌套邏輯清晰,方便前端開發人員使用熟悉的類Html標籤進行開發,調高了開發體驗和效率,並且也提供了更多有用錯誤和警告信息。
通過以上內容,相信面試的時候遇到此類問題,一定可以回答的比較深入了。
--- END ---
君偉說
本文分享自微信公衆號 - 君偉說(wayne90214)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。