中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

查詢網(wǎng)站備案號qq排名優(yōu)化網(wǎng)站

查詢網(wǎng)站備案號,qq排名優(yōu)化網(wǎng)站,三水網(wǎng)站建設(shè)首選公司,做羞羞事視頻網(wǎng)站React簡介 React 是一個聲明式,高效且靈活的用于構(gòu)建用戶界面的 JavaScript 庫。使用 React 可以將一些簡短、獨(dú)立的代碼片段組合成復(fù)雜的 UI 界面,這些代碼片段被稱作“組件”。 ui render (data) -> 單向數(shù)據(jù)流 MVC // model var myapp {}; // …

React簡介

React 是一個聲明式,高效且靈活的用于構(gòu)建用戶界面的 JavaScript 庫。使用 React 可以將一些簡短、獨(dú)立的代碼片段組合成復(fù)雜的 UI 界面,這些代碼片段被稱作“組件”。
ui = render (data) -> 單向數(shù)據(jù)流

MVC

在這里插入圖片描述

// model
var myapp = {}; // 創(chuàng)建這個應(yīng)用對象myapp.Model = function() {var val = 0;this.add = function(v) {if (val < 100) val += v;};this.sub = function(v) {if (val > 0) val -= v;};this.getVal = function() {return val;};* 觀察者模式 /var self = this, views = [];this.register = function(view) {views.push(view);};this.notify = function() {for(var i = 0; i < views.length; i++) {views[i].render(self);}};
};// view
myapp.View = function(controller) {var $num = $('#num'),$incBtn = $('#increase'),$decBtn = $('#decrease');this.render = function(model) {$num.text(model.getVal() + 'rmb');};/  綁定事件  /$incBtn.click(controller.increase);$decBtn.click(controller.decrease);
};// controller
myapp.Controller = function() {var model = null,view = null;this.init = function() {/ 初始化Model和View /model = new myapp.Model();view = new myapp.View(this);/ View向Model注冊,當(dāng)Model更新就會去通知View啦 /model.register(view);model.notify();};/ 讓Model更新數(shù)值并通知View更新視圖 */this.increase = function() {model.add(1);model.notify();};this.decrease = function() {model.sub(1);model.notify();};
};// init
(function() {var controller = new myapp.Controller();controller.init();
})();

MVVM

在這里插入圖片描述

// model
var data = {val: 0
};// view
<div id="myapp"><div><span>{{ val }}rmb</span></div><div><button v-on:click="sub(1)">-</button><button v-on:click="add(1)">+</button></div>
</div>// controller
new Vue({el: '#myapp',data: data,methods: {add(v) {if(this.val < 100) {this.val += v;}},sub(v) {if(this.val > 0) {this.val -= v;}}}
});// Vue是不是MVVM?React呢?
// 嚴(yán)格來講都不是
// React:ui = render (data) 單向數(shù)據(jù)流
// Vue:   ref 直接操作DOM,跳過了ViewModel

JSX模板語法

JSX稱為JS的語法擴(kuò)展,將UI與邏輯層耦合在組件里,用{}標(biāo)識
因?yàn)?JSX 語法上更接近 JS 而不是 HTML,所以使用 camelCase(小駝峰命名)來定義屬性的名稱;
JSX 里的 class 變成了 className,而 tabindex 則變?yōu)?tabIndex。

JSX支持表達(dá)式

支持JS表達(dá)式,變量,方法名

// 變量
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;function formatName(user) {return user.firstName + ' ' + user.lastName;
}// 方法
const user = {firstName: 'Harper',lastName: 'Perez'
};const element = (<h1>Hello, {formatName(user)}!</h1>
);function getGreeting(user) {if (user) {return <h1>Hello, {formatName(user)}!</h1>;}return <h1>Hello, Stranger.</h1>;
}

JSX指定屬性

const element = <img src={user.avatarUrl}></img>;注意:JSX支持防注入(防止XSS攻擊)
const title = response.potentiallyMaliciousInput;  // 此時只是字符串
// 直接使用是安全的: const element = <h1>{title}</h1>;React 如何預(yù)防XSS// 反射型 XSShttps://xxx.com/search?query=userInput// 服務(wù)器在對此 URL 的響應(yīng)中回顯提供的搜索詞:query=123
<p>您搜索的是: 123</p>// https://xxx.com/search?query=<img src="empty.png" onerror ="alert('xss')">
<p>您搜索的是: <img src="empty.png" onerror ="alert('xss')"></p>
// 如果有用戶請求攻擊者的 URL ,則攻擊者提供的腳本將在用戶的瀏覽器中執(zhí)行。// 存儲型 XSS,存儲到目標(biāo)數(shù)據(jù)庫
// 評論輸入,所有訪問用戶都能看到了
<textarea><img src="empty.png" onerror ="alert('xss')">
</textarea>// 部分源碼
for (index = match.index; index < str.length; index++) {switch (str.charCodeAt(index)) {case 34: // "escape = '&quot;';break;case 38: // &escape = '&amp;';break;case 39: // 'escape = '&#x27;';break;case 60: // <escape = '&lt;';break;case 62: // >escape = '&gt;';break;default:continue;}
}// 一段惡意代碼
<img src="empty.png" onerror ="alert('xss')"> 
//  React 在渲染到瀏覽器前進(jìn)行的轉(zhuǎn)義,可以看到對瀏覽器有特殊含義的字符都被轉(zhuǎn)義了,惡意代碼在渲染到 HTML 前都被轉(zhuǎn)成了字符串
&lt;img src=&quot;empty.png&quot; onerror =&quot;alert(&#x27;xss&#x27;)&quot;&gt; // JSX
const element = (<h1 className="greeting">Hello, world!</h1>
);// 通過 babel 編譯后的代碼
const element = React.createElement('h1',{className: 'greeting'},'Hello, world!'
);// React.createElement() 方法返回的 ReactElement
const element = {$$typeof: Symbol('react.element'),type: 'h1',key: null,props: {children: 'Hello, world!',className: 'greeting'   }...
}// 如何模擬一個Children會如何?
const storedData = {"ref":null,"type":"body","props":{"dangerouslySetInnerHTML":{"__html":"<img src=\"empty.png\" onerror =\"alert('xss')\"/>"}}
};
// 轉(zhuǎn)成 JSON
const parsedData = JSON.parse(storedData);
// 將數(shù)據(jù)渲染到頁面
render () {return <span> {parsedData} </span>; 
}// $$typeof 是用來標(biāo)記一個ReactElement的,JSON化后Symbol會丟失,React會報錯

JSX表示對象

const element = (<h1 className="greeting">Hello, world!</h1>
);// 等同于React.createElement
const element = React.createElement('h1',{className: 'greeting'},'Hello, world!'
);const element = {type: 'h1',props: {className: 'greeting',children: 'Hello, world!'}
};

JSX渲染DOM

// 使用ReactDOM.render
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));// render只能代表當(dāng)前時刻的狀態(tài)
// 更新元素 只能再次 ReactDOM.render
function tick() {const element = (<div><h1>Hello, world!</h1><h2>It is {new Date().toLocaleTimeString()}.</h2></div>);ReactDOM.render(element, document.getElementById('root')); 
}setInterval(tick, 1000); // 不建議多次render

