番外篇:入門React

歡迎關注公衆號:n平方
如有問題或建議,請後臺留言,我會盡力解決你的問題。

背景

當你覺得原生js代碼亂七八糟的時候,那就是要體驗一下React。(祕籍在最後

目標

  • 踢開React的大門。

簡介

React 的核心思想是:封裝組件。
各個組件維護自己的狀態和 UI,當狀態變更,自動重新渲染整個組件。
React 大體包含下面這些概念:

  • 組件:
  • JSX
  • Virtual DOM
  • Data Flow

經驗:
前端框架的基本組成:
組件及其生命週期、樣式、路由、網絡請求、數據存儲和傳遞。

HelloWorld

import React, { Component } from 'react';
import { render } from 'react-dom';

class HelloMessage extends Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}

// 加載組件到 DOM 元素 mountNode
render(<HelloMessage name="John" />, mountNode);

解析:

  • 組件:HelloMessage 就是一個 React 構建的組件,最後一句 render 會把這個組件顯示到頁面上的某個元素 mountNode 裏面,顯示的內容就是 <div>Hello John</div>。
  • JSX: 將 HTML 直接嵌入了 JS 代碼裏面(上面的js裏就寫了個div),這個就是 React 提出的一種叫 JSX 的語法.
  • Virtual DOM:

虛擬DOM

當組件狀態 state 有更改的時候,React 會自動調用組件的 render 方法重新渲染整個組件的 UI。
當然如果真的這樣大面積的操作 DOM,性能會是一個很大的問題,所以 React 實現了一個Virtual DOM,組件 DOM 結構就是映射到這個 Virtual DOM 上,React 在這個 Virtual DOM 上實現了一個 diff 算法,當要重新渲染組件的時候,會通過 diff 尋找到要變更的 DOM 節點,再把這個修改更新到瀏覽器實際的 DOM 節點上,所以實際上不是真的渲染整個 DOM 樹。這個 Virtual DOM 是一個純粹的 JS 數據結構,所以性能會比原生 DOM 快很多。

  • Data Flow:

“單向數據綁定”是 React 推崇的一種應用架構的方式。

與webpack結合

package.json看依賴

{
  "name": "learning-01",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --config ./webpack.config.js --mode development --open",
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.3.3",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "clean-webpack-plugin": "^1.0.1",
    "html-webpack-plugin": "^3.2.0",
    "react-hot-loader": "^4.7.1",
    "webpack": "^4.29.5",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.2.0"
  },
  "dependencies": {
    "react": "^16.8.3",
    "react-dom": "^16.8.3"
  }
}

webpack.config.js看配置

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
      path: __dirname + '/dist',
      publicPath: '/',
      filename: 'bundle.js'
    }, module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: ['babel-loader']
        }
      ]
    },
    resolve: {
      extensions: ['*', '.js', '.jsx']
    },
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new webpack.HotModuleReplacementPlugin(),
      new HtmlWebpackPlugin({template:'index.html'})
    ],
    devServer: {
      contentBase: './dist',
      hot: true
    }
  };

基本搭建環境參考(可直接clone)
https://github.com/xbmchina/r...

JSX

    1. HTML 裏的 class 在 JSX 裏要寫成 className,因爲 class 在 JS 裏是保留關鍵字。
  • 2.同理某些屬性比如 for 要寫成 htmlFor。
  • 3.屬性值使用表達式,只要用 {} 替換 ""
// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = React.createElement(
  Person,
  {name: window.isLoggedIn ? window.name : ''}
);
    1. 使用註釋要用 {} 包起來。
 {/* child comment, put {} around */}
  • 5.React 會將所有要顯示到 DOM 的字符串轉義,防止 XSS。
<div dangerouslySetInnerHTML={{__html: 'cc &copy; 2015'}} />
  • 6.屬性擴散
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;

組件

生命週期(主要兩個)

componentWillMount
只會在裝載之前調用一次,在 render 之前調用,你可以在這個方法裏面調用 setState 改變狀態,並且不會導致額外調用一次 render

componentDidMount
只會在裝載完成之後調用一次,在 render 之後調用,從這裏開始可以通過 ReactDOM.findDOMNode(this) 獲取到組件的 DOM 節點。

var React = require('react');
var ReactDOM = require('react-dom');
import ComponentHeader from './components/ComponentHeader';
import ComponentFooter from './components/ComponentFooter';
import BodyIndex from './components/BodyIndex';
import BasicExample from './root'

export default class Index extends React.Component {

 constructor(props) {
    super(props);
    this.state = { count: props.initialCount };
  }

  //組件即將加載
  componentWillMount() {
    //定義你的邏輯即可
    console.log("Index - componentWillMount");
  }
  //組件加載完畢
  componentDidMount() {
    console.log("Index - componentDidMount");
  }

  render() {

        /*
        var component;
        if (用戶已登錄) {
            component = <ComponentLoginedHeader/>
        }
        else{
            component = <ComponentHeader/>
        }
        */

    return (
      <div>
        <ComponentHeader />
        <BodyIndex />
        <ComponentFooter />
      </div>
    );
  }
}

ReactDOM.render(<BasicExample/>,document.getElementById('app'))
事件處理

給事件處理函數傳遞額外參數的方式:bind(this, arg1, arg2, ...)

render: function() {
    return <p onClick={this.handleClick.bind(this, 'extra param')}>;
},
handleClick: function(param, event) {
    // handle click
}
DOM操作

Refs
另外一種方式就是通過在要引用的 DOM 元素上面設置一個 ref 屬性指定一個名稱,然後通過 this.refs.name 來訪問對應的 DOM 元素。

