React框架相關知識點

React的起源和發展javascript

起初facebook在建設instagram(圖片分享)的時候嘞,由於牽扯到一個東東叫數據流,那爲了處理數據流而且還要考慮好性能方面的問題嘞,Facebook開始對市場上的各類前端MVC框架去進行一個研究,然而並無看上眼的,因而Facebook以爲,仍是本身開發一個纔是最棒的,那麼他們決定拋開不少所謂的「最佳實踐」,從新思考前端界面的構建方式,他們就本身開發了一套,果真大牛創造力仍是很強大的。
 
React的出發點
 
基於HTML的前端界面開發正變得愈來愈複雜,其本質問題基本均可以歸結於如何未來自於服務器端或者用戶輸入的動態數據高效的反映到複雜的用戶界面上。而來自Facebook的React框架正是徹底面向此問題的一個解決方案,按官網描述,其出發點爲:用於開發數據不斷變化的大型應用程序(Building large applications with data that changes over time)。相比傳統型的前端開發,React開闢了一個至關另類的途徑,實現了前端界面的高性能高效率開發。

 

React與傳統MVC的關係
 
React不是一個完整的MVC框架,最多能夠認爲是MVC中的V(View),甚至React並不很是承認MVC開發模式;
 
React高性能的體現:虛擬DOM
 
React高性能的原理:
 
在Web開發中咱們總須要將變化的數據實時反應到UI上,這時就須要對DOM進行操做。而複雜或頻繁的DOM操做一般是性能瓶頸產生的緣由(如何進行高性能的複雜DOM操做一般是衡量一個前端開發人員技能的重要指標)。
 
React爲此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現了一套DOM API。基於React進行開發時全部的DOM構造都是經過虛擬DOM進行,每當數據變化時,React都會從新構建整個DOM樹,而後React將當前整個DOM樹和上一次的DOM樹進行對比,獲得DOM結構的區別,而後僅僅將須要變化的部分進行實際的瀏覽器DOM更新。並且React可以批處理虛擬DOM的刷新,在一個事件循環(Event Loop)內的兩次數據變化會被合併,例如你連續的先將節點內容從A-B,B-A,React會認爲A變成B,而後又從B變成A UI不發生任何變化,而若是經過手動控制,這種邏輯一般是極其複雜的。
 
儘管每一次都須要構造完整的虛擬DOM樹,可是由於虛擬DOM是內存數據,性能是極高的,部而對實際DOM進行操做的僅僅是Diff分,於是能達到提升性能的目的。這樣,在保證性能的同時,開發者將再也不須要關注某個數據的變化如何更新到一個或多個具體的DOM元素,而只須要關心在任意一個數據狀態下,整個界面是如何Render的。
 
React的特色和優點
 
1. 虛擬DOM
 
咱們之前操做dom的方式是經過document.getElementById()的方式,這樣的過程其實是先去讀取html的dom結構,將結構轉換成變量,再進行操做
 
而reactjs定義了一套變量形式的dom模型,一切操做和換算直接在變量中,這樣減小了操做真實dom,性能真實至關的高,和主流MVC框架有本質的區別,並不和dom打交道
 
2. 組件系統
 
react最核心的思想是將頁面中任何一個區域或者元素均可以看作一個組件 component
 
那麼什麼是組件呢?
 
組件指的就是同時包含了html、css、js、image元素的聚合體
 
使用react開發的核心就是將頁面拆分紅若干個組件,而且react一個組件中同時耦合了css、js、image,這種模式整個顛覆了過去的傳統的方式
 
3. 單向數據流
 
其實reactjs的核心內容就是數據綁定,所謂數據綁定指的是隻要將一些服務端的數據和前端頁面綁定好,開發者只關注實現業務就好了
 
建立第一個組件
 
react開發須要引入多個依賴文件:react.js、react-dom.js,分別又有開發版本和生成版本
 
react.js中有React對象,幫助咱們建立組件等功能
 
react-dom.js中有ReactDOM對象,渲染組件的虛擬dom爲真實dom的爆發功能
 
在編寫react代碼的時候會大量的使用到jsx代碼,可是須要編譯:
 
1. 瀏覽器端編譯,經過引入browser、babel等對引入的script內的代碼作編譯
2. 利用webpack等開發環境進行編譯,將編譯好的文件引入到應用中
 
//建立組件
    var Hello = React.createClass({
        render:function () {
            //render函數和Vue組件裏的render徹底同樣,在vue組件中能夠不用編寫render函數,這個時候可使用template模板來編寫組件的虛擬dom結構,而後vue組件會自動講模板compile成虛擬dom結構放入到render中執行,可是react須要編寫render函數

            return (
                //jsx語法
                <div>asdasd</div>
            )
            
        }
    })
    //利用ReactDOM對象的render方法將組件渲染到某個節點裏
    ReactDOM.render(<Hello/>,document.getElementById("app"))

 

組件是經過React.createClass建立的(ES5),在es6中直接經過class關鍵字來建立

組件其實就是一個構造器,每次使用組件都至關於在實例化組件

react的組件必須使用render函數來建立組件的虛擬dom結構

組件須要使用ReactDOM.render方法將其掛載在某一個節點上

組件的首字母必須大寫
 
JSX語法糖
 
JSX是一種語法,全稱:javascript xml

JSX語法不是必須使用的,可是由於使用了JSX語法以後會下降咱們的開發難度,故而這樣的語法又被成爲語法糖

在不使用JSX的時候,須要使用React.createElement來建立組件的dom結構,可是這樣的寫法雖然不須要編譯,可是維護和開發的難度很高,且可讀性不好
 
var world = React.createElement('h1',{className:'abc',id:'haha'},[
    React.createElement('span',null,'Hello'),
    React.createElement('mark',null,'React')
])
            
//利用ReactDOM對象的render方法將組件渲染到某個節點裏
ReactDOM.render(world,document.getElementById("app1"))
 
及時使用了JSX語法了以後,也是須要將其編譯成原生的createElement的

JSX就是在js中使用的xml,可是,這裏的xml不是真正的xml,只能借鑑了一些xml的語法,例如:

最外層必須有根節點、標籤必須閉合
 
組件dom添加樣式
 
