react看这篇就够了(react+webpack+redux+reactRouter+sass)

Posted 黄大渣渣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)
    })
}

七、项目目录结构

 

以上是关于react看这篇就够了(react+webpack+redux+reactRouter+sass)的主要内容,如果未能解决你的问题,请参考以下文章

入门Webpack,看这篇就够了

入门 Webpack,看这篇就够了

入门Webpack,看这篇就够了

入门Webpack,看这篇就够了

入门Webpack,看这篇就够了

入门Webpack,看这篇就够了