React基礎
本人學習的一些筆記
1、什麼時候用react,什麼時候用vue
react靈活性比較大,處理複雜業務時有更多技術方案的選擇 。
vue提供了更豐富的api,實現功能簡單,但也因api多會對靈活性有一定的限制。
做複雜度比較高的項目時使用react,面向用戶端複雜度不高的使用vue 。
2、JSX
React發明了JSX,利用HTML語法來創建虛擬DOM。React的核心機制之一就是可以在內存中創建虛擬的DOM元素。以此來減少對實際DOM的操作從而提升性能。JSX即JavaScript XML,它是對JavaScript的語法擴展,React使用JSX來替代常規的JS。
JSX的優點
- JSX執行更快,因爲它在編譯爲了JS代碼進行了優化
- 它是類型安全的,在編譯過程中就能發現錯誤
- React DOM 在渲染所有輸入內容之前,默認會進行轉義。它可以確保在你的應用中,永遠不會注入那些並非自己明確編寫的內容。所有的內容在渲染之前都被轉換成了字符串。這樣可以有效地防止 XSS(cross-site-scripting, 跨站腳本)攻擊。
- 使用JSX編寫模板更加簡單快速
JSX語法
1、我們可以在代碼中嵌套多個HTML標籤,需要使用一個div元素包裹它。也可以用<Fragment></Fragment>
(相當於佔位符,但不會增加元素)來包裹
2、實例中的p元素添加了自定義屬性data-myattribute,添加自定義屬性需要使用“data-”前綴
3、我們可以在JSX中使用JS表達式(不能適用於語句),表達式寫在大括號“{}”中
-
{2+2}
{user.firstName}
{formatName(user)}
-
在JSX中不能使用if-else語句,但可以使用conditional(三元運算)表達式來替代
const show = true; {show ? <img src="xxx.png"/> : ''}
-
循環
const list = [1, 2, 3, 4, 5]; { list.map((item, index) => { return <li key={index}>{item}</li> }) }
4、樣式
-
React推薦使用內聯樣式。我們可以使用camelCase語法設置內聯樣式。
React會在指定元素數字後自動添加px
var myStyle = { fontSize: 100, // css中爲font-size color: '#FF0000' }; <h1 style={myStyle}>xxx</h1>
-
<h1 style = {{background: red;}}>xxx</h1> //兩個大括號
-
.red-btn { background: red; } <h1 className='red-btn'>xxx</h1> // 使用className而不是class
5、註釋
{/* ... */}
{
// (單行註釋要換行)
}
6、數組
JSX允許在模板中插入數組,數組會自動展開所有成員
var arr = [
<h1>xxx</h1>
<h2>xxx</h2>
];
<div>{arr}</div>
3、向事件處理程序傳遞參數
在循環中,通常我們會爲事件處理函數傳遞額外的參數。例如,若 id 是你要刪除那一行的 ID,以下兩種方式都可以向事件處理函數傳遞參數:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述兩種方式是等價的,分別通過箭頭函數和 Function.prototype.bind 來實現。
4、state
-
不要直接修改state
this.state.comment = 'hello'; // wrong this.setState({ comment: 'hello'; //right })
構造函數是唯一可以給this.state賦值的地方
-
State 的更新可能是異步的
出於性能考慮,React 可能會把多個 setState() 調用合併成一個調用。
因爲 this.props 和 this.state 可能會異步更新,所以你不要依賴他們的值來更新下一個狀態。
例如,此代碼可能會無法更新計數器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要解決這個問題,可以讓 setState() 接收一個函數而不是一個對象。這個函數用上一個 state 作爲第一個參數,將此次更新被應用時的 props 做爲第二個參數:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
5、阻止默認行爲
不能通過返回 false 的方式阻止默認行爲。必須顯式的使用 preventDefault 。
例如,傳統的 HTML 中阻止鏈接默認打開一個新頁面,你可以這樣寫:
<a href="#" onclick="console.log('The link was clicked.'); return false">
<a>Click me</a>
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
在這裏,e 是一個合成事件。React 根據 W3C 規範來定義這些合成事件,所以你不需要擔心跨瀏覽器的兼容性問題。
6、阻止組件渲染
在極少數情況下,你可能希望能隱藏組件,即使它已經被其他組件渲染。若要完成此操作,你可以讓 render 方法直接返回 null,而不進行任何渲染。
下面的示例中, 會根據 prop 中 warn 的值來進行條件渲染。如果 warn 的值是 false,那麼組件則不會渲染:
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')
);
7、一個元素的 key
最好是這個元素在列表中擁有的一個獨一無二的字符串。通常,我們使用來自數據 id 來作爲元素的 key:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
當元素沒有確定 id 的時候,萬不得已你可以使用元素索引 index 作爲 key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
**循環中的key值最好不是index,原始虛擬DOM樹和新的虛擬DOM樹的key值一致能提升虛擬DOM比對的性能,而列表項目的順序可能會變化,index是不穩定的,經常會改變。使用index做key值會導致性能變差,還可能引起組件狀態的問題。**如果你選擇不指定顯式的 key 值,那麼 React 將默認使用索引用作爲列表項目的 key 值。
8、setState的過程
- 每個組件實例,都有renderComponent方法
- 執行renderComponent會重新執行執行的render
- render函數返回newVnode,然後拿到vnode
- 執行patch(vnode, newVnode)
9、React和Vue對比
兩者本質區別
- Vue—本質是MVVM框架,由MVC發展而來
- React—本質是前端組件化框架,由後端組件化發展而來
模板的區別
- Vue—使用模板(最初由Angular提出)
- React—使用JSX
- 模板語法上,更傾向於JSX
- 模板分離上,更傾向於Vue(React模板與JS混在一起,未分離)
組件化的區別
- React本身就是組件化,沒有組件化就不是React
- Vue也支持組件化,不過是在MVVM上的擴展
- 對於組件化,更傾向於React,做得徹底而清新
兩者共同點
- 都支持組件化
- 都是數據驅動視圖
10、PWA
什麼是PWA
PWA全稱Progressive Web App,即漸進式WEB應用。
寫網頁的形式寫手機APP應用。
1、可以添加至主屏幕,點擊主屏幕圖標可以實現啓動動畫以及隱藏地址欄
2、實現離線緩存功能,即使用戶手機沒有網絡,依然可以使用一些離線功能
3、實現了消息推送
registerServiceWorker
引用它,網頁上線到支持https協議的服務器上。第一次訪問時需聯網才能看到,但突然斷網,第二次訪問時依然可以看到之前訪問過的頁面,因爲registerServiceWorker會把之前的網頁存儲在瀏覽器內。
11、html元素轉義
var item = `<h1>hello</h1>`
<li>
key={index}
onClick={this.handleItemDelete.bind(this, index)}
dangerouslySetInnerHTML={__html: item}
</li>
12、擴大點擊區域
<label htmlFor="insert">輸入內容</label>
<input id="insertArea"/>
13、react的思考
- 聲名式開發
- 可以與其它框架並存
- 組件化
- 單向數據流
- 視圖層框架
- 函數式編程
14、父子組件的通信
-
父組件通過屬性形式向子組件傳遞參數,子組件通過props接收父組件傳遞過來的參數
無論是使用函數聲明還是class聲明,都絕不能改變自身的props,所有React組件都必須像純函數一樣保護它們的props不被改變
// 父組件 <TodoItem delete={this.handleDelete} key={index} content={item} index={index} /> // 子組件 handleDelete() { this.props.delete(this.props.index); } <li key={this.props.index} onClick={this.handleDelete}>{this.props.content}</li>
-
子組件如果想與父組件通信,子組件要調用父組件傳過來的方法
-
子組件只能使用父組件傳遞過來的值,但不能改變值(單向數據流)
15、子組件PropTypes和DefaultProps
import PropTypes from 'prop-types';
// 屬性類型校驗
TodoItem.propTypes = {
test: PropTypes.string.isRequired,
content: PropTypes.string,
deleteItem: PropTypes.func,
index: PropTypes.number
}
// 定義屬性默認值
TodoItem.defaultProps = {
test: 'hello world'
}
16、props,state與render函數
當組件的state或props發生改變的時候,render函數就會重新執行。
當父組件的render函數被運行時,它的子組件的render都將被重新運行一次。
17、虛擬DOM
創建真實DOM損耗的性能遠大於創建虛擬DOM損耗的性能
1、state數據
2、JSX模板
3、數據+模板 生成虛擬DOM(虛擬DOM就是一個JS對象,用它來描述真實DOM)(損耗了性能)
['div', {id: 'abc'}, ['span', {}, 'hello world']]
4、用虛擬DOM的結構,生成真實的DOM來顯示
<div id='abc'><span>hello world</span></div>
5、state發生變化
6、數據+模板 生成新的虛擬DOM(極大地提升了性能)
['div', {id: 'abc'}, ['span', {}, 'byebye']]
7、比較原始虛擬DOM和新的虛擬DOM的區別,找到區別是span中的內容(極大地提升了性能)
8、直接操作DOM,改變span中的內容
JSX => createElement => 虛擬DOM(JS對象)=> 真實DOM
return <div>item</div>
等價於 return ReactElement('div', {}, 'item')
虛擬DOM的優點:
1、性能提升
2、它使得跨端應用得以實現 (React Native)
18、React中ref的使用(不過儘量少用)
<input value={this.state.inputValue} ref={(input) => {this.input = input;}} onChange={this.handleInputChange} />
handleInputChange() {
const value = this.input.value; //替換e.target.value
this.setState(() => ({
inputValue: value
}))
}
19、生命週期
生命週期函數指在某一時刻組件會自動調用執行的函數。
- constructor:在組件一創建的時刻就被調用。但不歸類在React的生命週期中,因爲它是ES6裏面的東西,不是React獨有的。
- componentWillMount:在組件即將被掛載到頁面的時刻自動執行。
- componentDidMount:在組件被掛載後自動執行。
- shouldComponentUpdate:組件被更新之前,自動被執行需要返回一個布爾值。true 更新 false 不會被更新
- componentWillUpdate:組件被更新之前,它會自動執行,但是它在shouldComponentUpdate之後被執行,如果返回true就執行,如果返回false,這個函數就不會被執行了。
- componentDidUpdate:組件被更新之後自動執行。
- componentWillReceiveProps:一個組件要從父組件接受參數。只要父組件的render函數被重新執行了,子組件的這個生命週期函數就會被執行(如果這個組件第一次存在與父組件中,不會執行;如果這個組件之前已經存在於父組件中,纔會執行)
- componentWillUnmount:當這個組件即將被從頁面中剔除的時候,會被執行。
20、性能提升
1、this.handleClick = this.handleClick.bind(this);
將這種作用域的修改放在constructor中,保證作用域綁定操作只執行一次。
2、setState異步函數:能將多個數據的改變結合成一次來做,降低虛擬DOM的比對頻率。
3、虛擬DOM,同層比對
4、藉助shouldComponentUpdate避免組件做多次無謂的render操作
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.content !== this.props.content) {
return true;
} else {
return false;
}
...
}
5、ajax請求放在componentDidMount裏
componentDidMount() {
axios.get('/api/todolist')
.then(() => { alert('succ'); })
.catch(() => { alert('error') })
}
21、React的css過渡動畫
.show {
opacity: 1;
transition: all 1s ease-in;
}
.hide {
animation: hide-item 2s ease-in forwords; // 保持動畫最後一幀css的樣式
}
@keyframes hide-item {
0% {
opacity: 1;
color: red;
}
50% {
opacity: 0.5;
color: green;
}
100% {
opacity: 0;
color: blue;
}
}
22、使用react-transition-group實現動畫
import {CSSTransition} from 'react-transition-group';
<CSSTransition
onEntered={(el) => {el.style.color = 'blue';}} // 結束時爲藍色
in={this.state,show}
className='fade'
timeout={1000} // 動畫執行時間
appear={true} // 第一次展現也有動畫效果
unmountOnExit // DOM消失時被移除
>
<div>hello</div>
</CSSTransition>
.fade-enter, .fade-appear {
opacity: 0;
}
.fade-enter-active, .fade-appear-active {
opacity: 1;
transition: opacity 1s ease-in;
}
.fade-enter-done {
opacity: 1;
color: red; // 結束之後爲紅色
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 1s ease-in;
}
.fade-exit-done {
opacity: 0;
}
23、Redux
Redux = Reducer + Flux
工作流程
使用
// store/index.js
import {createStore} from 'redux';
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__() // 用於redux調試
);
// store/reducer.js
const defaultState = {
inputValue: '',
list: []
};
// reducer 可以接收state,但絕不能修改state,所以要另外拷貝一個
export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
// 深拷貝
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
return state;
}
// Todolist.js (部分)
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
store.subscribe(this.handleStoreChange); // 訂閱方法設置更新數據
}
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
handleInputChange={this.handleInputChange}
handleBtnClick={this.handleBtnClick}
handleItemDelete={this.handleItemDelete}
/>
)
}
handleInputChange(e) {
const action = getInputChangeAction(e.target.value);
store.dispatch(action);
}
handleStoreChange() {
this.setState(store.getState());
}
}
改變store裏的數據
1、先派發一個action,通過dispatch方法傳遞給store
2、reducer中接收state和action進行處理,返回一個新的state返回給store,替換原來的store
3、store中數據改變react感知到store數據的改變,通過store.subscribe()訂閱方法設置更新數據
Redux設計和使用的三項原則
1.store是唯一的
2.只有store能改變自己的內容
3.reducer必須是純函數
純函數:給定固定的輸入,就一定會有固定的輸出,而且不會有任何的副作用
funcition sum(a, b) {
return a + b;
}
// 這樣的函數被稱爲純函數,因爲該函數不會嘗試更改入參,且多次調用下相同的入參始終返回相同的參數。
// 下面不是,自己更改了入參
function withdraw(account, amount) {
account,total -= amount;
}
Redux核心API
1、createStore ——創建store
2、store.dispatch ——派發action,這個action會傳遞給store
3、store.getState ——獲取store中所有的數據內容
4、store.subscribe ——訂閱store的改變,只要store發生改變,subscribe中接收的回調函數就會被執行
24、Redux的中間件
對dispatch方法進行升級:
接收對象,和原來一樣,直接傳遞對象給store
接收函數,先執行函數,執行完後需要調用store再操作
如:
redux-thunk中間件——改造store.dispatch使得後者可以接受函數作爲參數
redux-saga——單獨把邏輯拆分出來放到另一個文件中管理
ps:中間是指action和store的中間,中間件是Redux的中間件,而不是react
redux-thunk的使用
redux-saga的使用
25、React-Redux
核心API:
Provider:作用:連接store,內部組件都有能力獲取store的內容
connect:組件與store作連接
mapStateToProps:把store中state映射成組件中的props
mapDispatchToProps:將store.dispatch掛載到props上
使用
26、style-components
是針對React寫的一套css in js框架,簡單來講,就是在js中寫css。相對於預處理器(sass,less)的好處是,css in js使用的是js語法,不用重新再學習新技術,也不會多一道編譯步驟,加快網頁速度。