TypeScript + React + Ant Design项目开发总结(一)

写在前面

本次项目是公司进行的CRM改造项目,因此,项目主要用于内部员工,如销售,运营,售后等进行客户的相关数据的维护,项目前端技术选用React,UI框架选用Ant Design,由于是内部系统,因此对UI界面的要求并不算高,基本上基于Ant Design本身的控件进行开发。

项目总体预览

先对项目结构进行一个大致的预览:
TypeScript + React + Ant Design项目开发总结(一)_第1张图片
项目的框架是由前辈搭建的,我来主要剖析一下这个项目。项目主要分为两个大块:

  1. app模块(项目内部主要功能模块),下面再分common模块(提取内部公用模块,例如公共组件,公共枚举,公共服务类),module模块(细分各个功能模块)
  2. build模块(项目启动模块),由于使用了node进行首屏渲染,所以单独出文件夹;
  3. 其他文件,主要是babel文件用于转es6,tsconfig用于配置typescript
    下面开始先总结下build模块。

build模块

由于项目使用nodejs的express框架进行了首屏渲染,所以独立出来的build文件夹,就很有剖析的必要了。
TypeScript + React + Ant Design项目开发总结(一)_第2张图片
server文件夹
TypeScript + React + Ant Design项目开发总结(一)_第3张图片
这里的data文件夹下是放置了一些静态数据,比如省市区的信息。routes.js 文件配置了express的路由,server.dev.js 和 server.js 是启动服务的主要文件。

server.js

let express = require('express');
let compression = require('compression');
let path = require('path');
let app = express();
let distFolder = path.join(__dirname, './dist');
let serveStatic = require('serve-static');
let packageConfig = require('./package.json');
let routes = require('./routes');
const config = packageConfig.config;
const port = config.port;

routes(app, config);

app.use(compression());

app.use(serveStatic(distFolder, {
    maxAge: '7d',
    index: ['index.html'],
    etag: false,
    setHeaders: function (res, path, stat) {
        if (path.indexOf('index.html') >= 0) {
            res.setHeader('Cache-Control', 'max-age=0,no-cache,no-store,must-revalidate');
            res.setHeader('pragma', "no-cache");
        }
    }
}));

app.listen(port, () => {
    console.log('App is listening at http://localhost:%s', port);
});

文件中的node监听端口在package.json中进行配置,并且配置了index.html的启动文件。

server.dev.js

let WebpackDevServer = require("webpack-dev-server");
let webpack = require("webpack");
let webpackConfig = require('../webpack/webpack.config.dev.js');
let packageConfig = require('../../package.json');
let routes = require('./routes');

const compiler = webpack(webpackConfig);
const host = '127.0.0.1';

const config = packageConfig.config;
const port = config.port;

const server = new WebpackDevServer(compiler, {
    hot: true,
    quiet: false,
    noInfo: false,
    publicPath: webpackConfig.output.publicPath,
    stats: 'minimal',
    before: (app) => {
        routes(app, config);
    }
});

server.listen(port, host, function (error, result) {
    if (error) {
        console.log(error);
    }
    console.log('webpack dev server http://%s:%s', host, port);
});

server.dev.js用于开发时,进行本地调试,这个地方用到了webpack-dev-server进行了开发调试时,使用webpack的一个本地server,至于选择哪一个文件启动,是在package.json中配置了启动命令。

launcher文件夹
TypeScript + React + Ant Design项目开发总结(一)_第4张图片
这里的四个文件,callback.html 和 silent-refresh.html 是统一账户Oauth2登录所需要的文件,这里不展开叙述,主要看index.html 和 index.tsx。

index.html

  <html>
   <head>
   <script>
        (function (global) {
            if (global.location && !global.location.origin) {
                global.location.origin = global.location.protocol + "//" +
                    global.location.hostname +
                    (global.location.port ? ':' + global.location.port : '');
            }

            global.appsettingcb = function (appSettings) {
                global.appSettings = appSettings;
            };
        })(typeof window !== "undefined" ? window : this);
    script>
    <script src="settings?callback=appsettingcb">script>
head>

<body>
    <div id="root" class="root-container" style="height: 100%">
    div>
body>

html>

我截取了我觉得比较重要的一部分,就是如何读取我配置在package.json中的配置。首先看第二个script标签,src赋值了"settings?callback=appsettingcb"。一开始我很迷惑,这里的setting请求的到底是哪个文件,但是结合server中的routes.js就一目了然了,这里看一下routes.js,截取其中一段。

routes.js

const routes = (app, packageConfig) => {
    app.route('/settings').get((_, res) => {
        res.jsonp(packageConfig);
    });
}

这里指数,当get请求的路径是setting时,将packageConfig这个变量,最后以jsonp的形式返回,这里对于packageConfig这个变量,是在server.js 中调用时,传入的。

server.js

let packageConfig = require('./package.json');
let routes = require('./routes');
const config = packageConfig.config;
const port = config.port;

routes(app, config);

这里指出了config是获取的package.json文件中的config变量,这里我们再看一下package.json 中的config变量。

package.json

{
    "name": "reactboilerplate",
    "version": "1.0.0",
    "description": "boilerplate",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "clean": "gulp clean",
        "tslint": "gulp tslint",
        "start": "gulp start",
        "build": "cross-env NODE_ENV=development gulp build",
        "build:production": "cross-env NODE_ENV=production gulp build",
        "build:development": "cross-env NODE_ENV=development gulp build"
    },
    "keywords": [
        "react-boilerplate"
    ],
    "author": "n",
    "license": "ISC",
    "config": {
        "port": 9636,
        "api": {
            "server": "http://localhost:21022/"
        }
     }
 }

最后一段定义了config变量,在变量中写下平时需要定义的变量,比如端口号,或者服务器地址。

到了这里再回头看index.html,src中的settings?callback=appsettingcb,这里请求了setting,返回的是jsonp形式的config变量,“callback”,是jsonp中的一个默认的回调函数变量,这里将"appsettingcb"的值赋给他,再看回第一个script标签

            global.appsettingcb = function (appSettings) {
                global.appSettings = appSettings;
            };

这个函数的定义是把传入的appsettings赋值给全局的appsettings。这里全局的appsettings,是通过另外一个文件,考虑到项目是typescript进行开发,因此通过了global.d.ts来进行声明。

global.d.ts

interface AppSettings {
    api: {
        server: string;
    };
}
interface Window {
    appSettings: AppSettings;
}

如果用JavaScript进行开发的话可以省略这一步。最后通过setting.ts将其进行实例化,再export,就可以再项目中使用了。

setting.ts

const appSettings = window.appSettings;
export default appSettings;

至此,index.html中的script标签,已经全部剖析完毕,这里理清了如何把package.json中的config给读取到项目中。接下俩看下面的body标签。定义了一个id为root的div,这是为后面的react寻找根地址做准备,接下来就需要看index.tsx了。

index.tsx

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom';

import { Root } from '../../app/modules/systems';
ReactDOM.render(
  <Router>
    <Route component={Root} />
  </Router>,
  document.getElementById('root'));

这就正式开始写react了,首先确定了,路由是用的react-router-dom,然后锁定了id为root的div,开始进行渲染。

总结

这一篇对build文件夹,也就是node的server文件进行了剖析,着重点是读取package.json中的配置,很巧妙的运用了jsonp,以及node的路由,下一篇将对react项目写组件进行总结。

你可能感兴趣的:(Summary)