在react裏表達式的符號是 "{ }",做用和vue的表達式做用是同樣的

想給虛擬dom添加行內樣式,須要使用表達式傳入樣式對象的方式來實現
 
<p style = { {color:'red',fontSize:2+'em'} }>Hello world</p>

 

行內樣式須要寫入一個樣式對象,而這個樣式對象的位置能夠放在不少地方,例如React.createClass的配置項中、render函數裏、組件原型上、外鏈js文件中

React推薦咱們使用行內樣式,由於react以爲每個組件都是一個獨立的總體

其實咱們大多數狀況下仍是大量的在爲元素添加類名、id以使用某些樣式,可是須要注意的是,class須要寫成className(由於畢竟是在寫類js代碼,會收到js規則的如今,而class是關鍵字)
 
<p className="bg-p" id="myp" style = { this.style }>Hello world</p>

 

React Event
 
在react中,咱們想要給組件的dom添加事件的話,也是 須要在行內添加的方式,事件名字須要寫成小駝峯的方式,值利用表達式傳入一個函數便可

注意,在沒有渲染的時候,頁面中沒有真實dom,因此是獲取不到dom的

給虛擬dom結構中的節點添加樣式。在行內添加,寫成駝峯形式,值是一個函數名,須要用{}包裹
 
handleClick:function () {
    alert(1)
},
render:function () {
    return (
        <div>
            <button onClick = {this.handleClick} className="click-btn">click</button>
            <button onDoubleClick = {this.handleClick} className="click-btn">click</button>
        </div>
    )
}

 

組件嵌套
 
將一個組件渲染到某一個節點裏的時候,會將這個節點裏原有內容覆蓋

組件嵌套的方式就是將子組件寫入到父組件的模板中去,且react沒有Vue中的內容分發機制(slot),因此咱們在一個組件的模板中只能看到父子關係
 
var Hello = React.createClass({
    render(){
        return (
            <h1>
                Hello 
                <World></World>
            </h1>
        )
    }
})
var World = React.createClass({
    render(){
        return (
            <mark>
                World-<Person/> 
            </mark>
        )
    }
})
//無狀態組件
var Person =function(){
    return (<mark>lilei</mark>)
}
ReactDOM.render(<Hello/>,app)
 
注意,react中jsx裏的註釋要寫成{/* */}的方式
 
React中的數據承載-Props/State
 
React也是基於數據驅動的框架,組件中必然須要承載一些數據,在react中起到這個做用的是屬性和狀態(props & state)

1. 屬性(props) 在組件外部傳入,或者內部設置,組件內部經過this.props得到

2. 狀態(state) 在組件內部設置或者更改,組件內部經過this.state得到
 
屬性(props)
 
屬性通常是外部傳入的,組件內部也能夠經過一些方式來初始化的設置,屬性不能被組件本身更改

屬性是描述性質、特色的,組件本身不能隨意更改

使組件擁有屬性的方式:

1. 在裝載(mount)組件的時候給組件傳入

傳入數據的時候,除了字符串類型,其餘的都應該包上表達式,可是爲了規整,全部的數據傳遞,最好都包上{}
 
var Gouzi = React.createClass({
    render(){
        console.log(this)
        return (
            <div>
                <p>個人名字:{this.props.name}</p>
                <p>個人性別:{this.props.sex}</p>
                <p>個人年齡:{this.props.age}</p>  
                <p>個人父親是:{this.props.father}</p>                                              
            </div>
        )
    }
})

let info = {
    sex:'male',
    father:'狗爸'
}

ReactDOM.render(<Gouzi {...info} name={"大狗子"} age={26}/>,app)

 

2. 父組件給子組件傳入

父組件在嵌套子組件的時候爲子組件傳入,傳入的方式和上面的方式同樣
 
//父組件的render函數
render(){
    return (
        <div>
            <p>父組件:</p>
            <hr/>
            <Son name={'大狗子'}/>
            <Son name={'二狗子'}/>
        </div>
    )
}

  

3. 子組件本身設置

子組件能夠經過getDefaultProps來設置默認的屬性

getDefaultProps的值是函數,這個函數會返回一個對象,咱們在這裏對象裏爲組件設置默認屬性

這種方式設置的屬性優先級低,會被外部傳入的屬性值所覆蓋
 
getDefaultProps:function () {
    console.log('getDefaultProps')
    return {
        name:'狗爸',
        sonname:'二狗子'
    }
},
//render
<p>我是{this.props.sonname}的父親-{this.props.name}</p> 
 
根據屬性或狀態,咱們能夠在render中的表達式裏作一些邏輯判斷,可使用||、三元表達式、子執行函數等等

 

getName(){
    return this.props.name || '野狗子'
},
render:function () {
    let {name} = this.props
    return (
    <div>
        <p>我是子組件-{this.props.name || '野狗子'}</p>
        <p>我是子組件-{this.props.name?this.props.name:'野狗子'}</p>
        <p>我是子組件-{this.getName()}</p>
        <p>我是子組件-{(function (obj) {
            return obj.props.name || '野狗子'
        })(this)}</p>
    </div>
    )
}

  

狀態(state)
 
狀態就是組件描述某種顯示狀況的數據,由組件本身設置和更改,也就是說由組件本身維護,使用狀態的目的就是爲了在不一樣的狀態下使組件的顯示不一樣

在組件中只能經過getInitialState的鉤子函數來給組件掛載初始狀態,在組件內部經過this.state獲取

this.props和this.state是純js對象,在vue中,$data屬性是利用Object.defineProperty處理過的,更改$data的數據的時候會觸發數據的getter和setter,可是react中沒有作這樣的處理,若是直接更改的話,react是沒法得知的,因此,須要使用特殊的更改狀態的方法:

setState(params)

在setState中傳入一個對象,就會將組件的狀態中鍵值對的部分更改,還能夠傳入一個函數,這個回調函數必須返回像上面方式同樣的一個對象,函數能夠接收prevState和props
 
//1.
let doing = this.state.doing=='學習'+props.knowledge?'玩遊戲':'學習'+props.knowledge
this.setState({doing})

//2.
this.setState((prevState,props)=>{
    return {
        doing:prevState.doing=='學習'+props.knowledge?'玩遊戲':'學習'+props.knowledge
    }
})

  

