React零碎記憶

1:什麼是React?javascript

用來構建Ui的 javascript庫
React不是一個MVC框架,僅僅是視圖層的庫。

2:特色?html

使用JSX語法,建立組件,實現組件化開發,爲函數式的Ui編程打開大門。
性能: 經過diff算法和虛擬DOM實現視圖的高效更新。
html僅僅是個開始。

3: React的核心?java

虛擬DOM【React將DOM抽象爲虛擬DOM,虛擬DOM其實就是用一個對象來描述DOM,經過對比先後兩個對象的差別,最終只把變化的的部分從新                     渲染,提升渲染的效率】
diff算法(虛擬DOM的加速器,提高React性能的法寶)【使用React的時候,在某個時間點render()函數建立了一顆React元素樹, 在下一個                           state或者props更新的時候,render()函數會建立一顆新的React元素樹,React將對比這兩棵樹的不一樣之處,計算出如何高效的的更                        新UI】

4:diff算法說明?node

4.1:【兩棵樹的根元素類型不一樣,React會先銷燬舊樹,建立新樹】

// 舊樹
<div>react

<Counter/>

</div>webpack

// 新樹
<span>git

<Counter />

</span>es6

執行過程 destoey Counter => insert Countergithub

4.2: [類型相同的React DOM元素,React會對比二者的屬性是否相同,只更新不一樣的屬性]
        [當處理完這個DOM節點的時候,React會遞歸處理子節點]

// 舊
<div className="before" title="stuff" />
// 新
<div className="after" title="stuff" />
只更新:className 屬性web

// 舊
<div style={{color: 'red', fontWeight: 'bold'}} />
// 新
<div style={{color: 'green', fontWeight: 'bold'}} />

只更新:color屬性

4.3:
       4.3.1: [當在子節點的後邊添加一個節點,這時候兩棵樹的轉化工做執行的就很好]

// 舊
<ul>
<li>first</li>
<li>second</li>
</ul>

// 新
<ul>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>

執行過程:
React會匹配新舊兩個<li>first</li>,匹配兩個<li>second</li>,而後添加 <li>third</li> tree

4.3.2:[在開始的位置添加一個元素]

// 舊
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>

// 新
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>

在沒有key屬性時執行過程:
React將改變每個子刪除從新建立,而非保持 <li>Duke</li> 和 <li>Villanova</li> 不變

4.3.3:[key屬性]

// 舊
<ul>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>

// 新
<ul>
<li key="2014">Connecticut</li>
<li key="2015">Duke</li>
<li key="2016">Villanova</li>
</ul>

執行過程:

如今React知道帶有key 「2014」的元素是新的,對於「2015」、「2016」僅僅移動位置便可
  說明: key屬性在React內部使用,但不會傳遞給你的組件。
             在遍歷屬性的時候,推薦在組件中使用key屬性: <li key={ item.id }>{ item.name }</li>
              key只須要保值和他的兄弟節點惟一便可,不須要全局惟一
             儘量的減小數組index做爲key,數組中插入元素等操做的時候,會使得效率低下

5:React的基本使用?

npm install -S react react-dom
 [react 是React庫的入口點]
 [react-dom 提供了針對DOM的方法,好比把建立的虛擬DOM,渲染到頁面上去]

// 引入
import React from 'react'
import ReactDOM from 'react-dom'

// 建立虛擬DOM
// ['元素名稱', ‘元素屬性對象’, ‘當前元素得子元素string || createElement()返回值’]
const divVd = React.createElement('div', {

title: 'hello world'

}, 'Hello React!!!')

// 渲染
// ['虛擬DOM對象', ‘dom對象表示渲染到哪一個元素上內’, 回調函數]
ReactDOM.render(divVd, document.getElementById('app'))

說明: createElement()方式,代碼編寫不友好,太過於複雜

6:JSX的基本使用?

JSX最終會被編譯爲createElement()方法
  JSX語法炫耀經過babel-preset-react 編譯後,才能被解析執行
  npm install -D babel-preset-react (依賴與:babel-core/babel-loader)

/ 1 在 .babelrc 開啓babel對 JSX 的轉換 /
{
"presets": [

"env", "react"

]
}

