初学者的React全家桶完整实例

概述

该项目还有些功能在开发过程当中,若是您有什么需求,欢迎您与我联系。我但愿可以经过这个项目对React初学者,或者Babel/webpack初学者都有必定的帮助。我在此再强调一下,在我写的这些文章末尾都附加了不少参考文献,而这些参考文献的做用对个人帮助真的很大,在此表示感谢!!!!!

详细

写在前面的话javascript

自已之前对redux,React,rect-redux,react-router都是有一点的了解,而且在真实的项目中也多少有些涉及。可是不足的地方在于没有作一个demo将他们串起来,因此老是感受似懂非懂。特别是react服务端渲染这一块,对于本身彻底就是一个黑箱,这对我深刻理解react同构等稍微难一点的内容产生了很大的影响。因此我最后写了这个例子,但愿有一样困扰的同窗可以有所收获。也欢迎star,issue。css

不得不说,当你真实的去作一个项目的时候,哪怕是一个小小的demo,这都会彻底颠覆你对React生态的认识。从一开始的不知道如何入手,到遇到各类困难,而后各类google,最后解决问题,你会发现本身是真的在成长。遇到的问题以及解决方案,我在文章列表中也给出了。时间+经历=成长,对于我来讲就够了。默默的对本身说一句,加油把少年!html

一、项目说明

首先下载该项目,而后直接运行下面的命令。前端

npm install
npm run dev
//npm run pro
//生产模式下执行注释部分命令

打开http://localhost:3222/ 就能够看到效果。项目截图以下:java

这里写图片描述

二、项目基本知识点

2.1 代理与反代理的基本内容

使用http-proxy来完成。其反向代理的原理以下图:node

这里写图片描述

经过以下代码完成,其至关于一个反向代理服务器,向咱们的代理服务器,即API服务器发送请求:react

const targetUrl = 'http://' + (process.env.APIHOST||"localhost") + ':' + (process.env.APIPORT||"8888");//其中APIHOST和APIPORT分别表示API服务器运行的域名与端口号const proxy = httpProxy.createProxyServer({
  target:targetUrl,
  ws:true
  //反代理服务器与服务器之间支持webpack socket});
app.use("/api",(req,res)=>{
  proxy.web(req,res,{target:targetUrl});
});
app.use('/ws', (req, res) => {
  proxy.web(req, res, {target: targetUrl + '/ws'});
});

2.2 react全家桶常见库

react-router,react,redux,react-redux,redux-async-connect,redux-thunk等一系列react相关的基本内容。其中最重要的就是咱们的redux-async-connect,他能够在跳转到某个页面以前或者以后发起某一个ajax请求。用法以下:linux

@asyncConnect([{  //其中helpers来自于服务端渲染
  promise: ({store: {dispatch, getState},helpers}) => {  const promises = [];  const state = getState();  //获得store的当前状态
  if(!isInfoLoaded(state)){
    promises.push(dispatch(loadInfo()));
  }  if(!isAuthLoaded(state)){
    promises.push(dispatch(loadAuth()));
  }  //若是没有登陆或者相应的数据没有加载完成,那么咱们在此时加载数据
   return Promise.all(promises);
  }
}])

其中helpers方法来自于其服务端渲染的loadOnServer方法:webpack

   loadOnServer({...renderProps, store, helpers: {client}}).then(() => {        const component = (          <Provider store={store} key="provider">
            <ReduxAsyncConnect {...renderProps} />
          <\/Provider>
        )
        res.status(200);
        global.navigator = {userAgent: req.headers['user-agent']};
        res.send('<!doctype html>\n' +
          renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}\/>));
      });

2.3 自定义bootstrap

使用bootstrap-loader来加载自定义的bootstrap文件(.bootstraprc),从而减少打包后文件的大小。咱们经过在项目目录下创建.bootstraprc文件,该文件能够指定咱们须要使用的bootstrap样式,是否使用JavaScript等。如经过下面的配置:git

scripts: false

就能够在当前应用中不引入bootstrap的javascript,而只是单独使用样式。若是在单独使用样式的状况下咱们能够结合react-bootstrap,react-router-bootstrap来完成页面的各类交互。若是你要单独使用这部分的内容,你能够参考这里

2.4 webpack的HMR功能集成

使用webpack实现HMR(react-transform-hmr)等基本功能,以及介绍了webpack-dev-middleware,webpack-hot-middleware等的使用。

 babelReactTransformPlugin[1].transforms.push({
      transform: 'react-transform-hmr',
      imports: ['react'],
      locals: ['module']
    });