實現下拉菜單的方式
 
1. 經過數據來控制元素的行內樣式中display的值,或者去控制類名
 
<ul style={{display:isMenuShow?'block':'none'}}><li>國內新聞</li></ul>

<ul className={isMenuShow?'show':'hide'}><li>國內新聞</li></ul>

  

2. 根據數據控制是否渲染改節點、組件
 
{
    isMenuShow?<ul><li>國內新聞</li></ul>:''
}

  

3. 經過ref對dom、組件進行標記,在組件內部經過this.refs獲取到以後,進行操做
 
<ul ref='content'><li>國內新聞</li></ul>

this.refs.content.style.display = this.state.isMenuShow?'block':'none'

  

屬性和狀態的對比
 
類似點:都是純js對象,都會觸發render更新,都具備肯定性(狀態/屬性相同,結果相同)

不一樣點:

1. 屬性能從父組件獲取,狀態不能
2. 屬性能夠由父組件修改,狀態不能
3. 屬性能在內部設置默認值 ,狀態也能夠
4. 屬性不在組件內部修改 ,狀態要改
5. 屬性能設置子組件初始值 ,狀態不能夠
6. 屬性能夠修改子組件的值,狀態不能夠

狀態只和本身相關,由本身維護

屬性不要本身修改,能夠從父組件獲取,也能夠給子組件設置

組件在運行時本身須要修改的數據其實就是狀態而已

 

父子組件傳參的交互
 
咱們常常會令父組件將本身的某一條狀態傳遞給子組件,這個時候,當父組件的狀態更改的時候,子組件接收到的屬性就會發送改變,觸發render從新執行

也就是說state更改的時候(使用setState),會觸發render從新執行,屬性更改的時候,也能作到這一點
 
組件的生命週期
 
組件是一個構造器,每一次使用組件都至關於在實例化組件,在這個時候,組件就會經歷一次生命週期,從實例化實例開始到這個實例銷燬的時候,都是一次完整的生命週期

組件的生命週期,咱們會分爲三個階段,初始化、運行中、銷燬
 
初始化階段
 
1. 實例化組件以後,組件的getDefaultProps鉤子函數會執行

這個鉤子函數的目的是爲組件的實例掛載默認的屬性

這個鉤子函數只會執行一次,也就是說,只在第一次實例化的時候執行,建立出全部實例共享的默認屬性,後面再實例化的時候,不會執行getDefaultProps,直接使用已有的共享的默認屬性

理論上來講,寫成函數返回對象的方式,是爲了防止實例共享,可是react專門爲了讓實例共享,只能讓這個函數只執行一次

組件間共享默認屬性會減小內存空間的浪費,並且也不須要擔憂某一個實例更改屬性後其餘的實例也會更改的問題,由於組件不能本身更改屬性,並且默認屬性的優先級低。

2. 執行getInitialState爲實例掛載初始狀態,且每次實例化都會執行,也就是說,每個組件實例都擁有本身獨立的狀態呢

3. 執行componentWillMount,至關於Vue裏的created+beforeMount,這裏是在渲染以前最後一次更改數據的機會,在這裏更改的話是不會觸發render的從新執行

多作一些初始數據的獲取

4. 執行render,渲染dom

5. 執行componentDidMount ,至關於Vue裏的mounted,多用於操做真實dom
 
運行中階段
 
當組件mount到頁面中以後,就進入了運行中階段,在這裏有5個鉤子函數,可是這5個函數只有在數據(屬性、狀態)發送改變的時候纔會執行

1. componentWillReceiveProps

當父組件給子組件傳入的屬性改變的時候,子組件的這個函數纔會執行

當執行的時候,函數接收的參數是子組件接收到的新參數,這個時候,新參數尚未同步到this.props上,多用於判斷新屬性和原有屬性的變化後更改組件的狀態

2. 接下來就會執行shouldComponentUpdate,這個函數的做用:

當屬性或狀態發送改變後控制組件是否要更新,提升性能,返回true就更新,不然不更新,默認返回true

接收nextProp、nextState,根據根據新屬性狀態和原屬性狀態做出對比、判斷後控制是否更新

3. componentWillUpdate,在這裏,組件立刻就要從新render了,多作一些準備工做,千萬千萬,不要在這裏修改狀態,不然會死循環
至關於Vue中的beforeUpdate

4. render,從新渲染dom

5. componentDidUpdate,在這裏,新的dom結構已經誕生了,至關於Vue裏的updated
 
銷燬階段
 
當組件被銷燬以前的一剎那,會觸發componentWillUnmount,臨死前的掙扎

至關於Vue裏的beforeDestroy,因此說通常會作一些擦屁股的事情

爲何Vue中有destroyed,而react卻沒有componentDidUnmount

Vue在調用$destroy方法的時候就會執行beforeDestroy,而後組件被銷燬,這個時候組件的dom結構還存在於頁面結構中,也就說若是想要對殘留的dom結構進行處理必須在destroyed處理,可是react執行完componentWillUnmount以後把事件、數據、dom都所有處理掉了,因此根本不須要其餘的鉤子函數了

怎麼樣就算組件被銷燬:

1. 當父組件從渲染這個子組件變成不渲染這個子組件的時候,子組件至關於被銷燬

2. 調用ReactDOM.unmountComponentAtNode(node) 方法來將某節點中的組件銷燬

React中的事件對象

react中對於事件進行了處理,解決了一些兼容性問題,react事件對象上面掛載着nativeEvent,這個就是原生的事件對象

react對事件對象作了優化,若是不取值的話,值都是null
 
React中組件通訊方式
 
父組件與子組件通訊

1. 父組件將本身的狀態傳遞給子組件,子組件當作屬性來接收,當父組件更改本身狀態的時候,子組件接收到的屬性就會發送改變

2. 父組件利用ref對子組件作標記,經過調用子組件的方法以更改子組件的狀態

子組件與父組件通訊

1. 父組件將本身的某個方法傳遞給子組件,在方法裏能夠作任意操做,好比能夠更改狀態,子組件經過this.props接收到父組件的方法後調用。

兄弟組件通訊

在react沒有相似vue中的事件總線來解決這個問題,咱們只能藉助它們共同的父級組件來實現,將非父子關係裝換成多維度的父子關係

