网站横幅在线制作:一个人,小伙是怎么在45天开发完一款微信圈子
本文摘要: 之前主要说的是,我是怎么发生这个圈子小程序的主见和怎么上线的。有爱好的朋友可以回去参考前面两篇文章。这次来给我们讲讲,在技能上是怎么完成的。怎么快速把主见变成产品分享作为独立开发者阅历和一些上线阅历技能栈前端主要
之前主要说的是,我是怎么发生这个圈子小程序的主见和怎么上线的。有爱好的朋友可以回去参考前面两篇文章。这次来给我们讲讲,在技能上是怎么完成的。

怎么快速把主见变成产品

分享作为独立开发者阅历和一些上线阅历

技能栈


前端主要是基于Taro+Typescript+dva框架完成的,后端根本上是以Ruby on Rails为主。

这里说下为何要做这样的技能选型,关于技能选型,在《奔跑吧,程序员》一书中有很详细的分析,后边我会我在我的读书笔记系列把这本书做一次分享。

这里主要说下技能选型主要判断,假如是快速成型的项目,应该选择更加轻量的言语和有很多社区组件支撑的,另外一个就是自己熟悉的。

前端

为何用Taro?主要是以往本身阅历抉择的,认为本身我做了13年技能研发,虽然后边几年自己着手的时间少了,但也算在一线工作。前几年React Native刚兴起的时分,关于有着js开发经历和安卓开发经历的我很快就上手。借着之前饿了么做hybrid移动端动态模板烘托的经历,让我迅速了解了React的设计理念和原理。

所以这次就顺畅的用了以React为主的Taro前端开发框架,虽然uni-app台甫鼎鼎,但毕竟要从头了解vue。原先redux那一套回忆起来相比照较快。这里不去争论对错,能让你舒舒服服的快速完成,那么就是对的。

说回Typescript,记得曾经写js碰到最大的问题是无法提示,关于我们这种全栈开发来说,切换太快了,所以会花很多时间查他下面有哪些方法等,为了节省代码量,有段时间还张狂的写coffee script。虽然项目里写的还不是很规范,但确实解决了我很大一块问题。

看到这里肯定有人想说,那是你没用对。就好像Vim、Emacs一样,很多人觉得太便利了,为此我还专门花了时间去学vim的快捷键。可是终究用起来仍是习惯不了,切换模式dd删除,我仍是比较喜欢在VS Code里用`command + x` 来当删除用,十几年的习惯,不是说改就改的。

仍是那句,没有对错,只有你自己习惯就好。

后端

其实这几年写的比较多少的仍是Java,但这里就不多说了,现在的项目也没做过太大的压测,但假如用户量大到扛不住,那么再说甜蜜的烦恼问题。从头花时间调研了下Ruby on Rails,发现新增了很多特性,如Job、ActiveStroge、Webpack、turbolinks等等,关于全栈开发来说支撑愈来愈好了。

最重要的是Ruby on Rails对测试的支撑十分好,我个人习惯就是假如代码没有测试掩盖,我很容易改出问题,因为时间久了或者对这块事务不熟悉了,很容易有问题呈现。

小程序



项目结构


先丢张根本的结构图让我们了解下
  • pages 是小程序所有主页面的目录
  • models 是数据层,负责获取长途数据和处理数据的
  • components是组件层,主要是一些组件和放复用页面的
  • services是网络层,主要是各种api调用的
  • utils是各种协助类

大约的流程是这样的,这里不做过多说明,有爱好的朋友自己去看dva框架和redux


下面主要这次开发圈子小程序碰到的一些问题和我解决的思路和方式,假如你有更好的,欢迎找我交流。

登录

说真的,登录对错常烦人的一件事。之前一直有一定的概率呈现**Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt**,后来在官方的文档中找到:

在bindgetphonenumber 等返回加密信息的回调中调用 wx.login 登录,可能会改写登录态。此时效劳器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,导致解密失败。建议开发者提行进行 login;或者在回调中先使用 checkSession 进行登录态查看,防止 login 改写登录态。

所今后来我调整成
  1. 在进入页面后,会进行一次login,然后把获取的code保存起来
  2. 当用户点击登录按钮,会再一次进行checkSession查看,防止登录态失效
  3. 然后把获取到的`code`, `encryptedData`, `iv`悉数提交到效劳器进行解密和校验

Textarea穿透问题

