一我的,小夥是如何在45天內開發完成一款微信圈子小程序的

以前主要說的是,我是如何產生這個圈子小程序的想法和如何上線的。有興趣的朋友能夠回去參考前面兩篇文章。此次來給你們講講,在技術上是如何實現的。css

我是如何一個月內把想法變成產品的前端

技術棧

前端主要是基於Taro+Typescript+dva框架實現的,後端基本上是以Ruby on Rails爲主。vue

這裏說下爲何要作這樣的技術選型,關於技術選型,在《奔跑吧,程序員》一書中有很詳細的分析,後面我會我在個人讀書筆記系列把這本書作一次分享。node

這裏主要說下技術選型主要判斷,若是是快速成型的項目,應該選擇更加輕量的語言和有大量社區組件支持的,另一個就是本身熟悉的。git

前端程序員

爲何用Taro?主要是以往自身經歷決定的,覺得自己我作了13年技術研發,雖而後面幾年本身動手的時間少了,但也算在一線工做。前幾年React Native剛興起的時候,對於有着js開發經驗和安卓開發經驗的我很快就上手。藉着以前餓了麼作hybrid和移動端動態模板渲染的經驗,讓我迅速理解了React的設計理念和原理。github

因此此次就順利的用了以React爲主的Taro前端開發框架,雖然uni-app大名鼎鼎,但畢竟要從新瞭解vue。原先redux那一套回憶起來相對比較快。這裏不去爭論對錯,能讓你舒舒服服的快速完成,那麼就是對的。docker

說回Typescript,記得之前寫js碰到最大的問題是沒法提示,對於咱們這種全棧開發來講,切換太快了,因此會花不少時間查他下面有哪些方法等,爲了節省代碼量,有段時間還瘋狂的寫coffee script。雖然項目裏寫的還不是很規範,但確實解決了我很大一塊問題。數據庫

看到這裏確定有人想說,那是你沒用對。就好像Vim、Emacs同樣,不少人以爲太方便了,爲此我還專門花了時間去學vim的快捷鍵。可是最後用起來仍是習慣不了,切換模式dd刪除,我仍是比較喜歡在VS Code裏用command + x 來當刪除用,十幾年的習慣,不是說改就改的。json

仍是那句,沒有對錯,只有你本身習慣就好。

後端

其實這幾年寫的比較多少的仍是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到如今的微服務,概念一直在更新,但本質上沒有太大的改變。都是但願下降風險,早年我在小祕書的時候就開始作SOAP和WSDL,但對於創新業務來講,技術不該該做爲阻礙效率的存在。

當我聽到爲了一張表而專門建立一個服務的時候,我反而以爲是爲了微服務而搞微服務。當我想改一個問題的時候,我須要從網關一路改到最後層的服務,明明幾分鐘能解決的事情,在調試上硬花了一成天。

每一個人觀點不一致,技術沒有對錯。面對不一樣的背景,每一個人選擇不同。我見過不少技術架構很好,但迭代慢死掉的公司。也見過不少內耗很嚴重,但依然發展很好的公司。

前面稍微說的有點偏題了,回到主題。初創項目主要處理好幾件事情。固然你有其餘觀點,歡迎討論。裏面有些地方參考了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)

也就是不管如何都會返回code,message,data這三個key,data可能爲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分支的代碼都能經過測試才發佈。

最後

歡迎各位進行進行評論討論,互相學習。近期不少朋友找我聊項目的一些背景和想法,隻言片語沒法講清楚。後面會寫篇關於我對社羣的一些理解和商業模式的一些見解,歡迎繼續關注我。

專一互聯網創業分享,獨立開發者。全網同名:盧燦偉

相關文章
相關標籤/搜索