摘要:中的方法調用了并傳入了最新的值。在前一次渲染中,已經將的設置為,并將的設置為
本文采用 es6 語法,完全參考 https://reactjs.org/docs/安裝
本文完全參考 React 官方 Quick Start 部分,除了最后的 thinking-in-react 小節
首先你需要點擊安裝 nodejs(npm)。然后執行:
npm install -g create-react-app
如果上述命令執行失敗可以運行以下命令:
npm install -g create-react-app --registry=https://registry.npm.taobao.org
然后建立一個 react 并運行:
create-react-app myApp cd myApp npm start
這樣你就簡單的完成了一個 react app 建立,其目錄結構如下( 圖中不包括 node_modules 目錄,下同 ):
Hello World我們刪除一些不必要的東西,然后修改目錄結構如下(不能刪 node_modules 目錄,如果刪了就在項目目錄下運行 npm i 就好了):
其中 components 是個目錄。
修改 index.js 如下:
import React from "react"; import ReactDOM from "react-dom"; ReactDOM.render(hello world!
, document.getElementById("root") );
然后命令行運行:
npm start
你就可以看到熟悉的 "hello world" 了
JSXJSX 是 react 中允許 js 和 html 混寫的語法格式,需要依賴 babel 編譯。這里我就只研究它的語法:
const element =Hello, world!
;
可以通過花括號在其中插入表達式:
function formatName(user){ return user.firstName + " " + user.lastName; } const user = { firstName: "Harper", lastName: "Perez" }; const element = (Hello, {formatName(user)}!
); ReactDOM.render( element, document.getElementById("root") );
可以將 HTML 語句寫為多行以增加可讀性,用小括號括起來可以防止自動插入分號導致的錯誤。
JSX 也是個表達式,所以可以用在 for 和 if 中:
function getGreeting(user){ if (user){ returnHello, {formatName(user)}!
; } returnHello, Stranger.
; }
我們可以正常使用引號給 HTML 標簽添加屬性,也可以使用 js 表達式
const element = ; const element = ; //注意空標簽以 /> 結尾,像 XML 一樣
注意 html 屬性名請使用小駝峰(camelCase)寫法
React 會在渲染之前 escape 所有在 JSX 嵌入的值,可以有效的防止 XSS 攻擊。
babel 會編譯 JSX 成 React.createElement() 的參數調用:
const element = (Hello, world!
); // 編譯為以下形式 const element = React.createElement( "h1", {className: "greeting"}, "Hello, world!" );
而 React.createElement() 會生成這樣一個對象(React 元素):
const element = { type: "h1", props: { className: "greeting", children: "Hello, world" } };元素渲染
在 ./public/index.html 中有一個 id 為 root 的 div。我們將這個 div 作為 react 渲染的容器。
回看 hello world 程序,通過 ReactDOM.render() 方法很輕松的把內容渲染到了目標容器上:
ReactDOM.render(hello world!
, document.getElementById("root") );
當然也可以這樣寫:
let content =hello world!
; ReactDOM.render( content, document.getElementById("root") );
下面我們寫一個復雜的,這是個實時更新的時鐘,通過 setInerval 每隔 1s 調用 ReactDOM.render:
function Tick(){ const element = (); ReactDOM.render( element, document.getElementById("root") ); } setInterval(Tick, 1000);Hello, world!
It is {new Date().toLocaleTimeString()}.
重寫上面時鐘組件的代碼如下,使其組件化程度更高:
function Clock(props){ return (組件); } function Tick(){ ReactDOM.render( //這個地方不得不傳入一個參數, 但理論上獲取一個時鐘直接獲取就可以了,這個問題我們后面再解決Hello, world!
It is {props.date.toLocaleTimeString()}.
, document.getElementById("root") ); } setInterval(Tick, 1000);
React 給我們提供了更好的管理我的代碼——組件。這里我們還是首先我們先了解一下自定義標簽:
const element =;
對這個標簽的理解也不難,它實際上調用了 Welcome 函數,并且將所有的屬性(這里只有name)打包為一個對象傳給 Welcome 函數。所以下面這個代碼輸出 ”Hello Sara"
function Welcome(props){ returnHello, {props.name}
; } const element =; ReactDOM.render( element, document.getElementById("root") );
組件幫助我事先一些重復的工作,比如這樣:
function Welcome(props){ returnHello, {props.name}
; } function App(){ return (); } ReactDOM.render(, document.getElementById("root") );
我們可以通過傳遞參數得到同一個組件構建的不同模塊。
這里我們需要補充一個重要的概念:__純函數!!!__
如果一個函數執行過程中不改變其參數,也不改變其外部作用于參數,當相同的輸入總能得到相同的值時,我們稱之這樣的函數為純函數。__React 要求所有組件函數都必須是純函數。__
其實之前的一段代碼中 Tick, Welcome 函數就可以看做是一個組件,同時 React 建議組件名的首字母大寫。但是更多情況下我們會用到 es6 的語法構建組件。以之前時鐘代碼為例,轉換過程分為五個步:
新建一個類,類名同組件函數名Clock,并繼承自 React.Component;
給該類添加一個方法 render(/無參數/);
將 Clock 的函數體作為該函數的函數體;
將 render 方法中的 props 換為 this.props;
刪除原有的 Clock 函數
結果如下:
class Clock extends React.Component { render(){ return (); } }Hello, world!
It is {this.props.date.toLocaleTimeString()}.
但這樣計時的功能就不能用了,我們繼續往下看……
State 和 Lifecycle解決上面這個問題,就需要用到 State 和 Lifecycle 的知識了
我們給 Clock 類添加一個構造函數,并且刪除 Clock 標簽中的參數:
class Clock extends React.Component { constructor(props){ super(props); this.state = {date: new Date()}; //state 用來記錄狀態 } render(){ return (); } } ReactDOM.render(Hello, world!
It is {this.state.date.toLocaleTimeString()}.
, //刪除參數 document.getElementById("root") );
為了控制計時的生命周期,我們需要引入 2 個方法 componentDidMount() 和 componentWillUnmount(),前者在渲染(render方法)完成時立即執行,后者在該 render 的內容即將被移除前執行。
很明顯,前者適合注冊計時器,后者可以用來清除計時器(防止內存泄露)
componentDidMount(){ this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount(){ clearInterval(this.timerID); }
下一步我們重寫 tick 函數,此時的 tick 函數只需要修改 this.state 就行了。注意 React 要求不能直接修改該屬性,而是使用 setState() 方法,所以 tick 函數如下:
tick(){ this.setState({ date: new Date() }); }
這里需要注意的是,當 state 中有很多屬性的時候,比如:
this.state = {name:"Lily", age: 12};執行 setState 方法修改其中的內容時并不會影響未修改的屬性:
this.setState({name: "Bob"}); //此后 this.state 為 {name:"Bob", age: 12};此外 setState 可能是異步的,所以不要在更新狀態時依賴前值:
// 這是個反例 this.setState({ counter: this.state.counter + this.props.increment, });為例解決這個問題,你可以傳入函數參數:
// Correct
this.setState((prevState, props) => ({ //這里 prevState 更新前的 state 對象,props 為新值構成的對象
counter: prevState.counter + props.increment
}));
此時,完整的代碼為:
class Clock extends React.Component { constructor(props){ super(props); this.state = {date: new Date()}; } componentDidMount(){ this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount(){ clearInterval(this.timerID); } tick(){ this.setState({ date: new Date() }); } render(){ return (事件); } } ReactDOM.render(Hello, world!
It is {this.state.date.toLocaleTimeString()}.
, document.getElementById("root") );
React 事件注冊和原生 DOM 事件類似的,這里需要理解一些不同點即可:
事件名使用小駝峰寫法,而不是全小寫,例如:onclick 寫作 onClick
注冊事件使用花括號表達式替代原有函數寫法
無法通過事件函數 return false 的方式阻止默認事件,必須顯式的調用 preventDefault(),并且在使用時不用糾結瀏覽器兼容問題,React 已經幫你處理好了
React 建議通常不需要通過 addEventListener 添加事件,只需要像上方代碼那樣在 render 時綁定事件即可
在 es6 語法的組件中注冊事件只需要將事件函數定義為該類的一個方法,然后在 render 時綁定即可:
render(){ return ( ); }
在 class 中,除了箭頭函數定義的方法中 this 符合預期,其余方法中的 this 都是 undefined,應該手動綁定。因此以下三個按鈕中 click2 會報錯。
class Button extends React.Component { constructor(){ super(); this.name = "Bob"; this.click3 = this.click2.bind(this); this.click1 = () => { console.log(`hello ${this.name}`); } } click2(){ console.log(`hello ${this.name}`); } render(){ return (); } }
以上幾種方法,React 推薦使用 click3 的實現方法,重寫如下:
class Button extends React.Component { constructor(){ super(); this.name = "Bob"; this.click = this.click.bind(this); } click(){ console.log(`hello ${this.name}`); } render(){ return (); } }
傳遞參數給事件的時候,第一個參數為 id, 第二個參數為 event。實際調用可以去以下兩種方式:
以上兩種方法等效,后一個方法的參數 e 可以省略。
條件渲染根據不同的條件(通常指state)渲染不同的內容, 比如下面段代碼可以根據 isLoggenIn 渲染不同的問候語:
function UserGreeting(props) { returnWelcome back!
; } function GuestGreeting(props) { returnPlease sign up.
; } function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { // 根據 isLoggenIn 渲染不同的問候語 return; } return ; } ReactDOM.render( // 你可以嘗試設置 isLoggedIn={true}: , document.getElementById("root") );
下面用 class 實現一個復雜一點的,帶有登錄/注銷按鈕的:
function LoginButton(props) { return ( ); } function LogoutButton(props) { return ( ); } class LoginControl extends React.Component { constructor(props) { super(props); this.state = { isLoggedIn: false }; // 修正 this 綁定 this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this); } handleLoginClick() { this.setState({isLoggedIn: true}); } handleLogoutClick() { this.setState({isLoggedIn: false}); } render() { const { isLoggedIn } = this.state; let button = null; if (isLoggedIn) { button =; } else { button = ; } return ( {/* Greeting 取自上一個示例 (注意這里的注釋寫法)*/}); } } ReactDOM.render({button} , document.getElementById("root") );
當然,對于這樣一個簡單的示例,使用 if 可能你會覺的太復雜了,我們也可以使用 && ?: 這些運算符來代替 if 語句,就像寫 javascript 代碼一樣。我們極力的化簡一下上面的代碼:
class LoginControl extends React.Component { constructor(props) { super(props); this.state = { isLoggedIn: false }; } render() { const { isLoggedIn } = this.state; const button = isLoggedIn ? : ; return (); } } ReactDOM.render({ isLoggedIn ? "Welcome back!" : "Please sign up." }
{button}, document.getElementById("root") );
當然,如果你需要在某個條件下不進行渲染,那么直接輸出 null 即可,比如下面這個組件,在 props.warn 為 false 時不渲染任何內容:
function WarningBanner(props) { if (!props.warn) { return null; } return (Warning!); }
需要注意的是,即便你輸出了 null, react 也會再渲染一次。同理,componentWillUpdate 和 componentDidUpdate 也會被調用。
列表在 React 中我們可以使用 map() 方法渲染列表,比如如下這個例子,將一組數據映射(map)為一組 dom:
const data = [1, 2, 3, 4, 5]; const listItems = data.map((item) =>
我們注意到這里我們給 li (即列表的每個元素)標簽加了一個 key 屬性,這個 key 用來幫助 React 判斷哪個元素發生了改變、添加或移除。關于這個 key 我們需要明白以下幾點:
最好保證 key 是一個字符串,并且在該列表中唯一,如果你的數據中實在沒有唯一的 key 可以選擇,那么就使用數組的索引(index)吧(不推薦這樣)
值得注意的是,如果你不給每個元素指定一個 key, react 會默認使用索引(index)作為 key
key 的值只是給 React 起到類似暗示的作用,不會真正的傳遞給 dom, 所以如果你需要使用 key 的值,應使用一個其它變量傳遞該值。
當然,上面代碼我們也可以寫成 inline 的形式:
const data = [1, 2, 3, 4, 5]; ReactDOM.render(
表單的處理會和原生的 html 有一些區別,因為 React 可以很好的幫助你使用 js 控制你的表單,這里我們需要引入一個新的概念:受控組件。
受控組件說白了就是其值受 react 控制的組件。其中,表單的元素通常都會具有其自己的 state,該值會隨著用戶的輸入改變。比如下面這個例子,會在用戶提交時輸出用戶的名字:
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ""}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert("A name was submitted: " + this.state.value); event.preventDefault(); } render() { return (
不難發現,這里使用了,onchange 事件不斷的將用戶的輸入綁定到 this.state.value 上,然后通過和用戶輸入同步的重繪實現數據的顯示。這樣可以很好的控制用戶輸入,比如同步的將用戶輸入轉化為大寫:
handleChange(event) { this.setState({value: event.target.value.toUpperCase()}); }
理解了上面的內容我們可以知道,單純給一個 input 賦值一個值用戶是不能修改的,比如下面這行代碼:
ReactDOM.render(, mountNode);
但如果你不小心他的值設為 null 或 undefined(等同于沒有 value 屬性),這個 input 就可以被更改了:
ReactDOM.render(, mountNode); setTimeout(function() { ReactDOM.render(, mountNode); }, 1000);
在 React 中 textarea 也是通過 value 屬性實現其內容變化的,而非其子節點:
class EssayForm extends React.Component { constructor(props) { super(props); this.state = { value: "Please write an essay about your favorite DOM element." }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert("An essay was submitted: " + this.state.value); event.preventDefault(); } render() { return (
在 React 中,對于 select 也會顯得很方便,你不需要在 option 中通過 selected 改變其值了,而是在 select 標簽上通過 value 屬性實現:
class FlavorForm extends React.Component { constructor(props) { super(props); this.state = {value: "coconut"}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert("Your favorite flavor is: " + this.state.value); event.preventDefault(); } render() { return (
上面代碼默認選中 Coconut。 這里值得注意的是,對于多選框,你可以傳入一個數組作為值:
當你控制很多個表單組件的時候要是為每個組件寫一個 handler 方法作為 onChange 事件那就太麻煩了。所以 React 可以通過表單元素的 name 配合 event.target.name 來控制表單:
class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); } handleInputChange(event) { const { target } = event; const value = target.type === "checkbox" ? target.checked : target.value; const { name } = target; this.setState({ [name]: value }); } render() { return (
這個部分,想說的不是個語法問題,而是代碼結構問題。我們重點理解一個例子:計算溫度的功能。
我們實現2個輸入框(攝氏溫度和華氏溫度)的同步數據顯示,和對數據的簡單操作(判斷是否達到標況下水的沸點100攝氏度)
我們先做點準備工作,比如溫度轉換函數:
function toCelsius(fahrenheit) { return (fahrenheit - 32) * 5 / 9; } function toFahrenheit(celsius) { return (celsius * 9 / 5) + 32; }
別忘了,一個好的程序員要能夠很好的控制數據輸入,所以我們再寫一個函數用來處理溫度,參數是溫度和溫度轉換函數:
function tryConvert(temperature, convert) { const input = parseFloat(temperature); if (Number.isNaN(input) || typeof convert !== "function") { return ""; } const output = convert(input); const rounded = Math.round(output * 1000) / 1000; return String(rounded); }
我們先簡單實現這個功能:
function BoilingVerdict(props) { if (props.celsius >= 100) { returnThe water would boil.
; } returnThe water would not boil.
; }
然后我們寫一個組件用來讓用戶輸入溫度
class Calculator extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = {temperature: ""}; } handleChange(e) { this.setState({temperature: e.target.value}); } render() { const { temperature } = this.state; return (); } }
此時我們可以輸入攝氏溫度了,再添加一個數據華氏溫度的地方。這里我們從上面的 Calculator 中提出來輸入組件:
const scaleNames = { c: "Celsius", f: "Fahrenheit" }; class TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = {temperature: ""}; } handleChange(e) { this.setState({temperature: e.target.value}); } render() { const { temperature } = this.state; const { scale } = this.props; return (); } }
這樣 Calculator 就簡單了:
class Calculator extends React.Component { render() { return (); } }
這樣2個輸入框就有了,但是它們還不能同步變化。而且 Calculator 組件不知道水溫是多少了,沒法判斷溫度了。這是我們應該吧溫度狀態放在他們最近的公共祖先元素上,這里就是 Calculator 組件啦。
很明顯,首先要改的就是 TemperatureInput, 它不需要 state 了,我們應該從參數獲取溫度了:
class TemperatureInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange(e) { this.props.onTemperatureChange(e.target.value); } render() { const { temperature, scale } = this.props; return (); } }
之后我們修改 Calculator 的 state, 將 temperature 和 scale 放入其中, 并添加狀態轉換函數:
class Calculator extends React.Component { constructor(props) { super(props); this.handleCelsiusChange = this.handleCelsiusChange.bind(this); this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this); this.state = {temperature: "", scale: "c"}; } handleCelsiusChange(temperature) { this.setState({scale: "c", temperature}); } handleFahrenheitChange(temperature) { this.setState({scale: "f", temperature}); } render() { const { temperature, scale } = this.state; const celsius = scale === "f" ? tryConvert(temperature, toCelsius) : temperature; const fahrenheit = scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature; return (); } }
到此所有的工作就完成了。我們總結一下,輸入數據時都發生了什么
當用戶輸入時,react 調用了已經申明的函數作為 onChange 事件。在這個例子中,就是 TemperatureInput 中的 handleChange 方法。
TemperatureInput 中的 handleChange 方法調用了 this.props.onTemperatureChange(), 并傳入了最新的值。而這個方法由父組件 Calculator 提供。
在前一次渲染中,Calculator 已經將 Celsius TemperatureInput 的 onTemperatureChange 設置為 handleCelsiusChange,并將 Fahrenheit TemperatureInput 的 onTemperatureChange 設置為 handleFahrenheitChange。所以這兩個計算方法的調用取決于我們編輯哪一個 input。
在這兩個方法中,Calculator 組件通過調用 this.setState() 方法讓 react 以最新的輸入和當前 scale 重繪該組件。
React 調用 Calculator 組件的 render 方法渲染頁面,兩個 input 中的值會基于當前值和 scale 重新計算, 溫度轉換函數就是在這里被調用的。
React 通過 props 使用 Calculator 新傳入的數據,分別調用每個 TemperatureInput 模塊中的 render 方法渲染 input 組件。
React DOM 更新 DOM 樹匹配新的數據,我們編輯的 input 得到我們剛剛輸入的值,而另一個 input 得到轉換后的值。
我們看看官方給出的效果:
組合與繼承React 建議用組件組合的方式代替組件繼承。所以我們需要學習如何用組合代替繼承。
很多組件在事先是不知道自己的孩子(內部的元素)的。比如對話框這樣的盒子型元素。我們需要使用 children 屬性來解決這個問題
function FancyBorder(props) { return ({props.children}); }
props.children 表示通過其他組件調用 FancyBorder 時的全部孩子元素,對應下面例子,children 就是 h1 和 p 的 react 對象數組
function WelcomeDialog() { return (); } Welcome
Thank you for visiting our spacecraft!
但是當組件缺乏共同點的時候,我們需要在組件中開很多孔,就像下面這個例子,這些孔可以很好的幫我們組合使用很多組件,而且 react 并不限制我我們傳遞參數的類型
function SplitPane(props) { return (); } function App() { return ({props.left}{props.right}} right={ } /> ); }
有時候我們想對組件做具體化分類的時候,邏輯上會很像繼承,比如 WelcomeDialog 是 Dialog 的一個具體化分類。但在 React 中我們依然用組合的方式實現這個功能:
function Dialog(props) { return (); } function WelcomeDialog() { return ( ); } {props.title}
{props.message}
當然我們也可以用 class 的定義方式:
function Dialog(props) { return (); } class SignUpDialog extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.handleSignUp = this.handleSignUp.bind(this); this.state = {login: ""}; } render() { return ( ); } handleChange(e) { this.setState({login: e.target.value}); } handleSignUp() { alert(`Welcome aboard, ${this.state.login}!`); } } {props.title}
{props.message}
{props.children}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/97630.html
摘要:考慮到是快速入門,于是乎我們就記住一點,當修改值需要重新渲染的時候,的機制是不會讓他全部重新渲染的,它只會把你修改值所在的重新更新。這一生命周期返回的任何值將會作為參數被傳遞給。 安裝react npm install creat-react-app -gshowImg(https://segmentfault.com/img/remote/1460000015639868); 這里直...
摘要:一份開發者必備的技能清單,請查收。入門查漏補缺深入學習查看原圖下載源文件使用快速上手,并了解其中的概念。官方教程入門教程小書文章精讀,問題解答。 一份react開發者必備的技能清單,請查收。入門、查漏補缺、深入學習... showImg(https://segmentfault.com/img/remote/1460000018000950?w=1965&h=3332); 查看原圖 ...
摘要:本篇解釋中類的控制指令,與指令式界面設計相關。本專欄歷史文章介紹一項讓可以與抗衡的技術可視化開發工具非正經入門之一三宗罪可視化開發工具非正經入門之二分離界面設計可視化開發工具非正經入門之三雙源屬性與數據驅動可視化開發工具非正經入門之四 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點。本篇解釋 Shadow Widget 中類 Vue 的控制指令,與指令式界面...
摘要:今年以來,的文檔更新很快完善社區也日漸狀大,再加上于某廠你懂的大力的推廣,的前景十分光明。一般情況下,中小型的系統從遷移到版本大概只需要天的時間。快去動手嘗試吧原創新書移動前端高效開發實戰已在亞馬遜京東當當開售。 作者:曉飛(滬江Web前端開發工程師)本文原創,轉載請注明作者及出處 Vue.js框架已經火了好長一段時間了,早在2015年的雙11中,淘寶的部分導購業務——如:雙十一晚會搖...
摘要:一團隊組織網站說明騰訊團隊騰訊前端團隊,代表作品,致力于前端技術的研究騰訊社交用戶體驗設計,簡稱,騰訊設計團隊網站騰訊用戶研究與體驗設計部百度前端研發部出品淘寶前端團隊用技術為體驗提供無限可能凹凸實驗室京東用戶體驗設計部出品奇舞團奇虎旗下前 一、團隊組織 網站 說明 騰訊 AlloyTeam 團隊 騰訊Web前端團隊,代表作品WebQQ,致力于前端技術的研究 ISUX 騰...
閱讀 2169·2021-09-04 16:40
閱讀 1466·2021-08-13 15:07
閱讀 3609·2019-08-30 15:53
閱讀 3200·2019-08-30 13:11
閱讀 1078·2019-08-29 17:22
閱讀 1819·2019-08-29 12:47
閱讀 1479·2019-08-29 11:27
閱讀 2233·2019-08-26 18:42