圈子小程序里有个功用,用户可以评论某个人的帖子,然后会导致一个问题,就是底层的输入框会在弹层上层。详细可以参考下面这张图,我当时没截图。无论你怎么设置z-index都没用。


导致这个问题的原因是,Textarea是原生组件,层级会高于网页组件,所以我是这样解决的。
  1. 当Textarea失掉焦点后,把内容存储到内存里
  2. 隐藏Textarea,并显示一个View
  3. 填充之前缓存的内容

不过这样做又会导致另外一个问题,有可能因为焦点问题,缓存里没内容,用户直接点了发送按钮,这个时分就需要判断提交的内容里有没内容,有就直接提交,没有就用缓存里的。都没有就做非空提示。

还有就是有同学提到,输入框会被键盘盖住的问题。类似下图


这里解决方式也相比照较好解决,参考官方文档这个属性


dva-model-extend和model层

往往在完成逻辑的时间发现很蛋疼的问题,就是几个页面逻辑差不多,但有有点不太一样,然后这个页面又耦合了一些其他模块的逻辑。最多见的例如下面这个。


上图里边几块事务就触及了圈子、帖子、用户、赞、评论逻辑,还有自己页面的一些逻辑,那么我们应该怎么去划分呢?我们可以参考下图


事务基础类

主要是负责通用逻辑完成的,比如获取用户相关的baseUser,帖子相关的basePost等等,但他们没有自己的namespace,不能直接调用,只能作为基类存在。

事务类

就是负责通用事务真正被调用的,比如用户类userModel、帖子类postModel等等。这样做有什么优点,那就是任何页面都可以去调用。

比如我想在帖子列表的页面里获取每一个人的用户信息,那么我可以直接dispatch一个user的type。他的逻辑相对规范。

界面类

就是为每一个页面提供特性效劳的类,比如上面图里,我有定制对帖子的返回内容要单独存一个state,那么我就能够继承基础事务类,然后更改他的reducers

的action完成。并且每一个界面类都会关联一个page。

数据类

为何数据类单独的?这有什么用。在前端,我们碰到的很大的一个问题就是,比如A页面用了用户信息,B页面也用了用户信息。假如依照以往的做法,每一个页面单独维护一个用户信息,然后通过eventbus来更新到每一个页面,这样做的问题是很多的冗余数据放在内存里,然后event满天飞,终究也不知道这个页面的数据被哪一个当地触发改变了。

所以需要曾经数据层来维护,有点像前端的内存关系型数据库,界面拿到一堆ID,然后在要显示的时分才会去数据层查询详细的数据,然后烘托界面。

数据烘托

上面提到,以往我们都是直接烘托数据的,然后通过eventbus改。这样还会碰到一个很麻烦的问题。

仍是圈子小程序的例子,假如我要删除一张帖子里的评论,我需要怎么做?
  1. 找到帖子该帖子的数据
  2. 找到帖子里的评论数据
  3. 因为评论多是子评论,还需要在先找到上级评论后再找到其时评论。
  4. 防止其他页面数据未更新,发送工作告诉其他页面也需要重复一次以上操作

最开始一度让我很奔溃,底子没有方法继续继续下去,并且还很容易出问题,测试的工作量也倍增。

然后我找到了前端神器normalizr,这个库可以帮我们完成上面数据层说的工作。详细流程可以参考下图。


为节约篇幅,这里不做过多解释。因为所有页面都是引用性质,所以一旦数据发生变化,所有页面都会跟着变化。并且处理数据只需要处理一层的关联,不需要处理多层的数据结构,因为它帮你把数据进行扁平化处理了。

总结

上面分享了项意图根本结构、逻辑分层、数据处理的一些思路,相信应该对我们开发小程序有一定协助。

后端


说完前端的根本架构,现在来说说后端。关于初期的项目来说,前端只需处理好数据和逻辑的架构,其他都是一些界面的问题和css相关膂力活和不断的多设备兼容调优。

后端的事情比较了多了,比如监控、数据处理、微效劳、容灾等等,这些年或多或少触摸了一些,但作为新项目,这些东西反而不是最重要的。

完成一个新项目,最重要是怎么更快的迭代和提供新接口。crud仔的名声不是随意说说的。

从早年的WebService到现在的微效劳,概念一直在更新,但本质上没有太大的改变。都是期望下降风险,早年我在小秘书的时分就开始做SOAPWSDL,但关于立异事务来说,技能不该该作为阻碍功率的存在。

