快速搭建react項目骨架(按需加載、redux、axios、項目級目錄等等)

1、前言css

       最近整理了一下項目骨架,順便自定義了一個腳手架,方便往後使用。我會從頭開始,步驟一步步寫明白,若是還有不清楚的能夠評論區留言。先大體介紹一下這個骨架,咱們採用 create-react-app 搭建基礎骨架,修改一些基礎配置; 使用webpack的import模塊實現按需加載(俗稱切片打包); 引入 react-redux; 引入axios; 規劃好項目的目錄結構。咱們大體就作這些事,你們能夠根據本身項目須要,添加ui包等其餘插件。博客的代碼只是說明大體的流程,建議先拉代碼,對比代碼看博客。html

 

2、目錄前端

  一、安裝 create-react-app 腳手架並建立APPnode

  二、按照上線項目標準完善目錄結構react

  三、配置按需加載(俗稱切片打包)webpack

  四、配置react-redux及redux-sagas(sagas是我我的習慣,挺好用的,不喜歡的能夠不裝)ios

  五、配置axios統一請求(cookie、攔截、統一報錯等)git

  六、代碼地址 (若是以爲有用,記得給我 github 點個贊奧)   ps: 說不定博主還會開放幾個私有倉庫  ☺
github

 

3、安裝 create-react-app 腳手架並建立APPweb

         

      npm install -g create-react-app   //本地全局安裝 react 腳手架
      create-react-app webapp           //經過腳手架指令 建立 react webapp, 注意名字不能有大寫字母,如今就能夠直接跑了
      npm run eject                     //新版本的腳手架把配置文件等都以依賴的形式放到 node_modules 中了, eject 一下,把配置信息釋放出來
      scripts/start.js                  //修改一下端口號,默認是3000,改爲你想要的
      npm i                             //裝一下依賴

    咱們直接用react的官方腳手架搭建最基礎的骨架,經過 create-react-app 新建 react的webAPP。而後咱們的項目就能夠直接跑了,看一下package.json

文件,裏面有關於項目啓動、打包、測試的指令。執行 npm run start 就能夠運行咱們的項目了。

         而後咱們修改一下這個項目,由於如今 create-react-app 腳手架會把配置文件都以依賴的形式放到node_modelus裏面,執行 npm run eject 把配置文件釋放出來,然後執行 npm i,裝一下依賴(好像不用裝,咱們沒添加什麼,笑哭~~)。

         咱們看一下如今的目錄結構

    

 

 4、按照上線項目標準完善目錄結構

         目前爲止這個項目只有一個默認頁面,放在src錄下。咱們按照上線項目大體會用到的東西,先完善一下目錄結構

          assets            靜態資源,存放 字體、圖片、css hack 等
          components        創建公共組件文件夾,這是放公共組件的
          layouts           創建佈局文件夾  肯定好你的項目佈局樣式
          constants         全局常亮文件夾  存放全局常亮
          helpers           公共函數文件夾  存放公共函數、一些插件的啓動配置函數
          modules           咱們具體的功能模塊  存放咱們項目的實際頁面
          services          接口文件夾  存放全部請求
          store             裝redux的

          咱們在src目錄下新建上述文件夾,具體功能都已標明瞭。出於規範化、模塊化考慮咱們暫時將文件如此分類。

          而後咱們新建幾個簡單的頁面,內容自定義,可參考下述代碼:

import React, { PureComponent } from 'react'; export default class Register extends PureComponent { state = {} componentDidMount () {} render() { return ( <div className="g-default"> 默認頁 </div>
 ) } }

   這樣咱們新建幾個頁面 登陸、註冊、默認頁等等,這個隨意啦。而後咱們看一下如今的目錄結構:

           

 

5、配置按需加載(俗稱切片打包)

       如今咱們已經有了幾個最基礎的頁面了,咱們開始作路由按需加載。

       爲何要按需加載? 由於 單頁應用,只有一個html,一個主要的css、js。傳統打包方式是將整個項目的js、css都打包成一個文件引入。用戶瀏覽咱們的頁面

時就須要將整個項目拉下來才行(動不動就是幾M甚至幾十M),很是不友好。按需加載,按照路由切割js、css,用戶看哪一個,就加載哪一個頁面代碼。首次加載體驗很是好。

       按需加載方法有不少,咱們介紹一種目前配置簡單、效率也高的一種。咱們採用 webpack 的 import 模塊來實現按需加載。

       首先,封裝一個異步加載模塊的組件,而後用這個組件去引入要加載的模塊。代碼以下: 

