React基礎

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、生命週期

生命週期函數指在某一時刻組件會自動調用執行的函數。

react生命週期

  • 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

工作流程
redux的工作流程

使用

// 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語法,不用重新再學習新技術,也不會多一道編譯步驟,加快網頁速度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章