虛擬DOM的概念 這是React性能高效的核心算法javascript
React
爲此引入了虛擬DOM
(Virtual DOM
)的機制。基於React
進行開發時全部的DOM
構造都是經過虛擬DOM
進行,每當數據變化時,React
都會從新構建整個DOM
樹,而後React
將當前整個DOM
樹和上一次的DOM
樹進行對比,獲得DOM
結構的區別,而後僅僅將須要變化的部分進行實際的瀏覽器DOM
更新。儘管每一次都須要構造完整的虛擬DOM
樹,可是由於虛擬DOM
是內存數據,性能是極高的,而對實際DOM
進行操做的僅僅是Diff
部分,於是能達到提升性能的目的。css
React 組件,理解什麼是組件化html
像插入普通
HTML
標籤同樣,在網頁中插入這個組件前端
所謂組件,即封裝起來的具備獨立功能的UI
部件java
React
推薦以組件的方式去從新思考UI
構成,將UI
上每個功能相對獨立的模塊定義成組件,而後將小的組件經過組合或者嵌套的方式構成大的組件,最終完成總體UI
的構建。React
而言,則徹底是一個新的思路,開發者從功能的角度出發,將UI
分紅不一樣的組件,每一個組件都獨立封裝。React
一個組件應該具備以下特徵:node
Composeable
):一個組件易於和其它組件一塊兒使用,或者嵌套在另外一個組件內部。若是一個組件內部建立了另外一個組件,那麼說父組件擁有它建立的子組件,經過這個特性,一個複雜的UI
能夠拆分紅多個簡單的UI
組件.Reusable
):每一個組件都是具備獨立功能的,它能夠被使用在多個UI
場景Maintainable
):每一個小的組件僅僅包含自身的邏輯,更容易被理解和維護語法react
// Header組件 // export 導出供外部使用 export default class ComponentHeader extends React.Component{ //render方法:用於解析自己類的輸出 render(){ return (<header><h1>這裏是頭部</h1></header>) } } // Index組件 import ComponentHeader from './component/header'; class Index extends React.Component{ render(){ // 插入普通 HTML 標籤同樣 return <ComponentHeader/> } } // 將模版轉化爲HTML語言,並插入到指定節點 // 至關於程序的入口 ReactDOM.render(<Index/>,document.getElementById('one'))
React 多組件嵌套webpack
各個組件之間相互獨立,利於維護,重用。在Index
文件中進行調用,簡明。
須要變動時,咱們只須要改變相應的組件,全部引用該組件的的頁面就都發生的變動,很好維護。git
import ComponentHeader from './component/header'; import BodyIndex from './component/body'; import ComponentFooter from './component/footer'; class Index extends React.Component{ render(){ //將組件賦值給一個變量,這樣方便作判斷 var component; if(/*條件*/){ component = <ComponentLoginedHeader/>; }else{ component = <ComponentHeader/>; } return ( <div> { component} <BodyIndex/> <ComponentFooter/> </div> ) } }
JXS內置表達式github
HTML
語言直接寫在 JavaScript
語言之中,不加任何引號,這就是 JSX
的語法,它容許 HTML
與 JavaScript
的混寫。JSX
的基本語法規則:遇到 HTML
標籤(以 <
開頭),就用 HTML
規則解析;遇到代碼塊(以 {
開頭),就用 JavaScript
規則解析{/*註釋*/}
export default class BodyIndex extends React.Component{ render(){ var userName = 'xiaoxiong'; var boolInput; var html1 = 'Mooc Lesson'; //對 進行Unicode轉碼 var html2 = 'Mooc\u0020Lesson' return ( <div> <h2>頁面主體內容</h2> // 三元表達式的使用 <p>{userName = '' ?'用戶尚未登陸':'用戶名:' +userName }</p> // 使用{boolInput}進行值的綁定 <input type='button' value='按鈕' disable={boolInput}> //解析html <p>{html1}</p> //'Mooc Lesson' 不會被解析爲空格 <p>{html2}</p> //'Mooc Lesson' <p dangerouslySetInnerHTML={{__html:html1}}></p> //'Mooc Lesson' 此方法可能會存在xss攻擊 </div> ) } }
聲明週期,縱觀整個React的生命週期
在ES6中,一個React組件是用一個class來表示的
過程描述
在React
中有4
中途徑能夠觸發render
Initial Render
constructor --> componentWillMount --> render -->conponentDidMount
this.setState
(並非一次setState
會觸發一次render
,React
可能會合並操做,再一次性進行render
)shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
props
發生改變,可是就算props
沒有改變或者父子組件之間沒有數據交換也會觸發render
)componentWillReceiveProps --> shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate
this.forceUpdate
componentWillUpdate --> render --> componentDidUpdate
總結
constructor
、componentWillMount
、componentDidMount
只有第一次渲染時候會被調用componentWillUpdate
、componentDidUpdate
、shouldComponentUpdate
在之後的每次更新渲染以後都會被調用考慮到性能的問題,若是有些屬性的變化,不須要從新刷新頁面,咱們是使用 componentShouldUpdate()
進行控制。
官方文檔
https://facebook.github.io/re...
參考文獻
http://www.jianshu.com/p/4784...
state 屬性控制React的一切
組件自身的狀態,props爲外部傳入的狀態
state
對組件作了更新以後,會立刻反應到虛擬DOM
上,最後更新到DOM
上。這個過程是自動完成的。
組件免不了要與用戶互動,React
的一大創新,就是將組件當作是一個狀態機,一開始有一個初始狀態,而後用戶互動,致使狀態變化,從而觸發從新渲染 UI
.
export default class BodyIndex extends React.Component{ // 初始化 constructor(){ super();//調用基類的全部初始化方法 //state的做用域是當前組件,不會污染其餘模塊 this.state = { username:"xiaoxiong" }; }; //修改state setTimeOut(()=>{ this.setState({username:"miaomiao"}) },4000); render(){ return ( <div> <h2>頁面主體內容</h2> // 引用state值 <p>{this.state.username}</p> </div> ) } }
Props屬性
其餘組件傳遞參數,對於本模塊來講,屬於外來屬性。
//使用組件時,傳入參數 < BodyIndex userid="123" username={xiaoxiong}/> export default class BodyIndex extends React.Component{ render(){ return ( <div> <h2>頁面主體內容</h2> // 接收參數 <p>{this.props.userid}</p> <p>{this.props.username}</p> </div> ) } }
添加組件屬性,有一個地方須要注意,就是 class
屬性須要寫成 className
,for
屬性須要寫成 htmlFor
,這是由於 class
和 for
是 JavaScript
的保留字。
事件和數據的雙向綁定,包含了父子頁面之間的參數互傳
props
事件綁定
export default class BodyIndex extends React.Component{ constructor(){ super(); this.state={ username="xiaoxiong", age:20 } }; changeUserInfo(){ this.setState({age:50}) }; render(){ return ( <div> <h2>頁面主體內容</h2> <p>{this.props.username} {this.props.age }</p> //事件綁定 ES6寫法 //ES5寫法 onClick=this.chanchangeUserInfo <input type="button" value="提交" onClick=this.chanchangeUserInfo.bind(this)/> </div> ) } }
就是讓子頁面的變更體如今父頁面上,而頁面狀態的改變由state
控制,所以咱們讓父頁面經過props
將函數傳遞給子頁面,此函數能夠取得子頁面的值,並改變父頁面的state
//子組件 export default class BodyChild extends React.Component{ render(){ return ( <div> // 調用父組件傳過來的函數 <p>子頁面輸入:<input type="text" onChange={this.props.handler}/></p> </div> ) } } //父組件 import BodyChild from "./component/bodychild"; export default class Body extends React.Component{ constructor(){ super(); this.state={ age:20 } } // 父頁面的函數,能夠操控父頁面的 state handler(e){ this.setState({age:e.target.value}) } render(){ return ( <div> <p>{this.state.age}<p> <BodyChild handler={this.handler.bind(this)}/> </div> ) } }
可複用組件,真正讓React開發快速、高效的地方
使用組件時,傳遞
props
,在組件定義的文件中可以使用這些props
export default class BodyIndex extends React.Component{ render(){ return ( <div> <p>{this.props.userid}<p> <p>{this.props.username}<p> </div> ) } } // 對傳遞過來的 props 的類型進行約束 BodyIndex.propTypes = { // userid爲number類型且必須傳遞 userid:React.propTypes.number.isRuquired }; // 設置默認值,使用組件時,若是不傳遞參數,將顯示默認值 BodyIndex.defaultProps = { username:"xiaoxiong" };
組件多層嵌套時,傳遞參數的簡便方法
export default class Body extends React.Component{ render(){ return ( <div> //在父頁面定義props <BodySon username="xiaoxiong" age="25"/> </div> ) } } export default class BodySon extends React.Component{ render(){ return ( <div> <p>{this.props.username}</p> //取得父頁面(BodySon)的全部props屬性 <Bodygrandson ...this.props /> </div> ) } } export default class Bodygrandson extends React.Component{ render(){ return ( <div> // 使用傳遞過來的props <p>{this.props.username}{this.props.age}</p> </div> ) } }
組件的RefsReact
中多數狀況下,是經過state
的變化,來從新刷新頁面。但有時也須要取得html
節點,好比對input
進行focus
等;下面咱們來看下,怎麼取得原生的html
節點,並對其進行操做。
export default class BodyChild extends React.Component{ handler(){ //第一種方式 var mySubmitBotton = document.getElementById("submitButton"); //這樣也是能夠的 mySubmitBotton.style.color="red"; ReactDOM.findDOMNode(mySubmitBotton).style.color = 'red'; //第二種方式 this.refs.submitBotton.style.color = 'red'; } render(){ return ( <div> <input id="submitButton" ref="submitButton"type="button" onClick={this.handler.bind(this)}/> </div> ) } }
refs
是訪問到組件內部DOM
節點的惟一可靠方式refs
會自動銷燬對子組件的引用Render
或Render
以前對Refs
進行調用,由於Refs
獲取的是真實的DOM
節點,要在插入真實DOM
節點以後調用。Refs
DOM
時,用方法1
便可獨立組件間共享Mixins
在全部的組件見共享一些方法
ES6
中使用mixin
須要插件支持
npm install react-mixin --save
使用
//mixin.js const MixinLog = { //有自身的生命週期 componentDidMount(){ console.log('MixinLog ComponentDidMount'); }, log(){ console.log('Mixins'); } }; //暴露供外部使用 export default MixinLog; //body.js //導入MixinLog import MixinLog from './component/mixin.js'; import ReactMixin from 'react-mixin'; export default class Body extends React.Component{ handler(){ //調用MixinLog中方法 MixinLog.log(); } render(){ return ( <div> <p>{this.props.username}</p> </div> ) } } ReactMixin(Body.prototype,MixinLog);
內聯樣式
原生中用 -
鏈接的樣式屬性,在這裏要採用駝峯寫法或加引號""
,屬性值一概加引號""
,這樣的書寫方式,實際上就是加入了內聯樣式,結構和樣式混在一塊兒,不是很好的作法。而且這種寫法,不能使用僞類、動畫。
export default class Header extends React.Component{ render(){ //將樣式定義爲一個變量 //注意樣式屬性的書寫 const styleComponentHeader = { header:{ backgroundColor:"#333", color:"#fff", "padding-top":"15px", paddingBottom:"15px", }, //還能夠定義其餘樣式 }; return ( //使用樣式,這樣寫,實際上就是內聯樣式 <header style={styleComponentHeader. header}> <h1>頭部</h1> </header> ) } }
也能夠在index.html
文件中,引入css
文件,並在須要使用樣式的地方加入類名(className
),但這種寫法會污染全局。
//index.html <header> <link href="../css.js"> </header> <div id="one"></div> //組件 export default class Header extends React.Component{ render(){ return ( //使用類名,加入樣式 <header className="header"> <h1>頭部</h1> </header> ) } }
內聯樣式中的表達式
根據state
的值,控制樣式
export default class Header extends React.Component{ constructor(){ super(); this.state={ miniHeader:false } } //點擊頭部,樣式發生變化 swithHeader(){ this.setState({ miniHeader:!this.state.miniHeader }) } render(){ const styleComponentHeader = { header:{ backgroundColor:"#333", color:"#fff", "padding-top":"15px", //根據state的值,變化 paddingBottom的值 //樣式中可使用表達式 paddingBottom: (this.state.miniHeader)?"3px":"15px" }, }; return ( <header style={styleComponentHeader. header} onClick={this.swithHeader.bind(this)}> <h1>頭部</h1> </header> ) } }
CSS模塊化,學習如何使用require進行樣式的引用
問題:
-
使用webpack,安裝相關插件
//使用require導入css文件 npm install css-loader --save //計算以後的樣式加入頁面中 npm install style-loader --save
webpack.config.js
文件中進行配置
將css
文件導入相應的組件後,會生成一個對應關係
footer:_footer_minifooter_vxs08s
_footer_minifooter_vxs08s
是按照必定的規則生成一個惟一的類名,當咱們爲當前組件應用 .footer
類的時候,就會按照這個對應關係去樣式文件中找,而後應用響應的樣式。
其實是改變了樣式文件中類的名稱,使其惟一。
這樣即便將全部文件打包到一塊兒,也不會引發衝突。
不一樣的文件能夠 使用相同的類名,不會衝突,由於模塊化以後,類名都進行了轉換。
module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader', query: { presets: ['es2015','react'] } }, { test: /\.css$/, //modules 模塊化配置 loader: 'style-loader!css-loader?modules' }, ] },
使用
//footer.css .footer{ background-color:#333; } //Footer組件 //導入css樣式,這樣這個樣式文件只做用於這個組件 var footerCss = require('./css/footer.css'); export default class Footer extends React.Component{ render(){ return ( <footer className="footerCss.footer"> <h1>底部</h1> </footer> ) } }
總結
爲何要css模塊化
模塊化的優勢
class
名生成規則配置靈活,能夠以此來壓縮class
名(在webpack.config.js
文件中配置)import
組件,再使用便可,無需管理樣式。webpack.config.js
中進行配置,書寫時,仍是咱們熟悉的css
。JSX樣式與CSS樣式互轉
線上轉換工具
http://staxmanade.com/CssToRe...
Router 概念
控制頁面之間的層級關係
底層機制
經過狀態的改變,致使組件重新渲染,從而改變頁面顯示
React: state/props -> Component ->UI
經過改變url
,致使Router
變化,從而改變頁面顯示
React:location(hasj) -> Router ->UI
hashHistory
&& browserHistory
慕課老師的demo
使用的是hashHistory
,而另外一種方式則是使用browserHistory
。
若是但願使用browserHistory
達到hashHistory
的效果,則須要作2
件事情:
一、服務器支持。若是開發服務器使用的是webpack-dev-server
,加上--history-api-fallback
參數就能夠了。
二、做爲項目入口的HTML
文件中,相關外鏈的資源路徑都要改成絕對路徑,即以"/"
根路徑開頭。
安裝
// 版本 2.8.1 npm install react-router
使用
component
指定組件 path
指定路由的匹配規則
router
能夠進行嵌套ComponentDetails
嵌套在Index
頁面中,咱們要在Index
中進行展現。
//index.js export default class Index extends React.Component{ render(){ return ( <div> //此處展現的是ComponentDetails頁面 {this.props.children} </div> ) } }
import React from 'react'; import ReactDOM from 'react-dom'; import Index from './index'; import { Router, Route, hashHistory} from 'react-router'; import ComponentList from './components/list'; import ComponentHeader from './components/header'; import ComponentDetails from './components/details'; export default class Root extends React.Component{ render(){ //這裏替換了以前的index,變成了程序的入口 (注意修改webpack.conf.js中的入口文件爲root.js) return ( <Router history={hashHistory}> <Route component={Index} path="/"> <Route component={ComponentDetails} path="details"></Route> </Route> <Route component={ComponentList} path="list/:id"></Route> <Route component={ComponentHeader} path="header"></Route> </Router> ); }; } ReactDOM.render( <Root/>, document.getElementById('example') );
有了Router
以後,用Link
進行跳轉
<Link to={`/`}>首頁</Link> <Link to={`/details`}>嵌套的詳情頁面</Link>
Router 參數傳遞
//在Router中定義參數名稱 <Route component={ComponentList} path="list/:id"></Route> //在Link中傳入參數 <Link to={`/list`}></Link> //在list組件頁面中讀取傳入的參數 render(){ <div>{this.props.params.id}</div> }
//初始化 創建初始化文件 npm init
package.json
文件
npm
的start
是一個特殊的腳本名稱,它的特殊性表如今,在命令行中使用npm start
就能夠執行相關命令,若是對應的此腳本名稱不是start,想要在命令行中運行時,須要這樣用npm run {script name}
如npm run build
{ "name": "reactconf",//項目名稱 "version": "1.0.0",//項目版本 "description": "",//項目描述 "main": "root.js",//入口文件 //自定義的腳本任務 "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "webpack-dev-server --inline --content-base ." }, "author": "", "license": "ISC", "dependencies": { "antd": "^2.10.1", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "babelify": "^7.3.0", "css-loader": "^0.25.0", "react": "^15.5.4", "react-dom": "^15.5.4", "react-mixin": "^2.0.2", "react-router": "^2.8.1", "style-loader": "^0.13.1", "webpack": "^1.13.2", "webpack-dev-server": "^1.16.1" }, "devDependencies": { "babel-core": "^6.24.1", "babel-loader": "^7.0.0" } }
安裝依賴
babelifyBabel
實際上是一個編譯JavaScript
的平臺,它的強大之處表如今能夠經過編譯幫你達到如下目的:
JavaScript
標準(ES6
,ES7
),這些標準目前並未被當前的瀏覽器徹底的支持JavaScript
進行了拓展的語言,好比React
的JSX
babel-preset-react react
轉碼規則
babel-preset-es2015 ES2015
轉碼規則
npm install react react-dom babelify --save npm install babel-preset-react babel-preset-es2015 --save
使用webpack
打包
安裝相關的包
npm install webpack -g npm install webpack-dev-server -g npm install webpack --save npm install webpack-dev-server --save
//webpack.config.js // 引用webpack相關的包 var webpack = require('webpack'); var path = require('path'); var WebpackDevServer = require("webpack-dev-server"); module.exports = { //入口文件 __dirname 項目根目錄 //「__dirname」是node.js中的一個全局變量,它指向當前執行腳本所在的目錄 context: __dirname + "/src", entry:"./js/root.js", //loaders module:{ loaders:[ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015','react'] } }, //下面是使用 ant-design 的配置文件 再也不使用 ?modules 由於樣式是全局的 再也不須要局部樣式 { test: /\.css$/, //modules 模塊化配置 loader: 'style-loader!css-loader?modules' }, ] }, //出口文件 output: { path: __dirname + '/src/', filename: 'bundle.js' }, };
打包命令
// 正常狀況下 webpack //配置熱加載狀況下 webpack-dev-server --inline
使用 --watch
但是自動打包,但不會自動刷新
能夠用content-base
設定 webpack-dev-server
伺服的 directory
(就是指管這個路徑下文件的變更),若是不進行設定的話,默認是在當前目錄下
webpack-dev-server --content-base -src --inline
webpack
爲何使用webpack
現今的不少網頁其實能夠看作是功能豐富的應用,它們擁有着複雜的JavaScript
代碼和一大堆依賴包。爲了簡化開發的複雜度,前端社區涌現出了不少好的實踐方法
TypeScript
這種在JavaScript
基礎上拓展的開發語言,使咱們可以實現目前版本的JavaScript
不能直接使用的特性,而且以後還能能裝換爲JavaScript
文件使瀏覽器能夠識別.Scss
,less
等CSS
預處理器這些改進確實大大的提升了咱們的開發效率,可是利用它們開發的文件每每須要進行額外的處理才能讓瀏覽器識別,而手動處理又是很是繁瑣的,這就爲WebPack
類的工具的出現提供了需求。
webpack
並不強制你使用某種模塊化方案,而是經過兼容全部模塊化方案讓你無痛接入項目,固然這也是webpack
牛逼的地方
webpack
和gulp
的區別
gulp
是工具鏈、構建工具,能夠配合各類插件作js
壓縮,css
壓縮,less
編譯 替代手工實現自動化工做
1.構建工具 2.自動化 3.提升效率用
webpack
是文件打包工具,能夠把項目的各類js
文、css
文件等打包合併成一個或多個文件,主要用於模塊化方案,預編譯模塊的方案
1.打包工具 2.模塊化識別 3.編譯模塊代碼方案
gulp
嚴格上講,模塊化不是他強調的東西,他旨在規範前端開發流程。webpack
更是明顯強調模塊化開發,而那些文件壓縮合並、預處理等功能,不過是他附帶的功能。webpack工做方式
Webpack
的工做方式是:把你的項目當作一個總體,經過一個給定的主文件(如:index.js
),Webpack
將從這個文件開始找到你的項目的全部依賴文件,使用loaders
處理它們,最後打包爲一個瀏覽器可識別的JavaScript
文件。
Loaders
經過使用不一樣的
loader
,webpack
經過調用外部的腳本或工具能夠對各類各樣的格式的文件進行處理,好比說分析JSON
文件並把它轉換爲JavaScript
文件,或者說把下一代的JS
文件(ES6
,ES7
)轉換爲現代瀏覽器能夠識別的JS
文件。或者說對React
的開發而言,合適的Loaders
能夠把React
的JSX
文件轉換爲JS
文件。
Loaders
須要單獨安裝而且須要在webpack.config.js
下的modules
關鍵字下進行配置,Loaders
的配置選項包括如下幾方面:
test
:一個匹配loaders
所處理的文件的拓展名的正則表達式(必須)loader
:loader
的名稱(必須)include/exclude
:手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)(可選);query
:爲loaders
提供額外的設置選項(可選)
// MediaQuery 進行移動端適配 var MediaQuery = require('react-responsive'); <MediaQuery query='(min-device-width:1224px)'>
fetch 向後臺請求數據
響應式 以1224px
爲分界