若是你想深刻了解HMR,你也能够参考这里

2.5 redux开发工具使用

redux-devtools,redux-devtools-dock-monitor,redux-devtools-log-monitor等redux开发工具的使用。只须要添加下面的一段代码就能够了:

import React from 'react';
import { createDevTools } from 'redux-devtools';
import SliderMonitor   from "redux-slider-monitor";
import LogMonitor from 'redux-devtools-log-monitor';
import DockMonitor from 'redux-devtools-dock-monitor';
export default createDevTools(  <DockMonitor changeMonitorKey='ctrl-m' defaultPosition="right"  toggleVisibilityKey="ctrl-H"
               changePositionKey="ctrl-Q">
    <LogMonitor />
    <SliderMonitor  keyboardEnabled />
  <\/DockMonitor>);

固然,若是要添加这部分代码要作一个判断:

if (__DEVELOPMENT__ && __CLIENT__ && __DEVTOOLS__) {    const { persistState } = require('redux-devtools');    const DevTools = require('../containers/DevTools/DevTools');
    finalCreateStore = compose(
      applyMiddleware(...middleware),
      window.devToolsExtension ? window.devToolsExtension() : DevTools.instrument(),      //若是有window.devToolsExtension,那么使用用户本身的,不然使用咱们配置的
      persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
    )(_createStore);
  }

也就是说咱们只会在开发模式下,同时客户端代码(服务端显然是不须要的,该工具只是为了在客户端查看当前state的状态)中,以及DEVTOOLS为true中才会添加咱们的devTool工具。

2.6 react服务器端同构

服务端同构是react开发中不可避免的问题,由于服务端渲染在必定程度上可以减小首页白屏的时间,同时对于SEO也具备很重要的做用。React中关于服务端渲染的介绍只是给出一个match方法,而更加深刻的知识却要本身反复琢磨。

match({ history, routes: getRoutes(store), location: req.originalUrl }, (error, redirectLocation, renderProps) => {    if (redirectLocation) {
      res.redirect(redirectLocation.pathname + redirectLocation.search);      //重定向要添加pathname+search
    } else if (error) {
      console.error('ROUTER ERROR:', pretty.render(error));
      res.status(500);
      hydrateOnClient();
    } else if (renderProps) {
      loadOnServer({...renderProps, store, helpers: {client}}).then(() => {        const component = (          <Provider store={store} key="provider">
            <ReduxAsyncConnect {...renderProps} />
          <\/Provider>
        );
        res.status(200);
        global.navigator = {userAgent: req.headers['user-agent']};
        res.send('<!doctype html>\n' +
          renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}\/>));
      });
    } else {
      res.status(404).send('Not found');
    }
  });
});

针对这部份内容我写了react服务端渲染中的renderProps与react-data-checksum以及React服务端同构深刻理解与常见问题等系列文章,也欢迎阅读。文中提到了webpack-isomorphic-tools,该工具使得在服务端也可以处理less/css/scss,image等各类文件,从而使得服务端同构成为现实(服务端可使用css module等特性生成className,从而使得checksum在客户端与服务端一致,防止客户端从新渲染)。

2.7 各类打包工具

better-npm-run以及webpackcc等打包工具的使用。前者在package.json中直接配置就行:

"betterScripts": {   
 "start-prod": {     
  "command": "node ./bin/server.js",    
    "env": {     
       "NODE_PATH": "./src",     
       "NODE_ENV": "production",      
         "PORT": 8080,      
           "APIPORT": 3030
      }
    }
}

其主要做用在于方便设置各类环境变量。而webpackcc集成了多种打包方案,总有一个适合你

2.8 服务端客户端其余的库

superagent,express等与服务器相关的内容。其中前者主要用于向服务端发送请求,包括服务端向反向代理服务器以及客户端向服务器发送请求。

const methods = ['get', 'post', 'put', 'patch', 'del'];
import superagent from 'superagent'; this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {        const request = superagent[method](formatUrl(path));        if (params) {
          request.query(params);
        }        //若是传入了参数,那么经过query添加进去
        if (__SERVER__ && req.get('cookie')) {
          request.set('cookie', req.get('cookie'));
        }        if (data) {
          request.send(data);
        }        //request.end才会真正发送请求出去
        request.end((err, { body } = {}) => err ? reject(body || err) : resolve(body));
      }));

