1. 程式人生 > >React 入門-寫個 TodoList 例項

React 入門-寫個 TodoList 例項

React 是一個用於構建使用者介面的 JavaScript 庫,主要特點有: - **宣告式渲染**:設計好資料和檢視的關係,資料變化 React 自動渲染,不必親自操作DOM - **元件化**:頁面切分成多個小部件,通過組裝拼成整體頁面,利於程式碼複用 本文通過寫個簡單的 `TodoList` 例項,不求甚解,熟悉下 React 的開發過程。 ## 1. 安裝 Node.js `Node.js` 是一個執行環境,類似 `jdk`,用以支援在服務端執行 JavaScript。 您可以在這裡下載安裝包: ``` http://nodejs.cn/download/ ``` 以綠色版安裝為例,將 node-v10.16.1-win-x64.zip 解壓到 E:\software\ 並命名為 node-v10.16.1 在 Path 環境變數中增加兩項: ``` E:\software\node-v10.16.1\ E:\software\node-v10.16.1\node_global ``` 在 cmd 中使用 `node -v` 顯示版本號,表示安裝成功。 Node.js 中有個 `npm` 軟體包管理器,可以很方便的管理下載和使用第三方開源包,類似 `maven`,使用 `npm -v` 顯示版本號,表示 npm 也沒有問題。 綠色版安裝完成後一些必要的配置: ``` npm config set prefix "E:\software\node-v10.16.1\node_global" ``` 設定全域性安裝的模組儲存路徑 ``` npm config set cache "E:\software\node-v10.16.1\node_cache" ``` 設定下載快取的儲存路徑 ``` npm config set registry https://registry.npm.taobao.org` ``` 設定 npm 下載源為淘寶映象 簡單使用: - npm install xxx: 安裝到專案目錄 - npm install -g xxx 安裝到全域性目錄 - npm install -save xxx: 安裝到專案目錄,並在 package.json 中的 dependencies 節點記錄依賴 - npm install --save-dev xxx: 安裝到專案目錄,並在 package.json 中的 devDependencies 節點記錄依賴 ## 2. 腳手架建立專案 React 官方出的腳手架工具 `create-react-app` ,可以一鍵建立一個 Web 應用程式: ``` cmd> npm install -g create-react-app cmd> cd E: cmd> create-react-app react-todoapp cmd> cd react-todoapp ``` 腳手架會在當前目錄建立一個 `react-todoapp` 目錄: ``` react-todoapp ├── README.md ├── node_modules ├── package.json ├── package-lock.json ├── .gitignore ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json └── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg └── serviceWorker.js └── setupTests.js ``` 目錄中主要的檔案和資料夾說明: - README.md: 專案簡介,支援 Markdown 語法 - node_modules: 專案的依賴包,類似 `Maven Repository` - package.json: 配置專案依賴的第三方包,類似 `pom.xml` - package-lock.json: 鎖定第三方包的版本號,保證 `npm install` 版本一致 - public: 公開資源,網站路徑,類似 nginx 的 html 目錄 - src: 核心元件程式碼檔案 為了便於開發,刪除目錄中不必要的檔案,最終結構如下: ``` react-todoapp ├── README.md ├── node_modules ├── package.json ├── package-lock.json ├── .gitignore ├── public │ └── index.html └── src ├── App.css ├── index.js ├── TodoApp.js ├── TodoItem.js └── TodoList.js ``` 接下來,設計與實現一個 TodoList 的例子,我們把所有程式碼過一下,敲一遍,先不管為什麼,跑起來,最後再整理下知識點。 ## 3. 例項 TodoApp ![react-todoapp][1] 主要實現功能有: - 新增一個待辦事項 - 刪除一個待辦事項 - 勾選複選框標記事項已完成 如圖所示,總共將頁面拆分成了三個元件:`TodoApp`, `TodoList` 和 `TodoItem`。 ### 3.1 index.js 入口檔案 應該可以類比 java 的 `main` 方法,在 src 目錄新建 `index.js` 內容如下: ``` // 引入 React, ReactDOM import React from 'react'; import ReactDOM from 'react-dom'; // 引入 TodoApp 元件 import TodoApp from './TodoApp'; // 將渲染結果掛在到 root 節點,該節點在 index.html 中 ReactDOM.render( , document.getElementById('root') ); ``` 先匯入需要使用的元件(類),然後呼叫它們提供的方法和服務,有沒有些許眼熟? ### 3.2 TodoApp.js TodoApp 設計了頁面整體佈局,它包含全部資料以及操作這些資料的方法,是其他兩個元件的`父元件`: ``` import React, { Component } from 'react'; import TodoList from './TodoList'; import './app.css'; class TodoApp extends Component { constructor(props) { // 構造方法,props 應該是父類的一個成員變數 super(props); this.state = { // 元件狀態資料 text: '', items:[{id: 1, status: 1, text: "去月球"},{id: 2, status: 0, text: "去火星"}] }; // 設定 this 指向,預設 undefined this.handleChange = this.handleChange.bind(this); this.handleAdd = this.handleAdd.bind(this); this.handleComplete = this.handleComplete.bind(this); this.handleDelete = this.handleDelete.bind(this); } // 渲染解析 jsx render() { return (

