《慕课React入门》总结
React 入門與實戰
react組件
虛擬DOM的概念 這是React性能高效的核心算法
React為此引入了虛擬DOM(Virtual DOM)的機制。基于React進行開發時所有的DOM構造都是通過虛擬DOM進行,每當數據變化時,React都會重新構建整個DOM樹,然后React將當前整個DOM樹和上一次的DOM樹進行對比,得到DOM結構的區別,然后僅僅將需要變化的部分進行實際的瀏覽器DOM更新。盡管每一次都需要構造完整的虛擬DOM樹,但是因為虛擬DOM是內存數據,性能是極高的,而對實際DOM進行操作的僅僅是Diff部分,因而能達到提高性能的目的。
React 組件,理解什么是組件化
像插入普通 HTML 標簽一樣,在網頁中插入這個組件
所謂組件,即封裝起來的具有獨立功能的UI部件
- React推薦以組件的方式去重新思考UI構成,將UI上每一個功能相對獨立的模塊定義成組件,然后將小的組件通過組合或者嵌套的方式構成大的組件,最終完成整體UI的構建。
 - 對于React而言,則完全是一個新的思路,開發者從功能的角度出發,將UI分成不同的組件,每個組件都獨立封裝。
 
React一個組件應該具有如下特征:
語法
- 組件名稱一定要大寫
 - 組件只能包含一個頂層標簽
 
React 多組件嵌套
各個組件之間相互獨立,利于維護,重用。在Index文件中進行調用,簡明。
 需要變更時,我們只需要改變相應的組件,所有引用該組件的的頁面就都發生的變更,很好維護。
JXS內置表達式
- HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法,它允許 HTML 與 JavaScript 的混寫。
 - JSX 的基本語法規則:遇到 HTML 標簽(以 < 開頭),就用 HTML 規則解析;遇到代碼塊(以 { 開頭),就用 JavaScript 規則解析
 - JSX 中的注釋 {/*注釋*/}
 
聲明周期,縱觀整個React的生命周期
在ES6中,一個React組件是用一個class來表示的
過程描述
在React中有4中途徑可以觸發render
- 首次渲染Initial Render
 
- 調用this.setState (并不是一次setState會觸發一次render,React可能會合并操作,再一次性進行render)
 
- 父組件發生更新(一般就是props發生改變,但是就算props沒有改變或者父子組件之間沒有數據交換也會觸發render)
 
- 調用this.forceUpdate
 
總結
- constructor、componentWillMount、componentDidMount只有第一次渲染時候會被調用
 - componentWillUpdate、componentDidUpdate、shouldComponentUpdate 在以后的每次更新渲染之后都會被調用
 
考慮到性能的問題,如果有些屬性的變化,不需要重新刷新頁面,我們是使用 componentShouldUpdate() 進行控制。
官方文檔
https://facebook.github.io/re...
參考文獻
http://www.jianshu.com/p/4784...
react屬性與事件
state 屬性控制React的一切
組件自身的狀態,props為外部傳入的狀態
state對組件做了更新之后,會馬上反應到虛擬DOM上,最后更新到DOM上。這個過程是自動完成的。
組件免不了要與用戶互動,React 的一大創新,就是將組件看成是一個狀態機,一開始有一個初始狀態,然后用戶互動,導致狀態變化,從而觸發重新渲染 UI .
export default class BodyIndex extends React.Component{// 初始化constructor(){super();//調用基類的所有初始化方法//state的作用域是當前組件,不會污染其他模塊this.state = {username:"xiaoxiong"};};//修改statesetTimeOut(()=>{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>)} }- 子組件為父組件傳參使用事件
 - 在子頁面中通過調用父頁面傳遞過來的事件 props 進行組件間的參數傳遞
 
就是讓子頁面的變動體現在父頁面上,而頁面狀態的改變由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}}// 父頁面的函數,可以操控父頁面的 statehandler(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>)} }組件的Refs
React中多數情況下,是通過state的變化,來重新刷新頁面。但有時也需要取得html節點,比如對input進行focus等;下面我們來看下,怎么取得原生的html節點,并對其進行操作。
- 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);react樣式
內聯樣式
原生中用 - 連接的樣式屬性,在這里要采用駝峰寫法或加引號"",屬性值一律加引號"" ,這樣的書寫方式,實際上就是加入了內聯樣式,結構和樣式混在一起,不是很好的做法。并且這種寫法,不能使用偽類、動畫。
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進行樣式的引用
問題:
- 怎樣實現css的按需加載,使用這個模塊時,只加載這個模塊的樣式。
 - 模塊之間的樣式是相互獨立的,即使使用相同的類名,也不會相互影響。
 
-
使用webpack,安裝相關插件
//使用require導入css文件 npm install css-loader --save //計算之后的樣式加入頁面中 npm install style-loader --savewebpack.config.js文件中進行配置
將css文件導入相應的組件后,會生成一個對應關系
_footer_minifooter_vxs08s是按照一定的規則生成一個唯一的類名,當我們為當前組件應用 .footer 類的時候,就會按照這個對應關系去樣式文件中找,然后應用響應的樣式。
實際上是改變了樣式文件中類的名稱,使其唯一。
這樣即使將所有文件打包到一起,也不會引起沖突。
不同的文件可以 使用相同的類名,不會沖突,因為模塊化之后,類名都進行了轉換。
使用
//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模塊化
- 可以避免全局污染、命名混亂
 
模塊化的優點
- 所有的樣式都是local的,解決的命名沖突和全局污染的問題
 - class名生成規則配置靈活,可以以此來壓縮class名(在webpack.config.js文件中配置)
 - 使用某個組件時,只要import組件,再使用即可,無需管理樣式。
 - 不需要像書寫內部樣式一樣,屬性的名稱需要駝峰寫法。只需在webpack.config.js中進行配置,書寫時,還是我們熟悉的css。
 
JSX樣式與CSS樣式互轉
線上轉換工具
http://staxmanade.com/CssToRe...
react-router
Router 概念
控制頁面之間的層級關系
底層機制
通過狀態的改變,導致組件從新渲染,從而改變頁面顯示
通過改變url,導致Router變化,從而改變頁面顯示
React:location(hasj) -> Router ->UIhashHistory && browserHistory
慕課老師的demo使用的是hashHistory,而另一種方式則是使用browserHistory。
如果希望使用browserHistory達到hashHistory的效果,則需要做2件事情:
1、服務器支持。如果開發服務器使用的是webpack-dev-server,加上--history-api-fallback參數就可以了。
2、作為項目入口的HTML文件中,相關外鏈的資源路徑都要改為絕對路徑,即以"/"根路徑開頭。
安裝
// 版本 2.8.1 npm install react-router使用
component指定組件 
path 指定路由的匹配規則
router可以進行嵌套
ComponentDetails嵌套在Index頁面中,我們要在Index中進行展示。
有了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配置React環境
//初始化 建立初始化文件 npm initpackage.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"} }安裝依賴
babelify
Babel其實是一個編譯JavaScript的平臺,它的強大之處表現在可以通過編譯幫你達到以下目的:
- 下一代的JavaScript標準(ES6,ES7),這些標準目前并未被當前的瀏覽器完全的支持
 - 使用基于JavaScript進行了拓展的語言,比如React的JSX
 
babel-preset-react react轉碼規則
babel-preset-es2015 ES2015轉碼規則
使用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",//loadersmodule:{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為分界
總結
以上是生活随笔為你收集整理的《慕课React入门》总结的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: valgrind基础
 - 下一篇: Angular组件--动态组件