2.9 高阶组件的组件复用逻辑

高阶组件对于组件复用是至关重要的。好比有一种状况,你须要获取全部的用户列表,图书列表,**列表等等,而后在数据获取完成后来从新渲染组件,此时你也能够考虑高阶组件的方式:

//此时咱们只是须要考虑真正的异步请求数据的逻辑,以及对prop进行特别处理的逻辑,而不用管当前是图书列表,仍是用户列表等等function connectPromise({promiseLoader, mapResultToProps}) {
  return Comp=> {    return class AsyncComponent extends Component {
      constructor(props) {
        super();        this.state = {
          result: undefined
        }
      }
      componentDidMount() {
        promiseLoader()
          .then(result=> this.setState({result}))
      }
      render() {        return (          <Comp {...mapResultToProps(props)} {...this.props}/>
        )
      }
    }
  }
}
const UserList = connectPromise({
    promiseLoader: loadUsers,
    mapResultToProps: result=> ({list: result.userList})
})(List); //List can be a pure component

const BookList = connectPromise({
    promiseLoader: loadBooks,
    mapResultToProps: result=> ({list: result.bookList})
})(List);

你应该很容易就看出来了,对于这种列表类型的高阶组件抽象是至关成功的。咱们只须要关注重要的代码逻辑,在componentDidMount请求数据结束后咱们会自动调用setState来完成组件状态的更新,而真实的更新的组件倒是咱们经过本身的业务逻辑来指定的,能够是BookList,UserList,**List等等。这样具备反作用的高阶组件复用也就完成了。若是你须要深刻了解高阶组件的内容,请查看个人这篇文章。在该项目中咱们使用了multireducer

三、React全家桶文章总结

关于该项目中使用到的全部的react相关知识点我都进行了详细总结。可是很显然,若是你要学习react,必须对webpack和babel都进行必定的了解。由于在写这个项目以前,我只是一个react/webpack/babel的新手,所以也是在不断的学习中摸索前进的。遇到了问题就各类google,baidu。并且我对于本身有一个严格的要求,那就是要知其然并且要知其因此然,所以我会把遇到的问题都进行深刻的分析。下面我把我在写这个项目过程遇到问题,并做出的总结文章贴出来,但愿对您有帮助。我也但愿您可以关注每一篇文章下面的参考文献,由于他们确实都是很是好的参考资料。

3.1 React+redux相关

React高阶组件详解

React同构你了解多少以及经常使用问题

renderProps签名与React服务端渲染

React的material-ui学习实例

react的context困境与解决方法

redux-form的使用实例

redux的原理浅析

使用react组件的ref回调函数

react-redux服务端渲染的一个完整例子

3.2 webpack相关

webpack-dev-server原理分析

webpack热加载HMR深刻学习

集成webpack,webpack-dev-server的打包工具

prepack与webpack对比

webpack插件书写你须要了解的知识点

CommonsChunkPlugin深刻分析

CommonsChunkPlugin配置项深刻分析

webpack.DllPlugin提高打包性能

webpack实现code splitting方式分析

webpack中的externals vs libraryTarget vs library

webpack的compiler与compilation对象

webpack-dev-middleware原理分析

3.3 Babel相关

Babel编译class继承与源码打包结果分析

使用babel操做AST来完成某种特效

babylon你了解多少

3.4 其余内容

bootstrap-loader自定义bootstrap样式

前端工程师那些shell命令学习

npm环境变量与常见命令

npm中script生命周期方法的深刻探讨

npm version与npm dist tag详解

linux中软连接与硬连接的区别学习

React路上遇到的那些问题以及解决方案

npm,webpack学习中遇到的各类问题

四、你可以学到的东西

内部全部的代码都有详细的注释,并且都给出了代码相关说明的连接。经过这个项目,对于react*全家桶*应该会有一个深刻的了解。该项目牵涉到了常见的React生态中的库,所以命名为全家桶。该项目用到的React生态的主要库以下:

react react-addons-perf react-bootstrap react-dom react-helmet react-redux react-router react-router-bootstrap react-router-redux react-tap-event-plugin react-transform-hmr redux redux-async-connect redux-devtools redux-devtools-dock-monitor redux-devtools-log-monitor redux-form, redux-slider-monitor redux-thunk multireducer ........

注:本文著做权归做者,由demo大师发表,拒绝转载,转载须要做者受权

相关文章
相关标签/搜索