React學習筆記知識點整理

前端小智發表於2018-06-29

1. 幾個重要概念理解

  • 模組與元件

    • 模組:

      • 理解: 向外提供特定(區域性)功能的js程式, 一般就是一個js檔案
      • 為什麼: js程式碼更多更復雜
      • 作用: 複用js, 簡化js的編寫, 提高js執行效率
    • 元件:

      • 理解: 用來實現特定功能效果的程式碼集合(html/css/js)
      • 為什麼: 一個介面的功能更復雜
      • 作用: 複用編碼, 簡化專案編碼, 提高執行效率
  • 模組化與元件化

    • 模組化:

      • 當應用的js都以模組來編寫的, 這個應用就是一個模組化的應用
    • 元件化:

      • 當應用是以多元件的方式實現功能, 這上應用就是一個元件化的應用

2. React的基本認識

  • Facebook開源的一個js庫
  • 一個用來動態構建使用者介面的js庫
  • React的特點

    • Declarative(宣告式編碼)
    • Component-Based(元件化編碼)
    • Learn Once, Write Anywhere(支援客戶端與伺服器渲染)
    • 高效
    • 單向資料流
  • React高效的原因

    • 虛擬(virtual)DOM, 不總是直接操作DOM(批量更新, 減少更新的次數)
    • 高效的DOM Diff演算法, 最小化頁面重繪(減小頁面更新的區域)

3. 使用React

* 匯入相關js庫檔案(react.js, react-dom.js, babel.min.js)
* 編碼:
    ```
  <div id="container"></div>
  <script type="text/babel">
    var aa = 123
    ReactDOM.render(<h1>{aa}</h1>, containerDOM);
  </script>
    ```

4. JSX

  • 全稱: JavaScript XML
  • react定義的一種類似於XML的JS擴充套件語法: XML+JS

    • 作用: 用來建立react虛擬DOM(元素)物件

      • js中直接可以套標籤, 但標籤要套js需要放在{}中
      • 在解析顯示js陣列時, 會自動遍歷顯示
      • 把資料的陣列轉換為標籤的陣列:

        var liArr = dataArr.map(function(item, index){
        return <li key={index}>{item}</li>
        })
  • 注意:

    • 標籤必須有結束
    • 標籤的class屬性必須改為className屬性
    • 標籤的style屬性值必須為: {{color:`red`, width:12}}

5. Component : React是面向元件程式設計的(元件化編碼開發)

* 基本理解和使用
    * 自定義的標籤: 元件類(函式)/標籤
    * 建立元件類
      ```
      //方式1: 無狀態函式(最簡潔, 推薦使用)
      function MyComponent1() {
        return <h1>自定義元件標題11111</h1>;
      }
      //方式2: ES6類語法(複雜元件, 推薦使用)
      class MyComponent3 extends React.Component {
        render () {
          return <h1>自定義元件標題33333</h1>;
        }
      }
      //方式3: ES5老語法(不推薦使用了)
      var MyComponent2 = React.createClass({
        render () {
          return <h1>自定義元件標題22222</h1>;
        }
      });
      ```
    * 渲染元件標籤
        ```
        ReactDOM.render(<MyComp />,  cotainerEle);
        ```
* ReactDOM.render()渲染元件標籤的基本流程
  * React內部會建立元件例項物件/呼叫元件函式, 得到虛擬DOM物件
  * 將虛擬DOM並解析為真實DOM
  * 插入到指定的頁面元素內部
* props
    * 所有元件標籤的屬性的集合物件
    * 給標籤指定屬性, 儲存外部資料(可能是一個function)
    * 在元件內部讀取屬性: this.props.propertyName
    * 作用: 從目標元件外部向元件內部傳遞資料
    * 對props中的屬性值進行型別限制和必要性限制
     ```
     Person.propTypes = {
       name: React.PropTypes.string.isRequired,
       age: React.PropTypes.number.isRequired
     }
     ```
* 擴充套件屬性: 將物件的所有屬性通過props傳遞
    ```
    <Person {...person}/>
    ```
* 元件的組合
    * 元件標籤中包含子元件標籤
    * 拆分元件: 拆分介面, 抽取元件
    * 通過props傳遞資料
* refs
    * 元件內包含ref屬性的標籤元素的集合物件
    * 給操作目標標籤指定ref屬性, 打一個標識
    * 在元件內部獲得標籤物件: this.refs.refName(只是得到了標籤元素物件)
    * 作用: 操作元件內部的真實標籤dom元素物件
