react看這篇就夠了(react+webpack+redux+reactRouter+sass)

本帖將對一下內容進行分享: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'進來,接着經過函數

function mapDispatchToProps(dispatch) {
return {
userinfoActions: bindActionCreators(userinfoActions, dispatch)
}
}

 賦予到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、項目目錄結構

相關文章
相關標籤/搜索