/ 2 webpack.config.js /
module: [
rules: [

{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ },

]
]

/ 3 在 js 文件中 使用 JSX /
const dv = (
<div title="標題" className="cls container">Hello JSX!</div>
)

/ 4 渲染 JSX 到頁面中 /
ReactDOM.render(dv, document.getElementById('app'))

6.1:JSX的注意點?

[jsx元素添加類名 className 代替 class]
      [label的for屬性 使用htmlFor進行替換]
      [使用{}寫js代碼]
      [只能使用表達式,不能使用語句]

7:React組件?

React組件可讓你把UI分割爲獨立的可複用的,並將每一片斷
概念上講,組件就像js的函數,它們接受用戶傳入的prop,而且返回一個React對象,用來展描述展現在頁面中的內容
7.1: React建立組件的兩種方式?
     經過JS函數建立(無狀態組件)
     經過 class 建立(有狀態組件)

分類使用:

組件單純爲了展現數據,可使用哈數組件
組件有必定的業務邏輯,須要操做數據,可使用calss組件,阿里進行更改state裏面的值
  7.1.1:函數建立?
      [函數名必須爲大寫字母開頭,React經過這個特色來判斷師是不是一個組件]
      [函數必須有返回值,返回值可使JSX對象或者null]
      [返回的JSX,必須有一個根元素]
      [組件的返回值使用()進行包裹,避免換行]

function Welcome(props) {

return (
<div className="shopping-list">
  <h1>Shopping List for {props.name}</h1>
  <ul>
    <li>Instagram</li>
    <li>WhatsApp</li>
  </ul>
</div>
 )

}

ReactDOM.render(<Welcome name="wcj" />, document.getElementById('app'))

7.1.2: class建立
               在es6中class僅僅是一個語法糖,不是真正的類,本質上仍是構造+原型 實現繼承

class Person {

constructor(age) {
      this.age = age
 }

// 在class中定義方法 此處爲實例方法 經過實例打點調用
sayHello () {

console.log('你們好,我今年' + this.age + '了');

}

// 靜態方法 經過構造函數打點調用 Person.doudou()
static doudou () {

console.log('我是小明,我新get了一個技能,會暖牀');

}
}

// 添加靜態屬性
Person.staticName = '靜態屬性'

const p = new Person(19)

// 實現繼承的方式
class America extends Person{

constructor() {
      // 必須調用super(), super表示父類的構造函數
     super()
     this.skin = 'white'
     this.eyeColor = 'white'
 }

}

// 建立React獨對象
// React對象繼承的字眼是React.Component
class Shooping List extends React.Component{

constructor(props) {
     super(props)
 }
 // class建立組件的時候,必須有render方法,且顯示return一個react對象或者null 
 render() {
     return (    
         <div className="shopping-list">
           <h1>Shopping List for {this.props.name}</h1>
           <ul>
             <li>Instagram</li>
             <li>WhatsApp</li>
           </ul>
         </div>
     )
 }

}

7.1.3: 組件單獨封裝

import React from 'react'
function Hello(props) {

return (
<div>
  <div>這是Hello2組件</div>
  <h1>這是大大的H1標籤,我大,我驕傲!!!</h1>
  <h6>這是小小的h6標籤,我小,我傲嬌!!!</h6>
</div>
 )

}

export default Hello

//其餘地方引用

import Hello  from './components/Hello'

8: props 與 state?

8.1: props 
     [給組件傳遞數據,通常用在父子組件之間]
     [React把傳遞給組件的屬性轉化爲一個對象交給props]
     [props是隻讀的,沒法給props添加或者修改屬性]
8.2: state
      [用來給組件提供組件內部使用的數據]
      [經過class建立的組件才具備狀態]
      [狀態是私有的,徹底由組件來控制]
      [不要在state裏面添加render()方法中不須要的數據,會影響渲染性能]
      [不要在render方法中調用setState()方法來修改state的值]

class Hello extentds React.Component{

constructor(props) {
      super()
       this,state = {
              gender: 'male'
         }
 }
render() {
      return (
          <div>性別: { this.state.gender }</div>
     )
 }

}
9: 組件的生命週期?

