基于create-react-app官方脚手架搭建dva模式的项目(一)

思索良久,决定仍是记录下心得体会:一个基于create-react-app官方脚手架,搭建起来的dva开发模式的react项目。javascript

固然现今的前端市场如此强大,你能够在网络上找到你想要的任何脚手架,而且不少能够开箱即用,不能否认它们很优秀,开发它们的人或团队更值得咱们竖起大拇指,为他们点赞!比较适合国人仍是阿里系的一套react开发脚手架,dva-cliantdUmi等,固然gitHub上也有诸多优秀的脚手架,有兴趣的同窗能够自行查阅学习。css

这里,仅仅以react官方脚手架开启项目,安装采用dva方式的,构建项目,展现记录过程当中遇到的各类点和Keng,若你也遇到相似问题,也许能帮上你。html

1 安装create-react-app(鉴于国内网络如下使用cnpm,具体配置网上可找大量资料)前端

cnpm i create-react-app -g

2 建立项目,目录名projectjava

create-react-app project

等待命令执行完成,脚手架会为你安装好基础的组件包,目录生成完毕以下:node

这就是脚手架标准目录,咱们再来看下package.json文件以下:react

{
  "name": "project",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.4.0",
    "react-dom": "^16.4.0",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

如上所示,很干净的目录。webpack

此时已可运行npm start运行起来项目了,默认端口3000,效果如图:git

注意scripts执行命令中有一个eject,意为弹射暴露出全部配置,其实脚手架仍是为咱们封装了一些东西的,这里咱们就暴露出全部配置吧。github

运行npm run eject,一旦选择eject,那么所封装的组件依赖和项目结构会有所变化,如图:

咱们打开package.json文件以下所示,是暴露出的依赖配置项,能够看到已经为咱们安装好webpack,babel,eslint等:

{
  "name": "project",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "autoprefixer": "7.1.6",
    "babel-core": "6.26.0",
    "babel-eslint": "7.2.3",
    "babel-jest": "20.0.3",
    "babel-loader": "7.1.2",
    "babel-preset-react-app": "^3.1.1",
    "babel-runtime": "6.26.0",
    "case-sensitive-paths-webpack-plugin": "2.1.1",
    "chalk": "1.1.3",
    "css-loader": "0.28.7",
    "dotenv": "4.0.0",
    "dotenv-expand": "4.2.0",
    "eslint": "4.10.0",
    "eslint-config-react-app": "^2.1.0",
    "eslint-loader": "1.9.0",
    "eslint-plugin-flowtype": "2.39.1",
    "eslint-plugin-import": "2.8.0",
    "eslint-plugin-jsx-a11y": "5.1.1",
    "eslint-plugin-react": "7.4.0",
    "extract-text-webpack-plugin": "3.0.2",
    "file-loader": "1.1.5",
    "fs-extra": "3.0.1",
    "html-webpack-plugin": "2.29.0",
    "jest": "20.0.4",
    "object-assign": "4.1.1",
    "postcss-flexbugs-fixes": "3.2.0",
    "postcss-loader": "2.0.8",
    "promise": "8.0.1",
    "raf": "3.4.0",
    "react": "^16.4.0",
    "react-dev-utils": "^5.0.1",
    "react-dom": "^16.4.0",
    "resolve": "1.6.0",
    "style-loader": "0.19.0",
    "sw-precache-webpack-plugin": "0.11.4",
    "url-loader": "0.6.2",
    "webpack": "3.8.1",
    "webpack-dev-server": "2.9.4",
    "webpack-manifest-plugin": "1.3.2",
    "whatwg-fetch": "2.0.3"
  },
  "scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js --env=jsdom"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,mjs}"
    ],
    "setupFiles": [
      "<rootDir>/config/polyfills.js"
    ],
    "testMatch": [
      "<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}",
      "<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
    ],
    "testEnvironment": "node",
    "testURL": "http://localhost",
    "transform": {
      "^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|mjs|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"
    ],
    "moduleNameMapper": {
      "^react-native$": "react-native-web"
    },
    "moduleFileExtensions": [
      "web.js",
      "js",
      "json",
      "web.jsx",
      "jsx",
      "node",
      "mjs"
    ]
  },
  "babel": {
    "presets": [
      "react-app"
    ]
  },
  "eslintConfig": {
    "extends": "react-app"
  }
}