//這是異步加載組件的代碼
import {PureComponent} from 'react'; export default class Bundle extends PureComponent { constructor(props) { super(props); this.state = { mod: null }; } componentWillMount() { this.load(this.props) } componentWillReceiveProps(nextProps) { if (nextProps.load !== this.props.load) { this.load(nextProps) } } load(props) { this.setState({ mod: null }); //注意這裏,使用Promise對象; mod.default導出默認 props.load().then((mod) => { this.setState({ mod: mod.default ? mod.default : mod }); }); } render() { return this.state.mod ? this.props.children(this.state.mod) : null; } }
..............

//這是他的使用,新建Router.js文件,配置路由
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Bundle from './Bundle';

const Login = (props) => (<Bundle load={() => import('./modules/login')}>{(Login) => <Login {...props}/>}</Bundle>);
const Register = (props) => (<Bundle load={() => import('./modules/register')}>{(Register) => <Register {...props}/>}</Bundle>);
const Default = (props) => (<Bundle load={() => import('./modules/default')}>{(Default) => <Default {...props}/>}</Bundle>);
const Blog = (props) => (<Bundle load={() => import('./modules/blog')}>{(Blog) => <Blog {...props}/>}</Bundle>);
const User = (props) => (<Bundle load={() => import('./modules/user')}>{(User) => <User {...props}/>}</Bundle>);



const BasicRoute = () => (
<BrowserRouter>
<Switch>
<Route exact path="/login" component={Login}/>
<Route exact path="/register" component={Register}/>
<Route exact path="/default" component={Default}/>
<Route exact path="/blog" component={Blog}/>
<Route exact path="/user" component={User}/>

<Route exact path="/" component={Login}/>
<Route exact
component={Login}/>
</Switch>
</BrowserRouter>
);


export default BasicRoute;

.............

//這是對index文件的修改
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import BasicRoute from './Router';
import * as serviceWorker from './serviceWorker';
import store from './store'

ReactDOM.render( <BasicRoute />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
 

   注意,我把以前的App.js文件刪掉了,咱們用不上。而後新建了一個Router.js,這個文件就是咱們全部模塊的路由配置。而後修改src/index.js 文件,引入咱們的路由文件。如今咱們的路由以及路由的按需加載就都OK了。咱們能夠多建幾個文件加上內部路由跳轉試一下。執行 npm run build 看咱們build文件夾, build/static/js 能夠看到咱們已經實現切片打包了。

 

6、 配置react-redux及redux-sagas(sagas是我我的習慣,挺好用的不喜歡的能夠不裝)

         爲何要裝redux? 由於react單頁應用,咱們會涉及大量的數據,像用戶信息等數據會在不少地方用到,這會致使組件間的數據傳輸很麻煩,因此咱們使用redux,將變量統一管理,中心思想很簡單。和咱們定義一個命名空間,裏面放不少變量,而後寫一些方法指定性讀取、修改這些變量同樣,大體能夠這麼理解。

   而後,咱們安裝

      npm install --save redux 安裝redux npm install --save react-redux 安裝react的綁定庫 npm install --save redux-saga   安裝sagas, Redux-saga是Redux的一箇中間件,主要集中處理react架構中的異步處理工做

        我習慣用sagas,不喜歡的能夠不裝哈。可是後序代碼我都會用它來寫。

  裝好了依賴,接下來是如何使用。大體步驟是這樣的,創建reducer和sagas,而後用redux的Provider組件包裹項目,注入redux,而後就能夠在組建中使用了,咱們貼一下代碼

// src/index.js  引入redux,並注入數據
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux' import './index.css'; import BasicRoute from './Router'; import * as serviceWorker from './serviceWorker'; import store from './store' ReactDOM.render( <Provider store={store}> <BasicRoute /> </Provider>, document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: http://bit.ly/CRA-PWA serviceWorker.unregister();

// src/store/index.js 配置redux和sagas,並引入咱們的reducer和sagas import { createStore, applyMiddleware } from
'redux' import createSagaMiddleware from 'redux-saga' import rootReducer, { createReducer } from './reducers' import rootSaga from './sagas' const sagaMiddleware = createSagaMiddleware() const middlewares = [sagaMiddleware] const configureStore = (initialState = {}) => { const store = createStore( rootReducer, initialState, applyMiddleware(...middlewares), ) sagaMiddleware.run(rootSaga) store.runSaga = sagaMiddleware.run store.asyncReducers = store.asyncReducers || {} store.asyncSagas = store.asyncSagas || [] return store } export const injectAsyncReducer = ({ name, asyncReducer, store }) => { if ( store.asyncReducers[name] ) return store.asyncReducers[name] = asyncReducer store.replaceReducer(createReducer(store.asyncReducers)) } export const injectAsyncSagas = ({ name, sagas, store }) => { if ( !store.asyncSagas.includes(name) ) { sagas.forEach(store.runSaga) store.asyncSagas.push(name) } } export default configureStore({})

// 配置reducer入口文件 import { combineReducers } from
'redux' import user from './user' const rootReducer = { user, } export const createReducer = asyncReducers => combineReducers({ ...rootReducer, ...asyncReducers, }) export default combineReducers(rootReducer) // user模塊的 reducer const initState = { loading: false, dataSource: { list: [], pagination: { pageSize: 10, current: 1, }, }, } const reducer = (state = initState, action) => { switch (action.type) { case 'saveAssetsLoading': return { ...state, loading: action.payload, } default: return { ...state, } } } export default reducer

// sagas 的入口文件 import { all } from
'redux-saga/effects' import userSagas from './user' const run = sagas => sagas.map(saga => saga()) export default function* rootSaga() { yield all([ ...run([ ...userSagas, ]), ]) }

// user模塊的sagas import { put, call, takeEvery } from
'redux-saga/effects' import { login1, login2} from '../../services/user' const sagas = { * login1({ payload, callback }) { const result = yield call(login1, payload) if (callback) callback(result) }, * login2({ payload, callback }) { const result = yield call(login2, payload) if (callback) callback(result) }, } export default Object.keys(sagas).map(item => { return function * s() { yield takeEvery(item, function *(args) { try { yield sagas[item](args) } catch (e) { console.log(e) } }) } })

// 在user組件中使用 import React, { PureComponent } from
'react'; import { Link } from 'react-router-dom' import { connect } from 'react-redux'; export default @connect(state => ({ user: state.user })) class User extends PureComponent { state = { } componentDidMount () { // this.fetch() this.fetch2() } fetch = () => { const params = { test: 'web' } this.props.dispatch({ type: 'login1', payload: params, callback: result => { console.log(result) } }) } render() { return ( <div className="g-login"> 測試2 <br /> <Link to={`/default`}> 登陸 </Link> </div> ) } }

     使用redux大體就是這樣,先引入redux,而後作具體的文件配置,最後直接組件中使用便可。注意: sagas這裏,我沒有作做用域處理,sagas方法名不能重複。

 

7、配置axios統一請求(cookie、攔截、統一報錯等)

  爲何封裝axios? 首先,咱們使用axios做爲請求方式,各方面性能吧都不錯。其次,在單頁應用中,涉及到的請求會很是多,對於請求攔截、響應攔截、錯誤統一處理等常規操做,咱們把axios進行二次封裝會節省大量的代碼,好處不用我多說了。下邊是封裝axios的流程,以及使用sagas調用的方式,直接貼代碼了

// src/constants/index.js  設定請求的ip,這個根據我的狀況來
export const ORIGIN = {
production: window.location.origin,
development: `http://${window.location.hostname}:3009`,
test: window.location.origin,
// dev: 'http://localhost:3009',
}[process.env.NODE_ENV || 'development'];


// 對axios的封裝
import axios from 'axios' import { ORIGIN } from '../constants' // 添加一個請求攔截器 axios.interceptors.request.use(config => { return config // 暫時沒啥好寫的,咱們只是個骨架 }, error => { return Promise.reject(error); }) // 添加一個響應攔截器 axios.interceptors.response.use(response => { return response.data // 其餘的不要了,只拿data就好 }, error => { console.log(error.response) if (error.response.status === 401) { window.location.pathname = '/login' } // ......在作別的統一處理 return Promise.reject(error); }); export default function request(url, options = {}) { return axios({ url: /^http/.test(url) ? url : `${ORIGIN}${url}`, method: 'get', // 攜帶cookie信息 withCredentials: true, ...options, data: options.body, }) } // 添加具體的請求函數,供前端使用 import request from '../helpers/request' import { stringify } from 'querystring'; // 測試1 export function login1(params) { return request(`/xxx/xxx1?${stringify(params)}`) } // 測試2 export function login2(data) { return request(`/xxx/xxx2`, { method: 'post', body: data, }) }

  這個代碼裏面都有註釋,這裏簡單說明一下,這個地址常亮,你們根據本身實際狀況來改。關於請求的封裝,這裏主要寫了加cookie,未登陸401報錯直接跳到登陸頁,至於其餘錯誤處理,你們根據本身項目錯誤碼來就好。項目中涉及到一些node.js的小功能函數,你們一百度就知道了,好比說 stringify。封裝好的請求要麼直接用,要麼在sagas裏面用。大體就是這樣。

 

8、代碼地址 (若是以爲有用,記得給我 github 點個贊奧。)

  https://github.com/Aaron-China/react-cli

  這是代碼地址,以爲不錯,您別吝嗇,  地址右上方start點一下,謝謝。

 

小結

  至此,一個精簡的react骨架就出來了,沒有作太多的配置,以避免影響靈活度。這幾項幾乎都是項目中必須的東西。因此,就寫到這。後期看看反應吧,把ui框架加上去,再作上菜單、權限的配置,再敲幾個經常使用的頁面。若是作的話,我會在git上開一個分支,不會影響這個基本骨架。若是博客中哪裏寫的有問題,歡迎評論區留言。

  ps: 有點懶,很久沒寫博客了,將持續放點乾貨,但願能幫到你。

相關文章
相關標籤/搜索