Todos App

); } handleChange(e) { this.setState({ text: e.target.value }) } handleAdd(e) { e.preventDefault(); if (this.state.text.length === 0) { return; } const newItem = { id: Date.now(), text: this.state.text, status: 0 }; this.setState({ items: [...this.state.items, newItem], text: '' }); } handleComplete(taskid) { // 臨時變數,不直接修改原資料 let items = this.state.items; let findItem = items.find(item => item.id === taskid); findItem.status = findItem.status === 0 ? 1 : 0; this.setState({ items: items }); } handleDelete(taskid) { let items = this.state.items; items = items.filter(item => item.id !== taskid); this.setState({ items: items }); } } export default TodoApp; ``` ### 3.3 TodoList.js TodoList 接收父元件 TodoApp 中的陣列,並將其渲染成一個 `ul` 列表: ``` import React, { Component } from 'react'; import TodoItem from './TodoItem'; class TodoList extends Component { render() { return (
    { this.props.items.map((item)=>{ return (
); } } export default TodoList; ``` ### 3.4 TodoItem.js 在 TodoList 遍歷陣列時,把每一項元素交給 TodoItem 元件,它會渲染成一個 `li` 元素: ``` import React, { Component } from 'react'; class TodoItem extends Component { constructor(props) { super(props); this.taskComplete = this.taskComplete.bind(this); this.taskDelete = this.taskDelete.bind(this); } render() { let isCompleted = this.props.status === 1; return (
  • {this.props.text}
  • ); } taskComplete() { this.props.handleComplete(this.props.taskid); } taskDelete() { this.props.handleDelete(this.props.taskid); } shouldComponentUpdate(nextProps, nextState) { if (nextProps.text !== this.props.text || nextProps.status !== this.props.status) { return true; } else { return false; } } } export default TodoItem; ``` 這幾個檔案寫完之後,進入 `react-todoapp` 目錄,cmd 執行 `npm start`,訪問 http://localhost:3000 就能檢視最終的結果了。 ## 4. 思考 ### 4.1 JSX TodoApp 元件在 render 方法渲染時,使用了一個既不是字串也不是 HTML 的語法,它被稱為 `JSX`,是 JavaScript 的語法擴充套件,使用它可以很方便的建立 DOM。 JSX 看起來像是模板語言,但它具有 JavaScript 的全部功能: - 遇到 `<>` 就當作 HTML 解析 - 遇到 `{}` 就當作 JavaScript 解析 ### 4.2 元件通訊 這裡主要有兩種通訊情況: - 父元件向子元件通訊 - 子元件向父元件通訊 每個元件都有一個 `props` 物件,用以訪問元件的屬性,所以,父元件可以向子元件傳遞一組 props 供其使用,就像方法傳參一樣。 子元件向父元件通訊,可以利用回撥函式:父元件將一個函式作為 props 傳遞給子元件,子元件呼叫該回調函式,便可向父元件通訊。 回撥函式也是增刪一組資料,那麼為什麼不直接把資料傳給子元件,直接操作?這是因為 `props` 是**只讀**的,不能修改,改了就會報錯: ``` TypeError: Cannot assign to read only property 'items' of o