此处我本地更改下3000端口为9999,避免与其余本地项目冲突,安装cross-env:

cnpm i cross-env

修改start命令为:

- "start": "node scripts/start.js",
+ "start": "cross-env PORT=9999 node scripts/start.js",

而后启动:npm start 效果如图:

3 安装dva库(dva也有本身的脚手架dva-cli,也可快速构建项目,目前已升至2.x版本,采用react-router@4.x路由版本)

cnpm i dva --save

安装history组件,等会后面会用到,主要作BrowserHistory浏览器历史功能,有兴趣的同窗可网上自行学习。

4 改造项目为dva模式(不了解dva的同窗可网上自行学习),在src目录下新建目录:models,services,routes,utils(utils未来存放配置文件和工具方法),如图:

5 路由设计,简单点暂设计为三个demo地址,分别以下:

(1)http://localhost:9999/aaa

(2)http://localhost:9999/aaa/bbb

(3)http://localhost:9999/ccc

6 组件设计,在routes目录下新建三个文件:AAA.js BBB.js CCC.js 如图:

代码分别为:

AAA.js

import React, { Component } from 'react';

class AAA extends Component {
  render() {
    return (
      <div>
        <p>
          AAA页
        </p>
      </div>
    );
  }
}

export default AAA;

BBB.js

import React, { Component } from 'react';

class BBB extends Component {
  render() {
    return (
      <div>
        <p>
          BBB页
        </p>
      </div>
    );
  }
}

export default BBB;

CCC.js

import React, { Component } from 'react';

class CCC extends Component {
  render() {
    return (
      <div>
        <p>
          CCC页
        </p>
      </div>
    );
  }
}

export default CCC;

7 model设计,在model下新建四个文件:aaa.js bbb.js ccc.js 和app.js(app做为全局model使用,未来存放全局变量,如国际化参数,登陆用户信息等)

代码分别为:

aaa.js

export default {

    namespace: 'aaa',
  
    state: {
      name:'这是aaa的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      
    },
  
    reducers: {
      
    },
  
  };

bbb.js

export default {

    namespace: 'bbb',
  
    state: {
      name:'这是bbb的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      
    },
  
    reducers: {
      
    },
  
  };

ccc.js

export default {

    namespace: 'ccc',
  
    state: {
      name:'这是ccc的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      
    },
  
    reducers: {
      
    },
  
  };

app.js

export default {

    namespace: 'app',
  
    state: {
      name:'这是app的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      
    },
  
    reducers: {
      
    },
  
  };

8 src目录下建立router.js路由控制文件,其中menuGlobal后期会提出到配置文件中,此处暂先这样放便于查看,你们发现其中也定义了id,pid,name,icon等字段,这些并非dva路由必须字段,没错这些事自定义的字段,未来会用到,菜单关系,菜单是否显示,和icon图标等

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import dynamic from 'dva/dynamic'

const menuGlobal=[
  {
      id:'aaa',
      pid:'0',
      name:'aaa页',
      icon:'user',
      path: '/aaa',
      models: () => [import('./models/aaa')], //models可多个
      component: () => import('./routes/AAA'),
  }, 
  {
      id:'bbb',
      pid:'0',
      name:'bbb页',
      icon:'user',
      path: '/aaa/bbb',
      models: () => [import('./models/bbb')], //models可多个
      component: () => import('./routes/BBB'),
  }, 
  {
      id:'ccc',
      pid:'0',
      name:'ccc页',
      icon:'user',
      path: '/ccc',
      models: () => [import('./models/ccc')], //models可多个
      component: () => import('./routes/CCC'),
  }, 
];

function RouterConfig({ history, app }) {

  return (
    <Router history={history}>
      <Switch>
        {
          menuGlobal.map(({path,...dynamics},index)=>(
            <Route
              key={index} 
              path={path} 
              exact 
              component={dynamic({
                app,
                ...dynamics
              })} 
            />
          ))
        }
      </Switch>
    </Router>
  );
}