複雜的非父子組件通訊在react中很難處理,多組件間的數據共享也很差處理,因此咱們會使用flux、redux來實現這樣的功能,解決這個問題
 
React中表單元素默認值
 
在react中,若是須要 給表單元素設置默認value或者checked,須要設置成defaultValue/defaultChecked,不然設置默認值之後,用戶沒法更改
 
React中的mixins
 
在vue中咱們能夠將一些通用的、公用的方法放入到某一個純js對象中,而後,在須要使用改方法的組件中使用mixins配置(值爲對象)將該js對象中的方法注入到組件中,這樣就能實現代碼複用,便於維護

在React中曾經也有這樣的api,可是在高版本react中推薦咱們使用es6中的class來建立組件了,這個時候沒法使用mixinsapi,因此mixins被廢棄了,若是要使用公用代碼抽離,咱們可使用模塊化
 
React-keys
 
咱們在react中循環列表數據的時候,須要對循環出來的虛擬jsx節點傳入上key這個數據,

Keys能夠在DOM中的某些元素被增長或刪除的時候幫助React識別哪些元素髮生了變化。所以你應當給數組中的每個元素賦予一個肯定的標識。
 
狀態提高
 
就是若是有多個組件共享一個數據,把這個數據放到共同的父級組件中來管理
 
組合
 
在vue中有一個內容分發叫slot,在react中也有實現,就是能夠在使用組件的時候,在組件標籤內部放入一些不固定的內容,在該組件的模板中,只有{this.props.children}來表示
 
//App
<Dialog
close={this.ToggleDialogShow} isShow={isDialogShow}
>
    <ContentA/>
    <ContentA/>
    <ContentB/>
</Dialog>

//dialog
<div style={{display:isShow?'block':'none'}} className="dialog">
    <Button handler={this.props.close} text="關閉"/>
    
    {this.props.children}//這裏就是slot

</div>

 

webpack
 
webpack是一款模塊化打包工具,webpack是基於配置的,經過配置一些選項來讓webpack執行打包任務。

webpack在打包的時候,依靠依賴關係圖,在打包的時候須要告知webpack兩個概念:入口和出口

通常狀況下,咱們須要使用webpack.config.js進行配置
 
entry
 
entry配置項目打包的入口,值能夠爲單個的字符串執行某一個文件的地址,這個時候該文件就是入口文件,webpack會根據入口文件裏各模塊間的關係造成依賴關係圖,而後根據依賴關係圖進行打包
 
entry:'./src/app.js',
output:{
    path:path.join(__dirname,'build'),
    filename:'app.js'
}

  

可是有的時候咱們須要的是多入口,咱們就寫成數組的形式,數組裏的每個字符串地址指向的都是一個獨立的入口,webpack會將這些入口的依賴打包
 
entry:['./src/app.js','./src/vendor.js'],
output:{
    path:path.join(__dirname,'build'),
    filename:'[name].js'//不肯定名字的時候,這裏會打包成main.js
}

  

剛纔的兩種entry配置都只會打包出一個js文件,可是在某一個應用中咱們可能須要將js根據依賴關係打包成多個js文件,而且在多頁面應用中,咱們也確實不可能只使用一個js文件,那麼咱們就可使用以下的配置:
 
entry:{
        app:'./src/app.js',
        vendor:'./src/vendor.js'
    },
    output:{
        path:path.join(__dirname,'build'),
        filename:'[name]_[hash].js'
    }

  

這樣,由於filename裏寫成名字是[name],因此會根據entry的配置的鍵名來爲打包出的js文件命名,hash是每次打包的一個隨機的hash值,能夠用來作版本控制
 
output
 
在這裏咱們配置打包輸出的一些選項

filename能夠肯定打包出來的文件的名字,在裏面咱們可使用[name],[hash]這樣的佔位符

path配置打包出去的文件的路徑,須要是絕對路徑
 
 
env
 
在命令行或者終端中執行 webpack --env hello命令,就至關於在打包的時候傳入一個參數爲hello

在webpack.config.js中能夠暴露出一個函數,這個函數就能夠接收到env參數,固然函數就能夠根據env參數來有選擇的返回某一個或多個配置對象
 
module.exports = (env)=>{
    if(env=='production'){
        return productionConfig
    }
    return developmentConfig
}

  

plugins
 
在webpack編譯用的是loader,可是有一些loader沒法完成的任務,交由插件(plugin)來完成,插件的時候須要在配置項中配置plugins選項,值是數組,能夠放入多個插件的使用,而通常的插件都是一個構造器,咱們只需在plugins數組中放入該插件的實例便可,在實例化插件的時候又能夠傳入options,對插件的使用進行配置

html-webpack-plugin

