怎么做好一个项目 记一次项目开发经验

上个月的实训课程是需要我们小组组队完成一个和区块链相关的积分链项目,涉及区块链,智能合约编写,后端,前端,大家开发经验都不是很多,踩坑很多,在此记录一下项目开发过程遇到的坑。自己是负责前端部分,所以就对前端部分进行总结。

项目前期

需求分析和用例设计

项目一开始时老师让我们第一周先写出一份详细的需求分析,当时觉得很繁琐来着,越到后期越发现,需求分析很重要。在这个阶段写的各个用例,是之后你画页面原型,写页面最需要参考的东西,所以必须要写好这一部分。以下是我们小组写的一些用例:
画uml的工具是在youtube上找的,感觉还挺不错的,叫lucid chart画uml的工具是在youtube上找的,感觉还挺不错的,叫lucid chart,需要梯子。

详细用例:
主要用例场景:

企业申请发放积分
主要成功场景:

  1. 企业在系统中填写企业基本信息
  2. 企业在系统中提交发放积分申请
  3. 系统确认企业基本信息是否准确
  4. 系统将企业申请添加到银行审理目录

银行授信
主要成功场景:

  1. 银行审理企业提交的申请
  2. 若企业信誉良好,确认赋予企业发放积分权利
  3. 系统接收到银行的确认信息
  4. 系统将企业添加进联盟链中

企业发放积分
主要成功场景:

  1. 企业根据用户行为(消费额)确认给用户发放的积分数
  2. 系统将积分数添加到用户的账户中

企业申请积分兑换
主要成功场景:

  1. 企业确认需要兑换的积分数
  2. 企业向银行提交兑换申请
  3. 系统将企业申请添加到银行审理目录

银行承兑兑换需求
主要成功场景:

  1. 银行审理企业提交的申请
  2. 银行接受企业承兑需求,将积分对应金额转至企业账户
  3. 系统确认银行的行为,更新企业的积分数

用户转赠积分
主要成功场景:

  1. 用户选择要转赠积分的用户
  2. 用户输入要转赠的积分数
  3. 用户确认转赠
  4. 系统确认转赠信息,更新用户积分数

用户消费积分
主要成功场景:

  1. 用户在网站中选择想要购买的商品
  2. 在支付时选择用积分支付
  3. 系统确认用户的订单
  4. 系统更新用户的积分数

原型设计

因为是小组项目,没有什么产品经理专门来画页面原型,所以画原型这个任务也到了前端的身上。画原型也很重要,因为之后真正写页面的时候需要有个原型对着来构建页面,效率会更高。画原型用的工具是Axure。

项目中期

前端部分开发前期

首先是要搭起整个项目的框架,React可以用create react app脚手架快速搭建一个项目,Vue可以用Vue cli脚手架快速搭建项目,就不用自己去配webpack环境,省了很多功夫。
搭好脚手架之后就要开始思考需要新建一些什么文件夹,放置对应的文件。这是我的一个文件目录:
在这里插入图片描述

  • api:用于放置向后端发送请求的方法
  • application:用于放置大的组件,会有很多不同的子组件会放置在这个大组件里(会用到很多次)
  • components:用于放置小组件,这些组件会被放置在某些页面中
  • routes:用于放置路由配置
  • style:用于设置一些样式。因为项目中使用到的组件库为Ant design,它定义了很多组件库自己的class,所以如果想要它们提供的组件能够有正常的展示效果,就需要定义一些类,就在这里定义。

简单写一些页面,开始配置路由

一开始可以先将要写的页面先创建好,写一些简单的内容,然后就可以开始配置一下路由。
vue中是使用router-view来将组件渲染到router-view所在位置,react是使用render(routes)。

路由配置过程

以下详细描述一下react配置路由的过程。首先是安装HashRouter包和renderRoutes:

在App.js中定义:

// App.js
import { renderRoutes } from 'react-router
import { HashRouter } from 'react-router-dom'

还有要引入routes文件夹内的index.js,里面包含定义好的各种路由:

// App.js
import routes from './routes/index.js'
import { renderRoutes } from 'react-router
import { HashRouter } from 'react-router-dom'

routes/index.js:

import React from 'react'
import { Redirect } from 'react-router-dom'
import Home from '../application/Home'
import Signin from '../components/Signin'
import Signup from '../components/Signup'
import Consume from '../components/User/Consume'
import Item from '../components/User/Item'
import Person from '../application/Person'

export default [
    {
        path: "/",
        component: Home,
        routes: [
            {
                path: "/signin",
                component: Signin
            },
            {
                path: "/signup",
                component: Signup
            },
            {
                path: '/person',
                component: Person,
                routes: [
                    {
                        path: "/person",
                        exact: true,
                        render: () => (
                            <Redirect to={"/person/consume"} />
                        )
                    },
                    {
                        path: '/person/consume',
                        component: Consume,
                        routes: [
                            {
                                path: '/person/consume/:id',
                                component: Item
                            }
                        ]
                    },
                ]
            }
        ]
    },
]

App.js如下:

import React from 'react'
import { IconStyle } from './assets/iconfont/iconfont'
import { GlobalStyle } from './style'
import { renderRoutes } from 'react-router-config';//renderRoutes 读取路由配置转化为 Route 标签
import routes from './routes/index.js'
import { HashRouter } from 'react-router-dom'
import './App.less'

function App() {
  return (
    <HashRouter>
      <div className="app">
        {renderRoutes(routes)}
      </div>
    </HashRouter>
  );
}

export default App;