* 事件處理
    * 給標籤新增屬性: onXxx={this.eventHandler}
    * 在元件中新增事件處理方法
      ```
        eventHandler(event) {
                    
        }
      ```
    * 使自定義方法中的this為元件物件
      * 在constructor()中bind(this)
      * 使用箭頭函式定義方法(ES6模組化編碼時才能使用)
* state
    * 元件被稱為"狀態機", 頁面的顯示是根據元件的state屬性的資料來顯示
    * 初始化指定:
        ```
        constructor() {
          super();
          this.state = {
            stateName1 : stateValue1,
            stateName2 : stateValue2
          };
        }
        ```
    * 讀取顯示: 
        this.state.stateName1
    * 更新狀態-->更新介面 : 
        this.setState({stateName1 : newValue})
* 實現一個雙向繫結的元件
    * React是單向資料流
    * 需要通過onChange監聽手動實現
* 元件生命週期
    * 元件的三個生命週期狀態:
    * Mount:插入真實 DOM
    * Update:被重新渲染
    * Unmount:被移出真實 DOM
* 生命週期流程:
  * 第一次初始化顯示
    ```
    constructor()
    componentWillMount() : 將要插入回撥
    render() : 用於插入虛擬DOM回撥
    componentDidMount() : 已經插入回撥
    ```
  * 每次更新state
    ```
    componentWillReceiveProps(): 接收父元件新的屬性
    componentWillUpdate() : 將要更新回撥
    render() : 更新(重新渲染)
    componentDidUpdate() : 已經更新回撥
    ```
  * 刪除元件
    ```
    ReactDOM.unmountComponentAtNode(document.getElementById(`example`)) : 移除元件
    componentWillUnmount() : 元件將要被移除回撥
    ```
* 常用的方法
  ```
  render(): 必須重寫, 返回一個自定義的虛擬DOM
  constructor(): 初始化狀態, 繫結this(可以箭頭函式代替)
  componentDidMount() : 只執行一次, 已經在dom樹中, 適合啟動/設定一些監聽
  ```   

6. ajax

* React沒有ajax模組
* 整合其它的js庫(如axios/fetch/jQuery/), 傳送ajax請求
  * axios
    * 封裝XmlHttpRequest物件的ajax
    * promise
    * 可以用在瀏覽器端和伺服器
  * fetch
    * 不再使用XmlHttpRequest物件提交ajax請求
    * fetch就是用來提交ajax請求的函式, 只是新的瀏覽才內建了fetch
    * 為了相容低版本的瀏覽器, 可以引入fetch.js
* 在哪個方法去傳送ajax請求
  * 只顯示一次(請求一次): componentDidMount()
  * 顯示多次(請求多次): componentWillReceiveProps()
  

7. 虛擬DOM

  • 虛擬DOM是什麼?

    • 一個虛擬DOM(元素)是一個一般的js物件, 準確的說是一個物件樹(倒立的)
    • 虛擬DOM儲存了真實DOM的層次關係和一些基本屬性,與真實DOM一一對應
    • 如果只是更新虛擬DOM, 頁面是不會重繪的
  • Virtual DOM 演算法的基本步驟

    • 用 JavaScript 物件結構表示 DOM 樹的結構;然後用這個樹構建一個真正的 DOM 樹,插到文件當中
    • 當狀態變更的時候,重新構造一棵新的物件樹。然後用新的樹和舊的樹進行比較,記錄兩棵樹差異
    • 把2所記錄的差異應用到步驟1所構建的真正的DOM樹上,檢視就更新了
  • 進一步理解

    • Virtual DOM 本質上就是在 JS 和 DOM 之間做了一個快取。
    • 可以類比 CPU 和硬碟,既然硬碟這麼慢,我們就在它們之間加個快取:既然 DOM 這麼慢,我們就在它們 JS 和 DOM 之間加個快取。CPU(JS)只操作記憶體(Virtual DOM),最後的時候再把變更寫入硬碟(DOM)。

8. 使用React腳手架建立一個React應用

  • react腳手架

    • xxx腳手架: 用來幫助程式設計師快速建立一個基於xxx庫的空專案的庫

      • 包含了所有需要的配置
      • 指定好了所有的依賴
      • 可以直接安裝/編譯/執行一個簡單效果
    • react提供了一個專門用於建立react專案的腳手架庫: create-react-app
    • 專案的整體技術架構為: react + webpack + es6 + eslint
  • 建立專案並啟動

    • npm install -g create-react-app
    • create-react-app hello-react
    • cd hello-react
    • npm start

