zoukankan      html  css  js  c++  java
  • 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)
        })
    }

    七、项目目录结构

  • 相关阅读:
    Sandcastle 这个工具生成文档不错
    Windows 服务关闭自动重启
    『录』最全前端资源汇集
    利用Continuous Testing实现Eclipse环境自动单元测试
    (转载)const指针和指向const的指针(左值右指)
    为什么寄存器比内存快?
    Vim Buffer
    Linux操作系统文件系统基础知识详解(引用内容)
    详解BOM头以及去掉BOM头的方法
    对比MySQL,什么场景MongoDB更适用
  • 原文地址:https://www.cnblogs.com/superSmile/p/8195040.html
Copyright © 2011-2022 走看看