当我听到为了一张表而专门创建一个效劳的时分,我反而觉得是为了微效劳而搞微效劳。当我想改一个问题的时分,我需要从网关一路改到终究层的效劳,明明几分钟能解决的事情,在调试上硬花了一整天。

每一个人观念不一致,技能没有对错。面对不同的布景,每一个人选择不一样。我见过很多技能架构很好,但迭代慢死掉的公司。也见过很多内讧很严峻,但仍然开展很好的公司。

前面略微说的有点偏题了,回到主题。草创项目主要处理好几件事情。当然你有其他观念,欢迎评论。里边有些当地参考了ruby-china的源码,十分感谢。
  • 接口及呼应模板
  • 过错捕获及告警
  • 权限校验
  • 布置和测试

接口及呼应模板

怎么了解接口及呼应模板呢?说白了就是你的接口能返回数据。

这次我没有选用Grape的Gem,而是直接使用了Rails Api和Jbuilder的烘托模板。

首要我创建了一个父级烘托模板

# app/views/layouts/api/v1/application.json.jbuilder

json.code 200

json.message @message.blank? ? ‘’ : @message

json.data JSON.parse(yield)

也就是无论怎么都会返回codemessagedata这三个keydata可能为Array或者Object

然后在application_controller.rb里指定父layout

layout ‘api/v1/application'

然后在application的目录里为每个实体做一个通用的模板,如_user.json.jbuilder,通过参数判断是简易仍是杂乱对象。

比如你在列表里user可能主要3个值,nick_name,id,avatar,当你详细查看某个人的资料时,你可能需要知道他的其他信息,例如age,gender,cellphone等等。

然后相应的接口烘托可以参考下面的

json.partial! ‘user’, user: @user, detail: true

根本上你接口的呼应就到这里就完毕了。补充一点,假如你是使用Rails Api的话,使用Jbuilder需要加入以下引用

class ApplicationController < ActionController::API

include ActionView::Layouts # if you need layout for .jbuilder

include ActionController::ImplicitRender # if you need render .jbuilder

过错捕获及告警

这里分为几块

过错码

你可以选择新建一个专门的类来维护过错码

module Api

module V1

module Code

module HttpBase

HTTP_FORBIDDEN = 403

HTTP_INTERNAL_SERVER_ERROR = 500

HTTP_UNAUTHORIZED = 401

HTTP_BAD_REQUEST = 400

HTTP_UNPROCESSABLE_ENTITY = 422

HTTP_NOT_FOUND = 404

HTTP_BAD_GATEWAY = 502

HTTP_OK = 200

HTTP_CREATED = 201

HTTP_NO_CONTENT = 204

HTTP_METHOD_NOT_FOUND = 405

end

module HttpExtend

INVALID = 10000

end

end

end

end

过错音讯

这里就不提了,我直接新建了一分api.zh-CN.yml的I18n来维护过错音讯

过错类

这里略微讲一下,主要是分红3种三类别
  1. 体系过错类别,比如体系宕了,数据库查询报错,参数判断为空这类体系异常类,由体系抛出,捕获处理就行了。
  2. 另外一种是事务异常,比如这个人查不到,输入的东西包括敏感词等
  3. 第三种就是长途调用异常,这种属于后端调用过错,可能包括重试

因为不包括杂乱数据对象,所以过错直接render json就行了。

权限校验

这里用了`cancancan`,详细使用我们自己去github上看吧。这里讲下怎么应用的。主要分两块,第一块是后台事务逻辑的权限判断。

后台事务权限

if @user.blank?

roles_for_anonymous

elsif @user.admin?

can :manage, :all

end

elsif @user.normal?

roles_for_members

elsif @user.blocked?

roles_for_anonymous

else

roles_for_anonymous

end

可以参考上面,分为以下几种状况
  • 未登录 - 允许部分只读
  • 管理员 - 允许所有操作
  • 普通用户 - 按权限划分
  • 禁用用户 - 允许部分只读

这里说下roles_for_members权限,里边会有更详细依照各个事务进行划分,事务的权限会有接口级其他权限条件判断。

前端事务权限

比如在前端的时分,每一个人的状态是不一样的,比如我作为圈主,我可以删除自己圈子里一些用户发的帖子,但我不能删除其别人发的帖子。那这里是怎么做的?

在前端烘托的时分,每一个对象都会带一个权限表,标明我可以对这个对象做什么?

