那个执拗的少年回来了 《孤岛 App》这个系列已经停更了好久,此次全面升级,对的起给我star
的老铁们php
APP 名称:《独 °》css
客户端方案:flutterhtml
服务端方案:Koa2前端
写着完善着vue
- 个人
- 我的信息
- 头像修改
- 首页
- 列表页
- 发送图文、视频等
……
复制代码
当开始全面更新迭代的时候,没有产品
的思惟是多么可怕的一件事,开发的过程当中会同步更新系列文章,但愿一块撩一撩flutter
固然了这些文章都是没有更新的node
lsolated_island_app
这个分支。想要翻看的能够自行
clone
《独》全部的开发如今
master
分支。
可能旧的文档地址找不到的状况,这个我后续更新一下
以前个人评论区react
我以为本身开发个小 app 也挺好玩的。nginx
但愿多多鼓励很关注,有不恰当的地方也欢迎指正。git
友情建议:es6
最近一段时候因为公司需求,笔者在用 Vue 生态的 uniapp 技术栈来开发 app,整体体验是不太好的
不作什么横向对比,在正确使用 flutter 的前提下,flutter 开发的应用是相比于 uniapp 好不少的(这只是我我的见解)
我的感受 flutter 的学习成本仍是比较高的,若是公司想要经过这个技术来开发的话,可能须要有同事持续跟进 flutter 的生态发展,并按期分享给成员,由于 flutter 生态是愈来愈活跃,技术的更新迭代是至关的迅速,相关的第三方包插件今个能用。明天可能你就不知道咋回事了
flutter 只是一个简单的 UI(这里特别说一下并无所谓的嵌套问题),可是其在安卓 IOS 上的渲染能力,动画能力是十分的惊人
最后简单说一下,企业项目十分花里胡哨的话,可能生态中并无良好的解决方案,这就须要改一些现有的源码,什么和开发者沟通我该怎么实现,这也为何企业选择 taro uniapp rn 等等
简单的数听说话
多终端解决方案 | 星星数 | |
---|---|---|
flutter | 88.3K | |
react-native | 85.5K | |
taro | 24.3K | |
uni-app | 19K |
虽然星星数
并不能说明什么,但在技术选型的时候,它仍是一个十分重要的参考价值,笔者最近在作本身的全栈项目相信不久会出生吧
也只好经过以文字的时候,敦促本身,其实想一想录视频也挺好的
慢慢来吧
因为简单配了台主机flutter
的运行须要 主机开启BIOS
模式
f12
或者DEL
也就是删除键 进入 BIOS(不一样的电脑型号是不尽相同的)至于想我这样,多快的手速都进不去BIOS
的人,那可能须要简单的拆一下显卡
而后再简单的卸载一下主板的电池
乱七八糟的
唠嗑,好像跟
flutter
并无什么关系,不过
在咱们借用Asd
开启一个虚拟机设备调试的时候,可能会遇到一个问题,这就须要主机设备开启虚拟
通常状况下默认是不开启的。我是偏前端开发者,固然了看到这篇文章的你若是没在开发app
也不要走,由于技术就是金钱
图片素材 登陆页的背景图 免费图库相片 中文 台
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
是的,渐进式开发,遇到问题,解决问题。开发的过程当中,咱们能够本身写包而后上传到 flutter pub
import 'package:flutter/material.dart';
class LoginProvider extends State<StatefulWidget> with ChangeNotifier, TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return null;
}
}
复制代码
所谓的状态 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();
}
复制代码
拿到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
provider
其实在开发的过程当中,有错误是十分正常的,也是十分常见的,尤为是在flutter
的开发中。更是一些莫名奇妙的问题,
console.log
console.table
等等直接能够在控制台打印输出flutter run
,不要慌,不要怕 在 flutter 的开发中控制台一大大大大长串的错误非常常见主要就是参考关键字
常见的关键字 而后出门google
也能够推门 Flutter 实际项目开发中踩坑大合集(持续更新..)
像这种一上来就是什么堆栈溢出
不过通常状况下,就是我们的程序写的有问题
本篇章的上半段呢即是对客户端项目的初始化,其中使用到了动画
这也是 flutter ui 中的核心力量,优雅的渲染能力;还有就是provider
在闲鱼的fish redux
等等等等一系列的状态管理实践中,固然了,使用哪一个都行,但我以为provider
不错。(前提是在用对的状况下)
主要是登陆页的一个背景图,透明度的切换
,并无什么实质的内容。
再更新的话,即是,客户端登陆,而后请求到公网的数据,至于接口是什么,我想能够继续往下翻一番,感受文章很随意的话,也请随意的分享
一下,请注明,来源
后端的服务选型 ,最初有几种方案
因此这个就先用大名鼎鼎的Koa2
先来讲说我对 koa2 的理解
├─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
中 ,
还有一点十分重要就是在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": "获取用户信息成功"
}
复制代码
顺便先说一下,这个项目我们用MySql
,感兴趣的话,你也能够推门 Node | 自我爬虫掘金专栏文章
核能预警
一提到服务器相关的知识
不要慌,咱们一步步来实现公网IP
部署node 服务
目标一:掌握 pm2 部署 node JS 服务 进行守护,进程
目标二:掌握基本的 nginx 反向代理
目标三:暂时拜别本地服务
当咱们在任何一台有网的电脑上地址栏输入 http://62.234.111.140/api/v1/user
,便会成功的返回咱们所写接口返回的数据
为何说第一步要准备云服务器,由于哪怕你用原生html
或者说什什么的ssr渲染框架
或者说jQ
等等吧,哪怕是以前读书作的告白网站
你总得放在公网上吧,否则总不能把女孩子拉到本身的家里,而后npm run start
等等,你看你看………………
因此仍是国内的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
虽说咱们也是在本地开发而后同步到云服务器,云服务器的代码通常不会怎么变更,
启动项目
不要慌,解决问题
这个时候咱们就要引入PM2
纯纯的大写的高调的pm2
首先来看下pm2
干些什么
可能如今这样说,也没设好理解的,是有一个这样的场景,云服务器的环境可不像咱们本地的电脑,即开发环境(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
查看进程启动状况,显然咱们的项目已经在云服务器的3000
端口启动了,那么这个时候咱们把进程stop all
停掉
咱们经过命令先听到 3000 端口
pm2 start app.js -i max -n node-koa-pm2
复制代码
详细的含义自行google
, ok 到这里应该就没什么问题了
curl http://127.0.0.1:3000/api/v1/user
复制代码
是这样的,咱们考虑一下,接口访问的时候怎么才优雅,也不知道端口是 3000 啊,因此须要一个代理服务器
google
直接在云服务器经过yum
就能够了我以为
yum install nginx
-----
apt update
apt install nginx
复制代码
而后怎么办呢,触及到个人知识盲区了,仍是不要慌,遇到问题解决问题。勇于试错吧
查看当前云服务安装的nginx
版本
查看配置,这很重要,由于它会定位到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
复制代码
查看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 -s reload
复制代码
没什么错误的话,应该就能够了, 这个时候验证一下本身的成果
{
"code": 0,
"data": {
"nickName": "yayxs",
"fav": [
{
"id": 1,
"type": "writing"
}
]
},
"msg": "获取用户信息成功"
}
复制代码
一个简单的get
请求旧简单的部署到服务器上
了
在干撸node
的时候,如何当进程抛出错误的时候。构建高可用的 node 是十分有必要的以下的代码可参考阅读,方便理解在服务端的环境下为何须要 PM2 来管理进程、
// 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
var http = require("http");
setInterval(async () => {
try {
await http.get("http://localhost:3000");
} catch (error) {}
}, 1000);
复制代码
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);
});
复制代码
这一段的时间,上下班的时间一直在想产品
的相关的问题,才知道设计一个东西是多么的难,思惟很混乱,这也是为何这么久没更新(当初说好的一周一更呢)。
每一个人的思路,每一个人的共享对于产品的诞生是多么的重要
都同样的,惟一不一样的是工资
不同的,当个爱好,没事分享分享
这篇文章包含了两个大的方向flutter
与 node
nginx
等服务端运维相关的知识,即便皮毛感受有意思的也但愿一切探讨。完整项目的 github 仓库地址独 °,真的但愿能给个stat
这也是为何我从新构思继续开发。