renderRoutes(routes)的routes就是route文件夹中定义的路由配置,而且renderRoutes(routes)只能向下渲染一层,比如如果Home组件内还需要渲染子组件的话,需要在Home组件内也引入renderRoutes,这样才能成功渲染。
如果Home组件也想要成功渲染子组件的话,需要两步操作:

  1. 从props继承route:
const { route } = this.props;
  1. renderRoutes(route)
 render () {
        const { route } = this.props;

        return (
            <Container>
                <Title>
                    积分链商城
                </Title>
                <Buttons
                    handleSignIn={this.handleSignIn}
                    handleSignUp={this.handleSignUp}
                >
                </Buttons>
                <div style={{ zIndex: 10, position: 'fixed', top: 0 , left: 0, right: 0}}>
                    { renderRoutes(route.routes) }
                </div>
            </Container>
        )
    }

缺一不可。

react的router.push功能实现

有时候我们需要在一个页面跳转到另一个页面,而不是以子组件的形式展现,这时候就需要用到router.push的功能。
在react中使用router.push需要两步:

  1. 引入withRouter模块:
import { withRouter } from 'react-router-dom'
  1. 使用this.props.history.push进行路由跳转
handleSignIn() {
    this.props.history.push('/signin');
};

就可以实现路由跳转了。

api接口定义

前端需要向后端发送请求,后端响应请求才能获取到数据。前端一般用axios来发送请求。一般会在api文件夹里定义request.js来定义各种请求方法(大型项目如果有很多请求的话需要按文件来定义)。这是我们项目的request.js的内容:

import axios from 'axios'
const qs = require('qs')

const baseUrl = "http://120.79.177.124:8080";

axios.defaults.withCredentials = true; //默认请求携带cookie

// 登陆
export const signIn = (values) => {
    return axios.post(baseUrl + '/user/login_test', qs.stringify({
        user_name: values.username,
        password: values.password,
    }))
}

// 注册
export const signUp = (values) => {
    return axios.post(baseUrl + '/user/regist_test', qs.stringify({
        user_name: values.username,
        password: values.password,
        address: values.address
    }))
}

// 登出
export const logOut = () => {
    return axios.post(baseUrl + '/user/loginout');
}

// // 登陆之后获取用户信息(头像,用户名, 积分数)
// export const getPersonalInfo = () => { 
//     return axios.get(baseUrl + '/user/getlogin');
// }

// 获取积分余额
export const getBalance = () => {
    return axios.get(baseUrl + '/trans/b_enquiry');
}

// 获取商品列表
export const getProductList = () => {
    return axios.get(baseUrl + '/item/itemlist');
}

// // 获取用积分支付的商品列表
// export const getPointProductList = () => { 
//     return axios.get(baseUrl + '/item-list');
//  }

// 获取商品详细信息(通过商品id)
export const getProduct = (id) => {
    return axios.get(baseUrl + '/item/itemmsg', {
        params: {
            id: id
        }
    });
}

// 用现金购买,获得积分
export const buyProduct = (id) => {
    return axios.post(baseUrl + '/item/buy', qs.stringify({
        id: id,
    }))
}

列一下比较重要的几个点:

  1. 方法的名字要能够表明这个方法的用途。get方法写在前面,post方法写在后面。
  2. 写这个request.js的时间点。应该是在你已经写好所有的页面了的时候,这样你就能对着你写的页面,然后分析需要哪些api来获取哪些数据,在request.js中定义api对应的方法。
  3. 后端还没有写好时,学会用mock平台来mock接口。当时做项目时使用的是fast-mock,是国内的平台,比较快(之前一直用easy-mock,但现在的easy-mock非常卡,不推荐)。
  4. 使用qs.stringify来序列化post传输的数据。在没有使用qs.stringify前,当数据提交到后台之后,发现传过去的数据一直是null,这个有可能是后台那边接受的数据格式问题,axios默认传输的是json格式的。使用qs.stringify的话,是以Form Data格式传过去的,会将各个参数用&进行组合,拼接到路由上。所以,这里要注意后台要求的数据格式。(参考文章:axios中使用qs的作用是什么?)
    这个解释写的很好:
    在这里插入图片描述

项目后期

前后端联调

前后端联调是项目的必经之路。

  1. 为什么要前后端联调
    因为需要测试前端和后端的功能是否正常
  2. 前后端联调会遇到什么问题
    跨域问题。因为后台是部署到某个服务器上的,那它的ip地址,和你在本地运行前端项目(React一般是在localhost:3000)不在同一个域内,前端向后端发起请求时就会有跨域问题。
跨域问题的解决

跨域问题最简单的解决办法就是使用CORS,服务端在响应报文头部添加access-control-allow-origin字段,设置为*(网上都说设置为*无法携带cookie,但是设置了axios.default.withCredential之后就可以带上cookie了,这里还没太弄清楚),还有Access-Control-Allow-Credentials允许携带cookie。如果想要携带cookie,除了在服务端进行设置以外,客户端也需要设置携带cookie,比如:

axios.defaults.withCredentials = true;

后端设置参考文章:
Spring Boot如何解决前端的Access-Control-Allow-Origin跨域问题
在这里插入图片描述

前端如何部署到服务器上

需要将前端部分部署到服务器上只需要三步:

  1. 在package.json中添加homepage的定义(如果不定义,打包好的index.html文件是空的):
"homepage": ".",
  1. 在前端项目中使用npm run build,将前端文件进行打包。
  2. 将打包好的build文件夹给后端,后端将build文件夹内的内容放置在:
    在这里插入图片描述启动服务器后访问http://xxx/index.html就有效果了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值