React.js初探

 

 

React.js

菜鳥官方解釋:css

React 是一個用於構建用戶界面的 JAVASCRIPT 庫。html

React主要用於構建UI,不少人認爲 React 是 MVC 中的 V(視圖)。vue

React 起源於 Facebook 的內部項目,用來架設 Instagram 的網站,並於 2013 年 5 月開源。react

React 擁有較高的性能,代碼邏輯很是簡單,愈來愈多的人已開始關注和使用它android

React特色:ios

  • 1.聲明式設計 −React採用聲明範式,能夠輕鬆描述應用。json

  • 2.高效 −React經過對DOM的模擬,最大限度地減小與DOM的交互。小程序

  • 3.靈活 −React能夠與已知的庫或框架很好地配合。瀏覽器

  • 4.JSX − JSX 是 JavaScript 語法的擴展。React 開發不必定使用 JSX ,但咱們建議使用它。緩存

  • 5.組件 − 經過 React 構建組件,使得代碼更加容易獲得複用,可以很好的應用在大項目的開發中。

  • 6.單向響應的數據流 − React 實現了單向響應的數據流,從而減小了重複代碼,這也是它爲何比傳統數據綁定更簡單。

Virtual Dom

  • state建立數據

  • jsx 模板

  • 數據 + 模板 生成Virtual DOM(虛擬節點就是js建立的一個對象,該對象是對節點的一個描述,包含了該節點的全部信息,attrs,content等)

  • 用虛擬DOM生成真實的DOM,顯示

  • state改變

  • 數據 + 模板生成新的 Virtual DOM

  • 對比新舊Virtual DOM的區別,找到區別。

  • 直接操做DOM改變有區別的地方。

    注:由於瀏覽器在解析網頁時,js和html是分開的。就至關於兩個模塊,js要跨越界限操道別人的東西,因此說代價是比較大的。虛擬dom的建立使得性能很大程度提高,且他的應用 使得跨端應用得意實現 React Native.在android和ios端是不存在dom一說的,因此在建立虛擬dom後,會將虛擬dom生成對應的原生的東西。

Progressive Web Application

字義即:漸進式Web應用

PWA有能夠實現什麼呢?

  • 能夠將app的快捷方式放置到桌面上,全屏運行,與原生app無異

  • 可以在各類網絡環境下使用,包括網絡差和斷網條件下,不會顯示undefind

  • 推送消息的能力

  • 其本質是一個網頁,沒有原生app的各類啓動條件,快速響應用戶指令

    在React.js中。public文件夾下有一個mainfest.json的文件夾,該文件就是用來實現桌面快捷方式的配置,而無網絡狀況,或網絡狀況較差時,則使用service worker來實現離線緩存。

Service Worker

Web離線引用解決方案。指在在進過網頁後,當網絡斷開鏈接後,依然能夠再次訪問頁面。

ServiceWorker的主要能力集中在網絡代理和離線緩存上。具體的實現上,能夠理解爲ServiceWorker是一個能在網頁關閉時仍然運行的WebWorker

在經過react腳手架建立項目後,src根目錄下是有一個serviceWorker.js文件的,該文件就是用來寫app時,提供離線緩存技術。供線下仍可正常訪問。

Immutable

保證state中的數據不被修改。修改數據時,不能經過this.state.xx來就該數據須要用特定的方法this.setState({});能夠發現和小程序也是同樣的了

JSX

百度解釋:

JSX是一種JavaScript的語法擴展,運用於React架構中,其格式比較像是模版語言,但事實上徹底是在JavaScript內部實現的。元素是構成React應用的最小單位,JSX就是用來聲明React當中的元素,React使用JSX來描述用戶界面。

詳細使用:JSX

 render() {
     return (
       <div>
         <ul>
           <li></li>
         </ul>
       </div>
     )
 }

 

上面代碼等同於下面

 render() {
     return React.createElement('div',{},React.createElement('ul',{},React.craeteElement('li',{})));
 }

 

