为了账号安全,请及时绑定邮箱和手机立即绑定

快速搭建react项目骨架(按需加载、redux、axios、项目级目录等等)

标签:
React

一、前言

       最近整理了一下项目骨架,顺便自定义了一个脚手架,方便日后使用。我会从头开始,步骤一步步写明白,如果还有不清楚的可以评论区留言。先大致介绍一下这个骨架,我们采用 create-react-app 搭建基础骨架,修改一些基础配置; 使用webpack的import模块实现按需加载(俗称切片打包); 引入 react-redux; 引入axios; 规划好项目的目录结构。我们大致就做这些事,大家可以根据自己项目需要,添加ui包等其他插件。博客的代码只是说明大致的流程,建议先拉代码,对比代码看博客。

 

二、目录

  1、安装 create-react-app 脚手架并创建APP

  2、按照上线项目标准完善目录结构

  3、配置按需加载(俗称切片打包)

  4、配置react-redux及redux-sagas(sagas是我个人习惯,挺好用的,不喜欢的可以不装)

  5、配置axios统一请求(cookie、拦截、统一报错等)

  6、代码地址 (如果觉得有用,记得给我 github 点个赞奥)   ps: 说不定博主还会开放几个私有仓库  

 

三、安装 create-react-app 脚手架并创建APP

         

复制代码

      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,装一下依赖(好像不用装,我们没添加什么,笑哭~~)。

         我们看一下现在的目录结构

    

 

 四、按照上线项目标准完善目录结构

         目前为止这个项目只有一个默认页面,放在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>    )
  }
}

复制代码

   这样我们新建几个页面 登录、注册、默认页等等,这个随意啦。然后我们看一下现在的目录结构:

           

 

五、配置按需加载(俗称切片打包)

       现在我们已经有了几个最基础的页面了,我们开始做路由按需加载。

       为什么要按需加载? 因为 单页应用,只有一个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 可以看到我们已经实现切片打包了。

 

六、 配置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-PWAserviceWorker.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方法名不能重复。

 

七、配置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';// 测试1export function login1(params) {  return request(`/xxx/xxx1?${stringify(params)}`)
}// 测试2export function login2(data) {  return request(`/xxx/xxx2`, {
    method: 'post',
    body: data,
  })
}

复制代码

  这个代码里面都有注释,这里简单说明一下,这个地址常亮,大家根据自己实际情况来改。关于请求的封装,这里主要写了加cookie,未登录401报错直接跳到登录页,至于其他错误处理,大家根据自己项目错误码来就好。项目中涉及到一些node.js的小功能函数,大家一百度就知道了,比如说 stringify。封装好的请求要么直接用,要么在sagas里面用。大致就是这样。

 

八、代码地址 (如果觉得有用,记得给我 github 点个赞奥。)

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

  这是代码地址,觉得不错,您别吝啬,  地址右上方start点一下,谢谢。

 

小结

  至此,一个精简的react骨架就出来了,没有做太多的配置,以免影响灵活度。这几项几乎都是项目中必须的东西。所以,就写到这。后期看看反应吧,把ui框架加上去,再做上菜单、权限的配置,再敲几个常用的页面。如果做的话,我会在git上开一个分支,不会影响这个基本骨架。如果博客中哪里写的有问题,欢迎评论区留言。

作者:Mr.聂

原文出处:  https://www.cnblogs.com/pengfei-nie/p/10443310.html  

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消