React 已經是 JavaScript 生態系統中最受歡迎的前端框架之一 。儘管人們已經對它贊不絕口,但 React 團隊仍然在努力讓它變得更好。 在 2018 ReactConf 大會上,React 官方發佈了 Hooks ,隨後它席卷了整個 React 開發界。 React Hooks 是 R ...
React 已經是 JavaScript 生態系統中最受歡迎的前端框架之一。儘管人們已經對它贊不絕口,但 React 團隊仍然在努力讓它變得更好。
在 2018 ReactConf 大會上,React 官方發佈了 Hooks,隨後它席卷了整個 React 開發界。
React Hooks 是 React 庫的新增功能,它允許你編寫狀態邏輯並使用其他 React 功能,同時無需編寫類組件。你甚至可以單獨使用 Hooks 來製作自己的應用程式,這對 React 相關的從業者來說是一次重大變革。
今天給大家帶來一門 React Hooks 的課程React Hooks 從入門到實踐 ,課程將對 React Hooks 做全方位的分析,並通過純 Hooks 函數組件對 CNode 網站進行移動端頁面的開發,實戰過程中還會介紹前端開發中常用技術棧的使用。
課程實戰用到的技術棧 React + React-Router+ Antd-Mobile + Axios ,實戰部分使用最新 vw 方式做移動端的適配,拋棄 rem 的形式擁抱變化。重寫 Axios 請求庫,做到請求返回統一處理。開發環境的搭建可以作為一個種子項目,方便在工作中啟動新項目時直接使用。
項目效果圖:
教程開始:
React 作為 Facebook 推出的前端主流框架之一,在版本升級上一直是採用平滑升級的模式。在升新版本的時候,無論是增加或者刪除了某些 API,React 都能做到版本向後相容,也就是用舊版本的寫法,最新的 React 包也能做到基本支持。這次也不例外,在 v16.8 版本引入了全新的 API,名叫 React Hooks。引用官方的解釋就是三個點
- 完全可選的。你無需重寫任何已有代碼就可以在一些組件中嘗試 Hooks。但是如果你不想,你不必現在就去學習或使用 Hooks。
- 100% 向後相容的。Hooks 不包含任何破壞性改動。
- 現在可用。Hooks 已發佈於 v16.8.0。
React 官方沒有計劃從 React 中移除 Class,但是我相信不久的將來,Hooks 將被大範圍使用。相比之下 Hooks 可以涵蓋所有 Class 組件的應用場景,且提供了更高的靈活性、可測試性和代碼的復用能力。Hooks 不會影響你對 React 概念的理解。恰恰相反,Hooks 為已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。在後面的章節里,筆者還會一一介紹上面列出的 API。
Dan Abramov 在社交他的社交網站上也毫不吝嗇的給出了他的想法。
Hooks 將會是 React 的未來。
我們為什麼不再需要 Class 組件
存在即是合理的,React Hooks 要解決的問題是狀態共用,是繼 render-props 和 higher-order components 之後的第三種狀態共用方案,不會產生 JSX 嵌套地獄問題。這個狀態指的是狀態邏輯,所以稱為 狀態邏輯復用會更恰當,因為只共用數據處理邏輯,不會共用數據本身。
在 React Hooks 推出之前,React 便已經有函數組件了,那麼已經有了函數組件,為什麼開始還要引入 Class 組件呢?
早些時候的 React 組件以有無狀態(state)分為兩種,代碼如下。
// 有狀態組件
class Hello extends React.Component {
constructor(props) {
super(props)
this.state = {
text: 'Hello World'
}
render() {
return <div>{text}</div>
}
}
}
// 無狀態組件
// 狀態通過父組件傳入
const Hello = (props) => <div>{props.text}</div>
我們可以通過 Class 組件的 this 上下文去保存和訪問狀態(state),但是函數組件在其作用域內很難維持住這個狀態,試想如果再次運行函數的話,所有的狀態都將被重置。所以我們才一直使用 Class 的形式編寫有狀態組件。
Hooks 編寫函數組件,它的狀態是如何維持的呢
瞭解過 React Fiber 的同學應該知道,類組件中的狀態其實保存在 Fiber 的屬性 memoizedState 上,並不是在 Class 的 this.state 上。那麼回過頭來看 React Hooks 組件的狀態,其實也是去訪問 Fiber 上的 memoizedState 屬性,這樣看來,問題就迎刃而解了。
兩種寫法的對比分析
繁重的寫法。
下麵是一段簡單的以 Class 形式書寫的組件代碼。
import React, { Component } from "react";
export default class MyButton extends Component {
constructor() {
super();
this.state = {
text: "點擊"
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
text: "改變"
});
}
render() {
const { text } = this.state;
return <button onClick={this.handleClick}>{text}</button>;
}
}
僅僅是需要一個按鈕組件,代碼量已經接近 20 行之多,可能你會覺得 20 行並不多,但是當組件內部需要控制一些狀態的時候,那代碼量就不僅僅是 20 行這麼簡單了。包括整個 React 項目都是由各個組件拼裝而成,層層嵌套,再加上狀態管理插件如 Redux、Mobx 等,就會是一場 “災難”。
反觀 Hooks 的寫法:
import React from "react";
export default const MyButton = () => {
const [text, setText] = useState('點擊')
return <button onClick={setText('改變')}>{text}</button>
}
在 Hooks 出現之前,React 也是可以用函數寫組件的,但是只能寫一些無狀態的純組件 (Pure Component) ,也就是內部是不能有屬於自己的狀態變數。上述代碼中 Hooks 實現了函數組件管理自身狀態的方式,不僅在代碼量上得以控制,書寫上也簡便了不少。受限於現代瀏覽器,所有最新 ES6 + 的寫法,不是所有瀏覽器都會支持。類組件的寫法在經過 babel 編譯後會編譯成 ES5 寫法,才能讓各大瀏覽器得以支持和運行。這就導致了編譯後類組件比函數組件多一層繼承 React.Component 的代碼,從這個角度出發,Hooks 寫法降低了編譯後的代碼了減少 bundle 包的大小。
複雜組件變得難以理解。
隨著組件代碼的增多,狀態與狀態緊密相連,想把組件拆分的細緻那就變得難上加難。重覆的邏輯在不同的組件和生命周期函數之間不斷出現,到那時項目就變得不可維護。
開發環境的搭建
課程總共為 10 章,1 ~ 8 章以實例和原理講解為主,9 ~ 10 章 為實戰章節。
所以 1 ~ 8 章筆者只需要通過官方提供的項目初始化工具 create-react-app 來完成課程內部代碼的講解。
首先電腦里必須事先安裝 Node 環境。
檢測當前 Node 版本和 NPM 版本,執行命令行。
node -v
npm -v
如果已經安裝過的同學會列印出相應的 Node 版本,當然實驗樓也提供了前端的開發環境,內置 Node 和 NPM,已經在全局安裝了 cnpm 包,同學儘量使用 cnpm 安裝 node_modules 包,因為有些包是放在國外的伺服器,直接使用 npm 可能會安裝不上或者需要很久的時間,對大家的開發體驗也是不好的。
全局安裝 create-react-app。
cnpm install create-react-app -g
安裝完畢之後,通過指令可以生成項目。
// my-app 為項目文件夾的名字,可自定義
npx create-react-app my-app
or
npm init react-app myapp
初始化項目結束之後,進入進入文件夾,通過 npm run start 啟動項目,預設的埠會是 3000,而實驗樓線上開發環境只對外開放 8080 埠,所以我們要對文件里的腳本做一些改動。
有兩種方式改變 create-react-app 初始化項目的開發環境啟動埠。
- 修改 package.json 的 scripts 屬性。
"scripts": {
"start": "PORT=8080 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
要註意的是,如果是 Windows 用戶建議在 PORT 前加上一個 cross-env,需要通過 cnpm install cross-env -D 下載到開發依賴 devDependencies 中。
cross-env 是一個運行跨平臺設置和使用環境變數的腳本 .cross-env 使得您可以使用單個命令,而不必擔心為平臺正確設置或使用環境變數。
- 通過修改 node_modules/react-scripts/scripts/start.js 腳本第 60 行的埠號,如下。
// line 60
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 8080;
埠修改完之後,點開右邊的 “Web 服務”,會在瀏覽器打開一個線上頁面,如下圖所示。
打開瀏覽器,你會遇到如下報錯。
原因是實驗樓環境啟動的線上網址是 https 協議,而項目熱更新用的是 ws 協議,所以我們需要將 ws 協議改成 wss 協議。打開項目 node_modules 目錄,找到 node_modules/react-dev-utils/webpackHotDevClient.js 腳本,將第 60 行修改為 wss 如下圖。
重新通過 npm run start 啟動項目,再次點開 “Web 服務”,如下圖所示,便是實驗環境配置成功。
項目運行的時候有強迫症的同學可以關閉 eslint ,具體方法首先運行指令:
npm run eject
根目錄會多出一個 config 文件夾,進入文件夾打開腳本 webpack.config.js,把 eslint 的配置關閉,如下圖所示
將紅框的內容註釋之後,重啟項目,命令行就會不顯示 eslint 語法報錯。
篇幅有限,今天就介紹到這裡,對 前端 和 React Hooks 感興趣的同學,歡迎來實驗樓邊敲代碼邊學習~