轉自http://www.cnblogs.com/chenziyu-blog/p/5675086.html
參考http://www.tuicool.com/articles/BrAVv2y
React已成為前端當下最熱門的前端框架之一 , 其虛擬DOM和組件化開發讓前端開發更富靈活性,而Webpack憑借它異步加載和可分離打包等優秀的特性,更為React的開發提供了便利。其優秀的特性不再贅述。本文將詳細的記錄react babel webpack的環境搭建,以及搭建的過程中遇到的一些坑。
一、新建React項目
1、如圖為新建react項目結構,其中 entry.js放置react入口代碼,index.js放置react組件代碼,assets文件是webpack打包之后生成文件的存放地址。 由於react代碼需要jsx來寫,所以我們修改編輯器里的配置。我使用的是mac版的webstorm,在如下圖地方修改jsx提示配置
首先你得安裝npm 同時命令行輸入,新建node_modules
npm install npm init
2、輸入如下命令安裝react模塊
npm i --save react
由於react升級后把reactDOM獨立出來 ,所以我們還需要安裝 react-dom
npm install react-dom --save-dev
具體react代碼在安裝完webpack后介紹
二、安裝webpack
1、什么是Webpack?
事實上它是一個打包工具,而不是像RequireJS或SeaJS這樣的模塊加載器,通過使用Webpack,能夠像Node.js一樣處理依賴關系,然后解析出模塊之間的依賴,將代碼打包
2、安裝webpack
全局安裝
sudo npm install -g webpack
本項目安裝
npm install webpack --save-dev
3、webpack配置
每個項目下都必須配置有一個 webpack.config.js ,它的作用如同常規的 gulpfile.js/Gruntfile.js ,就是一個配置項,告訴 webpack 它需要做什么。 在根目錄新建js文件 webpack.config.js 文件內容大概如下
/** * Created by mac on 16/7/14. */ var webpack = require('webpack'); var path = require('path'); module.exports = { //頁面入口文件配置 entry: { index: [ 'webpack-dev-server/client?http://localhost:5000', 'webpack/hot/only-dev-server', './js/entry.js' ] }, //入口文件輸出配置 output: { path: __dirname + '/assets/', filename: 'bundle.js' }, module: { //加載器配置 loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' }, { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }, { test: /\.js|jsx$/, loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react,presets[]=stage-0'], include: path.join(__dirname, 'js') } ] }, //其它解決方案配置 resolve: { extensions: ['', '.js', '.json', '.scss'] }, //插件項 plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ] };
這里對Webpack的打包行為做了配置,主要分為幾個部分:
entry:指定打包的入口文件,每有一個鍵值對,就是一個入口文件 output:配置打包結果,path定義了輸出的文件夾,filename則定義了打包結果文件的名稱
resolve:定義了解析模塊路徑時的配置,常用的就是extensions,可以用來指定模塊的后綴,這樣在引入模塊時就不需要寫后綴了,會自動補全
module:定義了對模塊的處理邏輯,這里可以用loaders定義了一系列的加載器,以及一些正則。當需要加載的文件匹配test的正則時,就會調用后面的loader對文件進行處理,這正是webpack強大的原因。比如這里定義了凡是 .js 結尾的文件都是用 babel-loader 做處理,而 .jsx 結尾的文件會先經過 jsx-loader 處理,然后經過 babel-loader 處理。當然這些loader也需要通過 npm install 安裝
plugins: 這里定義了需要使用的插件,這里使用了react-hot會在之后介紹
安裝loader示例
npm install jsx-loader --save-dev
在我的webpack.config文件中,文件入口為 js/entry.js,entry中的index數組為之后使用的react-hot配置,若不使用插件則為
entry: { index: './js/entry.js' },
打包后的出口為assets/bundle.js
三、安裝babel
1、為什么babel
能夠實現 ES6 到 ES5 的代碼轉換多虧了 Babel (以前叫 6to5) 以及 Traceur 之類的項目。這些轉換器 (更准確地說是源代碼到源代碼的編譯器) 可以把你寫的符合 ECMAScript 6 標准的代碼完美地轉換為 ECMAScript 5 標准的代碼,並且可以確保良好地運行在所有主流 JavaScript 引擎中。
2、安裝babel
npm install -g babel
完成后可用命令`sudo babel --help`查看是否安裝成功
Babel提供babel-cli工具,用於命令行轉碼。
npm install babel-cli --save-dev
如果某些代碼需要調用Babel的API進行轉碼,就要使用babel-core模塊。
npm install babel-core --save-dev
Babel的配置文件是.babelrc,存放在項目的根目錄下。使用Babel的第一步,就是配置這個文件。 首先新建.babelrc文件 文件默認如下
{ "presets": [], "plugins": [] }
presets字段設定轉碼規則,官方提供以下的規則集,你可以根據需要安裝。
# ES2015轉碼規則 $ npm install --save-dev babel-preset-es2015 # react轉碼規則 $ npm install --save-dev babel-preset-react # ES7不同階段語法提案的轉碼規則(共有4個階段),選裝一個 $ npm install --save-dev babel-preset-stage-0 $ npm install --save-dev babel-preset-stage-1 $ npm install --save-dev babel-preset-stage-2 $ npm install --save-dev babel-preset-stage-3
之后在.babelrc文件中加入配置
{ "presets": [ "es2015", "react", "stage-0" ], "plugins": []
}
同時安裝babel-loader
npm install babel-loader --save-dev
這時在webpack.config文件中loader中加入配置,讓打包時加載babel
{ test: /\.js|jsx$/, loaders: ['babel?presets[]=es2015,presets[]=react,presets[]=stage-0'] },
注意!這里presets的順序要和.babelrc的順序一致
自此 模塊的安裝到此結束。
四、react代碼
接下來,我們來看看具體的demo代碼
1、index.html
<!DOCTYPE html> <head> <script src="build/jquery-2.1.4.js"></script> <script src="bootstrap-3.3.5-dist/js/bootstrap.min.js"></script> <link rel="stylesheet" href="css/css.css"> <script src="build/JSXTransformer.js"></script> <link rel="stylesheet" href="bootstrap-3.3.5-dist/css/bootstrap.min.css"> </head> <body> <div id="react"></div> <script type="text/jsx" src="assets/bundle.js"></script> </body> </html>
我們引入最基本的jquery,bootstrap,其中注意的是需要引入JSXTransformer.js用來把jsx代碼代碼轉換成瀏覽器讀得懂的js代碼。
之后我們需要引入的是webpack打包完成之后的bundle.js文件。
2、index.js
/** * Created by mac on 16/7/15. */ //組件類的第一個字母必須大寫,否則會報錯,比如HelloMessage不能寫成helloMessage。另外,組件類只能包含一個頂層標簽,否則也會報錯。例如return <h1 className="mt10 ml10">chen</h1> <p>wang</p> import React from 'react' class RefExa extends React.Component { constructor(props) { super(props); this.state = { value: 'Hello!', foo: '', bar: '', opacity: 1 } } handleClick() { this.refs.myInputEl.focus(); return true } handleChange() { this.setState({value: event.target.value}); console.log(this.state.value); } componentWillMount() { console.log('WillMount') } componentDidMount() { console.log('DidMount'); var that = this; this.setState({ foo: 'Hello', bar: 'React' }); this.timer = setInterval(function() { var opacity = this.state.opacity; opacity -= 0.1; if(opacity < 0) { clearInterval(that.timer); } this.setState({ opacity: opacity }) }.bind(this),1000) } stopTimer() { clearInterval(this.timer); this.setState({ opacity: 1 }) } componentDidUpdate(props,states) { console.log(props); console.log(states); } render() { console.log('render'); var value = this.state.value; return ( <form name="form" noValidate> <div className="form-group"> <label className="control-label col-sm-2">operate virtualDom</label> <div className="col-sm-9"> <input placeholder="focus" type="text" name="myInputEl" onChange={this.handleChange.bind(this)} value={value} ref="myInputEl" /> </div> </div> <footer className="col-sm-12"> <button onClick={this.handleClick.bind(this)} className="btn btn-success">focus {this.props.from}</button> </footer> <h2 style={{opacity: this.state.opacity}} className="col-sm-1">{this.state.foo}</h2> <h2 className="col-sm-11">{this.state.bar}</h2> </form> ) } } class ParentNode extends React.Component { constructor(props) { super(props); this.state = { foo: 'hello' } } handleClick() { this.refs.childNodeRef.stopTimer(); } render() { var fooArr = ['foo','bar','baz']; return ( <div className="col-sm-12"> <RefExa from="input" ref="childNodeRef"></RefExa> <button className="btn btn-default" onClick={this.handleClick.bind(this)}>stop childNode timer</button> { fooArr.map(function(v,i) { return <button key={i} className="ml10 btn btn-danger">{v}{i}</button> }) } </div> ) } } export default ParentNode //子節點通過props獲取父節點數據 父節點通過ref控制子節點
這里是用es6寫法的react代碼,首先import React 引入加載好的react模塊,具體代碼邏輯不再贅述,其中值得注意的是
1、使用map循環渲染時需要加上key={i},使react能夠按照index渲染
2、 es6寫法中 需要在綁定事件時在之后加上bind(this) ,例如
onChange = {this.handleChange.bind(this)}
3、最后 需要export default <component Name>用於暴露組件
3、entry.js
import React from 'react'; import ReactDom from 'react-dom'; import ParentNode from './index.js'; ReactDom.render( <ParentNode></ParentNode>, document.getElementById('react') );
引入React ReactDom 和寫好的組件ParentNode
之后 ReactDom.render 渲染組件
最后在命令行輸入
webpack --display-error-details
打包項目,--display-error-details用於查看報錯詳情
最后我們在瀏覽器中打開項目
五、超贊的react插件 react-hot
Webpack本身具有運行時模塊替換功能,稱之為Hot Module Replacement (HMR)。當某個模塊代碼發生變化時,Webpack實時打包將其推送到頁面並進行替換,從而無需刷新頁面就實現代碼替換。這個過程相對比較復雜,需要進行多方面考慮和配置。而現在針對React出現了一個第三方react-hot-loader加載器,使用這個加載器就可以輕松實現React組件的熱替換,非常方便。其實正是因為React的每一次更新都是全局刷新的虛擬DOM機制,讓React組件的熱替換可以成為通用的加載器,從而極大提高開發效率。
1、安裝react-hot 和 webpack-dev-server
npm install --save-dev webpack-dev-server react-hot-loader
通過這種方式指定資源熱啟動對應的服務器,然后需要配置 react-hot-loader
到loaders的配置當中,比如我的所有組件代碼全部放在scripts文件夾下:
{ test: /\.js|jsx$/, loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react,presets[]=stage-0'], include: path.join(__dirname, 'js') }
並且需要引入path
var path = require('path');
同時引入插件
plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ]
入口配置應為
entry: { index: [ 'webpack-dev-server/client?http://localhost:5000', 'webpack/hot/only-dev-server', './js/entry.js' ] },
這時完整的webpack.config.js文件如下
/** * Created by mac on 16/7/14. */ var webpack = require('webpack'); var path = require('path'); module.exports = { //頁面入口文件配置 entry: { index: [ 'webpack-dev-server/client?http://localhost:5000', 'webpack/hot/only-dev-server', './js/entry.js' ] }, //入口文件輸出配置 output: { path: __dirname + '/assets/', filename: 'bundle.js' }, module: { //加載器配置 loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' }, //{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }, { test: /\.js|jsx$/, loaders: ['babel?presets[]=es2015,presets[]=react,presets[]=stage-0'] }, { test: /\.js|jsx$/, loaders: ['react-hot', 'babel?presets[]=es2015,presets[]=react,presets[]=stage-0'], include: path.join(__dirname, 'js') } ] }, //其它解決方案配置 resolve: { extensions: ['', '.js', '.json', '.scss'] }, //插件項 plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ] };
2根目錄新建server.js文件
var webpack = require('webpack'); var WebpackDevServer = require('webpack-dev-server'); var config = require('./webpack.config'); new WebpackDevServer(webpack(config), { publicPath: config.output.publicPath, hot: true, noInfo: false, historyApiFallback: true }).listen(5000, '127.0.0.1', function (err, result) { if (err) { console.log(err); } console.log('Listening at localhost:5000'); });
此時端口號應與webpack.config.js中entry的端口號一致
大功告成后 命令行執行
webpack --display-error-details node server.js
之后在瀏覽器中輸入http://localhost:5001/index.html
即可查看項目,這時修改代碼,不需要刷新就可以時時重新渲染,是不是非常便利的功能!