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


本帖將對一下內容進行分享:

1、webpack環境搭建;

2、如何使用react-router;

3、引入sass預編譯;

4、react 性能優化方案;

5、redux結合react使用;

6、fetch使用;

7、項目目錄結構;

一、webpack配置,代碼如下:

1、在根目錄下新建一個webpack.config.js,這個為開發環境的webpack配置;因為得區分開發跟生產環境,所以還得新建一個webpack.production.config.js作為生產環境使用的配置文檔,

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的詳細配置這里就不再詳細說明,有興趣的可以去看下文檔。

二、使用react-router

1、安裝依賴:npm install react-router --save

2、在根目錄下新建router文件夾,然后新建一個routerMap.jsx文件

3、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

4、在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

5、在根目錄下的入口文件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')
);

6、使用菜單鏈接進行路由跳轉

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的使用基本就是這樣,詳細的參數配置可以具體看下文檔

三、使用sass編譯

如果想引入sass預編譯處理,需要安裝兩個依賴,node-sass以及sass-loader,然后在webpack中配置相應的加載器

loaders: [
            { test: /\.scss$/, exclude: /node_modules/, loader: 'style!css!postcss!sass' },
        ]

 這樣就能編譯sass了,可以加快編碼速度。

四、react 性能優化方案

這里將介紹兩種比較常用的性能優化方案

1、性能檢測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()然后打印出浪費性能的組件列表,如下圖所示:

在開發中很有必要檢測組件中的性能情況,如果是浪費幾毫秒或者十幾毫秒,感覺沒有必要再去優化他,畢竟十幾毫秒是很難感覺得出來的。當浪費時間比較多的話,比如幾十毫秒以上,這就有必要值得去優化他了。

2、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: []
        }
    }

3、Immutable.js優化

Immutable.js主要是處理數據的,用的不多,這里不詳細介紹

五、react-redux使用

redux使用分為5步:

1、定義計算規則,即reducers

在根目錄下新建一個reducers的文件夾,里邊新建一個index.js的文件用來放全部的規則文件,比如我需要定義userinfo部分的返回規則,則新建一個userinfo.js文件

然后在userinfo.js里邊定義相應的規則:

這里通過 actionTypes來統一管理這些變量的名稱,一是兩個地方用到,二是為了統一管理,所以單獨起的一個constants的文件,然后里邊有一個userinfo.js的文件

userinfo代碼如下:

然后reducers文件夾下index.js代碼如下

reducers文件夾下index.js就是為了統一管理各個模塊的數據,當有多個js時就可以把他們全部注冊進combineReducers然后統一輸出rootReducer。

 2、生成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
}

3、引用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')
)

4、組件中把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,不明白的可以看下源碼。

 六、fetch使用

1、安裝依賴

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)
    })
}

七、項目目錄結構

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM