正在努力加载中...
2018-纳尼酱
  1. 1. 功能设计
  2. 2. 页面预览
  3. 3. 技术相关
    1. 3.1. 前端相关问题
    2. 3.2. 后端技术
    3. 3.3. 更好的写法
  4. 4. 小结

大三下学期的实训项目,选题是一个在线日语单词的记忆网站,想着出来可以帮自己坚持学日语呢。因为只有三周时间,所以这次我觉得自己能完成核心的记忆、测试,以及查词的功能就足够了。另外自己选择主要技术是vue.js + koa2+mysql+redis。因为还没有学过koa和redis,也所以这次正好可以练练,反正让我用JAVA写后台我是不愿意啦>_<。

功能设计

请看这张图:
功能结构图
其实一开始的时候,我的功能设置并不是这样的,但是在自己的能力内,然后边思考边修改,最终呈现的就是这样一个效果了。主要的导航其实就是“单词本”和“查单词”两个按钮。
登录之后的个人控制版面,就拥有和自己有关系的单词呀,计划呀,单词本和学习计划之类的管理。

顶层数据流图
这是顶层的数据流图,其实还有一些操作没有写出来。
一级数据流图
然后再介绍一下,我的实体,和实体之间的关系:
E-R图
这里有几个比较容易让人感觉迷惑的点:

  1. 一般来说,一个单词会有多种意思(1:n),一种意思又对应多个例句(1:n)。我实际上是把每种意思重新插了一条进去,因为都放在一条的话,对例句的处理和显示会比较麻烦。如果拿单词+意思两个做主键的话,又容易产生很多数据杂糅,所以选择了这样一种折中的方法。
  2. 关于计划,其实计划美名其曰是这样,但是实际上它主要就是一个桥梁,建立起用户和单词书之间的关系,不过其实也包含了几个字段,比如创建日期还有每日记忆的数量。

页面预览

页面的设计自然是摆在最前面的,思考了好久才确定好了设计的风格,脑海里就是希望能简洁+可爱+扁平化,因为想要给人是一种比较有“活力”的网站,所以最后选了蓝绿色来做主题色,展示部分静态页面:
测试页面
登录页面
查单词页面
单词结果页面
再来摆一下自己设计的LOGO(字体用的是现成的)和默认头像,还是第一次自用Adobe Illustrator按自己的想法画出了正经的东西,虽然还是有很多不足(总感觉还是有点违和),但是不能在这上面花太多精力了,噗:
LOGO
默认头像
对于这个界面的设计,自己其实还是纠结了很久的,不过后面经过慢慢的修改和调整,算是确定了一个自己想要的风格,也就是现在这个感觉。但是我觉得还可以使用更丰富可爱的插图来代替一些地方,不过那是长远的计划了。包括这个记单词的系统也是的,也还有很多日语学习的功能想要扩充。

技术相关

其实做到后面之后,很多问题都是比较雷同的,所以这里我就讲几个比较频繁的问题吧

前端相关问题

Q:音频加载异步问题
很明显的一个异步问题就是在载入音频的时候,因为要不停地切换音频的地址来播放单词的发音,用v-if对DOM进行删除和添加显示也好,用v-show来直接对source进行更换也好,它都会出现载入中断的错误。后面查询了一下百度后发现,需要在每次更换source的时候,先load一次,这样就能安全的进行更改了。但是还有一种情况是,可能单词的发音还没来得及发音,用户就已经切换成了下一个单词,最后的时候,我就把切换的间隔时间加长了,也正好可以利用这个时间显示一下正确答案。

Q:浮点问题
这个超级有意思哈哈哈,就是我在统计我的正确率的时候,需要做一个百分号计算,然后我刚开始的做法就是Math.floor(正确回答次数/总回答次数)*100,结果后面发现,出现了一串小数点,然后就用.toFixed(2)来设置为2个小数点,但是算出来的还是有的情况是无数个小数。这样我马上意识到了JS的浮点精度之类的问题,最后就是一定要在结果都算完了后再进行.toFixed的计算。

Q:table-cell垂直居中起效的前置条件
table-cell类似于表格中的tr,所以按照verticle-align: middletext-align: center理论上是可以居中的。以下是我自己的测试结果:
table-cell这个盒子必须要有宽度和高度,要不然它就会只占内容那么个大小。并且宽度和高度必须是具体的数值,不能是百分比,要不然也没用(除非这个时候又有一个父盒子,设置为display: table,table-cell就会占满这个table)。
首先,如果table-cell里面的内容是inline/inline-block,是可以垂直居中的。
如果是block的块,它只会在垂直上显示居中,水平方向要设置margin: 0 auto才能实现。

Q:table-cell水平排列起效的前置条件
做的时候碰到了table-cell的块不能并排的情况。首先,如果父盒子是display: table,盒子都会挤在一条,并且按照设置的宽度比进行一个分配。这里的情况是,父盒子为block,也能并排在一起。

Q:在内容高度不足的时候,Footer能否贴近底端?
传统的CSS来说,给容器设置一个min-height应该是可以定位,后面可以借助CSS3的sticky这个选项。

Q:v-charts进行图表统计展示
v-charts是饿了么团队开发的一些vue图表组件,目前我还是简单的按照文档用了下,还是挺方便的,文档传送门:https://v-charts.js.org/

Q:for…of理解错误
必须要迭代器(Array,Set,Map等)才能进行类似操作:

for(var [key, value] of sth) {
  // ...
}

而普通的object并不是。

后端技术

Q:洋葱模型异步问题
因为去数据库中调取数据是异步的,所以会有拿到的结果是空的情况,就算是用promise也不好使,因为KOA的ctx在返回body的时候必须写在最外层,写在promise的回调函数里没什么用。所以这个时候就用到了async和await来解决。
但是这里也有一个问题,就是如果这几个promise是在一个层次的,它是可以等前面的执行了再执行,但是如果是调用模块引入方式的,它并不会进行等待。所以最后的时候,我用回调函数的形式进行封装,然后完成了自己的需求。

Q:验证码的存储
我准备做一个邮箱验证的功能,因为验证码是过一段时间会失效的,并且在用户断开网络后,重新连接,还是可能能用,所以最好还是存在数据库里面,但是存在内存里就够了(因为它还是会失效),所以这里就使用了redis来作为存储辅助。

Q:Koa2初认识
使用koa2-generator搭建出一个项目结构。根据koa-router配置的内容来接受请求。bodyparser帮助我们规格化接收到的数据。

Q:如何实现token验证
我没有使用cookie的方式来加载token验证,主要是在跨域的时候如果需要携带cookie就必须指定某一个域名才行,但是这个过程还是很纠结,因为百度了很多感觉说的不是很清楚这个过程。我最后的做法是后端把token存在redis里面,前端把token存在vuex和localStorage里面,在发送请求的时候直接放在了报文主体上。但是后面看了jerry的博文,才知道大家经常说的把token放在请求报文头用authorization字段来表明,是为了让用户感觉不到这个token的存在,这个时候我才恍然大悟哈哈哈。
有一个很常用的模块jsonwebtoken,可以直接根据一些信息生成一个token。

 const token =const jwt = require('jsonwebtoken')
 jwt.sign({
    ...: ...
  }, '...', {
    expiresIn: '1h'  //过期时间
  });

Q: Koa2解决跨域
使用koa2-cors模块,很容易就能实现:

app.use(cors({
  origin: 'http://localhost:8080',
  exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
  maxAge: 5,
  credentials: true,
  allowMethods: ['GET', 'POST', 'DELETE', 'PUT'],
  allowHeaders: ['Content-Type', 'Authorization', 'Accept']
}))