組件的生命後期包含三個部階段 建立階段(Mounting)、運行和交互階段(Updating)、卸載階段(Unmounting)
9.1: 建立階段(Mounting)
      [constructor()]
      [componentWillMount()]
      [render()]
      [componentDidMount()]
9.2: 運行和交互階段(Updating)
      [componentWillReceiveProps()]
      [shouldComponentUpdate()]
      [componentWillUpdate()]
      [render()]
      [componentDidUpdate()]
9.3: 卸載階段(Unmounting)
      [componentWillUnmount()]
9.4:詳述?
      9.4.1: constructor()

class Greeting extends React.Component{

constructor(props) {
           // 獲取props
          super(props)
           // 初始化
          this.state = {
                 count: props.initCount     
             }
     }

}
// 初始化 props
// 經過靜態屬性defaultProps 來設置
Greeting.defaultProps = {

initCount : 0

}

9.4.2: componentWillMount()
                [組件被掛在在頁面以前調用]
                [沒法獲取頁面中的dom對象]
                [可使用setState()改變狀態值]

componentWillMount() {
console.warn(document.getElementById('btn')) // null
this.setState({

count: this.state.count + 1

})
}

9.4.3: render()

                   [渲染組件到頁面中,沒法獲取頁面中的dom對象]
                   [不要在render方法中調用setState()方法,不然會遞歸渲染]
                   [緣由說明: 狀態改變會從新調用render()函數,render()又從新改變狀態]

render() {
console.warn(document.getElementById('btn')) // null

return (

<div>
  <button id="btn" onClick={this.handleAdd}>打豆豆一次</button>
  {
    this.state.count === 4
    ? null
    : <CounterChild initCount={this.state.count}></CounterChild>
  }
</div>

)
}

9.4.4: componentDidMount() 

                    [組件已經掛在到頁面中了]
                    [能夠進行dom操做]
                    [能夠經過setState()修改狀態的值]
                    [在這裏會修改狀態會從新渲染]

componentDidMount() {
// 此時,就能夠獲取到組件內部的DOM對象
console.warn('componentDidMount', document.getElementById('btn'))
}

9.4.5: compomentWillReceiveProps()

                  [組件接收到新的props錢出發這個事件]
                  [能夠經過this.props接收之前傳遞的值,進行比較]
                   [若你須要響應屬性的改變,能夠經過對比this.props和nextProps並在該方法中使用this.setState()處理狀態改變]
                   [修改state不會觸發該方法]

componentWillReceiveProps(nextProps) {
console.warn('componentWillReceiveProps', nextProps)
}

9.4.6: shouldComponentUpdate()

                  [根據這個返回值肯定是否進行渲染組件,返回true則進行從新渲染,不然不渲染]
                  [經過某個條件進行渲染組件,下降組件渲染頻率,提高組件性能]
                  [這個方法必須返回布爾值]
                  [若是返回值是false,那麼後續的render()將不會執行]

// - 參數:
// - 第一個參數:最新屬性對象
// - 第二個參數:最新狀態對象
shouldComponentUpdate(nextProps, nextState) {
console.warn('shouldComponentUpdate', nextProps, nextState)
return nextState.count % 2 === 0
}

9.4.7: componentWillUpdate()
                   [組件將要更新]
                   [最新的屬性和狀態對象]
                    [不能修改狀態,不然會重複渲染]

componentWillUpdate(nextProps, nextState) {
console.warn('componentWillUpdate', nextProps, nextState)
}

9.4.8: render()
                   [從新渲染組件]
                   [這個函數可以屢次執行,只要組件的屬性或者狀態改變了,這個方法就會從新執行]
      9.4.8:componentDidUpdate()
                   [組件已經被更新]
                   [舊的屬性和狀態對象]

componentDidUpdate(prevProps, prevState) {
console.warn('componentDidUpdate', prevProps, prevState)
}

9.4.9: componentWillUnmount()
               [清除定時器]
               [組件一生只能執行依次]

10: state 和 setState

[使用setState()方法修改,狀態改變後,React會從新渲染組件]
[不要直接修改state的值,這樣不會從新渲染組件]