9. app1: 實現一個評論管理功能

  • 拆分元件:

    • 應用元件: App
    • 新增評論元件: CommentAdd
    • 評論項元件: CommentItem
    • 評論列表元件: CommentList
  • 確定元件的state和props:

    • App:

      • state: comments/array
    • CommentAdd

      • state: username/string, content/string
      • props: add/func
    • commentList

      • props: comments/array, delete/func
    • CommentItem

      • props: comment/object, delete/func, index/number
  • 編寫靜態元件

    • 拆分頁面
    • 拆分css
  • 實現動態元件

    • 動態展示初始化資料

      • 初始化狀態資料
      • 傳遞屬性資料
    • 響應使用者操作, 更新元件介面

      • 繫結事件監聽, 並處理
      • 更新state

10. app2: 實現github使用者搜尋功能

  • 拆分元件

    • App
    • Search
    • List
  • 確定元件的state和props

    • App

      • state: searchName/string
    • Search

      • props: setSearchName/func
    • List

      • props: searchName/string
      • state: firstView/bool, loading/bool, users/array, errMsg/string
  • 編寫靜態元件
  • 編寫動態元件

    • componentWillReceiveProps(nextProps): 監視接收到新的props, 傳送ajax
    • 使用axios庫傳送ajax請求

11. 元件間通訊總結

  • 方式一: 通過props傳遞

    • 共同的資料放在父元件上, 特有的資料放在自己元件內部(state)
    • 通過props可以傳遞一般資料和函式資料, 只能一層一層傳遞
    • 一般資料–>父元件傳遞資料給子元件–>子元件讀取資料
    • 函式資料–>子元件傳遞資料給父元件–>子元件呼叫函式
  • 方式二: 使用訊息訂閱(subscribe)-釋出(publish)機制: 自定義事件機制

    • 工具庫: PubSubJS
    • 下載: npm install pubsub-js –save
    • 使用:

      import PubSub from `pubsub-js` //引入
      PubSub.subscribe(`delete`, function(data){ }); //訂閱
      PubSub.publish(`delete`, data) //釋出訊息

12. ES6新語法

  • const/let
  • 解構賦值: let {a, b} = this.props
  • 物件的簡潔表達
  • 箭頭函式:

    • 元件的自定義方法: xxx = () => {}
    • map/filter的回撥方法: (item, index) => {}
    • 優點:

      • 簡潔
      • 沒有自己的this,使用引用this查詢的是外部this
  • 擴充套件運算子(…)

    • 解構物件: const MyProps = {}, <Xxx {…MyProps}>
  • class/extends/constructor/super
  • ES6模組化(Babel)

13. 專案打包執行

  • 專案編譯打包並執行

    • npm build
    • npm install -g pushstate-server
    • pushstate-server build
    • 訪問: http://localhost:9000

14. 下載相關模組包

  • 建立package.json
  • react相關庫

    npm install react react-dom --save
  • babel相關庫

    npm install babel-core babel-preset-es2015 babel-preset-react --save-dev
  • webpack相關庫

    npm install webpack babel-loader --save-dev
    npm install webpack-dev-server

15. webpack配置檔案: webpack.config.js

const path = require(`path`); //path內建的模組,用來設定路徑。
        
module.exports = {
  entry: `./src/main.js`,   // 入口檔案
  output: {                     // 輸出配置
    filename: `bundle.js`,      // 輸出檔名
    path: path.resolve(__dirname, `dist`)   //輸出檔案路徑配置
  },
   module: {
      rules: [
        {
          test: /.css$/,
          use: [
            `style-loader`,
            `css-loader`
          ]
        },
        //babel處理js
        {
          test: /.js$/,
          exclude: /node_modules/, //排除此資料夾
          use: [
          `babel-loader`
          ]
        }
        
      ]
    }    
    
};

16. babel配置檔案: .babelrc

{
  "presets": ["es2015", "react"]
}

17. 編碼

  • src/js/App.js: 應用元件

    import React from `react`
    export default function App() {  //暴露元件都得使用預設暴露
      return <h1>Hello React Client Component</h1>
    }
  • src/js/main.js: 入口js

    import React from `react`
    import ReactDOM from `react-dom`
    import App from `./App`
    
    //渲染元件標籤到頁面元素
    ReactDOM.render(<App />, document.getElementById(`demo`))

