本帖將對一下內容進行分享:css
一、webpack環境搭建;html
二、如何使用react-router;node
三、引入sass預編譯;react
四、react 性能優化方案;webpack
五、redux結合react使用;git
六、fetch使用;es6
七、項目目錄結構;github
1、webpack配置,代碼以下:web
一、在根目錄下新建一個webpack.config.js,這個爲開發環境的webpack配置;由於得區分開發跟生產環境,因此還得新建一個webpack.production.config.js做爲生產環境使用的配置文檔,npm
webpack.config.js代碼以下:
var path = require('path') var webpack = require('webpack') var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); var OpenBrowserPlugin = require('open-browser-webpack-plugin'); // var nodeModulesPath = path.resolve(__dirname, 'node_modules') // console.log(process.env.NODE_ENV) module.exports = { entry: path.resolve(__dirname, 'app/index.jsx'), output: { path: __dirname + "/build", filename: "bundle.js" }, resolve:{ extensions:['', '.js','.jsx'] }, module: { // preLoaders: [ // // 報錯 ????? // {test: /\.(js|jsx)$/, loader: "eslint-loader", exclude: /node_modules/} // ], loaders: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel' }, { test: /\.scss$/, exclude: /node_modules/, loader: 'style!css!postcss!sass' }, { test: /\.css$/, exclude: /node_modules/, loader: 'style!css!postcss' }, { test:/\.(png|gif|jpg|jpeg|bmp)$/i, loader:'url-loader?limit=10000' }, // 限制大小10kb { test:/\.(png|woff|woff2|svg|ttf|eot)($|\?)/i, loader:'url-loader?limit=10000'} // 限制大小小於10k ] }, eslint: { configFile: '.eslintrc' // Rules for eslint }, postcss: [ require('autoprefixer') //調用autoprefixer插件,例如 display: flex ], plugins: [ // html 模板插件 new HtmlWebpackPlugin({ template: __dirname + '/app/index.tmpl.html' }), // 熱加載插件 new webpack.HotModuleReplacementPlugin(), // 打開瀏覽器 new OpenBrowserPlugin({ url: 'http://localhost:8888' }), // 可在業務 js 代碼中使用 __DEV__ 判斷是不是dev模式(dev模式下能夠提示錯誤、測試報告等, production模式不提示) new webpack.DefinePlugin({ __DEV__: JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false')) }) ], devServer: { /*proxy: { // 凡是 `/api` 開頭的 http 請求,都會被代理到 localhost:3000 上,由 koa 提供 mock 數據。 // koa 代碼在 ./mock 目錄中,啓動命令爲 npm run mock '/api': { target: 'http://localhost:3000', secure: false } },*/ port: 8888, contentBase: "./public", //本地服務器所加載的頁面所在的目錄 colors: true, //終端中輸出結果爲彩色 historyApiFallback: true, //不跳轉 inline: true, //實時刷新 hot: true // 使用熱加載插件 HotModuleReplacementPlugin } }
webpack.production.config.js代碼以下:
var path = require('path') var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { entry: { app: path.resolve(__dirname, 'app/index.jsx'), // 將 第三方依賴 單獨打包 vendor: [ 'react', 'react-dom', 'react-redux', 'react-router', 'redux', 'es6-promise', 'whatwg-fetch', 'immutable' ] }, output: { path: __dirname + "/build", filename: "[name].[chunkhash:8].js", publicPath: '/' }, resolve:{ extensions:['', '.js','.jsx'] }, module: { loaders: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, loader: 'babel' }, { test: /\.less$/, exclude: /node_modules/, loader: ExtractTextPlugin.extract('style', 'css!postcss!less') }, { test: /\.css$/, exclude: /node_modules/, loader: ExtractTextPlugin.extract('style', 'css!postcss') }, { test:/\.(png|gif|jpg|jpeg|bmp)$/i, loader:'url-loader?limit=5000&name=img/[name].[chunkhash:8].[ext]' }, { test:/\.(png|woff|woff2|svg|ttf|eot)($|\?)/i, loader:'url-loader?limit=5000&name=fonts/[name].[chunkhash:8].[ext]'} ] }, postcss: [ require('autoprefixer') ], plugins: [ // webpack 內置的 banner-plugin new webpack.BannerPlugin("Copyright by wangfupeng1988@github.com."), // html 模板插件 new HtmlWebpackPlugin({ template: __dirname + '/app/index.tmpl.html' }), // 定義爲生產環境,編譯 React 時壓縮到最小 new webpack.DefinePlugin({ 'process.env':{ 'NODE_ENV': JSON.stringify(process.env.NODE_ENV) } }), // 爲組件分配ID,經過這個插件webpack能夠分析和優先考慮使用最多的模塊,併爲它們分配最小的ID new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({ compress: { //supresses warnings, usually from module minification warnings: false } }), // 分離CSS和JS文件 new ExtractTextPlugin('[name].[chunkhash:8].css'), // 提供公共代碼 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: '[name].[chunkhash:8].js' }), // 可在業務 js 代碼中使用 __DEV__ 判斷是不是dev模式(dev模式下能夠提示錯誤、測試報告等, production模式不提示) new webpack.DefinePlugin({ __DEV__: JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev') || 'false')) }) ] }
在開發環境中跑webpack.config.js,打包的時候跑webpack.production.config.js的代碼
全部在package.json中分別設置命令:
在window系統下:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "set NODE_ENV=dev && webpack-dev-server --progress --colors", "build": "rd/s/q build && NODE_ENV=production && webpack --config ./webpack.production.config.js --progress --colors" },
在Mac系統下:
"scripts": { "dev": "NODE_ENV=dev webpack-dev-server --progress --colors", "build": "rm -rf ./build && NODE_ENV=production webpack --config ./webpack.production.config.js --progress --colors" },
全部當運行npm run dev 的時候就跑開發環境的代碼,當運行指令npm run build 的時候就會跑webpack.production.config.js進行打包。
關於webpack的詳細配置這裏就再也不詳細說明,有興趣的能夠去看下文檔。
2、使用react-router
一、安裝依賴:npm install react-router --save
二、在根目錄下新建router文件夾,而後新建一個routerMap.jsx文件
三、routerMap.jsx代碼以下
import React from 'react' import { Router, Route, IndexRoute } from 'react-router' import App from '../containers/App' import Home from '../containers/HomePage/Home' import List from '../containers/ListPage/List' import NotFound from '../containers/NotFound/Notfound' class RouteMap extends React.Component{ render(){ return( <Router history={this.props.history}> <Route path='/' component={App}> <IndexRoute component={Home}/> <Route path='list' component={List}/> <Route path="*" component={NotFound}/> </Route> </Router> ) } } export default RouteMap
四、在App.jsx裏引用相應的路由切換組件{this.props.children}
import React from 'react' import Head from '../components/head/head' import Menu from '../components/Menu/menu' class App extends React.Component { render() { return ( <div> <Head/> <div className="wrap"> <div className="menu"> <Menu/> </div> <div>{this.props.children}</div> </div> </div> ) } } export default App
五、在根目錄下的入口文件index.jsx引入routerMap,
import React from 'react'; import ReactDOM from 'react-dom'; import RouteMap from './router/routerMap' import {hashHistory} from 'react-router' import './static/index.scss' ReactDOM.render( <RouteMap history={hashHistory}/>, document.getElementById('root') );
六、使用菜單連接進行路由跳轉
import React from 'react' import { Link } from 'react-router' class Menu extends React.Component{ render(){ return( <div className="menu"> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/list" activeClassName="active">List</Link> </li> </ul> </div> ) } } export default Menu
react-router的使用基本就是這樣,詳細的參數配置能夠具體看下文檔
3、使用sass編譯
若是想引入sass預編譯處理,須要安裝兩個依賴,node-sass以及sass-loader,而後在webpack中配置相應的加載器
loaders: [ { test: /\.scss$/, exclude: /node_modules/, loader: 'style!css!postcss!sass' }, ]
這樣就能編譯sass了,能夠加快編碼速度。
4、react 性能優化方案
這裏將介紹兩種比較經常使用的性能優化方案
一、性能檢測react-addons-perf
安裝react 性能檢測工具 npm install react-addons-perf --save,而後在./app/index.jsx中使用依賴:
// 性能測試 import Perf from 'react-addons-perf' if (__DEV__) { window.Perf = Perf }
if(__DEV__)是指在Dev環境下使用這個性能檢測,而後把他賦到widow對象下。
使用方法:
在操做程序以前先在控制檯中輸入Perf.start()開始監聽,啓動檢測;而後操做程序,在進行若干操做以後輸入Perf.stop()中止檢測,而後再跑Perf.printWasted()而後打印出浪費性能的組件列表,以下圖所示:
在開發中頗有必要檢測組件中的性能狀況,若是是浪費幾毫秒或者十幾毫秒,感受沒有必要再去優化他,畢竟十幾毫秒是很難感受得出來的。當浪費時間比較多的話,好比幾十毫秒以上,這就有必要值得去優化他了。
二、PureRenderMixin優化
React最基本的優化方案就是用PureRenderMixin,安裝依賴:npm install react-addons-pure-render-mixin --save;
PureRenderMixin使用原理:
react有一個生命週期函數叫作shouldComponentUpdata,爲此組件更新以前,這個函數都會返回true,默認狀況下都是返回true,當返回false則組件不更新,因此,當組件的state或者props變化的時候應該返回true,但這兩個值不變化的時候則返回false,因此咱們不能一直讓這個函數返回true,實際的開發中組件會受一些其餘因素的影響當state或者props不變化的時候也更新,這是須要咱們去阻止的,因此要重寫shouldComponentUpdata這個函數,每次更新的時候判斷props和state這兩個屬性,有變化則返回true,無變化則返回false;
使用方法:
import PureRenderMixin from 'react-addons-pure-render-mixin' constructor(props, context) { super(props, context); this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this); this.state = { todos: [] } }
三、Immutable.js優化
Immutable.js主要是處理數據的,用的很少,這裏不詳細介紹
5、react-redux使用
redux使用分爲5步:
一、定義計算規則,即reducers
在根目錄下新建一個reducers的文件夾,裏邊新建一個index.js的文件用來放所有的規則文件,好比我須要定義userinfo部分的返回規則,則新建一個userinfo.js文件
而後在userinfo.js裏邊定義相應的規則:
這裏經過 actionTypes來統一管理這些變量的名稱,一是兩個地方用到,二是爲了統一管理,因此單獨起的一個constants的文件,而後裏邊有一個userinfo.js的文件
userinfo代碼以下:
而後reducers文件夾下index.js代碼以下
reducers文件夾下index.js就是爲了統一管理各個模塊的數據,當有多個js時就能夠把他們所有註冊進combineReducers而後統一輸出rootReducer。
二、生成store
在根目錄下新建一個store文件夾,裏邊新建一個文件configureStore.js,代碼以下:
import { createStore } from 'redux' import rootReducer from '../reducers' //引入第一步生成的規則 export default function configureStore(initialState) { const store = createStore(rootReducer, initialState, // 觸發 redux-devtools window.devToolsExtension ? window.devToolsExtension() : undefined ) return store }
三、引用store,監聽變化
在根目錄下入口文件index.jsx中引入store,而後用provide包裹着組件:
import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import configureStore from './store/configureStore' import Hello from './containers/Hello' const store = configureStore() render( <Provider store={store}> <Hello/> </Provider>, document.getElementById('root') )
四、組件中把state值賦予到props,看hello.jsx的代碼:
import React from 'react' import { connect } from 'react-redux' import { bindActionCreators } from 'redux' import * as userinfoActions from '../actions/userinfo' import A from '../components/A' import B from '../components/B' import C from '../components/C' class Hello extends React.Component { render() { return ( <div> <p>hello world</p> <hr/> <A userinfo={this.props.userinfo}/> <hr/> <B userinfo={this.props.userinfo}/> <hr/> <C actions={this.props.userinfoActions}/> </div> ) } componentDidMount() { // 模擬登錄 this.props.userinfoActions.login({ userid: 'abc', city: 'beijing' }) } } //把state賦予到props->userinfo function mapStateToProps(state) { return { userinfo: state.userinfo } } //第五步:定義事件,觸發action或者更改state的值,而後把事件賦予到props function mapDispatchToProps(dispatch) { return { userinfoActions: bindActionCreators(userinfoActions, dispatch) } } //經過connect函數把兩個方法跟組件聯繫到一塊兒而後輸出 export default connect( mapStateToProps, mapDispatchToProps )(Hello)
5.若是想改變reduce裏邊state的值,那麼就須要經過事件去dispatch action;
這裏定義了一些action事件:
而後在組件中經過import * as userinfoActions from '../actions/userinfo'進來,接着經過函數
賦予到props userinfoActions 中;
接着就是使用:
componentDidMount() { // 模擬登錄 this.props.userinfoActions.login({ userid: 'abc', city: 'beijing' }) }
這裏reduce的5步就總結完了,說的有點籠統,具體的方法建議仍是先看下文檔,這裏將不進行細節方法介紹。
reduce代碼已經方法GitHub上邊:https://github.com/huangjia727461876/react-reduce.git,不明白的能夠看下源碼。
6、fetch使用
一、安裝依賴
npm install whatwg-fetch --save
npm install es6-promise --save
在目錄下起一個fetch的文件夾
而後建兩個文件,一個get.js和post.js分別定義這兩個方法
get.js代碼以下:
import 'whatwg-fetch' import 'es6-promise' export function get(url) { var result = fetch(url, { credentials: 'include', headers: { 'Accept': 'application/json, text/plain, */*' } }); return result; }
post.js代碼以下:
import 'whatwg-fetch' import 'es6-promise' // 將對象拼接成 key1=val1&key2=val2&key3=val3 的字符串形式 function obj2params(obj) { var result = ''; var item; for (item in obj) { result += '&' + item + '=' + encodeURIComponent(obj[item]); } if (result) { result = result.slice(1); } return result; } // 發送 post 請求 export function post(url, paramsObj) { var result = fetch(url, { method: 'POST', credentials: 'include', headers: { 'Accept': 'application/json, text/plain, */*', 'Content-Type': 'application/x-www-form-urlencoded' }, body: obj2params(paramsObj) }); return result; }
用法:
import { get } from './get.js' import { post } from './post.js' export function getData() { // '/api/1' 獲取字符串 var result = get('/api/1') result.then(res => { return res.text() }).then(text => { console.log(text) }) // '/api/2' 獲取json var result1 = get('/api/2') result1.then(res => { return res.json() }).then(json => { console.log(json) }) } export function postData() { // '/api/post' 提交數據 var result = post('/api/post', { a: 100, b: 200 }) result.then(res => { return res.json() }).then(json => { console.log(json) }) }
7、項目目錄結構