constructor(props) {
super(props)

// 正確姿式!!!
// -------------- 初始化 state --------------
this.state = {

count: props.initCount

}
}

componentWillMount() {
// -------------- 修改 state 的值 --------------
// 方式一:
this.setState({

count: this.state.count + 1

})

this.setState({

count: this.state.count + 1

}, function(){

// 因爲 setState() 是異步操做,因此,若是想當即獲取修改後的state
// 須要在回調函數中獲取
// https://doc.react-china.org/docs/react-component.html#setstate

});

// 方式二:
this.setState(function(prevState, props) {

return {
  counter: prevState.counter + props.increment
}

})

// 或者 - 注意: => 後面須要帶有小括號,由於返回的是一個對象
this.setState((prevState, props) => ({

counter: prevState.counter + props.increment

}))
}
11:組件綁定事件

[ref獲取dom對象]
[onClick駝峯命名事件綁定]

<input type="button" value="觸發單擊事件"
onClick={this.handleCountAdd}
onMouseEnter={this.handleMouseEnter}
/>

11.1:事件綁定?
       [經過bind綁定]
       [經過箭頭函數綁定]
      【bind可以調用函數,改變函數內部的this指向,並返回一個新的函數】
      【bind第一個參數表示返回函數中的this的指向,後面的參數表示傳遞給函數的參數】

// 1:自定義方法:
handleBtnClick(arg1, arg2) {
this.setState({

msg: '點擊事件修改state的值' + arg1 + arg2

})
}

render() {
return (

<div>
  <button onClick={
    // 無參數
    // this.handleBtnClick.bind(this)

    // 有參數
    this.handleBtnClick.bind(this, 'abc', [1, 2])
  }>事件中this的處理</button>
  <h1>{this.state.msg}</h1>
</div>

)
}

//2:構造函數中綁定
constructor() {
super()

this.handleBtnClick = this.handleBtnClick.bind(this)
}

// render() 方法中:
<button onClick={ this.handleBtnClick }>事件中this的處理</button>

11.2: props校驗?
      npm install -s prop-types

// 引入模塊

import PropTypes from 'prop-types'

// 使用

static propTypes = {
initCount: PropTypes.number, // 規定屬性的類型
initAge: PropTypes.number.required // 規定屬性的類型,且規定爲必傳字段

}

12: React 單項數據流?

[數據流方向: 自上而下,也就是隻能從父組件傳遞到子組件]
[數據都是從父組件提供的,子組件想要使用數據,必須從父組件中去獲取]
[若是多個組件都要使用某個數據,那麼最好將這這部數據共享是狀態提高父集當中進行管理]
12.1: 組件通信

父 ---》 子 props
子 ---》 父組件經過prop傳遞迴調函數給子組件,子組件調用函數將數據參數傳遞給父組件。
兄弟組件 React是單項數據流,所以須要藉助於父組件進行傳遞,經過父組件的回調進行修改兄弟組件的props.
class Component {
constructor(props){

super(props);

}
render(){

return (
    <div title={this.props.title}></div>
)

}
}
<Component title="test"/>//調用title就傳進去了
父子傳值
class Child extends React.Component{
constructor(props){

super(props);
this.state = {}

}

render(){

return (
  <div>
    {this.props.text}
    <br />
    <button onClick={this.props.refreshParent}>
        更新父組件
    </button>
  </div>
)

}
}
class Parent extends React.Component{
constructor(props){

super(props);
this.state = {}

}
refreshChild(){

return (e)=>{
  this.setState({
    childText: "父組件溝通子組件成功",
  })
}

}
refreshParent(){

this.setState({
  parentText: "子組件溝通父組件成功",
})

}
render(){

return (
  <div>
    <h1>父子組件溝通</h1>
    <button onClick={this.refreshChild()} >
        更新子組件
    </button>
    <Child 
      text={this.state.childText || "子組件未更新"} 
      refreshParent={this.refreshParent.bind(this)}
    />
    {this.state.parentText || "父組件未更新"}
  </div>
)

}
}