這個插件能夠選擇是否依據模板來生成一個打包好的html文件,在裏面能夠配置、title、template、filename、minify等選項,詳情請查閱[文檔](https://segmentfault.com/a/1190000007294861)

在這個插件裏,咱們可使用jade、hbs、ejs等模板引擎來編譯成html,這裏舉例jade的配置:

[文檔](https://segmentfault.com/a/1190000000357534)
 
npm i jade jade-loader --save-dev

module:{
    rules:[
        {
            test:/\.jade$/,
            use:'jade-loader'
        }
    ]
},
plugins:[
    new HtmlWebpackPlugin({
        // title:'webpack-config-demo',
        template:'./src/index.jade',
        filename:'index.html'
    })
]
 
 
webpack-dev-server
 
webpack相輔相成的有一個server功能工具能夠提供開發的熱更新服務器

npm install webpack-dev-server -g
npm install webpack-dev-server -D

第一種啓動方式: 直接執行webpack-dev-server,若是有須要配置的選項,在後面跟上參數便可。例如
 
webpack-dev-server --hot true

 

第二種啓動方式:在webpack.config.js中配置devServer的選項,執行webpack-dev-server就ok
 
devServer:{
    port:9000,
    contentBase:'./build',
    historyApiFallback: true,
    open: true,
    proxy:{
        
    }
}

  

LOADERS
 
在webpack中專門有一些東西用來編譯文件、處理文件,這些東西就叫loader,loader的使用就是在配置項中,設置modules,在modules中設置rule值爲數組,在數組裏放入多個匹配規則:
 
module:{
    rules:[
        {test:/\.css$/,use:'css-loader'}
    ],
    //before
    loaders:[
        {test:/\.css$/,loader:'css-loader'}
    ],
}

  

test爲這次匹配要匹配的文件正則規則,use表明要使用的loader

使用url-loader能夠將css中引入的圖片(背景圖)、js中生成的img圖片處理一下,生成到打包目錄裏

視圖html-withimg-loader能夠將html中img標籤引入的img圖片打包到打包目錄
 
{
    test:/\.(png|jpe?g|svg|gif)$/,
    // use:'url-loader?limit=1000&name=images/[hash:8].[name].[ext]'
    use:[
        {
            loader:'url-loader',
            options:{
                limit:1000,
                name:'/static/images/assets/[hash:8].[name].[ext]'
            }
        }
    ]
},
{
    test:/\.html$/,
    use:'html-withimg-loader'
}

  

處理css:

cnpm i css-loader style-loader --save-dev

配置:
 
{
    test:/\.css$/,
    use:['style-loader','css-loader']
}

  

注意。webpack中loader的使用是從後往前的

css-loader能夠將引入到js中的css代碼給抽離出來,style-loader能夠將抽離出來的css代碼放入到style標籤中

處理sass
 
{
test:/\.scss$/,
use:['style-loader','css-loader','sass-loader']
},

  

將引入項目的css文件、scss文件抽成一個文件,引入到頁面中

cnpm i extract-text-webpack-plugin
 
 
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin')
///loader
{
	test:/\.css$/,
	use:ExtractTextWebpackPlugin.extract({
      fallback: "style-loader",
      use: "css-loader"
    })
},
{
	test:/\.scss/,
	use:ExtractTextWebpackPlugin.extract({
      fallback: "style-loader",
      use: ["css-loader","sass-loader"]
    })
}
///plugin
new ExtractTextWebpackPlugin({
	filename:'app.css',
	allChunks:true
})

  

處理es6:

須要的依賴:
 
"babel": "^6.23.0",
"babel-core": "^6.24.1",
"babel-loader": "^7.0.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1"

  

rules:
{
    test:/\.js$/,
    exclude: /node_modules/,
    loader:'babel-loader',
    query: {
        presets: ['es2015','react']
     }
}

  

ES6中的react
 
1.建立組件:
 
class App extends React.Component {

}

  

2.默認狀態的設置
 
在es6中再也不使用getInitialState來設置默認狀態,而是在constructor裏面直接給this.state上掛載狀態

class App extends Component {
    constructor(props){
        super(props)
        
        this.state={
            doing:'吃飯'
        }
    }
}

  

3. 默認屬性的設置

在es6中,經過給類設置defaultProps屬性來設置默認屬性
 
 
App.defaultProps = {
    name:'App根組件'
}

 

4. 作屬性傳參驗證
 
import PropTypes from 'prop-types';

App.propTypes = {
    name:PropTypes.string
}

 

5.鉤子函數有變化

getDefaultProps、getInitialState沒有了

多出了constructor,而這個函數自己是類的構造器,在這裏至關於getDefaultProps、getInitialState的結合
 
 
create-react-app 腳手架
 
npm install creat-react-app -g

create-react-app my-app //生成一個react開發模板在my-app目錄
//生成的過程特別緩慢,可使用yarn工具來下載,也就是說先去下載安裝yarn :npm install yarn -g

cd my-app

npm install

 

無狀態組件、純組件
 
當咱們使用某些組件的時候,發現,該組件不須要擁有本身的狀態,只須要接收到外界傳入的屬性以後作出相應的反應便可

這樣的話,咱們能夠利用純函數的方式將其製做成無狀態組件,提升性能
 
import React from 'react'

const Button = (props)=>{
    return <button onClick={props.handler}>我要花錢</button>
}

export default Button

 

Flux
 
在2014年,facebook提出了Flux,Flux 是一種架構思想,專門解決軟件的結構問題。它跟MVC 架構是同一類東西,可是更加簡單和清晰。
 
Flux的組成部分:

* View: 視圖層
* ActionCreator(動做創造者):視圖層發出的消息(好比mouseClick)
* Dispatcher(派發器):用來接收Actions、執行回調函數
* Store(數據層):用來存放應用的狀態,一旦發生變更,就提醒Views要更新頁面
 
Flux的流程:

1. 組件獲取到store中保存的數據掛載在本身的狀態上
2. 用戶產生了操做,調用actions的方法
3. actions接收到了用戶的操做,進行一系列的邏輯代碼、異步操做
4. 而後actions會建立出對應的action,action帶有標識性的屬性
5. actions調用dispatcher的dispatch方法將action傳遞給dispatcher
6. dispatcher接收到action並根據標識信息判斷以後,調用store的更改數據的方法
7. store的方法被調用後,更改狀態,並觸發本身的某一個事件
8. store更改狀態後事件被觸發,該事件的處理程序會通知view去獲取最新的數據

 

redux
React 只是 DOM 的一個抽象層,並非 Web 應用的完整解決方案。有兩個方面,它沒涉及。

* 代碼結構
* 組件之間的通訊

2014年 Facebook 提出了 Flux 架構的概念,引起了不少的實現。2015年,Redux 出現,將 Flux 與函數式編程結合一塊兒,很短期內就成爲了最熱門的前端架構。


若是你不知道是否須要 Redux,那就是不須要它

只有遇到 React 實在解決不了的問題,你才須要 Redux


簡單說,若是你的UI層很是簡單,沒有不少互動,Redux 就是沒必要要的,用了反而增長複雜性。
 
* 用戶的使用方式很是簡單
* 用戶之間沒有協做
* 不須要與服務器大量交互,也沒有使用 WebSocket
* 視圖層(View)只從單一來源獲取數據
 
 
須要使用redux的項目:

* 用戶的使用方式複雜
* 不一樣身份的用戶有不一樣的使用方式(好比普通用戶和管理員)
* 多個用戶之間能夠協做
* 與服務器大量交互,或者使用了WebSocket
* View要從多個來源獲取數據

從組件層面考慮,什麼樣子的須要redux:

* 某個組件的狀態,須要共享
* 某個狀態須要在任何地方均可以拿到
* 一個組件須要改變全局狀態
* 一個組件須要改變另外一個組件的狀態
 
redux的設計思想:

1. Web 應用是一個狀態機,視圖與狀態是一一對應的。
2. 全部的狀態,保存在一個對象裏面(惟一數據源)。

redux的流程:

1.store經過reducer建立了初始狀態
2.view經過store.getState()獲取到了store中保存的state掛載在了本身的狀態上
3.用戶產生了操做,調用了actions 的方法
4.actions的方法被調用,建立了帶有標示性信息的action
5.actions將action經過調用store.dispatch方法發送到了reducer中
6.reducer接收到action並根據標識信息判斷以後返回了新的state
7.store的state被reducer更改成新state的時候,store.subscribe方法裏的回調函數會執行,此時就能夠通知view去從新獲取state

注意:flux、redux都不是必須和react搭配使用的,由於flux和redux是完整的架構,在學習react的時候,只是將react的組件做爲redux中的視圖層去使用了。
 
reducer必須是一個純函數:

Reducer 函數最重要的特徵是,它是一個純函數。也就是說,只要是一樣的輸入,一定獲得一樣的輸出。

純函數是函數式編程的概念,必須遵照如下一些約束。

不得改寫參數

不能調用系統 I/O 的API

不能調用Date.now()或者Math.random()等不純的方法,由於每次會獲得不同的結果

因爲 Reducer 是純函數,就能夠保證一樣的State,一定獲得一樣的 View。但也正由於這一點,Reducer 函數裏面不能改變 State,必須返回一個全新的對象,請參考下面的寫法。
 
// State 是一個對象
function reducer(state, action) {
  return Object.assign({}, state, { thingToChange });
  // 或者
  return { ...state, ...newState };
}

// State 是一個數組
function reducer(state, action) {
  return [...state, newItem];
}

 

最好把 State 對象設成只讀。你無法改變它,要獲得新的 State,惟一辦法就是生成一個新對象。這樣的好處是,任什麼時候候,與某個 View 對應的 State 老是一個不變的對象。

咱們能夠經過在createStore中傳入第二個參數來設置默認的state,可是這種形式只適合於只有一個reducer的時候



劃分reducer

由於一個應用中只能有一個大的state,這樣的話reducer中的代碼將會特別特別的多,那麼就可使用combineReducers方法將已經分開的reducer合併到一塊兒
 
注意:

1. 分離reducer的時候,每個reducer維護的狀態都應該不一樣

2. 經過store.getState獲取到的數據也是會安裝reducers去劃分的

3. 劃分多個reducer的時候,默認狀態只能建立在reducer中,由於劃分reducer的目的,就是爲了讓每個reducer都去獨立管理一部分狀態
 
React-router
 
市場上的react-router的版本有一、二、三、4,1-3的差異不大,使用於16.0.0如下的版本

react-router 4.0 適用於16.0.0以上

在這裏使用15.6.1的react。這個版本的react容許使用React.createClass來建立組件,在16以上只能使用class類的方式來建立
 
1. 渲染根組件的時候,最外層包裹上Router組件,在其上能夠設置history屬性,值能夠是hashHistory||browserHistory

當值爲hashHistory的時候,url的變化爲hash值的變化,router會去檢測hash變化來實現組件的切換
 
當值爲browserHistory的時候,url的變化爲path的變化,須要後端進行配置

2. Router中使用Route組件來描述每一級路由,Route上有path、component屬性,表明着當path改變成...的時候,就渲染..組件

3. 在須要切換路由組件的地方,經過this.props.children來表示對應路由組件

4. 在Route中能夠屢次嵌套Route來實現多級路由

5. IndexRoute能夠設置該路由中的默認子路由
 
<IndexRoute component={Home}/>

6. IndexRedirect能夠設置在進入該路由以後立刻跳轉到哪裏

<IndexRedirect to='home'/>

7. 使用Redirect組件能夠作到從一個路由立刻重定向到其餘路由,利用這樣的屬性,當咱們form設置爲'*'的時候,就能夠將匹配不到的路由重定向到某げ路由下

<Redirect from="*" to="home"/>

8. 能夠在配置Route的時候給path里加入/:param 才表示此路由須要參數

傳入的時候,querystring參數能夠在Link裏的query中傳入和設置,在目標組件中,經過this.props中的,params、routePrams、location等來接收參數

9. 能夠經過過Router傳入routes參數,值爲數組,來設置路由配置:
 
const routeConfig = [
  { path: '/',
    component: App,
    indexRoute: { component: Home },
    childRoutes: [
      { path: 'home', component: Home },
      { path: 'news',
        component: News,
        childRoutes: [
          { path: 'inside', component: Inside },
          { path: 'outside',component:Outside}
        ]
      },
      { path: 'detail/:id', component: Detail },
      {path:'*',component:Home}
    ]
  }
]

ReactDOM.render(
	
	<Router routes={routeConfig} history={hashHistory}></Router>
	,document.getElementById('app'))

  

10. 編程式導航

* 在路由組件中經過this.props.history獲取到history對象,利用裏面push、replace、go、goBack方法來進行隱式跳轉

* 能夠從react-router中引入browserHistory或者hashHistory調用裏面的push、replace、go、goBack方法來進行隱式跳轉


11. 能夠經過在路由配置上設置 onLeave和onEnter路由鉤子來監聽路由的變化
 
UI組件庫
 
關於React的UI組件庫市場上也有不少,在這裏咱們使用螞蟻金服開發的AntDesign組件庫

這是PC端的,移動端的是Antd-Mobile
 
React-redux
 
這個庫或者說工具是redux的開發者專門爲react建立出來的,爲咱們在react中使用redux提供便利

React-Redux 將全部組件分紅兩大類:UI 組件/木偶組件(presentational component)和容器組件/智能組件(container component)。

UI 組件有如下幾個特徵。
 
* 只負責 UI 的呈現,不帶有任何業務邏輯
* 沒有狀態(即不使用this.state這個變量)
* 全部數據都由參數(this.props)提供
* 不使用任何 Redux 的 API
 
 
容器組件的特徵偏偏相反。

* 負責管理數據和業務邏輯,不負責 UI 的呈現
* 帶有內部狀態
* 使用 Redux 的 API
 
只要記住一句話就能夠了:UI 組件負責 UI 的呈現,容器組件負責管理數據和邏輯。

你可能會問,若是一個組件既有 UI 又有業務邏輯,那怎麼辦?回答是,將它拆分紅下面的結構:外面是一個容器組件,裏面包了一個UI 組件。前者負責與外部的通訊,將數據傳給後者,由後者渲染出視圖。

React-Redux 規定,全部的 UI 組件都由用戶提供,容器組件則是由 React-Redux 自動生成。也就是說,用戶負責視覺層,狀態管理則是所有交給它。


使用方法及步驟:
 
1. 使用Provider組件,包裹在應用的最外層,併爲Provider注入store屬性,此時,Provider就會將本身的store屬性傳遞給子組件組合中的容器組件

2. 使用connect函數,能夠根據一個現有的UI組件生成一個容器組件,且咱們在使用的時候,其實一直在使用的都是容器組件,connect函數執行以後返回一個函數,將返回的函數傳入UI組件並執行以後就會生成一個容器組件

3. connect函數有兩個參數:mapStateToProps,mapDispatchToProps

* mapStateToProps的做用很簡單,就是將redux中的state傳遞到UI組件的props上,此參數是一個函數,接收到store的state而後再返回一個對象,返回的對象中的屬性就會傳遞到UI組件的屬性上

    mapStateToProps對store進行了訂閱,只要state更改,mapStateToProps會自動執行並獲取到最新的state傳入到UI組件的屬性上

* mapDispatchToprops 函數,接收到dispatch參數,其實就是store.dispatch,返回的對象中設置的方法可使用到dispatch,且能傳入到UI組件的屬性上
 
 
那麼,有了mapDistpatchToProps以後,咱們就不須要actions了嗎?

咱們須要將一些複雜的業務邏輯,或者說異步的業務邏輯抽離出來放入到actions裏面去,也就是後所mapDispatchToProps裏本身建立的只是一些簡單的方法就能夠了

第一次使用react-redux等工具的作法

建立了actionCreator,專門生成action,又設置 了actions,在actions裏放一些異步的、複雜的操做以後,調用actionCreator生成action再dispatch到reducer

其實咱們上面建立actions的目的,就是由於ActionCreator不能作複雜的動做,其實咱們可使用redux-thunk來對reducer建立中間件,讓actionCreator的方法能返回一個函數,這個函數就能夠接收到dispatch,且作出異步操做以後dispatch出action,也就是說,咱們不須要再建立actions來分離異步複雜操做,並且直接能夠在ActionCreator裏寫異步方法

步驟:
 
1. 對store中的reducer使用redux-thunk
 
import {createStore,applyMiddleware} from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
const store = createStore(reducer,applyMiddleware(thunk))

export default store

  

2. 在ActionCreator的方法中返回方法來作異步處理
 
const actions_thunk = {
	clearCart(){
		//作異步操做以後生成action且dispatch
		
		return (dispatch)=>{
			
			setTimeout(function(){				
				localStorage.removeItem('cars')
				
				let action = change_cars([])
				
				dispatch(action)
				
			},500)
			
		}
		
	}
}

  

3. 能夠將actionCreator的方法利用bindActionCreator放入到mapDispatchToProps中
 
import {bindActionCreators} from 'redux'
import actions_thunk from '../../redux/ActionCreators/actions_thunk'
export default connect(state=>state,dispatch=>{
    return {
        actions_thunk:bindActionCreators(actions_thunk,dispatch)
    }
})(ClearCar)

///
button onClick={this.props.actions_thunk.clearCart} 

 

其實,如今有這樣的流行作法:

將全部的數據都交由redux管理,這樣的話,咱們的組件在UI層的邏輯就更純粹了,並且能夠作到數據緩存,好比,A組件獲取了數據放入到redux中,當A組件被切換掉以後從新切換回來的時候,數據依然在redux中能夠找到,也就是說直接取出來用就ok,不須要從新獲取
 
React 擴展

1. ref推薦使用函數的方法:

* 字符串方式
 
<Son ref="son"></Son>


this.refs.son

 

* 函數方式(推薦)
 
<Son ref={(el)=>{this.son = el}}></Son>



this.son

 

 

 

與react有關的試題分析:
 

1. react的特色不包括什麼?

聲明式設計、高效、靈活、(雙向數據流)

2. 動畫可使用哪一個第三方插件實現:

(ReactTransitionGroup)/animate.css/transitionTranslate/redux-thunk

3. ReactRouter中,路由的onLeave應該寫在哪裏:

路由對應的組件中、父組件中、 (路由組件中)、最外層大組件中

4. react-redux中的 connect方法的返回值是一個:

對象、( 容器組件)、UI組件、數組

5 . react中常提到的中間件的概念,指的是:

react中間件、flux中間件、( redux中間件)、react-redux中間件

6. redux設計的三大原則:

(store惟一/惟一數據源、
state只讀、
reducer是純函數)
reducer只讀

7. 哪些不是react-router的組件:

(Provider)、Route、Router、(MapStateToProps)

8. 下面哪些方法 可使componentWillUpdate執行

屬性更改或者狀態更改

9. 請簡述 對虛擬dom的理解,爲何使用虛擬DOM能夠極大的提高react的性能

虛擬dom是真實dom的ja對象映射,使用虛擬dom,避免對原生dom的建立和比對,取而代之的建立和比對的是js對象

原生dom的建立和比對是很是消耗性能的,而js對象的對比和建立對性能開銷很小,從這種方式來提供應用的性能

10. 請說明在react中ref的做用,並寫出使用ref的兩種方式,說明哪種是官方推薦的


ref可使咱們在react對dom或者子組件作出標記並獲取:

<Son ref="son"></Son>//this.refs.son
<Son ref={(el)=>{this.son = el}}></Son>//this.son(官方推薦)

11. 說明react中,父子組件項目傳值的方式,並說明在大型項目中爲何要引入flux或者redux這種開發架構
父組件將本身的狀態當成屬性傳遞給子組件
父組件將本身的方法傳遞給子組件,子組件在調用的時候傳參
父組件經過ref獲取到子組件,調用子組件的方法傳參

react是一款視圖層的輕量級前端框架,大量的非父子組件通訊、狀態共享會致使整個項目數據複雜,難以維護,因此react不適合處理大量的數據通訊,爲了解決這個問題,引入了FLUX、REDUX這樣的數據架構,react結合FLUX或者redux才能實現比較複雜的前端項目

12. 在react中,列表循環儘可能不要使用index做爲key值,這和diff算法有關係,請簡述 diff算法中key值有什麼做用,爲何key中使用index值會下降代碼性能

key值是diff算法中對比兩個虛擬dom的最重要參考,決定了哪些列表中的組件能夠複用,若是使用index做爲key中,列表數據改變後,會致使同一個dom元素的key中發送改變,原本能夠複用的組件必須從新建立,下降頁面性能,除非列表不須要改變,通常狀況不使用index做爲key值

13. 請列舉你所瞭解的react中的性能優化

* 不必存在store中的數據,存在state中就能夠
* 函數的this執行放在constructor中改變
* 和頁面顯示無關的數據不要放在state中
* shouldComponentUpdate來判斷組件是否須要從新render
* 使用性能分析工具進行性能分析,找問題解決問題

14. 請說明react中引入redux-thunk、redux-promise這兩種中間件能夠解決什麼樣的問題

一般狀況下,action只是一個對象,不能包含異步操做,這致使了不少建立action的邏輯只能寫在組件中,代碼量較多也不便於複用,同時對該部分代碼測試的時候也比較困難,組件的業務邏輯也不清晰,使用中間件了以後,能夠經過actionCreator異步編寫action,這樣代碼就會拆分到actionCreator中,可維護性大大提升,能夠方便於測試、複用,同時actionCreator還集成了異步操做中不一樣的action派發機制,減小編碼過程當中的代碼量

15. 畫圖說明redux的架構,寫出redux中經常使用的函數

dispatch , subscribe,combineReducers,getState,createStore

16. 簡述react-redux結合react-router在項目中的使用方式

* 建立store,編寫reducer
* 在<Router/>外面嵌套<Provider store={store}></Provider>,將store傳遞給各個子組件
* 編寫UI組件
* 使用react-redux的connect方法結合mapStateToProps、mapDispatchToProps生成容器組件,容器組件和store鏈接在一塊兒

17. 下列說法 錯誤的是:

(React是一款專一於數據層的前端框架)、react中須要調用setState方法來重置狀態、react中的虛擬dom能夠提高框架自身的性能、(react是一款符合MVVM設計思想的前端框架)

18. 關於前端組件化說法 錯誤的是:

前端組件使得複雜的代碼得以被更好的劃分、組件化的代碼設計方式增長了代碼的可複用性、(在拆分組件的時候將組件拆分的越小越細越好)、組件化開發是一種設計思想,不是react獨享的

19. 在react中, 異步獲取ajax數據通常放在那個生命週期函數:componentWillMount

20. 使用es6定義組件,能夠在那個生命週期鉤子函數裏使用this.state=state 對state進行賦值。而不須要調用this.setState方法:constructor

21. 在redux中,重要的組成部分不包括:

store、action、reducer、(dispatcher)

22. webpack中html-webpack-plugin插件能夠完成的任務是:

(在打包輸出目錄中自動生成index.html)、(向打包目錄中的index.html文件內插入打包生成的js文件引用)、將js源碼中引用的css代碼抽離ちゅ單獨的css文件並放置到打包輸出目錄、 (向打包輸出目錄中的index.html文件插入打包生成的css引用)

23. 關於jsx,說明正確的是:(ad)

a:jsx中能夠經過{}來使用js的表達式,b:jsx中能夠經過{}來使用js的代碼,c:jsx中可使用style={color:'red'}來設置元素的樣式、 d:jsx代碼會被解析成一個js對象

24. react組件被掛載到頁面 的時候,被執行的生命週期函數包括:(ab)

a: componentWillMount,b:render,c:componentDidUpdate,d:shouldComponentUpdate

25. 在自定義的react組件中,哪些生命週期函數能夠不寫(acd)

a: constructor b:render c:componentWillMount d:componentWillUnmount

26. 說法正確的是:(ab)

a: 父組件經過屬性的方式給子組件傳值,b:子組件經過props接收父組件的值,c:state中和頁面無關的屬性發送變化時,render不會執行,d:shouldComponentUpdate函數的兩個參數分別是當前的state和當前的props

27. 在react組件中,當(props或者state)發送變化的時候, 會致使render生命週期函數從新執行

28. 使用react-redux時,頁面上的組件須要被拆分紅(容器)組件和(UI)組件,經過使用(connect/mapStateToProps)方法,能夠把store中固定state內容映射到組件上

29. 使用ES6建立react組件的方式是(class ... extend是 React.Component), ES5建立組件的方法是(React.createClass) ,建立無狀態組件的方式是(function(props){})

30. react中, ref屬性的做用是獲取jsx中元素的真實dom節點或子組件

31. es5語法建立react組件比es6多了兩個生命週期函數(getDefaultProps/getInitialState)

32. 請簡述react-router中hashHistory和browserHistory的區別:

這是react-router中設置監聽url路徑變化的兩種方式,hashHistory在切換路由的時候會在url上跟着哈希值,browserHistory經過判斷path變化才切換路由,且path變化的時候後端能夠接收到請求,須要後端配置忽略掉

33. 請畫圖 說明flux中的單向數據流

用戶訪問View-》view發出用戶的action-》dispatcher收到action要求store進行更新-》store更改後,觸發一個事件-》view接收到該事件的觸發,更新頁面

34. 簡述react-redux的用法

建立store、reducer,經過provider將store傳遞給各個子組件,建立ui組件,生成容器組件,利用connect將store和容器組件鏈接

35. 說明redux設計和使用的三大原則

惟一數據源、保持狀態只讀、數據改變只能經過純函數完成

36. 說明redux-thunk的使用方法:

npm install redux-thunk -S

//store
import {createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'

import reducer from './reducer'

const store = createStore(reducer,applyMiddleware(thunk))

//actionCreator

const actionCreator = {
  handlerChange(){
    return (dispatch)=>{

      ...

    }
  }
}
相關文章
相關標籤/搜索