18. 下載css載入器

npm install style-loader css-loader --save-dev

建立css檔案  src/css/test.css

body{
    background : red
}

19. 配置webpack-dev-server

devServer:{
    contentBase: `./`,//內建伺服器動態載入頁面所在的目錄
}
``
##20. 執行命令
構建任務:webpack
熱載入任務: webpack-dev-server
```

21 package.json: 新增編譯/執行指令碼

"scripts": {
  "start": "webpack-dev-server",
  "build": "webpack"
}

react-router使用教程

0. 關於url中#的作用:

  • 學習: http://www.ruanyifeng.com/blo…
  • `#`代表網頁中的一個位置。其右面的字元,就是該位置的識別符號
  • 改變#不觸發網頁過載
  • 改變#會改變瀏覽器的訪問歷史
  • window.location.hash讀取#值
  • window.onhashchange = func 監聽hash改變

1. reat-router

2. react-router庫中的相關元件

  • 包含的相關元件:

    • Router: 路由器元件, 用來包含各個路由元件
    • Route: 路由元件, 註冊路由
    • IndexRoute: 預設路由元件
    • hashHistory: 路由的切換由URL的hash變化決定,即URL的#部分發生變化
    • Link: 路由連結元件
  • Router: 路由器元件

    • 屬性: history={hashHistory} 用來監聽瀏覽器位址列的變化, 並將URL解析成一個地址物件,供React Router匹配
    • 子元件: Route
  • Route: 路由元件

    • 屬性1: path=”/xxx”
    • 屬性2: component={Xxx}
    • 根路由元件: path=”/”的元件, 一般為App
    • 子路由元件: 子<Route>配置的元件
  • IndexRoute: 預設路由

    • 當父路由被請求時, 預設就會請求此路由元件
  • hashHistory

    • 用於Router元件的history屬性
    • 作用: 為地址url生成?_k=hash, 用於內部儲存對應的state
  • Link: 路由連結

    • 屬性1: to=”/xxx”
    • 屬性2: activeClassName=”active”

3. 配置(從官方教程樣例中拷貝)

  * webpack配置: webpack.config.js
    ```
    module.exports = {
      //入口js
      entry: `./index.js`,
      //編譯打包輸出
      output: {
        filename: `bundle.js`,
        publicPath: ``
      },
    
      module: {
        //使用的loaders
        loaders: [
          {test: /.js$/, exclude: /node_modules/, loader: `babel-loader?presets[]=es2015&presets[]=react`}
        ]
      }
    }
    ```

  * 包配置: package.json
    ```
    {
      "name": "tutorial",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "start": "webpack-dev-server --inline --content-base ."
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "react": "^0.14.7",
        "react-dom": "^0.14.7",
        "react-router": "^2.0.0"
      },
      "devDependencies": {
        "babel-core": "^6.5.1",
        "babel-loader": "^6.2.2",
        "babel-preset-es2015": "^6.5.0",
        "babel-preset-react": "^6.5.0",
        "http-server": "^0.8.5",
        "webpack": "^1.12.13",
        "webpack-dev-server": "^1.14.1"
      }
    }
    ```

4. 編碼

  * 定義各個路由元件
    * About.js
      ```
      import React from `react`
      function About() {
        return <div>About元件內容</div>
      }
      export default About
      ```
    * Home.js
      ```
      import React from `react`
      function Home() {
        return <div>Home元件內容2</div>
      }
      export default Home
      ```
    * Repos.js
      ```
      import React, {Component} from `react`
      export default class Repos extends Component {
        render() {
          return (
            <div>Repos元件</div>
          )
        }
      }
      ```
  * 定義應用元件: App.js
    ```
    import React, {Component} from `react`
    import {Link} from `react-router`
    
    export default class App extends Component {
      render() {
        return (
          <div>
            <h2>Hello, React Router!</h2>
            <ul>
              <li><Link to="/about" activeClassName="active">About2</Link></li>
              <li><Link to="/repos" activeClassName="active">Repos2</Link></li>
            </ul>
            {this.props.children}
          </div>
        )
      }
    }
    ```
  * 定義入口JS: index.js-->渲染元件
    ```
    import React from `react`
    import {render} from `react-dom`
    import {Router, Route, IndexRoute, hashHistory} from `react-router`
    import App from `./modules/App`
    import About from `./modules/About`
    import Repos from `./modules/Repos`
    import Home from `./modules/Home`
    
    render((
      <Router history={hashHistory}>
        <Route path="/" component={App}>
          <IndexRoute component={Home}/>
          <Route path="/about" component={About}></Route>
          <Route path="/repos" component={Repos}></Route>
        </Route>
      </Router>
    ), document.getElementById(`app`))
    ```
  * 主頁面: index.html
    ```
    <style>
      .active {
        color: red;
      }
    </style>
    <div id=app></div>
    <script src="bundle.js"></script>
    ```