兄弟傳值(context上下文的方式進行傳值)
class Brother1 extends React.Component{
constructor(props){

super(props);
this.state = {}

}
render(){

return (
  <div>
    <button onClick={this.context.refresh}>
        更新兄弟組件
    </button>
  </div>
)

}
}
Brother1.contextTypes = {
refresh: React.PropTypes.any
}

class Brother2 extends React.Component{
constructor(props){

super(props);
this.state = {}

}
render(){

return (
  <div>
     {this.context.text || "兄弟組件未更新"}
  </div>
)

}
}
Brother2.contextTypes = {
text: React.PropTypes.any
}

class Parent extends React.Component{
constructor(props){

super(props);
this.state = {}

}
getChildContext(){

return {
  refresh: this.refresh(),
      text: this.state.text,
  }
}

refresh(){

return (e)=>{
  this.setState({
    text: "兄弟組件溝通成功",
  })
}

}
render(){

return (
  <div>
    <h2>兄弟組件溝通</h2>
    <Brother1 />
    <Brother2 text={this.state.text}/>
  </div>
)

}
}
Parent.childContextTypes = {
refresh: React.PropTypes.any,
text: React.PropTypes.any,
}
跨級傳值
Context 做用屬性:跨級傳遞數據,避免向下每層手動的傳遞props.須要配合PropTypes
class Grandfather extends React.Component {
// 類型限制(必須),靜態屬性名稱固定
static childContextTypes = {

color: PropTypes.string.isRequired

}

// 傳遞給孫子組件的數據
getChildContext() {

return {
  color: 'red'
}

}

render() {

return (
  <Father></Father>
)

}
}

class Child extends React.Component {
// 類型限制,靜態屬性名字固定
static contextTypes = {

color: PropTypes.string

}

render() {

return (
  // 從上下文對象中獲取爺爺組件傳遞過來的數據
  <h1 style={{ color: this.context.color }}>爺爺告訴文字是紅色的</h1>
)

}
}

class Father extends React.Component {
render() {

return (
  <Child></Child>
)

}
}
13: 路由管理React-router

npm install react-router-dom -Save
 13.1: 使用?
      [引入路由組件]
      [使用 <Router></Router> 做爲根容器,包裹整個應用(JSX)]
      [使用 <Link to="/movie"></Link> 做爲連接地址,並指定to屬性]
      [使用 <Route path="/" compoent={Movie}></Route> 展現路由內容]
      【<Router></Router>做爲整個組件的根組件,是路由容器,只能是惟一的子元素】
      【<Link></Link> 相似與Vue中的<router-link></router-link>, to屬性指定路由地址】
      【<Route></Route>相似於Vue中的<router-view></router-view>,指定路由內容(組件)展現的位置】

1: 引入組件

import { HashRouter as Router, Link, Route } from "react-router-dom"

2: 使用<Router>

<Router>
// 3 設置 Link
<Menu.Item key="1"><Link to="/">首頁</Link></Menu.Item>
<Menu.Item key="2"><Link to="/movie">電影</Link></Menu.Item>
<Menu.Item key="3"><Link to="/about">關於</Link></Menu.Item>

// 4 設置 Route
// exact 表示:絕對匹配(徹底匹配,只匹配:/)
<Route exact path="/" component={HomeContainer}></Route>
<Route path="/movie" component={MovieContainer}></Route>
<Route path="/about" component={AboutContainer}></Route>
 </Router>
13.2: 路由傳參?

// 配置路由參數
<Route path="/movie/:movieType"></Route>
// 獲取路由參數
const type = this.props.match.params.movieType

13.3: 路由跳轉?

this.props.history.push('/movie/movieDetail/' + movieId)
history.go(-1) 用來實現頁面的前進(1)和後退(-1)
history.push() 方法用於在JS中實現頁面跳轉
14: fetch?

[fetch 是一個現代的概念, 等同於XMLHttpRequest, 它提供了許多與XMLHttpRequest相同的功能,但被設計成更具備可擴展性和高效性]

