初學者的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大師發表,拒絕轉載,轉載須要做者受權

相關文章
相關標籤/搜索