5. 傳遞請求引數

  * repo.js: repos元件下的分路由元件
    ```
    import React from `react`
    export default function ({params}) {
      let {username, repoName} = params
      return (
        <div>使用者名稱:{username}, 倉庫名:{repoName}</div>
      )
    }
    ```
  * repos.js
    ```
    import React from `react`
    import NavLink from `./NavLink`
    
    export default class Repos extends React.Component {
    
      constructor(props) {
        super(props);
        this.state = {
          repos: [
            {username: `faceback`, repoName: `react`},
            {username: `faceback`, repoName: `react-router`},
            {username: `Angular`, repoName: `angular`},
            {username: `Angular`, repoName: `angular-cli`}
          ]
        };
        this.handleSubmit = this.handleSubmit.bind(this)
      }
    
      handleSubmit () {
    
        const repos = this.state.repos
        repos.push({
          username: this.refs.username.value,
          repoName: this.refs.repoName.value
        })
        this.setState({repos})
        this.refs.username.value = ``
        this.refs.repoName.value = ``
      }
    
      render() {
        return (
          <div>
            <h2>Repos</h2>
            <ul>
              {
                this.state.repos.map((repo, index) => {
                  const to = `/repos/${repo.username}/${repo.repoName}`
                  return (
                    <li key={index}>
                      <Link to={to} activeClassName=`active`>{repo.repoName}</Link>
                    </li>
                  )
                })
              }
              <li>
                <form onSubmit={this.handleSubmit}>
                  <input type="text" placeholder="使用者名稱" ref=`username`/> / {` `}
                  <input type="text" placeholder="倉庫名" ref=`repoName`/>{` `}
                  <button type="submit">新增</button>
                </form>
              </li>
            </ul>
            {this.props.children}
          </div>
        );
      }
    }
    ```
  * index.js: 配置路由
    ```
    <Route path="/repos" component={Repos}>
      <Route path="/repos/:username/:repoName" component={Repo}/>
    </Route>
    ```
6. 優化Link元件
  * NavLink.js
    ```
    import React from `react`
    import {Link} from `react-router`
    export default function NavLink(props) {
      return <Link {...props} activeClassName="active"/>
    }
    ```
  * Repos.js
    ```
    <NavLink to={to}>{repo.repoName}</NavLink>
    ```

使用開源的ant-design庫開發專案指南

1. 最流行的開源React UI元件庫

2. ant-design使用入門

使用create-react-app搭建react開發環境

npm install create-react-app -g
create-react-app antd-demo
cd antd-demo
npm start

搭建antd的基本開發環境

  • 下載

    npm install antd --save
  • src/App.js

      
      import React, { Component } from `react`;
      import { Button } from `antd`;
      import `./App.css`;
      
      class App extends Component {
        render() {
          return (
            <div className="App">
              <Button type="primary">Button</Button>
            </div>
          );
        }
      }
    
      export default App;
    
  • src/App.css

      @import `~antd/dist/antd.css`;
      
      .App {
        text-align: center;
      }
    

實現按需載入(css/js)

  • 使用 eject 命令將所有內建的配置暴露出來

    npm run eject
  • 下載babel-plugin-import(用於按需載入元件程式碼和樣式的 babel 外掛)

    npm install babel-plugin-import --save-dev
  • 修改配置: config/webpack.config.dev.js

      // Process JS with Babel.
      {
        test: /.(js|jsx)$/,
        include: paths.appSrc,
        loader: `babel`,
        query: {
      +   plugins: [
      +     [`import`, [{ libraryName: "antd", style: `css` }]],
      +   ],
          // This is a feature of `babel-loader` for webpack (not Babel itself).
          // It enables caching results in ./node_modules/.cache/babel-loader/
          // directory for faster rebuilds.
          cacheDirectory: true
        }
       },
    
  • 去除引入全量樣式的語句: src/App.css

    @import `~antd/dist/antd.css`

願你成為終身學習者

相關文章