JSX轉(zhuǎn)換為JS

JSX可以當(dāng)做語法糖,可以在babel官網(wǎng)中嘗試,https://babeljs.io/repl
可以使用官網(wǎng)提供的create-react-app npm run eject 來看babelrc中的配置,主要使用
https://www.babeljs.cn/docs/babel-preset-react


// 安裝babel 及react 的依賴
npm install core-js @babel/core @babel/preset-env @babel/preset-react @babel/register babel-loader @babel/plugin-transform-runtime --save-dev.babelrc
{"presets" : [ "@babel/preset-env" ,"@babel/preset-es2015","@babel/preset-react"],"plugins" : ["@babel/plugin-transform-runtime"]
}

Props & State

組件,從概念上類似于 JavaScript 函數(shù)。它接受任意的入?yún)?#xff08;即 “props”),并返回用于描述頁面展示內(nèi)容的 React 元素。

組件

  • 函數(shù)式組件
  • Class類組件
function Welcome(props) {return <h1>Hello, {props.name}</h1>;
}class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;}
}

渲染組件

function Welcome(props) {return <h1>Hello, {props.name}</h1>;
}const element = <Welcome name="Sara" />;
ReactDOM.render(element,document.getElementById('root')
);// 自定義組件使用大寫字母開頭
import React from 'react';// 正確!組件需要以大寫字母開頭:
function Hello(props) {// 正確! 這種 <div> 的使用是合法的,因?yàn)?div 是一個有效的 HTML 標(biāo)簽:return <div>Hello {props.toWhat}</div>;
}function HelloWorld() {// 正確!React 知道 <Hello /> 是一個組件,因?yàn)樗谴髮懽帜搁_頭的:return <Hello toWhat="World" />;
}

組件組合與拆分

// 頁面內(nèi)多次引用
<div><Welcome name="Sara" /><Welcome name="Cahal" /><Welcome name="Edite" />
</div>function Comment(props) {return (<div className="Comment"><div className="UserInfo"><img className="Avatar"src={props.author.avatarUrl}alt={props.author.name}/><div className="UserInfo-name">{props.author.name}</div></div><div className="Comment-text">{props.text}</div><div className="Comment-date">{formatDate(props.date)}</div></div>);
}// 拆分后為
function Comment(props) {return (<div className="Comment"><UserInfo user={props.author} /><div className="Comment-text">{props.text}</div><div className="Comment-date">{formatDate(props.date)}</div></div>);
}

受控組件和非受控組件

  • 受控組件:對某個組件狀態(tài)的掌控,它的值是否只能由用戶設(shè)置,而不能通過代碼控制;

在HTML的表單元素中,它們通常自己維護(hù)一套state,并隨著用戶的輸入自己進(jìn)行UI上的更新,這種行為是不被我們程序所管控的。而如果將React里的state屬性和表單元素的值建立依賴關(guān)系,再通過onChange事件與setState()結(jié)合更新state屬性,就能達(dá)到控制用戶輸入過程中表單發(fā)生的操作。被React以這種方式控制取值的表單輸入元素就叫做受控組件。

