javascript
react 数字转字符_深入浅出 React -- JSX
什么是 JSX
JSX 是一個 JavaScript 的語法擴展。JSX 可能會使人聯想到模版語言,但它具有 JavaScript 的全部功能在 React 中,JSX 僅僅是 React.createElement(component, props, ...children) 函數的語法糖
如下 JSX 代碼:
<MyButton color="blue" shadowSize={2}>Clicke Me </MyButton>會編譯為:
React.createElement(MyButton,{color: 'blue', shadowSize: 2},'Click Me' )React 必須在作用域內
由于 JSX 會編譯為 React.createElement 調用形式,所以 React 庫也必須包含在 JSX 代碼作用域內
如果不使用打包工具而是直接通過 <script> 標簽加載 React,則必須將 React 掛載到全局變量中
用戶自定義的組件必須以大寫字母開頭
以小寫字母開頭的元素代表一個 HTML 內置組件,比如 <div> 或者 <span> 會生成相應的字符串 'div' 或者 'span' 傳遞給 React.createElement(作為參數)。大寫字母開頭的元素則對應著在 JavaScript 引入或自定義的組件,如 <Foo /> 會編譯為 React.createElement(Foo)
JSX 語法
在 JSX 中嵌入表達式
在 JSX 語法中,你可以在打括號內放置任何有效的 JavaScript 表達式
function formatName(user) {return user.firstName + ' ' + user.lastName; }const user = {firstName: 'Harper',lastName: 'Perez' }const element = (<h1>Hello, {formatName(user)}!</h1> )ReactDOM.render(element,document.getElementById('root') )JSX 也是一個表達式
在編譯后,JSX 表達式會被轉為普通 JavaScript 函數調用,并且對其取值后得到 JavaScript 對象
function getGreeting(user) {if (user) {return <h1>Hello, {formatName(user)}!</h1>;}return <h1>Hello, Stranger.</h1>; }JSX 特定屬性
在屬性中嵌入 JavaScript 表達式時,不要在大括號外面加上引號。你應該僅使用引號(對于字符串值)或大括號(對于表達式)中的一個,對于同一屬性不能同時使用這兩種符號。
const element = <img src={user.avatarUrl}></img> 因為 JSX 語法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase(小駝峰命名)來定義屬性的名稱,而不使用 HTML 屬性名稱的命名約定。使用 JSX 指定子元素
假如一個標簽里面沒有內容,你可以使用 /> 來閉合標簽,就像 XML 語法一樣:
const element = <img src={user.avatarUrl} />JSX 標簽里能夠包含很多子元素:
const element = (<div><h1>Hello!</h1><h2>Good to see you here.</h2></div> )JSX 防止注入攻擊
React DOM 在渲染所有輸入內容之前,默認會進行轉義。
所有的內容在渲染之前都被轉換成了字符串。這樣可以有效地防止 XSS(cross-site-scripting, 跨站腳本)攻擊。
JSX 表示對象
Babel 會把 JSX 轉譯成一個名為 React.createElement() 函數調用
以下兩種示例代碼完全等效:
const element = (<h1 className="greeting">Hello, world!</h1> )const element = React.createElement('h1',{className: 'greeting'},'Hello, world!' )JSX 中的 Props
JavaScript 表達式作為 Props
<MyComponent foo={1 + 2 + 3 + 4} />字符串字面量
以下兩個 JSX 表達式是等價的:
<MyComponent message="hello world" /><MyComponent message={'hello world'} />當你將字符串字面量賦值給 prop 時,它的值是未轉義的
以下兩個 JSX 表達式是等價的:
<MyComponent message="<3" /><MyComponent message={'<3'} />Props 默認值為 true
以下兩個 JSX 表達式是等價的:
<MyTextBox autocomplete /><MyTextBox autocomplete={true} />屬性展開
可以使用展開運算符 ... 來在 JSX 中傳遞整個 props 對象。
以下兩個組件是等價的:
function App1() {return <Greeting firstName="Ben" lastName="Hector" />; }function App2() {const props = {firstName: 'Ben', lastName: 'Hector'};return <Greeting {...props} />; }JSX 中的子元素
包含在開始和結束標簽之間的 JSX 表達式內容將作為特定屬性 props.children 傳遞給外層組件。
有幾種不同的方法來傳遞子元素:
字符串字面量
<MyComponent>Hello world!</MyComponent>編譯為:
<div>This is valid HTML & JSX at the same time.</div>JSX 會移除行首尾的空格以及空行。與標簽相鄰的空行均會被刪除,文本字符串之間的新行會被壓縮為一個空格。
因此以下的幾種方式都是等價的:
<div>Hello World</div><div>Hello World </div><div>HelloWorld </div><div>Hello World </div>JSX 子元素
<MyContainer><MyFirstComponent /><MySecondComponent /> </MyContainer>JavaScript 表達式作為子元素
JavaScript 表達式可以被包裹在 {} 中作為子元素。
以下表達式是等價的:
<MyComponent>foo</MyComponent><MyComponent>{'foo'}</MyComponent>這對于展示任意長度的列表非常有用。例如,渲染 HTML 列表:
function Item(props) {return <li>{props.message}</li>; }function TodoList() {const todos = ['finish doc', 'submit pr', 'nag dan to review'];return (<ul>{todos.map((message) => <Item key={message} message={message} />)}</ul>); }函數作為子元素
你可以將任何東西作為子元素傳遞給自定義組件,只要確保在該組件渲染之前能夠被轉換成 React 理解的對象。這種用法并不常見,但可以用于擴展 JSX。
function Item(props) {return <li>{props.message}</li>; }function TodoList() {const todos = ['finish doc', 'submit pr', 'nag dan to review'];return (<ul>{todos.map((message) => <Item key={message} message={message} />)}</ul>); }布爾類型、Null 以及 Undefined 將會忽略
false, null, undefined, and true 是合法的子元素。但它們并不會被渲染。
以下的 JSX 表達式渲染結果相同:
<div /><div></div><div>{false}</div><div>{null}</div><div>{undefined}</div><div>{true}</div>這有助于依據特定條件來渲染其他的 React 元素。例如,在以下 JSX 中,僅當 showHeader 為 true 時,才會渲染 <Header /> 組件:
<div>{showHeader && <Header />}<Content /> </div>值得注意的是有一些 “falsy” 值,如數字 0,仍然會被 React 渲染。例如,以下代碼并不會像你預期那樣工作,因為當 props.messages 是空數組時,0 仍然會被渲染:
<div>{props.messages.length &&<MessageList messages={props.messages} />} </div>要解決這個問題,確保 && 之前的表達式總是布爾值:
<div>{props.messages.length > 0 &&<MessageList messages={props.messages} />} </div>反之,如果你想渲染 false、true、null、undefined 等值,你需要先將它們轉換為字符串:
<div>My JavaScript variable is {String(myVariable)}. </div>JSX 的本質:JavaScript 的語法擴展
React 官網給出的一段定義:
JSX 是一個 JavaScript 的語法擴展。JSX 可能會使人聯想到模版語言,但它具有 JavaScript 的全部功能那么 “JSX 語法時如何在 JavaScript 中生效的”
JSX 語法是如何在 JavaScript 中生效的:認識 Babel
JSX 定位是 JavaScript 的“擴展”,這就直接決定了瀏覽器不會天然支持 JSX。那么,JSX 的語法是如何在 JavaScript 中生效的呢?React 官網給出了答案:
JSX 會被編譯為 React.createElement(), React.createElement() 將返回一個叫作“React Element”的 JS 對象。“編譯” 這個動作,是由 Babel 來完成的
什么是Babel
Babel 是一個工具鏈,主要用于將 ECMAScript 2015+ 版本的代碼轉換為向后兼容的 JavaScript 語法,以便能夠運行在當前和舊版本的瀏覽器或其他環境中。 —— Babel 官網例如箭頭函數:
// Babel 輸入: ES2015 箭頭函數 [1, 2, 3].map((n) => n + 1);// Babel 輸出: ES5 語法實現的同等功能 [1, 2, 3].map(function(n) {return n + 1; });類似的,Babel 也具備將 JSX 語法轉換為 JavaScript 代碼的能力
看看開頭的示例:
我們寫的 JSX 其實寫的就React.createElement,雖然它看起來有點像 HTML,但也只是“看起來像”而已。JSX 的本質是 React.createElement 這個 JavaScript 調用的語法糖,這也呼應了 React 官方給出的“JSX 充分具備 JavaScript 的能力”這句話。
為什么選擇 JSX
既然 JSX 等價于一次 React.createElement 調用,為什么不直接使用 React.createElement 來創建元素呢?
原因很簡單,在效果一致的前提下,JSX 代碼層次分明、嵌套關系清晰,而 React.createElement 代碼混亂雜糅,不僅難以閱讀,也難以編碼
JSX 語法糖允許前端開發者使用我們最為熟悉的類 HTML 標簽語法來創建虛擬 DOM,在降低學習成本的同時,也提升了研發效率與研發體驗。
React 官網也提出:
React 并沒有采用將標記與邏輯進行分離到不同文件這種人為地分離方式,而是通過將二者共同存放在稱之為“組件”的松散耦合單元之中,來實現關注點分離。JSX 是如何映射為 DOM 的:閱讀 createElement 源碼
我們先大致理解 createElement 函數源碼的作用:
//注意:react只寫了3個參數,實際上,從第三個參數往后都是children export function createElement(type, config, children) {let propName; // 用于存儲后面需要用到的元素屬性// Reserved names are extractedconst props = {}; // 用于存儲元素屬性的鍵值對集合let key = null;let ref = null;let self = null;let source = null;// 賦給標簽的props不為空時// config 存儲元素的屬性if (config != null) {// 依次對 ref、key、self 和 source 屬性賦值if (hasValidRef(config)) {ref = config.ref;}if (hasValidKey(config)) {// 防止是Numberkey = '' + 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 objectfor (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];}// 開發中寫的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];}}}return ReactElement(type, //'div'key, //nullref, //nullself, //nullsource, //nullReactCurrentOwner.current, // null或Fiberprops, // 自定義的屬性、方法,注意:props.children=childArray); }參數:創建一個元素需要哪些信息
export function createElement(type, config, children)- type:用于表示節點的類型。它可以是標準 HTML 標簽字符串,也可以是 React 組件類型或 React Fragment 類型
- config:以對象形式傳入,組件所有的屬性都會以鍵值對的形式存儲在 config 對象中
- children:以對象形式傳入,它記錄的是組件標簽之間嵌套的內容,也就是所謂的 “子節點” “子元素”
下面的示例可以幫助增進對 createElement 的理解:
React.createElement('div', {className: 'wrapper' }, React.createElement('h1', {className: 'header' }, 'header'), React.createElement('p', {className: 'content' }, 'content'))對應的 DOM 結構:
<div class="wrapper"><h1 class="header">header</h1><p class="content">content</p> </div>createElement 分析
邏輯流程圖
createElement 中并沒有復雜的邏輯,它的每一步步驟幾乎都是在格式化數據
createElement 就如同是開發者和 ReactElement 調用之間的一個 “轉換器”,對數據進行處理
返回值:初識虛擬 DOM
createElement 執行到最后會 return 一個 ReactElement 的調用
下面是關于 ReactElement 的源碼及解析:
const ReactElement = function(type, key, ref, self, source, owner, props) {const element = {// REACT_ELEMENT_TYPE是一個常量,用來標識該對象是一個ReactElement$$typeof: REACT_ELEMENT_TYPE,// 內置屬性賦值type: type,key: key,ref: ref,props: props,// 記錄創造該元素的組件_owner: owner,};if (__DEV__) {}return element; };ReactElement 只做了一件事,那就是創建:ReactElement 把傳入的參數按照一定的規范,“組裝”進 element 對象,并將它返回給 React.createElement,最終 React.createElement 又將它返回到開發者
對于 ReactElement 對象實例,其本質上是以 JavaScript 對象形式存在的對 DOM 的描述,也就是 “虛擬 DOM”(更準確地說,是虛擬 DOM 中的一個節點)
從虛擬 DOM 到真實 DOM 需要調用 ReactDOM.render 方法
在每個 React 項目的入口文件中,都有對 React.render 函數的調用。下面是 ReactDOM.render 的函數簽名:
ReactDOM.render(// 需要渲染的元素(ReactElement)element,// 元素掛載的目標容器(真實 DOM)container,// 回調函數,用于處理渲染結束后的邏輯。可選[callback] )總結
JSX 經過 babel 轉換為 React.createElement 函數,再調用 React.createElement 和 ReactElement 返回一個 element 對象(虛擬 DOM),最后通過 React.render 函數的調用,生產真實 DOM 節點并掛載到 “容器” 上。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的react 数字转字符_深入浅出 React -- JSX的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 极氪007真的比特斯拉快!001 FR直
- 下一篇: 视频会议演示露出马脚,英伟达被智能汽车业