那個固執的少年回來了 《孤島 App》這個系列已經停更了很久,這次全面升級,對的起給我star
的老鐵們
介紹
APP 名稱:《獨 °》
客戶端方案:flutter
服務端方案:Koa2
功能
寫著完善著
- 我的
- 個人資訊
- 頭像修改
- 首頁
- 列表頁
- 傳送圖文、視訊等
……
複製程式碼
前序準備
- 下文連結預警
- 長文預警
- 嘮嗑方式不正經預警
- 錯別字警告
當開始全面更新迭代的時候,沒有產品
的思維是多麼可怕的一件事,開發的過程中會同步更新系列文章,希望一塊撩一撩flutter
當然了這些文章都是沒有更新的
- Flutter 實戰 從頭擼一個「孤島」APP(No.1、專案初始化、螢幕適配)
- Flutter 實戰 從頭擼一個「孤島」APP(No.2、閃屏 Splash Page、引導頁)
- Flutter 實戰 從頭擼一個「孤島」APP(No.3、書單、搜尋框、Dio 初探)
- Flutter 實戰 從頭擼一個「孤島」APP(No.4、流行、點贊、動畫)
分支變動
是這樣的,我們們把之前持續更新的移動到lsolated_island_app
這個分支。想要翻看的可以自行clone
《獨》所有的開發現在master
分支。可能舊的文件地址找不到的情況,這個我後續更新一下
之前我的評論區
- 問:為啥 flutter 評論那麼少
- 答:可能大家還不太瞭解
我覺得自己開發個小 app 也挺好玩的。
希望多多鼓勵很關注,有不恰當的地方也歡迎指正。
友情建議:
最近一段時候由於公司需求,筆者在用 Vue 生態的 uniapp 技術棧來開發 app,總體體驗是不太好的
不做什麼橫向對比,在正確使用 flutter 的前提下,flutter 開發的應用是相比於 uniapp 好很多的(這只是我個人看法)
個人感覺 flutter 的學習成本還是比較高的,如果公司想要通過這個技術來開發的話,可能需要有同事持續跟進 flutter 的生態發展,並定期分享給成員,因為 flutter 生態是越來越活躍,技術的更新迭代是相當的迅速,相關的第三方包外掛今個能用。明天可能你就不知道咋回事了
flutter 只是一個簡單的 UI(這裡特別說一下並沒有所謂的巢狀問題),但是其在安卓 IOS 上的渲染能力,動畫能力是十分的驚人
最後簡單說一下,企業專案十分花裡胡哨的話,可能生態中並沒有良好的解決方案,這就需要改一些現有的原始碼,什麼和開發者溝通我該怎麼實現,這也為什麼企業選擇 taro uniapp rn 等等
資料分析
- 為什麼有的人說 flutter 涼了嗎
- 有的人說 2020flutter 你跳槽張薪資必備
簡單的資料說話
多終端解決方案 | 星星數 | |
---|---|---|
flutter | 88.3K | |
react-native | 85.5K | |
taro | 24.3K | |
uni-app | 19K |
雖然星星數
並不能說明什麼,但在技術選型的時候,它還是一個十分重要的參考價值,筆者最近在做自己的全棧專案相信不久會出生吧
也只好通過以文字的時候,敦促自己,其實想想錄視訊也挺好的
慢慢來吧
BIOS 開啟
由於簡單配了臺主機flutter
的執行需要 主機開啟BIOS
模式
- 開機快速按
f12
或者DEL
也就是刪除鍵 進入 BIOS(不同的電腦型號是不盡相同的) - 先不切換語言模式(一般情況下預設是 english)點選 Advanced Mode(F7)進入高階選項。
- 點選 Advanced,然後點 CPU Configuration。
- 下拉選單找到 Intel Virtualization Technology,在其子選單下把選項改成 Enabled。
- 按 F10 儲存退出,開啟成功。
- 這樣一般就可以成功重啟了
至於想我這樣,多快的手速都進不去BIOS
的人,那可能需要簡單的拆一下顯示卡
然後再簡單的解除安裝一下主機板的電池
亂七八糟的
嘮嗑,好像跟flutter
並沒有什麼關係,不過
在我們借用Asd
開啟一個虛擬機器裝置除錯的時候,可能會遇到一個問題,這就需要主機裝置開啟虛擬
一般情況下預設是不開啟的。我是偏前端開發者,當然了看到這篇文章的你如果沒在開發app
也不要走,因為技術就是金錢
flutter_du 初始化
準備
-
圖片素材 登入頁的背景圖 免費相簿相片 中文 臺
assets: - lib/images/login_bg1.jpeg - lib/images/login_bg2.jpeg - lib/images/login_bg3.jpeg - lib/images/login_bg4.jpeg 複製程式碼
-
狀態管理:全域性狀態管理方案(這一點在實際的開發中也是十分必要的)
-
外掛:Flutter Provider Snippets vscode 外掛 類和方法的集合 也規範化
provider
的書寫
可以參考閱讀一下我之前的分享 Flutter 狀態管理一鍋端:第一章 Provider ,這篇簡單的介紹瞭如何在一個專案中管理資料,當然了即使是專案很簡單,統一的管理資料可以儘可能的方便後期的維護,檢視UI層與資料狀態層分離
實現效果
實現的效果是底部輪播圖,全屏的滑動
,由於這個效果圖,我搞的gif
有點大7,8M
,放這個圖片吧
目錄結構
完全新建一個新的flutter專案
刪除 main.dart 中的檔案先,保留一個整潔的開始,它暫時是這樣的
├─lib
│ ├─pages
│ │ └─login
│ └─provider
└─test
複製程式碼
provider
首先先實現provider
登入的狀態管理,其中主要就是運用到的動畫
相關的內容。動畫相關的內容推薦閱讀
那麼剛開始我們就直接使用provider
是的,漸進式開發,遇到問題,解決問題。開發的過程中,我們可以自己寫包然後上傳到 flutter pub
- provider V4.04 這裡最好可以直接 dev,也有中文網站
基本結構
import 'package:flutter/material.dart';
class LoginProvider extends State<StatefulWidget>
with ChangeNotifier, TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return null;
}
}
複製程式碼
狀態 init
所謂的狀態 init ,就是我們邏輯部分所用得到的初始化的資料,一般是空的 list 或者字串等
Animation<double> bgAnimation; // 動畫的
AnimationController bgController; // 控制器 文字輸入同樣有控制器
複製程式碼
double mainPicOp = 1; // 透明度
double otherPicOp = 0; // 透明度
複製程式碼
List<String> imgsList; // 背景圖輪播的素材列表
imgsList = List<String>(); // 初始化
imgsList.add('lib/images/login_bg1.jpeg');
imgsList.add('lib/images/login_bg2.jpeg');
imgsList.add('lib/images/login_bg3.jpeg');
imgsList.add('lib/images/login_bg4.jpeg');
複製程式碼
int mainPicIndex = 0; // 當前正在顯示的圖片編號
int otherPicIndex = 1; // 備胎是1
複製程式碼
定時器
需要我們初始化定時器,讓圖片的透明度切換
Timer dingShiQi; // 定時器
dingShiQi = Timer.periodic(Duration(seconds: 2), (cb) {
bgController.forward(from: 0);
});
複製程式碼
主要邏輯
if (state == AnimationStatus.completed) {
mainPicIndex = mainPicIndex + 1;
otherPicIndex = otherPicIndex + 1;
if (mainPicIndex == imgsList.length) {
mainPicIndex = 0;
}
if (otherPicIndex == imgsList.length) {
otherPicIndex = 0;
}
mainPicOp = 1.0;
otherPicOp = 0.0;
notifyListeners();
}
複製程式碼
該釋放的釋放,該取消的取消
void dispose() {
dingShiQi.cancel();
bgController.dispose();
super.dispose();
}
複製程式碼
ui 佈局
使用 provider
拿到provider
這樣我們在無狀態的元件中同樣可以來取自如的使用資料
LoginProvider provider = Provider.of<LoginProvider>(context);
複製程式碼
核心程式碼
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => LoginProvider()),
],
child: Consumer<LoginProvider>(
builder: (context, counter, _) {
return MaterialApp(home: LoginPage());
},
),
);
}
複製程式碼
上邊分享的程式碼是十分有必要的,因為provider
有個一次大的更新就是廢除了build
然後改成了create
- MultiProvider 這是在需要多個
provider
- Consumer 相當於是監聽訂閱的變化
錯誤解決
其實在開發的過程中,有錯誤是十分正常的,也是十分常見的,尤其是在flutter
的開發中。更是一些莫名奇妙的問題,
- 不像 web 端有
console.log
console.table
等等直接可以在控制檯列印輸出
方案
- 一種有效的方案是世界
flutter run
,不要慌,不要怕 在 flutter 的開發中控制檯一大大大大長串的錯誤很是常見
主要就是參考關鍵字
常見的關鍵字 然後出門google
也可以推門 Flutter 實際專案開發中踩坑大合集(持續更新..)
- 還有一種方案是 ,利用編輯器的除錯工具
像這種一上來就是什麼堆疊溢位
不過一般情況下,就是我們們的程式寫的有問題
小結
本篇章的上半段呢便是對客戶端專案的初始化,其中使用到了動畫
這也是 flutter ui 中的核心力量,優雅的渲染能力;還有就是provider
在閒魚的fish redux
等等等等一系列的狀態管理實踐中,當然了,使用哪個都行,但我覺得provider
不錯。(前提是在用對的情況下)
實現模組
主要是登入頁的一個背景圖,透明度的切換
,並沒有什麼實質的內容。
預覽
再更新的話,便是,客戶端登入,然後請求到公網的資料,至於介面是什麼,我想可以繼續往下翻一番,感覺文章很隨意的話,也請隨意的分享
一下,請註明,來源
Koa2 初始化
後端的服務選型 ,最初有幾種方案
- node 原生開擼 (這個主要是寫的太囉嗦)
- Express Node 的一個框架(早期的)這個也行
- Koa2 現在一般都更新到第二代了,V2.11.0
- eggjs 這個企業級約定俗稱也還不錯(在筆者的其他專案用了,這個 flutter 就不用了)
- Nestjs 這個同步介面文件十分方便(屬於嚐鮮玩法,也在筆者的其他專案用了,那就也先不用在這兒)
所以這個就先用大名鼎鼎的Koa2
先來說說我對 koa2 的理解
- 洋蔥模型,兜一圈又回來
- 非同步程式設計 可以說 koa2 很好實踐 js 非同步程式設計理念
- 可定製化,根據習慣隨意開發,擴充性強
專案目錄
├─app
│ └─api
│ └─v1 // RESTful API 介面規範 v1 版本
├─core // 核心的程式碼
└─test
複製程式碼
開發依賴
在前端的專案中,package.json
一般這個就是管理包檔案的,那在我們的flutter
專案中使用pubspec.yaml
檔案是一樣的,差不多
"dependencies": {
"koa": "^2.11.0", // 這個是專案依賴的koa 雖然名字還是koa但她已經是2代了
"koa-router": "^8.0.8", // 這個是路由跳轉的
"require-directory": "^2.1.1" // 這個等會再說
}
複製程式碼
當然了這只是專案的初始化,後續更新會在此基礎上,所以還是強烈建議看一下的。也可以收藏,沒事的時候翻出來看看
入口檔案
目前還是用js
來開發,如果對ts
感興趣也可以推門
const Koa = require("koa");
const app = new Koa();
const Init = require("./core/init");
Init.entrance(app);
app.listen(3000);
複製程式碼
在這裡我們新建了一個類
主要就是初始化應用的,這一點在 Nuxt
和 Next
等等的框架中都有核心的體現,包括在egg js
中 ,
- 匯入 koa require("koa");
- 例項化 new Koa();
- 引入核心程式碼 require('./core/init')
- 傳入 app Init.entrance(app)
- 監聽埠 3000
還有一點十分重要就是在node
環境中模組化的規範不同於es6
的import
至於模組化規範,自行右轉google
應用初始化類
// 初始化載入路由
const requireDirectory = require("require-directory");
const Router = require("koa-router");
class Init {
// 入口方法
static entrance(app) {
Init.app = app;
Init.initRoute();
}
// 路由匯入初始化
static initRoute() {
requireDirectory(module, "../app/api", { visit: visitor });
function visitor(obj) {
if (obj instanceof Router) {
Init.app.use(obj.routes());
}
}
}
}
module.exports = Init;
複製程式碼
在這個檔案中,我們引入了上文提及的require-directory
- 左轉 批量匯入相關檔案
var requireDirectory = require('require-directory'),
visitor = function(obj) {
return obj(new Date());
},
hash = requireDirectory(module, {visit: visitor});
複製程式碼
意識是說我們可以匯入檔案,那麼我們要做的就是自動化注入路由檔案
-
user.js
// 個人中心v1-api const Router = require("koa-router"); const router = new Router(); router.get(`/api/v1/user`, (ctx, next) => { ctx.body = { code: 0 }; }); module.exports = router; 複製程式碼
我們整體遵循RESTful API
風格 api 不得不思考一個問題,關於後端介面迭代的問題,這也是為什麼我們呼叫的介面會有v1
v2
版本字首,這對於規範化開發十分重要。所以我們把路由檔案這樣安排
- app
- v1
- home.js
- v2
- v3
……
複製程式碼
啟動後臺服務
npm i // 沒有幾個包下起來很快
npm i -g nodemon // 自動監聽檔案變化,這在node開發過程中十分重要
複製程式碼
nodemon app.js
複製程式碼
直接在瀏覽器輸入:
當看到介面返回的資訊就說明後臺的基本服務已經啟動了,但是這還遠遠不夠,遠遠的不夠
{
"code": 0,
"data": {
"nickName": "yayxs",
"fav": [
{
"id": 1,
"type": "writing"
}
]
},
"msg": "獲取使用者資訊成功"
}
複製程式碼
- 一、這是我們自己的本地服務,當寫了十分炫酷的服務,顯然是不便分享到他人的
- 二、這也還是我們自己的寫的測試的 json 資料,並沒有連線資料庫
順便先說一下,這個專案我們們用MySql
,感興趣的話,你也可以推門 Node | 自我爬蟲掘金專欄文章
伺服器部署
核能預警
一提到伺服器相關的知識
不要慌,我們一步步來實現公網IP
部署node 服務
目標
目標一:掌握 pm2 部署 node JS 服務 進行守護,程式
目標二:掌握基本的 nginx 反向代理
目標三:暫時拜別本地服務
效果
當我們在任何一臺有網的電腦上位址列輸入 http://62.234.111.140/api/v1/user
,便會成功的返回我們所寫介面返回的資料
雲伺服器準備
為什麼說第一步要準備雲伺服器,因為哪怕你用原生html
或者說什什麼的ssr渲染框架
或者說jQ
等等吧,哪怕是之前讀書做的告白網站
你總得放在公網上吧,不然總不能把女孩子拉到自己的家裡,然後npm run start
等等,你看你看………………
- X裡雲
- X訊雲
- X為雲
所以還是國內的BATH
等等這幾家的,都差不多真的不騙你
問:什麼是雲伺服器?什麼是域名解析?什麼是部署?怎麼反向代理?那你能幫幫我嗎??
答:你玩遊戲的是啥 ,電腦,雲伺服器就是電腦(當然了在這裡是不正確的也不是很準確的)
雖然我沒有那麼浪漫和騷,但是我有云伺服器 還是包年的 公網 IP http://62.234.111.140/
希望大佬們不要對我做壞事情,跪拜
連線遠端伺服器
輸入使用者名稱密碼連線就行了
這樣就可以了,我發現真的是廢話好多啊 具體的細節,如果大家有什麼疑問,可以再評論區留言,能力所及,會回覆的。首先上來的時候要安裝幾個東西npm i -g node // 全域性安裝最新版的node環境
npm i -g pm2 // 全域性安裝執行緒管理
// 等等等等
複製程式碼
一切的環境準備好之後,就需要同步一下我們們的服務端程式碼到雲伺服器
這一點同樣十分的重要,不然就沒得進行了。
依賴三方伺服器
總得有個同步的伺服器,我們選擇github
伺服器,這樣在雲服務也好,還是我們們的本地電腦,程式碼最起碼丟不了
我是放在了home
資料夾下
那就裝依賴唄,老套路
cd /home/flutter-koa2-du/koa2-server
npm i
複製程式碼
趁著也安裝一下nodemon
吧 答應我安裝下好嗎npm-nodemon
雖然說我們也是在本地開發然後同步到雲伺服器,雲伺服器的程式碼一般不會怎麼變動,
啟動專案
不要慌,解決問題
- 問題原因:主要是在同一環境 3000 這個埠已經被佔用
- 解決問題:那就關閉 3000 程式
這個時候我們就要引入PM2
純純的大寫的高調的pm2
Pm2 應用
首先來看下pm2
幹些什麼
- 內建負載均衡(使⽤ Node cluster 叢集模組、⼦程式,可以參考樸靈的《深⼊淺出 node.js》⼀書 第九章)
- 執行緒守護,keep alive
- 0 秒停機過載,維護升級的時候不需要停機.
- 現在 Linux (stable) & MacOSx (stable) & Windows (stable).
- 多平臺⽀持
- 停⽌不穩定的程式(避免⽆限迴圈)
- 控制檯檢測 id.keymetrics.io/api/oauth/l…
- 提供 HTTP API
可能現在這樣說,也沒設好理解的,是有一個這樣的場景,雲伺服器的環境可不像我們本地的電腦,即開發環境(dev),一旦上線,會有各種複雜的問題出現,導致程式崩掉。不能夠為我們提供服務
配置
npm install -g pm2
pm2 start app.js --watch -i 2
// watch 監聽⽂件變化
// -i 啟動多少個例項
pm2 stop all
pm2 list
pm2 start app.js -i max # 根據機器CPU核數,開啟對應數⽬的程式
複製程式碼
這只是簡單的配置,詳細的玩法可以自行右轉google
也可以下翻參閱文章關於pm2
的分享,當然了還是要滑上來的
pm2 list
我們可以通過pm2 list
檢視程式啟動情況,顯然我們的專案已經在雲伺服器的3000
埠啟動了,那麼這個時候我們把程式stop all
停掉
pm2 stop all
我們通過命令先聽到 3000 埠
pm2 優雅啟動 node 程式服務
pm2 start app.js -i max -n node-koa-pm2
複製程式碼
詳細的含義自行google
, ok 到這裡應該就沒什麼問題了
curl 自行訪問測試
curl http://127.0.0.1:3000/api/v1/user
複製程式碼
Nginx 反向代理
是這樣的,我們考慮一下,介面訪問的時候怎麼才優雅,也不知道埠是 3000 啊,所以需要一個代理伺服器
- 正向代理 :*******
- 反向代理:像這種就是反向代理,具體右轉
google
安裝
直接在雲伺服器通過yum
就可以了我覺得
yum install nginx
-----
apt update
apt install nginx
複製程式碼
然後怎麼辦呢,觸及到我的知識盲區了,還是不要慌,遇到問題解決問題。敢於試錯吧
nginx -v
檢視當前雲服務安裝的nginx
版本
nginx -t
檢視配置,這很重要,因為它會定位到nginx的主要配置所在的位置
,不同的安裝方式所在的位置是不同的以下是筆者的
nginx: the configuration file /www/server/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /www/server/nginx/conf/nginx.conf test is successful
複製程式碼
cat nginx.conf
檢視nginx
主要的配置檔案,
…………………………
server
{
listen 888;
server_name phpmyadmin;
index index.html index.htm index.php;
root /www/server/phpmyadmin;
#error_page 404 /404.html;
include enable-php.conf;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}
location ~ .*\.(js|css)?$
{
expires 12h;
}
location ~ /\.
{
deny all;
}
access_log /www/wwwlogs/access.log;
}
include /www/server/panel/vhost/nginx/*.conf;
}
複製程式碼
主要的要看重這句話include /www/server/panel/vhost/nginx/*.conf;
意思是說會引入字尾名.conf
的檔案作為配置的一部分,所以當我們新增配置的時候,檔名要是.conf
這樣 nginx 會匯入並作為配置
這時候我們只需要新建一個**.conf
的檔案就可,新增如下的配置
server {
listen 80;
server_name localhost;
location /api {
proxy_pass http://127.0.0.1:3000;
}
}
複製程式碼
我們新增的nginx
配置並沒有包括
- 靜態路由的配置
- 等等
重啟 nginx
nginx -s reload
複製程式碼
沒什麼錯誤的話,應該就可以了, 這個時候驗證一下自己的成果
{
"code": 0,
"data": {
"nickName": "yayxs",
"fav": [
{
"id": 1,
"type": "writing"
}
]
},
"msg": "獲取使用者資訊成功"
}
複製程式碼
一個簡單的get
請求舊簡單的部署到伺服器上
了
構建高可用的 node 環境
在幹擼node
的時候,如何當程式丟擲錯誤的時候。構建高可用的 node 是十分有必要的如下的程式碼可參考閱讀,方便理解在服務端的環境下為什麼需要 PM2 來管理程式、
- app.js
// app.js
// 引入koa
const Koa = require("koa");
// 建立⼀個Koa物件表示web app本身:
const app = new Koa();
// 對於任何請求,app將調⽤該非同步函式處理請求:
app.use(async (ctx, next) => {
// 隨機產⽣錯誤
Math.random() > 0.9 ? yayxs() : "2";
await next();
ctx.response.type = "text/html";
ctx.response.body = "<h1>success</h1>";
});
if (!module.parent) {
app.listen(3000);
console.log("app started at port 3000...");
} else {
module.exports = app;
}
複製程式碼
- test.js
// test.js
var http = require("http");
setInterval(async () => {
try {
await http.get("http://localhost:3000");
} catch (error) {}
}, 1000);
複製程式碼
- cluster.js
var cluster = require("cluster");
var os = require("os"); // 獲取CPU 的數量
var numCPUs = os.cpus().length;
var process = require("process");
console.log("numCPUs:", numCPUs);
var workers = {};
if (cluster.isMaster) {
// 主程式分⽀
cluster.on("death", function(worker) {
// 當⼀個⼯作程式結束時,重啟⼯作程式 delete workers[worker.pid];
worker = cluster.fork();
workers[worker.pid] = worker;
});
// 初始開啟與CPU 數量相同的⼯作程式
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
workers[worker.pid] = worker;
}
} else {
// ⼯作程式分⽀,啟動伺服器
var app = require("./app");
app.use(async (ctx, next) => {
console.log("worker" + cluster.worker.id + ",PID:" + process.pid);
next();
});
app.listen(3000);
}
// 當主程式被終⽌時,關閉所有⼯作程式
process.on("SIGTERM", function() {
for (var pid in workers) {
process.kill(pid);
}
process.exit(0);
});
複製程式碼
其他
近期感想
這一段的時間,上下班的時間一直在想產品
的相關的問題,才知道設計一個東西是多麼的難,思維很混亂,這也是為什麼這麼久沒更新(當初說好的一週一更呢)。
- 剛開始可能是面向自己,孤獨的自己
- 接著可能會面向 B 端使用者
- 大眾的 C 端產品
每個人的思路,每個人的共享對於產品的誕生是多麼的重要
- 哪怕一個實習生
- 哪怕一個剛開始企業開發的小生
- 哪怕一個巨集觀架構的大佬
都一樣的,唯一不同的是工資
不一樣的,當個愛好,沒事分享分享
總結
這篇文章包含了兩個大的方向flutter
與 node
- 如何重新出發,構思一個簡答的跨端 app ,登入頁
- 如何從 0 開始搭建一個簡單的 node 後臺服務,實現前端人的後端夢
- 如何入門瞭解
nginx
等服務端運維相關的知識,即使皮毛
求求
感覺有意思的也希望一切探討。完整專案的 github 倉庫地址獨 °,真的希望能給個stat
這也是為什麼我重新構思繼續開發。
行文思路
參考閱讀
- node 服務開發和伺服器部署
- pm2---node 程式管理工具
- 開課吧全棧系列 12 期關於 node 相關分享
- B 站 馬有發 發哥的 flutter 相關分享