他的順序:jsx => createElement => Virtual Dom => 真實Dom

vue和react的虛擬節點的渲染實際上是一致的

className

在設置樣式時,咱們原來都是經過添加class或者id來設置對應的類名,在react中由於建立模板時使用的就是class類,因此在渲染模板時,樣式的設置能夠經過className=""來設置

Fragment

每一個組件都要求有大的標籤包裹,可是會產生沒必要要的DOM,所以react提供了Fragment來坐佔位符。這樣就不會產生新的節點,且組件不會抱任何錯誤。

 import React, { Component,Fragment } form 'react';
 ​
 class Demo extends Component {
     render() {
         return (
           <Fragment>
           Html content
           </Fragment>
         )
     }
 }

dangerouslySetInnerHTML

在render中若是要使某些內容不被轉義。

則咱們就須要使用dangerouslySetInnerHTML來保持元素不被轉義

 <div dangerouslySetInnerHTML={{ __html: '<div>123</div>' }} />
  1. dangerouslySetInnerHTMl 是React標籤的一個屬性,相似於angular的ng-bind;

  2. 有2個{{}},第一{}表明jsx語法開始,第二個是表明dangerouslySetInnerHTML接收的是一個對象鍵值對;

  3. 既能夠插入DOM,又能夠插入字符串;

  4. 可是存在一個問題,由於使用標籤內容後會將其轉換,因此頗有可能遭到XSS攻擊。

import React,{ Component,Fragment } from 'react';
import './style/todoList.css'

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: []
    }
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);      
  }
  render() {
    return (
      <Fragment>
        {/**這裏是註釋 */}
        <input 
          className="input"
          onChange={this.handleInputChange.bind(this)}
          value={this.state.inputValue} 
        />
        <button className="btn" onClick={this.handleSubmit}>提交</button>
        <ul>
          {
            this.state.list.map((item,index) => {
              return (
                <li 
                  key={index}
                  onClick={this.handleDeleteItem.bind(this,index)}
                  dangerouslySetInnerHTML={{__html:item}}
                >
                </li>
              )
            })
          }
        </ul>
      </Fragment>
    )
  };
  handleInputChange(e) {
    this.setState({
      inputValue: e.target.value
    })
  };
  handleSubmit() {
    this.setState({
      list: [...this.state.list,this.state.inputValue],
      inputValue: ''
    })
  };
  handleDeleteItem(index) {
    let list = [...this.state.list];
    list.splice(index,1);
    this.setState({
      list: list
    })
  }
}
export default TodoList;

當咱們在input中輸入<h1>Hello World</h1>時,若是使用了dangerouslySetInnerHTML的話,h1標籤就會生效。

htmlFor

在使用label時,通常咱們會經過for與input上的id進行綁定。在react中,咱們則須要經過htmlFor=""來綁定input

組件傳值

父傳子

父傳子經過屬性傳遞,將須要傳遞的數據放在子組件上,經過屬性的方式傳遞,子組件經過this.props.attr則就能夠獲取到父組件傳遞過來的數據

父組件:

import TodoItem from './TodoItem'
render() {
    return (
        <ul>
          {
            this.state.list.map((item,index) => {
              return (
                <div>
                  <TodoItem content={item} index={index} />
                  {/*<li 
                    key={index}
                    onClick={this.handleItemDelete.bind(this,index)}
                    dangerouslySetInnerHTML={{__html:item}}
                  >
                  </li>*/}
                </div>
              )
            })
          }
        </ul>      
    )
}

給子組件將數據掛在到content和index上,

子組件的獲取

import React, { Component } from "react";

class TodoItem extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  render() {
    return (
      <div>{this.props.content}</div>
    )
  }
}

export default TodoItem;

經過this.props就能夠獲取到父組件傳遞過來的數據。

子傳父

子傳父的話,沒有vue那麼多的方法,這裏須要在父組件先將方法傳遞給子組件。

子組件調用父組件更改數據的方法。獲取將數據做爲方法的參數調用傳遞給父組件的方法就能夠。