/*
經過fetch請求回來的數據,是一個Promise對象.
調用then()方法,經過參數response,獲取到響應對象
調用 response.json() 方法,解析服務器響應數據
再次調用then()方法,經過參數data,就獲取到數據了
*/
fetch('/api/movie/' + this.state.movieType)
// response.json() 讀取response對象,並返回一個被解析爲JSON格式的promise對象
.then((response) => response.json())
// 經過 data 獲取到數據
.then((data) => {

console.log(data);
this.setState({
  movieList: data.subjects,
  loaing: false
})

})
/*

  • 方式一

*/
fetch(url, {

method: 'POST',
 header: {
    "Content-Type": "application/x-www-form-urlencoded"
 }
 body: "key1=value1&key2=value2"

}).then(res => {

console.log(res)

}, err => {

console.log(err)

}).catch(e => {

console.log(e)

})

15: 跨域獲取數據的三種方式?
JSONP
代理
CORS

npm install -s fetch-jsonp
 [利用JSONP實現跨域獲取數據的時候,只能獲取get請求]
 【限制: 1 只能發送GET請求 2 須要服務器支持JSONP請求】

JSONP
fetchJsonp('https://api.douban.com/v2/mov...')
.then(rep => rep.json())
.then(data => { console.log(data) })
代理

【webpack-dev-server 代理配置以下】
【問題:webpack-dev-server 是開發期間使用的工具,項目上線了就再也不使用 webpack-dev-server】
【解決:項目上線後的代碼,也是會部署到一個服務器中,這個服務器配置了代理功能便可(要求兩個服務器中配置的代理規則相同】

// webpack-dev-server的配置
devServer: {
// https://webpack.js.org/config...
// https://github.com/chimurai/h...
// http://www.jianshu.com/p/3bdf...
proxy: {

// 使用:/api/movie/in_theaters
// 訪問 ‘/api/movie/in_theaters’ ==> 'https://api.douban.com/v2/movie/in_theaters'
'/api': {
  // 代理的目標服務器地址
  target: 'https://api.douban.com/v2',
  // https請求須要該設置
  secure: false,
  // 必須設置該項
  changeOrigin: true,
  // '/api/movie/in_theaters' 路徑重寫爲:'/movie/in_theaters'
  pathRewrite: {"^/api" : ""}
}

}
}

/ movielist.js /
fetch('/api/movie/in_theaters')
.then(function(data) {

// 將服務器返回的數據轉化爲 json 格式
return data.json()

})
.then(function(rep) {

// 獲取上面格式化後的數據
console.log(rep);

})
CORS-服務器端跨域
// 經過Express的中間件來處理全部請求
app.use('*', function (req, res, next) {
// 設置請求頭爲容許跨域
res.header('Access-Control-Allow-Origin', '*');

// 設置服務器支持的全部頭信息字段
res.header('Access-Control-Allow-Headers', 'Content-Type,Content-Length, Authorization,Accept,X-Requested-With');
// 設置服務器支持的全部跨域請求的方法
res.header('Access-Control-Allow-Methods', 'POST,GET');
// next()方法表示進入下一個路由
next();
});

16: redux 狀態管理工具,用來管理應用中的數據

16.1: 核心?
      Store: 
          [Redux應該只有一個store]
          [getState() 獲取state]
          [dispatch(action) 用來修改state]

/ action/
【 在redux中, action就是一個對象。】
【action必須提供一個: type屬性,表示當前動做的標識】
【其餘參數,標識這個動做須要用到的數據】

{ type: ‘ADD_TOTD’,name: '要添加的任務名稱'}
{type: 'TOGGLE_TOTD', id: 1}

/reducer/
第一個參數:表示狀態(數據),咱們須要給初始狀態設置默認值
第二個參數:表示 action 行爲
function todo(state = [], action) {
switch(action.type) {

case 'ADD_TODO':
  state.push({ id: Math.random(), name: action.name, completed: false })
  return state
case 'TOGGLE_TODO':
  for(var i = 0; i < state.length; i++) {
    if (state[i].id === action.id) {
      state[i].completed = !state[i].completed
      break
    }
  }
  return state
default:
  return state

}
}

// 要執行 ADD_TODO 這個動做:
dispatch( { type: 'ADD_TODO', name: '要添加的任務名稱' } )

// 內部會調用 reducertodo(undefined, { type: 'ADD_TODO', name: '要添加的任務名稱' })

相關文章
相關標籤/搜索