比如有一種情況是必須直接操作 DOM 來實現的,你希望一個 <input/> 元素在你清空它的值時 focus,你沒法僅僅靠 state 來實現這個功能。

class App extends Component {
  constructor() {
    return { userInput: '' };
  }

  handleChange(e) {
    this.setState({ userInput: e.target.value });
  }

  clearAndFocusInput() {
    this.setState({ userInput: '' }, () => {
      this.refs.theInput.focus();
    });
  }

  render() {
    return (
      <div>
        <div onClick={this.clearAndFocusInput.bind(this)}>
          Click to Focus and Reset
        </div>
        <input
          ref="theInput"
          value={this.state.userInput}
          onChange={this.handleChange.bind(this)}
        />
      </div>
    );
  }
}
組合組件

使用組件的目的就是通過構建模塊化的組件,相互組合組件最後組裝成一個複雜的應用。在 React 組件中要包含其他組件作爲子組件,只需要把組件當作一個 DOM 元素引入就可以了。

var React = require('react');
var ReactDOM = require('react-dom');
import ComponentHeader from './components/ComponentHeader';
import ComponentFooter from './components/ComponentFooter';
import BodyIndex from './components/BodyIndex';

class Index extends React.Component {
  //組件即將加載
  componentWillMount() {
    //定義你的邏輯即可
    console.log("Index - componentWillMount");
  }
  //組件加載完畢
  componentDidMount() {
    console.log("Index - componentDidMount");
  }

  render() {

        /*
        var component;
        if (用戶已登錄) {
            component = <ComponentLoginedHeader/>
        }
        else{
            component = <ComponentHeader/>
        }
        */

    return (
      <div>
        <ComponentHeader />
        <BodyIndex />
        <ComponentFooter />
      </div>
    );
  }
}


ReactDOM.render(
  <Index />, document.getElementById('app'));
組件間通信

父子組件間通信

  • 1.父組件傳遞到子組件:

就是通過 props 屬性傳遞,在父組件給子組件設置 props,然後子組件就可以通過 props 訪問到父組件的數據/方法,這樣就搭建起了父子組件間通信的橋樑。

  • 2.父組件訪問子組件?
    用 refs

非父子組件間的通信
使用全局事件 Pub/Sub 模式,在 componentDidMount 裏面訂閱事件,在 componentWillUnmount 裏面取消訂閱,當收到事件觸發的時候調用 setState 更新 UI。
一般來說,對於比較複雜的應用,推薦使用類似 Flux 這種單項數據流架構

使用css樣式

1.內聯樣式

在render函數裏定義

const styleComponentHeader = { header: { backgroundColor: '#333333', color: '#FFFFFF', 'padding-top': '12px', 'paddingBottom: '16px' } };

注意樣式的駝峯寫法 style = {styleComponentHeader.header}

文件中引用css的樣式 注意class需要更改成className確定是動畫、僞類(hover)等不能使用

2.內聯樣式中的表達式

paddingBottom:(this.state.minHeader)?"3px":"15px" 

注意好好理解這裏的state引起樣式的即時變化

3.CSS模塊化

原因:避免全局污染、命名混亂、依賴管理不徹底、無法共享變量、代碼壓縮不徹底

npm install --save-dev style-loader css-loader npm install --save-dev babel-plugin-react-html-attrs  //爲了使用原生的html屬性名

全局樣式和局部樣式

:local(.normal){color:green;}  //局部樣式
:global(.btn){color:red;}  //全局樣式

CSS模塊化的優點 所有樣式都是local的,解決了命名衝突和全局污染問題 class名生成規則配置靈活,可以此來壓縮class名 只需引用組件的JS就能搞定組件所有的js和css 依然是css,幾乎零學習成本

jsx樣式與css的互轉 工具:https://staxmanade.com/CssToReact/

react-router

官網:https://reacttraining.com/rea...
GitHub:https://github.com/ReactTrain...

注意點:
1.引用的包是有區別的。

2.控制頁面的層級關係 單頁面構建Router控制
底層機制 React: state/props -> Components ->UI Router: location ->Router -> UI

3.router傳參
定義: path="list/:id" 使用: this.props.match.params.id

4.地址無法刷新(巨坑)
表現:'/' 根節點 Home 顯示無誤,不過其他任何路由 例如 /detail,均顯示 Cannot GET /detail。
解決方法:

  • 4.1 用的 BrowserRouter 改爲 HashRouter 即可
  • 4.2 修改 webpack.config.js 配置文件
module.exports = {
    // 省略其他的配置
    devServer: {
        historyApiFallback: true
    }
}

詳情可以參考:https://blog.csdn.net/zwkkkk1...

網絡請求Fetch

官網:https://github.com/github/fetch

fetch('/users.json')
  .then(function(response) {
    return response.json()
  }).then(function(json) {
    console.log('parsed json', json)
  }).catch(function(ex) {
    console.log('parsing failed', ex)
  })

Redux

下期再講

學習資料


練習代碼
學習Demo樣例:https://github.com/xbmchina/r...
項目Demo樣例:https://github.com/xbmchina/r...


React相關資料

**React官網:https://reactjs.org/docs/hell...
React中文網:https://react.docschina.org/
React學習文檔:http://caibaojian.com/react/
webpack搭建React:https://segmentfault.com/a/11...
React-router官網:https://reacttraining.com/rea...
阿里UI庫Ant Design:https://ant.design/index-cn
阿里圖標庫:https://www.iconfont.cn/
谷歌的ReactUI庫:https://material-ui.com/
css轉React:https://staxmanade.com/CssToR...
Fetch請求:https://github.com/github/fetch**

最後

本人水平有限,歡迎各位建議以及指正。順便關注一下公衆號唄,會經常更新文章的哦。
n平方

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