react方法返回html_React全家桶之React基础(推荐新手必看)
學習目標
- 安裝create-react-app腳手架
- 熟練React基礎語法
- 掌握JSX語法
- 掌握setState
- 掌握React生命周期
- 掌握props傳遞參數
- 掌握React組件通信
資源
- react
- create-react-app
起步
上手
- npm i -g create-react-app 安裝官方腳手架
- create-react-app 01_react 初始化
- react的api比較少,基本學習一次,就再也不用再看文檔,它的核心是JS
React&ReactDOM
React只做邏輯層,reactDOM去渲染真實的DOM
刪除src下面所有代碼,新建index.js
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App' ReactDOM.render(<App />,document.querySelector('#root'))新建 App.js
import React from 'react' class App extends React.Component {render() {return (<div>hello,小馬哥</div>)} } export default App;以上代碼感覺都沒有問題,但是發現了一個很有趣的標簽語法,它既不是字符串也不是HTML
它被稱為JSX,是一個JavaScript的語法擴展.我們建議在 React 中配合使用 JSX,JSX 可以很好地描述 UI 應該呈現出它應有交互的本質形式。JSX 可能會使人聯想到模版語言,但它具有 JavaScript 的全部功能。
JSX
在JavaScript中直接寫的標簽,是一個JSX(JS+XML,由于HTML也是XML的一種,可以認為JS+HTML)元素,也是一個react元素,這個元素實際是一個對象,就是虛擬DOM元素
React 認為渲染邏輯本質上與其他 UI 邏輯內在耦合.比如,在 UI 中需要綁定處理事件、在某些時刻狀態發生變化時需要通知到 UI,以及需要在 UI 中展示準備好的數據。
import React, { Component } from 'react'import logo from './favicon.ico' const ele = <h2>hello,world</h2> function formatName(user) {// 也可以是個表達式return user.firstName + '' + user.lastName; } const user = {firstName: '張',lastName: '三豐' }//jsx也可以是表達式 function getGreeting(user) {if (user) {return <h1>hello {formatName(user)}</h1>}return <h1>hello,小馬哥</h1> } export default class App extends Component {render() {return (<div>{ele}{/* jsx運算 */}{2 + 1}<br />{/*jsx嵌入表達式*/}{formatName(user)}{/* 添加屬性 */}<img src={logo} alt="" /></div>// getGreeting())} }強烈建議大家閱讀官網,針對React.render()內部的實現操作
元素渲染
元素是構成React應用的最小磚塊,比如:const ele = <h1>hello,world</h1>與瀏覽器的 DOM 元素不同,React 元素是創建開銷極小的普通對象。React DOM 會負責更新 DOM 來與 React 元素保持一致
上節課的ReactDOM.render()其實就是在渲染DOM節點
更新已渲染的元素
React元素是不可變對象,一旦被創建,無妨更改它的子元素或者屬性
計時器的例子
function tick() {const element = (<div><h1>Hello, world!</h1><h2>{new Date().toLocaleTimeString()}.</h2></div>);ReactDOM.render(element, document.querySelector('#root')); }setInterval(tick, 1000);大多數情況下,React應用只會調用一次ReactDOM.render()
React只需要更新它需要更新的部分
React DOM會將元素和它的子元素與它們之前的狀態進行比較,并只會進行必要的更新來使DOM達到預期的狀態
循環綁定元素
當數據從后端請求回來之后,在React中,一般都需要循環綁定元素
map綁定
在React中,循環綁定元素都是使用map方法,不能使用forEach是因為forEach沒有返回值
let ul = (<ul>{ arr.map((item, index)=>{return <li key={index}>{item}</li>}) } </ul>);結果會是一個 JSX 元素組成的數組,放入頁面中,不會使用逗號分隔開。
循環綁定的 JSX 元素,必須要有 key 屬性,來區分不同的元素,否則會報錯。過濾元素
同樣通過map方法,只要把不符合條件的元素,返回為null即可,原因在于,null會被表示為空.如果使用filter,那么就沒有辦法對元素進行處理,只能過濾,還是需要使用map進行處理
let ul = (<ul>{ arr.map((item, index)=>{return (item.price < 1000 ? null : <li key={index}>{item}</li>;)}) } </ul>)ref和refs
在React中,類似于Vue,可以通過ref標記元素,然后通過this.refs獲取元素,只是Vue中使用的是this.$refs獲取元素
字符串寫法
通過設置一個字符串值來標記元素,然后通過這個字符串作為屬性獲取元素
class Input extends Component {handleChange = (e) => {console.log(this.refs.a.value)}render() {return (<div><input type="text" ref="a" onChange={this.handleChange} /></div>)} }函數寫法
函數作為一個ref的屬性值,這個函數接受一個參數,就是真實的DOM元素
可以把這個元素掛載到實例上,方便后面的操作
class Input extends React.Component {componentDidMount() {console.log(this.a); //獲取真實的DOM元素}render() {return (<div><input type="text" ref={x=>this.a = x}/> </div>);} }組件&props
React創建組件有來兩種方式
- 函數聲明
- 類聲明
React組件特點:
- 組件名稱應該首字母大寫,否則會報錯
- 組件定義之后,可以像JSX元素一樣使用
- 必須使用render函數才能將虛擬DOM渲染成真實的DOM
- 使用組件時,可以使用單標簽,也可以使用雙標簽
函數組件
組件,從概念上類似于 JavaScript 函數。它接受任意的入參(即 “props”),并返回用于描述頁面展示內容的 React 元素。
- 函數聲明的組件,必須返回一個JSX元素
- 可以通過屬性給組件傳遞值,函數通過props參數屬接收
定義組件最簡單的方式就是編寫JavaScript函數
function Welcome(props){return <h2>hello,{props.name}</h2> }我們稱為該組件為"函數組件",因為它本質上就是一個函數
類聲明組件
使用ES6的class的方式定義組件
類聲明組件需要注意
- 在React中有一個屬性Component,是一個基類,使用類聲明組件時,必須繼承這個基類
- 在類中,必須有render函數,constructor不是必須的
- 在render函數中**,需要return一個JSX元素**
例如,<div />代表HTML的div標簽,而<Welcome />則代表一個組件,并且需在作用域內使用Welcome
兩種方式的區別
真實項目中,都只使用class定義組件
- class定義的組件中有this,狀態,生命周期
- function聲明都沒有
組合組件
組件可以輸出的時候嵌入其他的組件。這就可以讓我們用同一組件中來抽離出任意層次的細節(復用組件)。按鈕,表單,對話框,甚至整個屏幕的內容:在 React 應用程序中,這些通常都會以組件的形式表示。
什么是復合組件:
- 將多個組件進行組合,例如調用兩次相同的組件
- 結構非常復雜時需要將組件拆分成小組件
- 會存在父子關系的數據傳遞
可想而知,React開發其實就是組件化開發,因為React真的是組件化開發的鼻祖
提取組件
將組件拆分成更小的組件
import React, { Component } from 'react'; import ReactDOM from 'react-dom';// 所有props是只讀的,不能直接修改 class Avatar extends Component {render() {return (<img src={this.props.user.avatarUrl} alt={this.props.user.name} />);} }class UserInfo extends Component {constructor(props) {super(props);console.log(this.props);}render() {return (<div className="userinfo"><Avatar user={this.props.user}></Avatar><div className='username'><h3>{this.props.user.name}</h3></div></div>);} }class Comment extends Component {constructor(props) {super(props);}render() {return (<div className='comment'><UserInfo user={this.props.user}></UserInfo><div className="Comment-text">{this.props.user.text}</div><div className="Comment-date">{this.props.user.date}</div></div>);} } class App extends Component {constructor() {super();this.user = {avatarUrl: 'https://hcdn1.apeland.cn/media/course/icon2.png',name: '張三',text: "hello,React component",date: new Date().toLocaleString()}}render() {return (<div><Comment user={this.user}></Comment></div>)} } // 組合組件 ReactDOM.render(<App></App>,document.querySelector('#root'));最初看上去,提取組件可能是一件繁重的工作,但是,在大型應用中,構建可復用組件庫是完全值得的。 根據經驗來看,如果 UI 中有一部分被多次使用(Button,Panel,Avatar),或者組件本身就足夠復雜(App,Comment),那么它就是一個可復用組件的候選項。你說對吧!
父子組件通信
父傳子
父組件通過行間屬性傳遞數據到子組件,子組件通過實例上的props屬性接收新的數據
React 的數據是單向數據流,只能一層一層往下傳遞。當組件的屬性發生改變,那么當前的視圖就會更新
子傳父
通過給子組件傳遞一個函數,子組件調用父親的函數將值作為參數傳遞給父組件,父組件更新值,刷新視圖。
父組件中定義一個函數,通過屬性傳遞給子組件。
這個傳遞的函數必須是一個箭頭函數import React, { Component } from 'react'; import ReactDOM from 'react-dom';class ChildCom extends Component {constructor(props) {super(props);console.log(props);this.state = {val:''}this.handlerClick = this.handlerClick.bind(this);this.handlerChange = this.handlerChange.bind(this);}handlerChange(event){this.setState({val: event.target.value})}handlerClick(){if(this.state.val){this.props.addHandler(this.state.val);// 清空輸入框this.setState({val: ''})}}render() {return (<div><input type="text" value = {this.state.val} onChange = {this.handlerChange}/><button onClick={this.handlerClick}>添加</button>{this.props.menus.map((item,index) => {return <p key = {index}>{item}</p>})}</div>);} }class App extends Component {constructor(props) {super(props);this.state = {menus: ['烤腰子', '辣炒雞丁', '炸黃花魚']}}// 一定要使用箭頭函數addHandler = (val)=>{this.state.menus.push(val);this.setState({menus:this.state.menus})}render() {// 修改狀態之后,會重新調用renderreturn (<div><ChildCom menus={this.state.menus} addHandler = {this.addHandler}></ChildCom></div>);} }ReactDOM.render(<App />, document.querySelector('#root'));Props的只讀性
組件無論是使用函數聲明還是通過 class 聲明,都決不能修改自身的 props
//該函數不會嘗試更改入參,且多次調用下相同的入參始終返回相同的結果。 function sum(a, b) {return a + b; } //它更改了自己的入參 function withdraw(account, amount) {account.total -= amount; }所有 React 組件都必須像純函數一樣保護它們的 props 不被更改。
state
組件狀態
組件中數據的來源
- 屬性:是由外接傳遞過來的
- 狀態:是自己的,只能通過setState來改變狀態
修改狀態
除了constructor之外的其它地方,如果需要修改狀態,都只能通過this.setState方法
這個方法傳入的第一個參數,可以是一個對象,也可以是一個函數
- 是一個對象,這個對象中包含需要改變的屬性,它會與原有的狀態進行合并
- 是一個函數,接收第一個參數是 prevState,上一個狀態對象,第二個參數是 props
這個方法的第二個參數,是一個回調函數,在狀態改變之后執行。
如果下一個狀態依賴于上一個狀態,需要寫成函數的方式
關于setState
- 在 react 組件的生命周期或事件的綁定中,setState 是異步的
- 在定時器或原生的事件中,setState 不一定是異步的
在元素渲染章節中,我們只了解了一種更新 UI 界面的方法。通過調用 ReactDOM.render() 來修改我們想要渲染的元素
function tick() {const element = (<div><h1>Hello, world!</h1><h2>{new Date().toLocaleTimeString()}.</h2></div>);ReactDOM.render(element, document.querySelector('#root')); }setInterval(tick, 1000);本節學習如何封裝真正可復用的Clock組件
import React, { Component } from 'react'; import ReactDOM from 'react-dom';// 學習如何封裝真正可復用的Clock組件。 class Clock extends Component {constructor(props) {super(props);this.state = {date: new Date().toLocaleString()}}componentDidMount() {this.timer = setInterval(() => {// 注意1 不能直接修改state// this.state.date = new Date(); //錯誤// 注意2: setState()是異步的this.setState({date: new Date().toLocaleString()})}, 1000);}componentWillUnmount() {clearInterval(this.timer);}render() {// 修改狀態之后,會重新調用renderreturn (<div><h3>當前時間為:{this.state.date}</h3></div>);} }ReactDOM.render(<Clock />, document.querySelector('#root'));生命周期
當然react16.x之后,又更新了新的生命周期方法。如圖,React生命周期主要包括三個階段:初始化階段、運行中階段和銷毀階段,在React不同的生命周期里,會依次觸發不同的鉤子函數,下面我們就來詳細介紹一下React的生命周期函數import React, { Component } from 'react'; import ReactDOM from 'react-dom';class SubCounter extends Component {// 組件將要接收屬性componentWillReceiveProps(newProps){console.log('9.子組件將要接收到新屬性',newProps);}shouldComponentUpdate(newProps,newState){console.log('10.子組件是否需要更新')if(newProps.num % 3 === 0){return true;}else{return false;}}componentWillUpdate() {console.log('11、子組件將要更新');}componentDidUpdate() {console.log('13、子組件更新完成');}componentWillUnmount() {console.log('14、子組件將卸載');}render() {console.log('12.子組件掛載中')return (<div><p>{this.props.num}</p> </div>);} }class Counter extends Component {static defaultyProps = {//1.加載默認屬性name:'小馬哥',age:18}constructor(props) {super(props);//2.記載默認狀態this.state = {num: 0}}componentWillMount() {// 此時可以訪問屬性和狀態,可以進行api調用,但沒辦法做DOM相關操作console.log('3.父組件將要被掛載');}componentDidMount() {// 組件已掛載,可進行狀態更新操作。通常 都在此方法中發送請求console.log('5.組件掛載完成');}shouldComponentUpdate(newProps, newState) {// 組件是否需要更新,返回布爾值,優化點console.log('6.父組件是否被更新');// console.log(newProps, newState);if (newState.num % 2 === 0) {return true;} else {// 此函數 會返回一個boolean值,返回true更新頁面,返回false不更新頁面return false;}}componentWillUpdate(){console.log('7.父組件將要更新');}componentDidUpdate(){console.log('8.父組件更新完成');}handlerClick = () => {// 可能,只是說可能,官網上都是這樣說的.......會導致計數可能不準確,// this.setState({// num: parseInt(this.props.increment) + this.state.num// })// 發現點擊之后,得到的結果為0,這是因為setState()是異步的// console.log(this.state.num);// 要解決這個問題,可以讓 setState() 接收一個函數而不是一個對象。// 這個函數用上一個 state 作為第一個參數,將此次更新被應用時的 props 做為第二個參數this.setState((state, props) => {return {num: state.num + parseInt(props.increment)}}, () => {console.log(this.state.num);})}render() {// 修改狀態之后,會重新調用renderconsole.log('4.render(父組件)渲染了');return (<div><h3>當前數值:{this.state.num}</h3><button onClick={this.handlerClick}>+1</button><h3>我是子組件</h3><SubCounter num = {this.state.num}></SubCounter></div>);} }ReactDOM.render(<Counter increment='1' />, document.querySelector('#root'));
受控組件
受控組件,就是受狀態控制的組件,需要與狀態進行相應的綁定
- 受控組件必須要有一個 onChange 事件,否則不能使用
- 受控組件可以賦予默認值(實際上就是設置初始狀態)
- 官方推薦使用受控組件的寫法
可以使用受控組件實現雙向綁定。
非受控組件,則不是通過與狀態進行綁定來實現的,而是通過操作 DOM 來實現。除非操作 DOM,否則沒有辦法設置默認值。
受控組件實現
- 設置初始狀態,也就是設置默認值
- 將輸入框的 value`` 值與相應狀態進行綁定
- 使用 onChange 事件,對狀態進行修改,從而反映到 value 上
非受控組件的實現
- 通過 ref 標記一個元素,然后可以通過 this.refs.xx 來獲取這個元素
- 通過 onChange 事件監聽到 value 的變化,獲取到這個數據
- 然后通過操作 DOM 將數據放到需要的地方
下面來實現雙向綁定:
class Input extends Component {handleChange = (e) => {//=> 這里可以通過 e.target.value 獲取if (e.target === this.refs.a) {this.refs.b.value = e.target.value;} else {this.refs.a.value = e.target.value;}}render() {return (<div onChange={this.handleChange}><input type="text" ref="a" /><input type="text" ref="b" /></div>)} }關于前端學習路線的一些建議,對你一定有用!?www.bilibili.com作者:前端開發小馬哥鏈接:https://juejin.cn/post/6898512934100533261
來源:掘金
總結
以上是生活随笔為你收集整理的react方法返回html_React全家桶之React基础(推荐新手必看)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bearshare.exe进程安全吗 b
- 下一篇: 2021年5月中国银行外汇汇率是多少?人