export default RouterConfig;

9 修改src目录下index.js入口文件:

import dva from 'dva';
import './index.css';
import createHistory from 'history/createBrowserHistory'

// 1. Initialize
const app = dva({
    history:createHistory()
});

// 2. Plugins
// app.use({});

// 3. Model
app.model(require('./models/app').default);

// 4. Router
app.router(require('./router').default);

// 5. Start
app.start('#root');

 

至此,dva的改造基本完毕,后续还有其余的细节补充,运行起来吧:npm start

效果如图:

试试看,基本的路由已经OK了,接下来,咱们将进行对以上主体构造的细节坐下补充。

此处路由只是经过浏览器地址栏直接访问的形式,咱们还须要增长路由跳转<Link>

(1)AAA.js中引入import { Link } from 'dva/router'; 并增长跳转连接以下:

import React, { Component } from 'react';
import { Link } from 'dva/router';

class AAA extends Component {
  render() {
    return (
      <div>
        <p>
          AAA页
        </p>
        <Link to={'/aaa/bbb'}>去BBB页面</Link>
        <br />
        <Link to={'/ccc'}>去CCC页面</Link>
      </div>
    );
  }
}

export default AAA;

(1)BBB.js中引入import { Link } from 'dva/router'; 并增长跳转连接以下:

import React, { Component } from 'react';
import { Link } from 'dva/router';

class BBB extends Component {
  render() {
    return (
      <div>
        <p>
          BBB页
        </p>
        <Link to={'/aaa'}>去AAA页面</Link>
        <br />
        <Link to={'/ccc'}>去CCC页面</Link>
      </div>
    );
  }
}

export default BBB;

(1)CCC.js中引入import { Link } from 'dva/router'; 并增长跳转连接以下:

import React, { Component } from 'react';
import { Link } from 'dva/router';

class CCC extends Component {
  render() {
    return (
      <div>
        <p>
          CCC页
        </p>
        <Link to={'/aaa'}>去AAA页面</Link>
        <br />
        <Link to={'/aaa/bbb'}>去BBB页面</Link>
      </div>
    );
  }
}

export default CCC;

此时页面中已有连接,三个页面可相互跳转了。

如今咱们将router.js中的menuGlobal提出,放于公用文件中,首先在utils下新建文件config.js和index.js 代码分别以下:

config.js文件:

const menuGlobal=[
    {
        id:'aaa',
        pid:'0',
        name:'aaa页',
        icon:'user',
        path: '/aaa',
        models: () => [import('../models/aaa')], //models可多个
        component: () => import('../routes/AAA'),
    }, 
    {
        id:'bbb',
        pid:'0',
        name:'bbb页',
        icon:'user',
        path: '/aaa/bbb',
        models: () => [import('../models/bbb')], //models可多个
        component: () => import('../routes/BBB'),
    }, 
    {
        id:'ccc',
        pid:'0',
        name:'ccc页',
        icon:'user',
        path: '/ccc',
        models: () => [import('../models/ccc')], //models可多个
        component: () => import('../routes/CCC'),
    }, 
  ];
  
export default {
    menuGlobal
}

index.js文件(以后utils中的配置文件,都可在index.js暴露出去):

import config from './config';

export {
    config
}

修改src下的router.js文件,以下:

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import dynamic from 'dva/dynamic'

import {config} from './utils'
const { menuGlobal } = config

function RouterConfig({ history, app }) {

  return (
    <Router history={history}>
      <Switch>
        {
          menuGlobal.map(({path,...dynamics},index)=>(
            <Route
              key={index} 
              path={path} 
              exact 
              component={dynamic({
                app,
                ...dynamics
              })} 
            />
          ))
        }
      </Switch>
    </Router>
  );
}

export default RouterConfig;

刷新页面便可,之因此把menu提出配置文件中,便于管理和后期拓展。

接下一篇:http://www.javashuo.com/article/p-mjeuavkj-mu.html