代理系统开发记录

2017/8/31 JavaScriptReact

React + Express + MongoDB

# 工具们

# Yarn

之前一直使用npm,但是有时候实在是太慢(改host也比较麻烦),我就换用了cnpm。听说上个月出来了个新的包管理工具:Yarn,我先拿来试一下......

话说Yarn的官网全是喵。咪桑万岁!

# Version

yarn install v0.27.5

# ex-GF => NPM

npm啥都好,就是没有科学上网前,这就是新世界...

npm会把依赖安装在node_modules中,但是这个过程具有不确定性:当依赖的安装顺序不同的时候,node_modules的目录结构可能会发生变化。

=>结果可能是在不同机子上可能会出现不能运行的情况。

话说我是没有见过这种情况,可能是package.json可能会发生版本匹配错误啥的。为了预防万一,偶尔还是把node_modules一起存一下。(7z压缩这个贼快!)

# 特性

以下,从官网 (opens new window)上看到的...

超快:从缓存中拿到下载过的包,重复利用 安全:自动校验包的完整性 可靠:各系统无差异工作

# 差异

  1. yarn.lock,解决了只用package.json可能会发生的版本匹配错误。
  2. 并行安装依赖,缓存中获取依赖
  3. 好多命令变了

# 使用

因为node自带了npm,所以安装yarn只需npm install yarn就好。

二房居然是大房拉进来的!天呐。

  • 初始化新项目(npm init)
  • yarn init
    
    1
yarn init
1
  • 添加依赖(npm install)
  • yarn add
    
    1
yarn add
1
  • 更新依赖(npm upgrade)
  • yarn upgrade
    
    //感觉不怎么用,除非有特别需要的功能更新了,更新依赖可能需要修改依赖的使用方式。
    
    1
    2
    3
yarn upgrade

//感觉不怎么用,除非有特别需要的功能更新了,更新依赖可能需要修改依赖的使用方式。
1
2
3
  • 删除依赖(npm uninstall)
  • yarn remove
    
    1
yarn remove
1
  • 安装依赖
  • yarn
    yarn install
    
    1
    2
yarn
yarn install
1
2

# Express

在毕业设计里我有使用过Express,简单的使用,可以在毕设相关的文章里了解一下。

这次的项目由于会上线使用,如果需要,我想更深入的学习一下Express。

yarn install v4.15.4

# 对比

毕设中,我用Express来作为整体框架,前后端并没有分开;这一次,我使用前后端分离的理念来做。

So,区别还是有一些的,从route.js来说明。

# route.js
  • 毕设

请求方法有getpostdelete,效果为在页面中直接使用地址即可访问对应页面。

使用了依据Schema对应写了对应的controller方法,通过render来调用模板引擎。

模板引擎我选择了Jade。

  • 本次

请求方法只是post,因为这些接口我需要在前端使用fetch来请求。

请求调用了Schema的方法,对数据库进行操作

至此,Express的任务就没有了。

两个项目构造不同,所以Express的使用权重并不类似,如果非得说相似处,只能说Schema通过mongoose来操作数据库的方法类似,都是返回json数据,一个是引擎调用,一个是接口获取。

# React

运气不错,找到了很多文档,比较方便去了解。

说白了,就是在写组件,用props来传入数据,用不同的状态来操作数据去刷新组件。

另外,由于对react组件状态的了解不是很深入,没有点得心应手···

# React-Router

这个是真的坑。

百度搜索只有v2.X的文档,然后我以为新的就是2.X版本,没想到我用的已经算是新版本4.X了,其中的差距非常大。

为了赶时间,我并没有看说明上的例子,直接开始写,就采坑无数...

# MongoDB

还是使用mongoose操作,用到了更多的方法,也更熟悉了Robomongo。

有意思的就属于模糊查询、条件筛选啦

# 初想

# 蓝图

关于页面结构的想法

刚开始,我没有想的很深入,只是草草的画了一下结构,还有页面的组成。

实在是草图,很随意···

# 页面结构

页面结构如下图所示,其中名称后有#的为非重点页面(不一定实现)。