if object && object.is_a?(User)

%I[ban report].each do |action|

json.set! action, can?(action, object)

end

end

比如上面就是,我点开或人的信息页,会返回我对这个人是否可以禁用或者举报。类似的还有对帖子是否可以删除,置顶扥等。

布置

关于布置来说,方式有很多。早些年容器化技能还没盛行的时分,我们都需要配置环境,然后用Capistrano进行布置,或者用jenkins等等。

可是关于刚开始的项目来说,没必要搞那么效劳,可能你的效劳器一共就2个G,你还在上面折腾个jenkins,真实是不划算。所以这里主要是用

docker+docker-compose来做。

我们可以看张图,便利了解


首要是Dockerfile

# Dockerfile

FROM ruby:2.6.5

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -

RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list

RUN apt-get update -qq && apt-get install -y build-essential nodejs yarn

ENV APP_HOME /app

RUN mkdir $APP_HOME

WORKDIR $APP_HOME

RUN gem install bundler:2.1.2

ADD Gemfile Gemfile.lock yarn.lock $APP_HOME/

ADD vendor/cache vendor/cache # 因为是开发机直接布置,没必要从效劳器再每次拉一边,所以先缓存了下来

RUN bundle install

ADD . $APP_HOME

RUN yarn install --check-files

RUN bundle exec rails assets:precompile

然后是docker-compose.production.yml

version: ‘3’

services:

your_project_name-production:

image: #长途image

container_name: #容器名称

command: bundle exec rails s -e production

volumes:

- /app/log/#{your_project_name}-production:/app/log

ports:

- “7001:3000”

env_file:

- .env.production

network_mode: bridge

your_project_name-production-backup:

image: #长途image

container_name: #容器名称

command: bundle exec rails s -e production

volumes:

- /app/log/#{your_project_name}-production-backup:/app/log

ports:

- “7000:3000”

env_file:

- .env.production

network_mode: bridge

终究是我们的布置脚本deploy-production.sh

#!/bin/bash

export SERVER=‘#你的效劳地点’

export service=‘#你的项目名’

# 先打包bundle gem

bundle package

# 1. 传输布置文件

scp .env.production $SERVER:/app/$your_project_name/.env.production

scp docker-compose.production.yml $SERVER:/app/$your_project_name/docker-compose.yml

scp Dockerfile $SERVER:/app/$your_project_name/Dockerfile

# # 2. 打包镜像

# 现在我用的是阿里云的镜像效劳

docker build -t $镜像地点 .

# 3. push镜像

docker push $镜像地点

# 4. Pull镜像 && 启动

ssh $SERVER << EOF

cd /app/$your_project_name

docker-compose pull $your_project_name

docker-compose down && docker-compose up -d

docker-compose run -d $your_project_name bundle exec rails db:migrate RAILS_ENV=production

rm -rf .env.production

docker image prune -f

docker container prune -f

EOF

docker image prune -f

docker container prune -f

echo ‘deploy success!!!’

上面方式当然存在很多问题,比如布置失败他也提示成功等等。可是这个时分你也很容易到机器上去解决问题,或者更改配置进行镜像回滚。

测试

测试主要走rspec,详细各位自行去了解吧,有点写不动了。因为独立项目,所以没有人会帮你测试,你需要自己保证代码的强健性。假如我们感爱好,回头专门开一篇讲测试思路的文章吧。横竖尽量保证你所有接口和核心类的测试用例掩盖就行了,像我的话代码放在github,直接接 Travis CI 就好了,保证你每次master分支的代码都能通过测试才发布。


终究


欢迎各位进行进行评论评论,相互学习。近期很多朋友找我聊项意图一些布景和主见,只言片语无法讲清楚。后边会写篇关于我对社群的一些了解和商业模式的一些观点,欢迎继续重视我。

专注互联网创业分享,独立开发者。全网同名:卢灿伟

【免责声明】本文仅代表作者或发布者个人观念,不代表(www.lmnkf.cn)及其所属公司官方发声,对文章观念有疑义请先联络作者或发布者自己修正,若内容触及侵权或违法信息,请先联络发布者或作者删除,若需我们协助请联络平台管理员,Emailcxb5918(本平台不支撑其他投诉反馈渠道,谢谢合作)。若需要学习以上相关常识请到巨推学院观看视频教程,网站地址www.tsllg.cn。

相关内容