將方法經過屬性的方式掛在到子組件上,不過須要注意的是,這裏須要綁定指向爲父組件

仍是剛纔的內容

import React,{ Component,Fragment } from 'react';
import TodoItem from './TodoItem';
import './style/todoList.css';

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: []
    }
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleItemDelete = this.handleItemDelete.bind(this);
  }
  render() {
    return (
      <Fragment>
        <div>
          <label htmlFor="inputArea">輸入內容</label>
          <input
            id="inputArea"
            className="input"
            onChange={this.handleInputChange}
            value={this.state.inputValue} 
          />
          <button 
            className="btn" 
            onClick={this.handleSubmit}
          >提交</button>
        </div>
        <ul>
          {this.getTodoItem()}
        </ul>
      </Fragment>
    )
  };
  getTodoItem() {
    return this.state.list.map((item,index) => {
      return (
        <TodoItem
          key={index}
          content={item} 
          index={index} 
          deleteItem={this.handleItemDelete} 
        />
      )
    })
  };
  
  handleInputChange(e) {
    const value = e.target.value;
    this.setState(() => ({
      inputValue: value
    }))
  };
  handleSubmit() {
    this.setState((prevState) => ({
      list: [...prevState.list,prevState.inputValue],
      inputValue: ''
    }))
  };
  handleItemDelete(index) {
    this.setState((prevState) => {
      const list = [...prevState.list];
      list.splice(index,1);
      return {list}
    })
  }
}
export default TodoList;

deleteItem就是掛在的方法,供子組件調用

子組件

import React, { Component } from "react";

class TodoItem extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  render() {
    const { content }= this.props;
    return (
      <div onClick={this.handleClick}>
        {content}
      </div>
    )
  }
  handleClick() {
    const { deleteItem,index } = this.props;
    deleteItem(index);
  }
}

export default TodoItem;

注:react是單向數據流,舉這樣一個例子:父組件引用了5個子組件,每一個子組件都引用了父組件傳遞的list.若是不是單向數據流,子組件在對this.props.list= [];作出修改後,其餘4個子組件會怎樣?

顯示這就是一個bug了,修改一個其餘的都修改了。顯然是一個比較嚴重的問題。因此react單向數據裏就是解決這樣一個問題。

父組件傳遞給子組件的數據,是沒法作出修改的。state中的數據有一個只讀屬性,是沒法作出修改的。若是非要修改,須要父組件傳遞給子組件一個方法,子組件經過調用該方法來實現數據的更改。

setState

該方法用來更新state數據,調用該方法時,就算state中的數據沒有作修改,也會從新執行render方法。

setState({
    isActive: true,
})
console.log(this.state.isActive)//false

該方法實際上是一個異步操做,因此要獲得最新的state須要在第二個參數回調函數中獲取或者經過async和await來解決啦

async handleClick() {
    await this.setState({
        isActive: true
    })
    console.log(this.state.isActive);//true
}

該方法有兩個參數

setState((prevState) => {
    //prevState是舊的數據
},()=> {
    //這裏能夠獲得更新之後的數據
})

PropTypes

組件間傳值時,對接受數據的一個限制

詳細doc可查看官網https://reactjs.org/docs/typechecking-with-proptypes.html

import { PropTypes } from 'prop-types'
class 組件 extends Component {}
組件.propTypes = {
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,  
}

isRequired

要求該傳值對象不能爲空,必須有值

組件.propTypes = {
    obj: PropTypes.string.isRequired
}

arrayOf()

要求類型能夠爲多項

組件.propTypes = {
    obj: PropTypes.arrayOf(PropTypes.number,PropTypes.string)
}

defaultProps

爲組件間傳值的對象設置默認值

 class 組件 extends Component {}
 組件.defaultProps = {
     obj1: 'hello world'
 }

設置默認值後,父組件在未傳遞數據時,會默認使用該默認值

後續繼續學習更新

相關文章
相關標籤/搜索