graph TD
    A[登录页] --> B[管理员首页]
    A[登录页] --> C[代理首页]
    B --> D[项目管理]
    B --> H[用户管理]
    D --> E[项目查看#]
    D --> F[项目查询]
    D --> G[项目状态修改]
    H --> I[新增用户]
    H --> J[删除用户]
    H --> K[充值用户]
    H --> L[查询用户]
    C --> M[项目管理]
    C --> N[查看账单]
    C --> O[项目申请]
    C --> P[账户设置]
    M --> Q[项目状态修改]
    M --> R[查看项目详情]
    N --> R
    O --> S[选择模板]
    O --> V[保存申请]
    P --> T[基本设置]
    P --> U[修改密码]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 技术结构

sequenceDiagram
React->>Express: I need data.
Express->>MongoDB: I told you.
MongoDB->>Express: I find this!
Express->>React: Hey man,look at this.
1
2
3
4
5

大概就是这个意思,中间的通讯方式自然就是接口啦。

# 实现

初上手的感觉就是无从下手,但是在一切都梳理好之后,还都不算很难。

# 代码

代码也在github的这个项目:Agent-customer-management-system (opens new window)上放着呢,但是我还不会用npm上传那种方式,就只好把除了node_module的其他文件上传。

另外,我没有上传数据库备份,拿下来的肯定是没有数据的。

#

大概记录一下做项目中间遇见的坑。

# Q1·React不能引用插件

问题描述:

在本地写的react项目,转化为node上的react后,原来的插件不能用。

解答:

实际很简单,插件中没有export语法,所以在引用插件时会发生很多问题。解决办法就是用插件对应的react版本或者自行改造。

刚遇见的时候以为碰见什么巨难的问题了,所以就写了篇,记录一下。文章链接 (opens new window)

# Q2·修改链接(React-Router v4)

问题描述:

退出功能时需要修改链接(返回登录页),失败

解答:

不是真的失败,是版本修改之后需要使用withRouter才能push新路径。

代码:

import {
 Link,
 withRouter
} from 'react-router-dom';

class Header extends Component {
    A(){
        this.props.history.push("/");
    }
}
export default withRouter(Header);
1
2
3
4
5
6
7
8
9
10
11

# Q3·修改路径之后不主动刷新(React-Router v4)

问题描述:

这个问题是出在上一个解决之后,链接成功跳转但是组件没有刷新。

解答:

原理是什么我还没有搞清楚,但是解决的办法就是手动刷新···

window.location.reload();
1

# Q4·Link跳转时组件不出现或刷新后消失

问题描述:

问题在于我页面间切换时发现链接变了但是组件没有变。

解答:

实际上是由于我没有明确组件的概念,把react组件与router混着写,虽然看起来没有报错,但会出现很多奇怪的问题。

在分开之后,明确什么是container文件夹应该有的代码,什么是component文件夹应该有的东西。

//container文件夹存放页面以及对应的跳转
import React, {
 Component
} from 'react';
import {
 BrowserRouter as Router,
 Route,
 Redirect
} from 'react-router-dom';

import Project from '../containers/project';
import Bill from '../containers/bill';
import ApplyPro from '../containers/applyPro';
import Setting from '../containers/setting';

import Header from '../components/Header';

class Main extends Component {
 render() {
  var username = document.cookie ? JSON.parse('{"' + document.cookie.replace(/;/g, '","').replace(/=/g, '":"').replace(/\s/g, '') + '"}').username : undefined;
  if (username === undefined) {
   return (
    <Redirect to="/" />
   )
  } else {
   return (
    <Router>
     <div className="body">
         <Header user={this.state.user} />

      <Route path="/main/pro" component={Project}/>
            <Route path="/main/setting" component={Setting}/>
            <Route path="/main/billList" component={Bill}/>
            <Route path="/main/applyPro" component={ApplyPro}/>
     </div>
       </Router>
   );
  }
 }
}

export default Main;

//component文件夹存放组件还有Link与强制跳转
import React, {
 Component
} from 'react';
import {
 Link,
 withRouter
} from 'react-router-dom';
import './Header.css';

class Header extends Component {
 render() {
  return (
   <div className="header">
    <div className="header-left">
           <span>欢迎 {$data.name} 进入XXX营销管理平台</span>
           <span>
              余额
              <span>{money}</span>{
               this.props.location.pathname === "/main/pro" ?
               <Link to="/main/billList">查看消费明细</Link> :
               <Link to="/main/pro">查看项目列表</Link> 
              }
              <Link to="/main/applyPro" className="applyBtn">项目申请</Link>
           </span>
       </div>
       <div className="header-right"> 
     <Link to="/main/setting">账户设置</Link>
     <a onClick={this.handleClick.bind(this)}>退出</a>
       </div>
   </div>
  );
 }
}

export default withRouter(Header);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

widthRouter

# Q5·react组件化

问题描述:

自以为明白了啥是组件化,就以为自己写的代码是组件化···

解答:

写完了才发现,当前这些我自认为是组件的东西换个地方很难复用。

现在心里面有个想法,就是把其中获取数据的部分提取出来,然后通过props传进去,以后再用,只需要把所有的参数都传进去就可以复用了。

找个闲了的时间,先提取一下看看可行与否。

# Q6·模糊查询

问题描述:

因为我需要做一个搜索的功能,所以就牵扯到了模糊查询。

解答:

官方文档是英文的,不是很好看。

所以我一般是先查询MongoDB有什么方法、功能,然后根据功能点去查询mongoose的方法。

看起来比较麻烦,但是实际执行起开还是蛮快的。

实现原理是通过正则表达式来匹配合适的值,如果有哪里不会查询了,先去想合适的正则表达式。

var _name = new RegExp(sortName, "g");
this.find({
 "name": _name
}).sort({
 "_id": -1
}).exec((err, userList) => {
 if (err) {
  //logger.error(err);
  console.log(err);
 } else {
  callback(userList);
 }
});
1
2
3
4
5
6
7
8
9
10
11
12
13

# Q7·mongoose创建新文档失败

问题描述:

我把接收到的json直接存入,报错save is not a function

解答:

出现这个问题是因为加盐(bcrypt)后存入,this混乱。

因为这个json只是属性一样,但并不是当前的Schema对象,所以不存在这个方法。

只需要使用当前Schema的方法就可以。

var self = this;
self.create(_newUser, (err) => {
 if (err) {
  console.log(err);
  callback("faile");
 } else {
  callback("success");
 }
})
1
2
3
4
5
6
7
8
9

# Q8·校验密码接口不会实现

问题描述:

因为校验密码需要用到User的多个static方法,直接把这样写在static里面不合适,写在外面时,我搞不明白参数的传递,导致一直报错。

解答:

说白了,是由于逻辑不清晰,导致传参混乱,然后报了一堆错误。

router.post('/checkPassword', function(req, res, next) {
 User.findUserByName(req.body.username, (user) => {
  if (null != user) {
   User.comparePassword(req.body.password, user.pwd, (err, isMatch) => {
    if (err) {
     console.log(err)
    } else {
     if (isMatch) {
      res.send(200, {
       status: "ok",
       username: user.name,
       userGroup: user.group
      });
     } else {
      res.send(200, {
       status: "error",
       message: "密码错误。"
      });
     }
    }
   })
  } else {
   res.send(200, {
    statuskey: "error",
    message: "用户名不存在。"
   });
  }
 });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

有必要专门对于参数这里,重新阅读js高程。

# Q8·自适应的样式不合适

问题描述:

没有明确自适应方案时,没有合适的显示自适应效果。

解答:

先想好,再写。

不要担心因为前期的构思而耽误太久的时间,在构思好后,比较花时间的只有两个:工作量和坑。

需要说明一下,遇见坑的时候,千万记得要找个新思路。我的方法是:和别人聊聊,思路就会丰富的多。

因为没有移动版的需求,自适应时没有用媒体查询,以后需要自己写个东西对移动端更加熟悉。

# 总结

嘿嘿,写个版本号激励一下自己。

暂时是可以打开测一测了(1.2.1),不知道还有哪些需要改的。

过程中因为踩坑没法解决,心情不好很多次,结局证明调整心态是做事中重要的一点,可以保证效率,保证质量。

其余的,就是业务、流程的调整还有上面说的组件化还会有一些工作量,其余的,就可以慢慢优化我的方法了。

希望这个项目可以被稍稍的认可。

_〆(。。)