这里有一个很有意思的地方就是,因为我设置了请求允许携带cookie(虽然并没有用到cookie,摔(′д` )…彡…彡),而且在实际开发过程中,它也不会直接就一个星号来结束,所以它的域名设置只能设置一个。但是我不仅仅是前端页面,我还有管理后台的前端页面也要进行申请,所以我要设置多个。后面发现唔,不能传数组,也不能拼接字符串,结果就是只能返回一个。最后的解决方法是借鉴了java里常用的一种方法:

origin: function (ctx) {
if(ctx.request.header.origin == ‘http://localhost:8080‘ || ctx.request.header.origin == ‘http://localhost:9528‘)
return ctx.request.header.origin;
}

也就是在配置koa2的时候,然后把申请的那个origin地址返回去,加上if语句来判断,这样就可以按照限定的进行跨域请求了。

Q:获取用户的IP地址
这里要获取原本的reques对象,直接let req = ctx.req就是原本的对象,然后:

req.headers['x-forwarded-for'] ||
  req.connection.remoteAddress ||
  req.socket.remoteAddress ||
  req.connection.socket.remoteAddress;

Q:传输过程中带有特殊字符,造成解析错误
使用axios发送数据的时候,如果是设置了:

   headers: {
     'Content-Type': 'application/x-www-form-urlencoded'
   }

后台解析数据的时候,会解析错误。原因是因为发送的时候是一个整的JSON字符串,也就是它是一个key,它的属性为空,所以解析必定出错。
解决方法是发送的时候加一个转化的函数:

transformRequest: [function (data) {
    let ret = ''
    for (let it in data) {
      ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
    }
    return ret
  }]

Q:Koa2操作redis
电脑安装好redis,打开redis-cli。
项目中引入npm包redis,查看官方文档会发现很容易操作:https://npmjs.com/package/redis

Q:实现邮箱验证
使用nodemailer这个包,在网易邮箱或者QQ邮箱之类的注册好一个账号,开启它的SMTP服务,设置密码啥的。

var config = {
  host: '...',
  port: 25,
  auth: {
    user: '...',
    pass: '...'
  }
}

var transporter = nodemailer.createTransport(config)

module.exports = function (mail) {
  transporter.sendMail(mail, function (error, info) {
    if(error) {
      return console.log(error);
    }
    console.log('mail sent:', info.response);
  });
};

这个导出的模块函数直接接受一个写了邮件信息的对象,执行后就会去发送了:

var mail = {
  from: 'xxx <..@.>',
  subject: '...',
  to: '..@..',
  text: '...'
};

Q:git退出服务的时候端口还是占用的情况
对于后台应用,输入npm run dev方式启动是可以自动刷新服务的,如果是npm run start按了ctrl+c或者ctrl+d它的端口还是会被占用(应该是开了另一个线程吧)。
windows的话,以管理员模式打开命令行,输入netstat -aon | findstr '3000'就可以找到那个PID是多少,然后再taskkill /pid 那个进程ID /f强制进行关闭就可以了。

更好的写法

其实我的代码是非常,唔,糟糕的,因为写的很急忙,没有时间去整理。后面写着写着发现其实有很多更好的写法:

  1. 在vuex的使用里,我完全可以把用户的信息写在一个对象里,但是我把他们属性一个个都分开写了,这样重复的代码就很多。
  2. 在传props的时候,官方更推荐,传简单值,但是我写了很多对象嘻嘻,而且也没有约定好类型。
  3. 在写后台的时候,我应该建立数据模型的,用面向对象的编程思维去做。
  4. 给vue的对象属性之类的赋值它不会检测更新,这个时候官方更推荐用Object.assign()的做法。
  5. 把图片转成base64编码可以直接用post方法传过去,不用formdata的创建,但是这样的话它对文件的大小也有要求的。
  6. node端要把HTTP的状态码都充分利用起来,这样直接抛一个错误过去,前端能用catch来捕捉错误判断最严重的侵权之类的错误,其他小错误就可以其他再判断了。

小结

有一些东西都是初次接触,所以目前了解的都还比较浅,我的感觉就是好好看文档真的很重要,还有就是发现自己在计算机网络有的方面存在一切误解,希望自己能发现更多慢慢更正过来。再然后一个问题就是自己的进度不算快,仔细想了想这样的任务量老师可能还不会满意,所以后面我很努力的(赶集样的)把东西都做完了。当时做的时候可以说是风风火火了,答辩完下午就跑去北京实习了,最后的结果还算满意。

2022年的糕糕有话说:说实话现在回去看当时的自己真的好稚嫩啊!!(甚至有的我自己都看不懂是在说什么问题了,这就是成长吗~~~///(^v^)\~~~)哈哈哈哈哈

上一篇 下一篇
ABOUT
欢迎来到小年糕的后花园,年糕的小站开设于2017年,博主年糕君是一个爱好十分广泛的人,也是一个比较佛系的人,不定时爆发脑洞更新(因为是社畜,也可能失踪很久,工作太累了)。 查看更多