// input自身維護(hù)的狀態(tài),外界無法獲取數(shù)據(jù)
class TestComponent extends React.Component {render () {return <input name="username" />}
}// 可以設(shè)置初始值
class TestComponent extends React.Component {constructor (props) {super(props);this.state = { username: 'test' };}render () {return <input name="username" value={this.state.username} />}
}// 可以讀取并設(shè)置初始值
class TestComponent extends React.Component {constructor (props) {super(props);this.state = {username: "test"}}onChange (e) {console.log(e.target.value);this.setState({username: e.target.value})}render () {return <input name="username" value={this.state.username} onChange={(e) => this.onChange(e)} />}

非受控組件

對應(yīng)的,組件內(nèi)的狀態(tài)不由用戶控制;

// 如果不想關(guān)心表單元素的值是如何變化的,只想取值,可以使用ref
import React, { Component } from 'react';export class UnControll extends Component {constructor (props) {super(props);this.inputRef = React.createRef();}handleSubmit = (e) => {console.log('我們可以獲得input內(nèi)的值為', this.inputRef.current.value);e.preventDefault();}render () {return (<form onSubmit={e => this.handleSubmit(e)}><input defaultValue="lindaidai" ref={this.inputRef} /><input type="submit" value="提交" /></form>)}
}

props

所有 React 組件都必須像純函數(shù)一樣保護(hù)它們的 props 不被更改。

// 錯誤,要像純函數(shù)一樣冪等
function withdraw(account, amount) {account.total -= amount;
}

State

// 使用props形式
function Clock(props) {return (<div><h1>Hello, world!</h1><h2>It is {props.date.toLocaleTimeString()}.</h2></div>);
}function tick() {ReactDOM.render(<Clock date={new Date()} />,document.getElementById('root'));
}setInterval(tick, 1000);// 如何避免多次React.DOM render?// 引用生命周期,根組件保留一個
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 (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);}
}ReactDOM.render(<Clock />,document.getElementById('root')
);
1. setState 
構(gòu)造函數(shù)是唯一可以給state賦值的地方
this.setState({comment: 'Hello'});2. state更新可能是異步的
// Wrong
this.setState({counter: this.state.counter + this.props.increment,
});
// Correct
this.setState(function(state, props) {return {counter: state.counter + props.increment};
});3. state更新會合并
constructor(props) {super(props);this.state = {posts: [],comments: []};
}componentDidMount() {fetchPosts().then(response => {// 相當(dāng)于{post: response.posts, ...otherState}this.setState({posts: response.posts});});fetchComments().then(response => {this.setState({comments: response.comments});});
}

單向數(shù)據(jù)流

state 只在當(dāng)前的組件里生效,屬于組件內(nèi)的屬性,重復(fù)實(shí)例化相同的組件,內(nèi)部的內(nèi)存地址也是不一樣的;
例如Clock中計時器都是獨(dú)立的
// setState 異步
// 異步目的:batch 處理,性能優(yōu)化

  1. 合成事件
class App extends Component {state = { val: 0 }increment = () => {this.setState({ val: this.state.val + 1 })console.log(this.state.val) // 輸出的是更新前的val --> 0}render() {return (<div onClick={this.increment}>{Counter is: ${this.state.val}}</div>)}

生命周期:

class App extends Component {state = { val: 0 }componentDidMount() {this.setState({ val: this.state.val + 1 })console.log(this.state.val) // 輸出的還是更新前的值 --> 0}render() {return (<div>{Counter is: ${this.state.val}}</div>)}
}

原生事件:

class App extends Component {state = { val: 0 }changeValue = () => {this.setState({ val: this.state.val + 1 })console.log(this.state.val) // 輸出的是更新后的值 --> 1}componentDidMount() {document.body.addEventListener('click', this.changeValue, false)}render() {return (<div>{Counter is: ${this.state.val}}</div>)}
}

setTimeout

class App extends Component {state = { val: 0 }componentDidMount() {setTimeout(_ => {this.setState({ val: this.state.val + 1 })console.log(this.state.val) // 輸出更新后的值 --> 1}, 0)}render() {return (<div>{Counter is: ${this.state.val}}</div>)}
}

批處理:

class App extends Component {state = { val: 0 }batchUpdates = () => {this.setState({ val: this.state.val + 1 })this.setState({ val: this.state.val + 1 })this.setState({ val: this.state.val + 1 })}render() {return (<div onClick={this.batchUpdates}>{Counter is ${this.state.val}} // 1</div>)}
}
  1. setState 只在合成事件和生命周期中是“異步”的,在原生事件和 setTimeout 中都是同步的;
  2. setState的“異步”并不是說內(nèi)部由異步代碼實(shí)現(xiàn),其實(shí)本身執(zhí)行的過程和代碼都是同步的, 只是合成事件和鉤子函數(shù)的調(diào)用順序在更新之前,導(dǎo)致在合成事件和鉤子函數(shù)中沒法立馬拿到更新后的值,形式了所謂的“異步”, 當(dāng)然可以通過第二個參數(shù) setState(partialState, callback) 中的callback拿到更新后的結(jié)果。
  3. setState 的批量更新優(yōu)化也是建立在“異步”(合成事件、鉤子函數(shù))之上的,在原生事件和setTimeout 中不會批量更新,在“異步”中如果對同一個值進(jìn)行多次 setState , setState 的批量更新策略會對其進(jìn)行覆蓋,取最后一次的執(zhí)行,如果是同時 setState 多個不同的值,在更新時會對其進(jìn)行合并批量更新。

React生命周期

在這里插入圖片描述

render

是class組件必需的方法
獲取最新的 props 和 state
在不修改組件 state 的情況下,每次調(diào)用時都返回相同的結(jié)果

constructor

如果不初始化 state 或不進(jìn)行方法綁定,則不需要為 React 組件實(shí)現(xiàn)構(gòu)造函數(shù)。

  • 通過給 this.state 賦值對象來初始化內(nèi)部 state。
  • 為事件處理函數(shù)綁定實(shí)例
  1. 不要調(diào)用 setState()
  2. 避免將 props 的值復(fù)制給 state
this.state = { color: props.color }; // wrong

componentDidMount

會在組件掛載后(插入 DOM 樹中)立即調(diào)用
依賴于 DOM 節(jié)點(diǎn)的初始化應(yīng)該放在這里,如需通過網(wǎng)絡(luò)請求獲取數(shù)據(jù);
可以在此生命周期里加 setState,但發(fā)生在瀏覽器更新屏幕之前,會導(dǎo)致性能問題;
有更新在render階段的 constructor 中 init State,但有更新可以在此方法時 setState.

componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot)
//會在更新后會被立即調(diào)用。首次渲染不會執(zhí)行此方法。
componentDidUpdate(prevProps) {// 典型用法(不要忘記比較 props):加條件判斷,不然死循環(huán)if (this.props.userID !== prevProps.userID) {this.fetchData(this.props.userID);}
}

如果組件實(shí)現(xiàn)了 getSnapshotBeforeUpdate() 生命周期,
則它的返回值將作為 componentDidUpdate() 的第三個參數(shù) “snapshot” 參數(shù)傳遞。否則此參數(shù)將為 undefined。
如果 shouldComponentUpdate() 返回值為 false,則不會調(diào)用 componentDidUpdate()。

componentWillUnmount

componentWillUnmount() 會在組件卸載及銷毀之前直接調(diào)用。例如,清除 timer,取消網(wǎng)絡(luò)請求;
componentWillUnmount() 中不應(yīng)調(diào)用 setState(),因?yàn)樵摻M件將永遠(yuǎn)不會重新渲染;

shouldComponentUpdate

(不常用)
shouldComponentUpdate(nextProps, nextState)
根據(jù) shouldComponentUpdate() 的返回值,判斷 React 組件的輸出是否受當(dāng)前 state 或 props 更改的影響。默認(rèn)行為是 state 每次發(fā)生變化組件都會重新渲染。
作為性能優(yōu)化使用,返回false可以跳過re-render

shouldComponentUpdate() 返回 false,不會調(diào)用 UNSAFE_componentWillUpdate(),render() 和 componentDidUpdate()。

getDerivedStateFromProps

(不常用)
是為了取代componentWillReceiveProps 和 componentWillUpdate設(shè)置的
根據(jù)props的變化改變state,它應(yīng)返回一個對象來更新 state,如果返回 null 則不更新任何內(nèi)容。

  • 在使用此生命周期時,要注意把傳入的 prop 值和之前傳入的 prop 進(jìn)行比較;
  • 因?yàn)檫@個生命周期是靜態(tài)方法,同時要保持它是純函數(shù),不要產(chǎn)生副作用;
static getDerivedStateFromProps(nextProps, prevState) {const {type} = nextProps;// 當(dāng)傳入的type發(fā)生變化的時候,更新stateif (type !== prevState.type) {return {type,};}// 否則,對于state不進(jìn)行任何操作return null;
}Class ColorPicker extends React.Component {state = {color: '#000000'}static getDerivedStateFromProps (props, state) {if (props.color !== state.color) {return {color: props.color}}return null}... // 選擇顏色方法render () {.... // 顯示顏色和選擇顏色操作,setState({color: XXX})}
}Class ColorPicker extends React.Component {state = {color: '#000000',prevPropColor: '' // setState 和 forceUpdate也會觸發(fā)此生命周期,會覆蓋}static getDerivedStateFromProps (props, state) {if (props.color !== state.prevPropColor) {return {color: props.color,prevPropColor: props.color}}return null}... // 選擇顏色方法render () {.... // 顯示顏色和選擇顏色操作}
}

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate() 
//在最近一次渲染輸出(提交到 DOM 節(jié)點(diǎn))之前調(diào)用;
//此生命周期方法的任何返回值將作為參數(shù)傳遞給 componentDidUpdate()。
class ScrollingList extends React.Component {constructor(props) {super(props);this.listRef = React.createRef();}getSnapshotBeforeUpdate(prevProps, prevState) {// 我們是否在 list 中添加新的 items ?// 捕獲滾動位置以便我們稍后調(diào)整滾動位置。if (prevProps.list.length < this.props.list.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {// 如果我們 snapshot 有值,說明我們剛剛添加了新的 items,// 調(diào)整滾動位置使得這些新 items 不會將舊的 items 推出視圖。//(這里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}}render() {return (<div ref={this.listRef}>{/* ...contents... */}</div>);}
}

static getDerivedStateFromError

(不常用)
配合Error boundaries使用
此生命周期會在后代組件拋出錯誤后被調(diào)用。 它將拋出的錯誤作為參數(shù),并返回一個值以更新 state;

componentDidCatch

(不常用)
componentDidCatch() 會在“提交”階段被調(diào)用,因此允許執(zhí)行副作用。 它應(yīng)該用于記錄錯誤之類的情況;
componentDidCatch(error, info)

class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {// 更新 state 使下一次渲染可以顯示降級 UIreturn { hasError: true };}componentDidCatch(error, info) {// "組件堆棧" 例子://   in ComponentThatThrows (created by App)//   in ErrorBoundary (created by App)//   in div (created by App)//   in ApplogComponentStackToMyService(info.componentStack);}render() {if (this.state.hasError) {// 你可以渲染任何自定義的降級 UIreturn <h1>Something went wrong.</h1>;}return this.props.children;}
}

UNSAFE_componentWillMount

(不建議使用)
UNSAFE_componentWillMount() 在掛載之前被調(diào)用;
它在 render() 之前調(diào)用,因此在此方法中同步調(diào)用 setState() 不會生效;
需要的話用componentDidMount替代。

UNSAFE_componentWillReceiveProps

(不建議使用)
UNSAFE_componentWillReceiveProps() 會在已掛載的組件接收新的 props 之前被調(diào)用;
如果你需要更新狀態(tài)以響應(yīng) prop 更改(例如,重置它),你可以比較 this.props 和 nextProps 并在此方法中使用 this.setState() 執(zhí)行 state 轉(zhuǎn)換。

UNSAFE_componentWillUpdate

(不建議使用)

  • 當(dāng)組件收到新的 props 或 state 時,會在渲染之前調(diào)用 UNSAFE_componentWillUpdate();
  • 使用此作為在更新發(fā)生之前執(zhí)行準(zhǔn)備更新的機(jī)會;
  • 初始渲染不會調(diào)用此方法;
    如果 shouldComponentUpdate() 返回 false,則不會調(diào)用 UNSAFE_componentWillUpdate();

事件處理

  1. 在JSX元素上添加事件,通過on*EventType這種內(nèi)聯(lián)方式添加,命名采用小駝峰式(camelCase)的形式,而不是純小寫(原生HTML中對DOM元素綁定事件,事件類型是小寫的);
  2. 無需調(diào)用addEventListener進(jìn)行事件監(jiān)聽,也無需考慮兼容性,React已經(jīng)封裝好了一些的事件類型屬性;
  3. 使用 JSX 語法時你需要傳入一個函數(shù)作為事件處理函數(shù),而不是一個字符串;
  4. 不能通過返回 false 的方式阻止默認(rèn)行為。你必須顯式的使用 preventDefault
// DOM
<button onclick="activateLasers()">Activate Lasers
</button>// React
<button onClick={activateLasers}>Activate Lasers
</button>// JS
<form onsubmit="console.log('You clicked submit.'); return false"><button type="submit">Submit</button>
</form>

// React
一般不需要使用 addEventListener 為已創(chuàng)建的 DOM 元素添加監(jiān)聽器;
function Form() {function handleSubmit(e) {e.preventDefault();console.log('You clicked submit.');}return (<form onSubmit={handleSubmit}><button type="submit">Submit</button></form>);
}
class Toggle extends React.Component {constructor(props) {super(props);this.state = {isToggleOn: true};// 為了在回調(diào)中使用 this,這個綁定是必不可少的this.handleClick = this.handleClick.bind(this);}handleClick() {this.setState(prevState => ({isToggleOn: !prevState.isToggleOn}));}render() {return (// class 的方法默認(rèn)不會綁定 this。如果沒有綁定 this.handleClick 并把它傳入了 onClick,// this 的值為 undefined。<button onClick={this.handleClick}>{this.state.isToggleOn ? 'ON' : 'OFF'}</button>);}
}ReactDOM.render(<Toggle />,document.getElementById('root')
);// 為什么要綁定this
function createElement(dom, params) {var domObj = document.createElement(dom);domObj.onclick = params.onclick;domObj.innerHTML = params.conent;return domObj
}

// createElement 的onClick函數(shù)是綁定到domObj上的,如果this不顯式綁定,不會綁定到Toggle上

// 不顯式使用bind

  1. public class fields 語法

class LoggingButton extends React.Component {// 此語法確保 handleClick 內(nèi)的 this 已被綁定。// 注意: 這是 實(shí)驗(yàn)性 語法。handleClick = () => {console.log('this is:', this);}render() {return (<button onClick={this.handleClick}>Click me</button>);}
}2. 箭頭函數(shù),問題: 每次render都會創(chuàng)建不同的回調(diào)函數(shù),如果該回調(diào)函數(shù)作為props傳入子組件,每次子組件都要re-render
class LoggingButton extends React.Component {handleClick() {console.log('this is:', this);}render() {// 此語法確保 handleClick 內(nèi)的 this 已被綁定。return (<button onClick={() => this.handleClick()}>//  <button onClick={this.handleClick().bind(this)}>Click me</button>);}
}3. createReactClass代替

接收參數(shù)

  1. 事件對象 e 會被作為第二個參數(shù)傳遞;
  2. 通過箭頭函數(shù)的方式,事件對象必須顯式的進(jìn)行傳遞;
  3. 通過 Function.prototype.bind 的方式,事件對象以及更多的參數(shù)將會被隱式的進(jìn)行傳遞;
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

條件渲染

if else 渲染

class LoginControl extends React.Component {constructor(props) {super(props);this.handleLoginClick = this.handleLoginClick.bind(this);this.handleLogoutClick = this.handleLogoutClick.bind(this);this.state = {isLoggedIn: false};}handleLoginClick() {this.setState({isLoggedIn: true});}handleLogoutClick() {this.setState({isLoggedIn: false});}render() {const isLoggedIn = this.state.isLoggedIn;let button;if (isLoggedIn) {button = <LogoutButton onClick={this.handleLogoutClick} />;} else {button = <LoginButton onClick={this.handleLoginClick} />;}return (<div><Greeting isLoggedIn={isLoggedIn} />{button}</div>);}
}ReactDOM.render(<LoginControl />,document.getElementById('root')
);

與運(yùn)算符 &&

function Mailbox(props) {const unreadMessages = props.unreadMessages;return (<div><h1>Hello!</h1>{unreadMessages.length > 0 &&<h2>You have {unreadMessages.length} unread messages.</h2>}</div>);
}const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(<Mailbox unreadMessages={messages} />,document.getElementById('root')
);// 返回false的表達(dá)式,會跳過元素,但會返回該表達(dá)式
render() {const count = 0;return (<div>{ count && <h1>Messages: {count}</h1>}</div>);
}

三元運(yùn)算符

render() {const isLoggedIn = this.state.isLoggedIn;return (<div>{isLoggedIn? <LogoutButton onClick={this.handleLogoutClick} />: <LoginButton onClick={this.handleLoginClick} />}</div>);
}

如何阻止組件渲染

function WarningBanner(props) {if (!props.warn) {return null;}return (<div className="warning">Warning!</div>);
}class Page extends React.Component {constructor(props) {super(props);this.state = {showWarning: true};this.handleToggleClick = this.handleToggleClick.bind(this);}handleToggleClick() {this.setState(state => ({showWarning: !state.showWarning}));}render() {return (<div><WarningBanner warn={this.state.showWarning} /><button onClick={this.handleToggleClick}>{this.state.showWarning ? 'Hide' : 'Show'}</button></div>);}
}ReactDOM.render(<Page />,document.getElementById('root')
);

列表

function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) =><li key={number.toString()}>{number}</li>);return (<ul>{listItems}</ul>);
}const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root')
);
// 若沒有key,會warning a key should be provided for list items
// key可以幫助react diff,最好不用index作為key,會導(dǎo)致性能變差;
// 如果不指定顯式的 key 值,默認(rèn)使用索引用作為列表項目的 key 值;

Key 注意點(diǎn)

key要保留在map的遍歷元素上

// demo1
function ListItem(props) {// 正確!這里不需要指定 key:return <li>{props.value}</li>;
}function NumberList(props) {const numbers = props.numbers;const listItems = numbers.map((number) =>// 正確!key 應(yīng)該在數(shù)組的上下文中被指定<ListItem key={number.toString()} value={number} />);return (<ul>{listItems}</ul>);
}const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root')
);// demo2
function Blog(props) {const sidebar = (<ul>{props.posts.map((post) =><li key={post.id}>{post.title}</li>)}</ul>);const content = props.posts.map((post) =><div key={post.id}><h3>{post.title}</h3><p>{post.content}</p></div>);return (<div>{sidebar}<hr />{content}</div>);
}const posts = [{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(<Blog posts={posts} />,document.getElementById('root')
);// demo3
function NumberList(props) {const numbers = props.numbers;return (<ul>{numbers.map((number) =><ListItem key={number.toString()}value={number} />)}</ul>);
}

Create-react-app

官方地址:https://create-react-app.dev/
github:https://github.com/facebook/create-react-app
create-react-app是一個官方支持的創(chuàng)建React單頁應(yīng)用程序的腳手架。它提供了一個零配置的現(xiàn)代化配置設(shè)置。
在這里插入圖片描述

immutable 及immer

immutable

官方地址:https://immutable-js.com/
解決的問題:
JavaScript 中的對象一般是可變的(Mutable),因?yàn)槭褂昧艘觅x值,新的對象簡單的引用了原始對象,改變新的對象將影響到原始對象。如 foo={a: 1}; bar=foo; bar.a=2 你會發(fā)現(xiàn)此時 foo.a 也被改成了 2。雖然這樣做可以節(jié)約內(nèi)存,但當(dāng)應(yīng)用復(fù)雜后,這就造成了非常大的隱患,Mutable 帶來的優(yōu)點(diǎn)變得得不償失。為了解決這個問題,一般的做法是使用 shallowCopy(淺拷貝)或 deepCopy(深拷貝)來避免被修改,但這樣做造成了 CPU 和內(nèi)存的浪費(fèi)。

什么是immutable data

  • Immutable Data 就是一旦創(chuàng)建,就不能再被更改的數(shù)據(jù);
  • 對 Immutable 對象的任何修改或添加刪除操作都會返回一個新的 Immutable 對象;
  • Immutable 實(shí)現(xiàn)的原理是 Persistent Data Structure(持久化數(shù)據(jù)結(jié)構(gòu)):也就是使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)時,要保證舊數(shù)據(jù)同時可用且不變。同時為了避免 deepCopy 把所有節(jié)點(diǎn)都復(fù)制一遍帶來的性能損耗,Immutable 使用了 Structural Sharing(結(jié)構(gòu)共享),即如果對象樹中一個節(jié)點(diǎn)發(fā)生變化,只修改這個節(jié)點(diǎn)和受它影響的父節(jié)點(diǎn),其它節(jié)點(diǎn)則進(jìn)行共享。

immutable.js

Facebook 工程師 Lee Byron 花費(fèi) 3 年時間打造,與 React 同期出現(xiàn),但沒有被默認(rèn)放到 React 工具集里(React 提供了簡化的 Helper)。它內(nèi)部實(shí)現(xiàn)了一套完整的 Persistent Data Structure,還有很多易用的數(shù)據(jù)類型。像 Collection、List、Map、Set、Record、Seq。有非常全面的map、filter、groupBy、reduce``find函數(shù)式操作方法。同時 API 也盡量與 Object 或 Array 類似。

// 原來的寫法
let foo = {a: {b: 1}};
let bar = foo;
bar.a.b = 2;
console.log(foo.a.b);  // 打印 2
console.log(foo === bar);  //  打印 true// 使用 immutable.js 后
import Immutable from 'immutable';
foo = Immutable.fromJS({a: {b: 1}});
bar = foo.setIn(['a', 'b'], 2);   // 使用 setIn 賦值
console.log(foo.getIn(['a', 'b']));  // 使用 getIn 取值,打印 1
console.log(foo === bar);  //  打印 false

immmutable.js 優(yōu)點(diǎn)

  1. 降低了mutable帶來的復(fù)雜性
function touchAndLog(touchFn) {let data = { key: 'value' };touchFn(data);console.log(data.key);// 因?yàn)椴恢纓ouchFn進(jìn)行了什么操作,所以無法預(yù)料,但使用immutable,肯定是value
}
  1. 節(jié)省內(nèi)存
    會盡量復(fù)用內(nèi)存,甚至以前使用的對象也可以再次被復(fù)用。沒有被引用的對象會被垃圾回收。
import { Map} from 'immutable';
let a = Map({select: 'users',filter: Map({ name: 'Cam' })
})
let b = a.set('select', 'people');a === b; // false
a.get('filter') === b.get('filter'); // true
  1. Undo/Redo,Copy/Paste
    因?yàn)槊看螖?shù)據(jù)都是不一樣的,所有可以存儲在數(shù)組里,想回退到哪里就拿出對應(yīng)數(shù)據(jù)即可

immutable.js缺點(diǎn)

  1. 需要學(xué)習(xí)新的API
  2. 容易與原生對象混淆
    雖然 Immutable.js 盡量嘗試把 API 設(shè)計的原生對象類似,有的時候還是很難區(qū)別到底是 Immutable 對象還是原生對象,容易混淆操作。
  3. Immutable 中的 Map 和 List 雖對應(yīng)原生 Object 和 Array,但操作非常不同,比如你要用 map.get(‘key’) 而不是 map.key,array.get(0) 而不是 array[0]。另外 Immutable 每次修改都會返回新對象,也很容易忘記賦值;
  4. 當(dāng)使用外部庫的時候,一般需要使用原生對象,也很容易忘記轉(zhuǎn)換。

下面給出一些辦法來避免類似問題發(fā)生:
5. 使用TypeScript 這類有靜態(tài)類型檢查的工具;
6. 約定變量命名規(guī)則:如所有 Immutable 類型對象以 $$ 開頭;
7. 使用 Immutable.fromJS 而不是 Immutable.Map 或 Immutable.List 來創(chuàng)建對象,這樣可以避免 Immutable 和原生對象間的混用;

immutable.is & cursor

  • immutable.is
    // 兩個 immutable 對象可以使用 === 來比較,這樣是直接比較內(nèi)存地址,性能最好。
    // 但即使兩個對象的值是一樣的,也會返回 false:

let map1 = Immutable.Map({a:1, b:1, c:1});
let map2 = Immutable.Map({a:1, b:1, c:1});
map1 === map2;             // false// 為了直接比較對象的值,immutable.js 提供了 Immutable.is 來做『值比較』,結(jié)果如下:Immutable.is(map1, map2);  // true
Immutable.is 
// 比較的是兩個對象的 hashCode 或 valueOf(對于 JavaScript 對象)。
// 由于 immutable 內(nèi)部使用了 Trie 數(shù)據(jù)結(jié)構(gòu)來存儲,只要兩個對象的 hashCode 相等,值就是一樣的。
// 這樣的算法避免了深度遍歷比較,性能非常好。
  • cursor
    由于 Immutable 數(shù)據(jù)一般嵌套非常深,為了便于訪問深層數(shù)據(jù),Cursor 提供了可以直接訪問這個深層數(shù)據(jù)的引用。
import Immutable from 'immutable';
import Cursor from 'immutable/contrib/cursor';let data = Immutable.fromJS({ a: { b: { c: 1 } } });
// 讓 cursor 指向 { c: 1 }
let cursor = Cursor.from(data, ['a', 'b'], newData => {// 當(dāng) cursor 或其子 cursor 執(zhí)行 update 時調(diào)用console.log(newData);
});cursor.get('c'); // 1
cursor = cursor.update('c', x => x + 1);
cursor.get('c'); // 2

使用immutable.js優(yōu)化react

  1. React可以使用 shouldComponentUpdate()進(jìn)行性能優(yōu)化,但它默認(rèn)返回 true,即始終會執(zhí)行 render() 方法,然后做 Virtual DOM 比較,并得出是否需要做真實(shí) DOM 更新;
  2. 可以在shouldComponentUpdate 周期里執(zhí)行deepCopy 和 deepCompare 避免無意義的render,但deepFn也很耗時;
import { is } from 'immutable';shouldComponentUpdate: (nextProps = {}, nextState = {}) => {const thisProps = this.props || {}, thisState = this.state || {};if (Object.keys(thisProps).length !== Object.keys(nextProps).length ||Object.keys(thisState).length !== Object.keys(nextState).length) {return true;}for (const key in nextProps) {if (!is(thisProps[key], nextProps[key])) {return true;}}for (const key in nextState) {if (thisState[key] !== nextState[key] && !is(thisState[key], nextState[key])) {return true;}}return false;
}

如何解決引用類型對象被修改?

  1. 深度拷貝,但是深拷貝的成本較高,會影響性能;
  2. ImmutableJS,非常棒的一個不可變數(shù)據(jù)結(jié)構(gòu)的庫,可以解決上面的問題,但跟 Immer 比起來,ImmutableJS 有兩個較大的不足:
  3. 需要使用者學(xué)習(xí)它的數(shù)據(jù)結(jié)構(gòu)操作方式,沒有 Immer 提供的使用原生對象的操作方式簡單、易用;
  4. 它的操作結(jié)果需要通過toJS方法才能得到原生對象,這使得在操作一個對象的時候,時刻要主要操作的是原生對象還是 ImmutableJS 的返回結(jié)果,稍不注意,就會產(chǎn)生問題;
    // 如何使用immer解決上述問題

// Q1 Q3
import produce from 'immer';
let o1 = produce(currentState, draft => {draft.p.x = 1;
})// Q2
import produce from 'immer';
fn(currentState);
function fn(o) {return produce(o, draft => {draft.p1 = 1;})
};// Q4
import produce from 'immer';
let o4 = produce(currentState, draft => {draft.p.x.push(1);
})

概念說明

  • currentState:被操作對象的最初狀態(tài)
  • draftState:根據(jù) currentState 生成的草稿狀態(tài),它是 currentState 的代理,對 draftState 所做的任何修改都將被記錄并用于生成 nextState 。在此過程中,currentState 將不受影響
  • nextState:根據(jù) draftState 生成的最終狀態(tài)
  • produce:用來生成 nextState 或 producer 的函數(shù)
  • producer:通過 produce 生成,用來生產(chǎn) nextState ,每次執(zhí)行相同的操作
  • recipe:用來操作 draftState 的函數(shù)

produce的使用

  1. produce(currentState, recipe: (draftState) => void | draftState, ?PatchListener): nextState
// Q1
let nextState = produce(currentState, (draft) => {})currentState === nextState; // true// Q2
let currentState = {a: [],p: {x: 1}
}let nextState = produce(currentState, (draft) => {draft.a.push(2);
})currentState.a === nextState.a; // false
currentState.p === nextState.p; // true
  1. 對 draftState 的修改都會反應(yīng)到 nextState;
  2. Immer 使用的結(jié)構(gòu)是共享的,nextState 在結(jié)構(gòu)上又與 currentState 共享未修改的部分;
    immer支持自動凍結(jié):通過produce生產(chǎn)的nextState是被Object.freeze的
const currentState = {p: {x: [2],},
};
const nextState = produce(currentState, draftState => {draftState.p.x.push(3);
});
console.log(nextState.p.x); // [2, 3]
nextState.p.x = 4;
console.log(nextState.p.x); // [2, 3]
nextState.p.x.push(5); // 報錯
  1. produce(recipe: (draftState) => void | draftState, ?PatchListener)(currentState): nextState
    利用高階函數(shù)的特點(diǎn),提前生成一個 producer
let producer = produce((draft) => {draft.x = 2
});
let nextState = producer(currentState);

使用immer優(yōu)化react

// 定義state
state = {members: [{name: 'ronffy',age: 30}]
}// 如何給member中第一個元素的age+1// error
this.state.members[0].age++;// setState
const { members } = this.state;
this.setState({members: [{...members[0],age: members[0].age + 1,},...members.slice(1),]
})// 使用reducer
const reducer = (state, action) => {switch (action.type) {case 'ADD_AGE':const { members } = state;return {...state,members: [{...members[0],age: members[0].age + 1,},...members.slice(1),]}default:return state}
}// 使用immer
this.setState(produce(draft => {draft.members[0].age++;
}))// 使用immer結(jié)合reduce
// 注意: produce 內(nèi)的 recipe 回調(diào)函數(shù)的第2個參數(shù)與obj對象是指向同一塊內(nèi)存
let obj = {};let producer = produce((draft, arg) => {obj === arg; // true
});
let nextState = producer(currentState, obj);const reducer = (state, action) => produce(state, draft => {switch (action.type) {case 'ADD_AGE':draft.members[0].age++;}
})
http://www.risenshineclean.com/news/11622.html

相關(guān)文章:

  • 網(wǎng)站服務(wù)器 內(nèi)存seo怎么才能做好
  • 做網(wǎng)站的如何找客戶平臺引流推廣怎么做
  • 做網(wǎng)站可以賺錢嗎?atp最新排名
  • 農(nóng)村網(wǎng)站建設(shè)百度上怎么免費(fèi)開店
  • 設(shè)置本機(jī)外網(wǎng)ip做網(wǎng)站技術(shù)培訓(xùn)
  • 做線下活動的網(wǎng)站泉州百度關(guān)鍵詞排名
  • 可做生物試卷的網(wǎng)站站長統(tǒng)計在線觀看
  • 高端網(wǎng)站開發(fā)企業(yè)百度百度一下
  • 有做自由行包車的網(wǎng)站qq群推廣網(wǎng)站免費(fèi)
  • 大理北京網(wǎng)站建設(shè)app開發(fā)定制
  • 青島app網(wǎng)站開發(fā)企業(yè)網(wǎng)站建設(shè)價格
  • 唐河做網(wǎng)站網(wǎng)站推廣鄭州
  • 如何選擇贛州網(wǎng)站建設(shè)網(wǎng)站關(guān)鍵詞排名服務(wù)
  • 長沙app下載seo網(wǎng)站推廣實(shí)例
  • 網(wǎng)站運(yùn)營隊伍與渠道建設(shè)網(wǎng)站的宣傳與推廣
  • wordpress問答社區(qū)模板合肥seo整站優(yōu)化網(wǎng)站
  • 自己做pc網(wǎng)站建設(shè)seo教學(xué)視頻教程
  • 開80服務(wù)器怎么做網(wǎng)站網(wǎng)站設(shè)計的基本原則
  • 泰州做網(wǎng)站哪家好自動化測試培訓(xùn)機(jī)構(gòu)哪個好
  • 建站時網(wǎng)站地圖怎么做網(wǎng)絡(luò)推廣員一個月多少錢
  • 用php做網(wǎng)站用什么框架百度域名查詢官網(wǎng)
  • 做網(wǎng)站開發(fā)有哪些優(yōu)點(diǎn)呢百度賬號快速注冊
  • 做淘寶網(wǎng)站如何提取中間的提成2024年新聞?wù)?/a>
  • 北京景觀設(shè)計公司長春網(wǎng)絡(luò)優(yōu)化哪個公司在做
  • 軟件工程就業(yè)方向什么是網(wǎng)站推廣優(yōu)化
  • html網(wǎng)站標(biāo)題怎么做百度如何購買關(guān)鍵詞
  • 哪個網(wǎng)站可以做信用社的題免費(fèi)發(fā)布推廣的網(wǎng)站
  • 網(wǎng)頁制作建立站點(diǎn)廈門人才網(wǎng)官方網(wǎng)站
  • 電商網(wǎng)站開發(fā)的流程圖招商外包
  • dw網(wǎng)站制作怎么做滑動的圖片seo在線排名優(yōu)化