前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上
ccwgpt 2024-10-12 02:43 31 浏览 0 评论
作者 | Derek Yeung
编辑 | Nodejs技术栈
转发链接:https://mp.weixin.qq.com/s/C3PUMDlQxbndar9oHXixWg
目录
前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)上 本篇
前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)中
前后端全部用 JS 开发是什么体验(Hybrid + Egg.js经验分享)下
Class 前言
最近刚从公司离职,回顾下来之前项目中除了代码,关心更多的是业务,一直没好好梳理整个技术栈,趁着现在时间自由,打算好好梳理总结一下,算是给过去几年的node生涯交一份答卷,也顺便给大家分享一下前后端全部用javascript开发是什么样的体验
Class 项目背景
项目是一款面向学校、教师、家长以及学生的教育类app,涉及的平台有:
- ios
- android
- pc web
- h5 web
使用的技术栈有:
- For 前端:html5plus + moe-plus + vue
- For 后端:egg + egg-moe
Class 前端
既然项目是一个app应用,那么第一个问题就是:原生 or 混合
原生和混合的优劣势,我相信大家都清楚,不引战不讨论,这里就不多说了
对于我们而言,在项目初期我们并没有对应两个端的开发人员,并且初期的版本需要大量的迭代和测试,所以在这种条件下采用原生开发是不合适的
那么,第二个问题就是要确定混合开发的方案,项目立项是在2016年,那么可以来看一下,在当时我们可以选择的方案有哪些:
- Html5 Plus
- React Native
- Weex
- Flutter
- 其他移动框架 + 套壳(理论上也是一种方案...)
Weex和Flutter在当时属于新生儿,各自的生态圈还不够完善,所以没有继续考虑
剩下的5+和React各有千秋,不过最终还是选择了5+,原因其实也很简单:方便(图省事)
熟悉HB的同学应该清楚,5+在打包方面是秒杀其他方案的,够简单也够省事,不用对环境进行配置,也不需要各种命令行build,当然这一切也是建立在5+稳定的情况下
那么5+是不是就是最佳的方案了呢?
当然不是,5+在打包方面可以说是最佳的,但是在开发和调试上简直就是噩梦
比如理想中的场景是在PC浏览器中预览并且调试,但是现实情况是只要是需要调用plus的地方,只 能 真 机...
没有所见即得所得,也没有F12,只有一条数据线和一个只能打印String的控制台...
现在回想起来,唯一让我坚持下去的理由大概就是自己选的框架,跪着也要啃完
终于在18年的时候,我决定对架构动刀,彻底解决这个问题
恰巧当时5+的兄弟uni-app也诞生了,但是刚出生的uni-app是否稳定,是否能持续下去都是一个未知数,我也不敢贸然采用一个全新的方案
并且此时app已经稳定,更换框架已经不单纯是面临学习成本的问题,更多的是风险
所以经过大概两周的测试,最终我选择在5+的基础上搭建一层中间件,用来解决这个问题,于是moe-plus就诞生了
About moe-plus
moe-plus是一个基于vue和5+开发的中间层 moe-plus的定位不是框架,moe-plus更像是一个定制开发的“翻译官”,通过调用统一的api,实现跨平台的效果
moe-plus目录结构:
├─ components
│ ├── webview
│ └── request
├─ core
├─ runtimes
│ └── html5plus
│ └── components
│ ├── webview
│ └── request
│ └── vue
│ └── components
│ ├── webview
│ └── request
moe-plus规范了每个component的api,在不同环境api有差异的情况下,通过runtime内部的component来覆盖默认的component,
以最常见的页面跳转举例:
this.Page.open(path, param);
在vue环境下执行:
open(path, param = {}, meta = {}) {
const vm = plus.vm;
const prod = process.env.NODE_ENV === 'production';
const hashMode = ~window.location.href.indexOf('#');
if (!prod) {
const json = {
param: JSON.stringify(param)
};
const stringified = queryString.stringify(json);
const isHasParam = path.indexOf('?') > -1 ? true : false;
if (hashMode) {
path = path + (isHasParam ? '&' : '?') + stringified;
}
}
this.clearWebview();
if (vm) {
vm.isForwarding = true;
vm.$router.push({
path,
query: param,
meta: meta
});
}
}
在5+环境下则会执行:
open(path, param) {
return this.openView(path, param, 'slide-in-right');
}
openView(path, param = {}, animate = '', extras = {}) {
const url = this.loadUrl(path);
let webview = this.exists(path);
if (param && param.isInvoke) {
extras.isInvoke = true;
}
const extend = Object.assign({}, extras, {
param,
});
const onPageShowed = () => {
this.trigger(webview, 'page-show-success');
};
if (!webview) {
const styles = param.styles || {};
webview = this.create(path, url, Object.assign({
top: 0,
bottom: 0,
}, styles), extend);
const paramWatcher = `window.addEventListener('param-change', function(listener) {
var webview = plus.webview.currentWebview();
var param = listener.detail;
if (!(param instanceof Object)) {
param = JSON.parse(param);
}
webview.param = param;
});`;
let loading = null;
const timer = setTimeout(() => {
loading = Toast.showWaiting('载入中...');
}, isIos ? 600 : 600);
webview.addEventListener('loaded', () => {
if (timer) {
clearTimeout(timer);
}
if (loading) {
loading.close();
}
webview.evalJS(paramWatcher);
webview.show(animate, Duration, onPageShowed);
});
} else {
this.trigger(webview, 'param-change', param);
this.trigger(webview, 'page-open');
this.trigger(webview, 'page-show');
webview.show(animate, Duration, onPageShowed);
}
return webview;
}
在H5页面中,moe-plus调用的是vue-router,而在app中则是5+的webview,其他component也是同理
同时moe-plus也提供了环境判断的接口,可以在一些有硬性差异的部分手动判断,分别实现不同的效果
前面也提到了,moe-plus并不是从框架的角度去开发的,所以也并没有开源的计划,这里只做为一个案例分享,如果有感兴趣的小伙伴可以自行下载该仓库体验
https://github.com/DerekYeung/moe-plus-demo
另外毕竟现在0202年了,uni-app也已经成熟,对于有多端需求的小伙伴我的推荐是直接上手uni-app(dcloud打钱),虽然无法达到100%全端适配,但也是目前来讲多端里最好的方案了
当然了,如果只是追求H5和App两个端的体验的话,那么我的推荐是RN或者类moe-plus的方案
About 项目
前面花了不少篇幅向大家介绍了moe-plus,下面就给大家分享一下我们的项目开发日常和一些小工具
首先我们来说说环境配置那些事
不管是什么样的项目,都会遇到同样一个问题,那就是环境配置
每一个项目都会有prod或者dev环境,有的项目还会有更多(如beta、test)
我们在开发过程中经常会遇到需要切换不同的环境,那么不同的环境该如何切换呢?
Plan A:
const config = {
devConfig
};
const config = {
prodConfig
};
这是最简单的做法,缺点也很明显,代码有污染,切换容易出错,可变的配置项越多就越复杂
Plan B:
const env = require(envFilePath) // process.env.NODE_ENV;
const envConfigPath = `${env}.conf`;
cosnt config = require(envConfigPath);
这个是我们采用的方案,应该也是最普遍的做法,通过env文件或环境变量,读取不同的配置文件
这个方案没有什么问题,但当环境越来越多,配置变更越来越多的时候,则需要记住越来越多的env
针对这种情况,我们内部设计一套内部的cli工具,用来创建/管理配置信息
通过npm run config生成配置文件
生成配置文件之后通过npm run dev/build来选择当前编译环境,配置文件默认随环境选择,亦可手动选择
同时使用electron配套搭配了可视化界面
可视化界面中更加清晰的显示了每个项目的状态
细心的小伙伴应该注意到了上图中还有一个远程调试二维码
这也是我们在开发过程中做的一个功能
在项目开发的过程中,难免会遇到测试的问题,有的时候哪怕是很微小的变动,往往也需要发布一个版本
如果测试团队是异地测试,更新包也有公网泄露的风险
我们解决这个问题的方法是:
我们将内网与外网打通,让外部能够访问内网的开发机,异地预览实时的效果
打包好的更新包通过加密上传到服务器/oss,通过扫码授权更新
同时记录下每个包的版本信息,方便测试人员在不同的版本之间进行切换
最后将运行/发布的版本的二维码提供给相应的测试人员,测试人员通过App扫码即可远程更新
远程调试演示:
安装好的开发版本也会自动连接到远程日志服务器,将App运行过程中产生的console实时传递到开发人员一端
除了扫码远程调试之外,我们也做了扫码登录
用户在扫码之后服务器会建立一条通道,当用户确认登录之后服务器会下发新授权令牌到授权网页,页面中则是通过postMessage进行通信,使得原网页拿到对应的token
由于产品的定位是平台,那么必然也少不了与第三方应用进行交互
我们给第三方提供了用户的Oauth授权以及敏感信息授权(比如手机号)
第三方通过对接公共平台,调用我们的js sdk可以轻松接入到我们的系统
前端的部分,就先讲到这里,虽然有一些功能因为业务的原因没有办法拿出来讲,不过相信通过上面的几个演示大家也可以看到从感知度来讲,混合方案与原生并无很大差异,更多的是性能上的差异(主要存在于低端机型),不过我相信在未来配置越来越高的情况下,这种差异会逐渐变小甚至无感
Class 后端
说完前端,接下来就说一说后端那些事
在2017年之前整个系统并不完全是由node支撑的,核心业务部分是"almost世界上最好的语言"php开发的
因为前端部分也是h5的混合开发方案,所以切换成node其实更多的原因是想体验一下用一种语言统一前后端的感觉,顺便挑战一下只招js工程师的成就
虽然想法很美好,奈何现实给了我一拳
由于是大规模替换,如果要将所有的代码进行重写那将耗费非常多的时间,为了减少重构的时间,我选择的是基于Koa重建yii2(还是图省事)
结果就是带来了《由一行代码引发的“血案”》
感兴趣的小伙伴可以进传送门:https://link.zhihu.com/?target=https%3A//cnodejs.org/topic/5aaba2dc19b2e3db18959e63https://zhuanlan.zhihu.com/p/34702356 这次事故之后我们也彻底放弃了偷懒的做法,选择拥抱egg的怀抱(真香)
之所以选择egg,是因为我们需要一套有自己内部规范并且可靠的框架,而egg所提供的插件开发和框架开发恰好就是我们需要的
我们的业务涉及到前台、后台、鉴权、支付、三方服务、socket等等大大小小14个平台
每个平台中既有独立的业务,也有公共的部分,所以我们在egg的基础上研发了自己的framework:egg-moe
About egg-moe
egg-moe通过egg的扩展loader功能,将common目录下的service、model和config进行挂载
将所有公共部分业务全部放到common下,平台私有业务放在各自目录
目录结构:
├─ common
│ └── service
│ └── common.js
│ └── ...
│ └── model
│ └── common.js
│ └── ...
├─ frontend
│ └── service
│ └── frontend-custom.js
│ └── ...
│ └── model
│ └── frontend-custom.js
│ └── ...
├─ backend
│ └── service
│ └── backend-custom.js
│ └── ...
│ └── model
│ └── backend-custom.js
│ └── ...
这样在开发过程中,涉及到公共部分的业务由common统一接口,每个业务下只需要关注自己本身的业务
同时,egg-moe统一了路由规范,统一错误捕获,另外把项目中常用的module整合到了一起,避免各自调用不同的module
虽然这里不想讨论其他的框架,但是这里不得不说一句,如果你的团队需要的是一套符合自己内部规范的框架,那么通过egg+定制框架的方式一定是最佳的
当然了,没有最完美的框架,只有最合适的框架,根据项目情况选择最合适的框架才是真理
About 后端
在后端开发上,因为涉及到业务的原因,很少能有具体的功能拿来讲,这里主要就和大家分享一下我们平台的架构和我们在过程中开发的一些插件
平台架构:
前台、后台、Socket三个服务分别为面向客户和管理一端的前置服务,负责接收处理有人为操作的请求
统一服务是面向业务层面的后置服务,负责统一接口、鉴权、清洗数据等任务
第三方服务与公共平台则是负责与第三方数据交互以及我们对外开放接口部分
Oauth则是用户与第三方之间建立授权的核心服务,所有第三方与用户之间的关系均由该服务进行处理
Schedule顾名思义,我们的计划任务服务,负责90%的计划任务,剩下的10%则为各个模块内部任务
Slave这个服务就比较惨了,什么脏活累活都是他干,属于名副其实的slave...
在学校端,我们还有一部分业务系统,但这部分与平台其实没什么关系,后面也会讲到一部分,这里就不列出了
目前的配置是9台ecs + 4个mysql节点+2slave节点 + 1redis
部署方面没有采用容器而是传统方案,运维和监控方面则是完全交给了alinode
About 插件
egg-database egg-database是一个orm插件,之所以没有选择sequelize而是新造了轮子主要的原因还是习惯了yii的风格,所以参考了yii的风格来实现了node版本,熟悉yii的同学应该对下面的代码不陌生
在egg-database中,我们这样定义模型
app/model/user.js
'use strict';
const { ActiveRecord, Validate, Field } = require('moe-query');
const { Rule } = Validate;
module.exports = app => {
class Model extends ActiveRecord {
extras() {
return {};
}
tableName() {
return 'user';
}
}
const model = new Model();
model.fields({
name: new Field('name').label('昵称'),
mobile: new Field('mobile').label('手机号').mobile()
.required(),
password: new Field('password').label('密码').string(128)
.required(),
last_login_time: new Field('last_login_time').label('上次登录时间').time(),
update_time: new Field('update_time').label('更新时间').time(),
create_time: new Field('create_time').label('创建时间').time(),
});
// 或简写模式:model.fields([ 'name', 'mobile', ... ]);
model.rules([
new Rule(model.Fields.mobile),
new Rule(model.Fields.password),
]);
if (app.rule.Time) {
model.mount(app.rule.Time);
}
return model;
};
查询单条数据:
const model = this.ctx.model.User;
// 1
const user = await model.fetch(123);
// 2
const user = await model.fetch('张三', 'name');
// 3
const user = await model.fetch({
name: '张三',
password: '李四'
});
查询多条数据:
const model = this.ctx.model.User;
const all = await model.query.where([
[ 'name', '=', '张三' ]
]).desc().all(); /* or asc(), order()*/
// 分页
const list = await model.query.where([
[ 'name', '=', '张三' ]
]).list(/* request */);
// 排序 分组
const datas = await model.query.where([
[ 'name', '=', '张三' ]
]).limit(20).offset(0).order('id', 'desc').group('name').all();
新增数据:
const model = this.ctx.model.User;
const user = model.create();
user.name = '张三';
user.password = '李四';
await user.save();
// or
const user = model.create({
name: '张三',
password: '李四'
});
await user.save();
// or
const user = model.create();
await user.save({
name: '张三',
password: '李四'
});
修改数据:
const model = this.ctx.model.User;
const user = await user.fetch(1); // 得到张三
user.name = '王五';
await user.save();
// or
await model.query.update({
name: '王五'
}, 1 /* or [where Condition] */);
关联查询:
const model = this.ctx.model.User;
const list = await model.query.leftJoin('table', [ /* join Condition*/ ]);
除了关联查询之外,同样也提供关系查询
首先我们添加一个与user相关的model,这里以user device举例
app/model/user/device.js
'use strict';
const { ActiveRecord, Validate, Field } = require('moe-query');
const { Rule } = Validate;
module.exports = app => {
class Model extends ActiveRecord {
tableName() {
return 'user_device';
}
}
const model = new Model();
model.fields([
'userid',
'name',
'token',
'update_time',
'create_time'
]);
if (app.rule.Time) {
model.mount(app.rule.Time);
}
return model;
};
然后在 app/model/user.js 的Model中添加relation和对应的extras
...
relation() {
return {
// hasOne or hasMany
Device: this.hasMany(app.model.User.Device, {
id: 'userid',
})
};
}
extras() {
return {
devices() {
return this.Device || [];
}
};
}
...
最后在查询时,通过joinWith带入
const model = this.ctx.model.User;
const list = await model.query.joinWith('Device').all();
另外,model也提供了各个阶段的查询事件,如before save/after save等等
比如通过 model.on('before save'); 可以在数据保存前做最后的处理, 通过 model.on('after save'); 则是在数据保存后得到对应的事件
同时egg-database也提供了规则的概念(Rule),可以将重复、公共部分的事件处理成规则
比如上面model中model.mount(app.rule.Time)的部分,具体的实现是这样的:
'use strict';
const { ActiveRecord, Validate, Field } = require('moe-query');
const { Rule } = Validate;
const CreateAttribute = 'create_time';
const UpdateAttribute = 'update_time';
const TimeRule = new Rule('time:save');
TimeRule.inject(function(query) {
const isNew = this.is('exists'); // 判断是新增还是更新,true为新增,false为更新
const time = Math.round(new Date().getTime() / 1000);
if (!isNew || !this[CreateAttribute]) { // 如果是新增并且有创建时间字段,设置该字段为当前时间
if (this.Fields[CreateAttribute]) {
this[CreateAttribute] = time;
}
}
if (this.Fields[UpdateAttribute]) { // 如果是有更新时间字段,设置该字段为当前时间
this[UpdateAttribute] = time;
}
return true;
}).on([ 'before save' ]);
module.exports = TimeRule;
通过该rule实现了数据创建时间、更新时间的自动设置,业务中再也不需要手动指定创建/更新时间
通过事件和rule的配合,我们还可以做一些更加灵活的事情
本篇未完结,请见下一篇
推荐JavaScript经典实例学习资料文章
《一文带你搞懂 babel-plugin-import 插件(上)「源码解析」》
《一文带你搞懂 babel-plugin-import 插件(下)「源码解析」》
《教你如何使用内联框架元素 IFrames 的沙箱属性提高安全性?》
《细说DOM API中append和appendChild的三个不同点》
《NodeX Component - 滴滴集团 Node.js 生态组件体系「实践」》
《浅谈浏览器架构、单线程js、事件循环、消息队列、宏任务和微任务》
《了不起的 Webpack HMR 学习指南(上)「含源码讲解」》
《了不起的 Webpack HMR 学习指南(下)「含源码讲解」》
《图解 Promise 实现原理(二):Promise 链式调用》
《图解 Promise 实现原理(三):Promise 原型方法实现》
《图解 Promise 实现原理(四):Promise 静态方法实现》
《使用Service Worker让你的 Web 应用如虎添翼(上)「干货」》
《使用Service Worker让你的 Web 应用如虎添翼(中)「干货」》
《使用Service Worker让你的 Web 应用如虎添翼(下)「干货」》
《一个轻量级 JavaScript 全文搜索库,轻松实现站内离线搜索》
《细品269个JavaScript小函数,让你少加班熬夜(一)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(二)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(三)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(四)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(五)「值得收藏」》
《细品269个JavaScript小函数,让你少加班熬夜(六)「值得收藏」》
《手把手教你7个有趣的JavaScript 项目-上「附源码」》
《手把手教你7个有趣的JavaScript 项目-下「附源码」》
《JavaScript 使用 mediaDevices API 访问摄像头自拍》
《一文彻底搞懂JavaScript 中Object.freeze与Object.seal的用法》
《可视化的 JS:动态图演示 - 事件循环 Event Loop的过程》
《可视化的 js:动态图演示 Promises & Async/Await 的过程》
《Pug 3.0.0正式发布,不再支持 Node.js 6/8》
《通过发布/订阅的设计模式搞懂 Node.js 核心模块 Events》
《「速围」Node.js V14.3.0 发布支持顶级 Await 和 REPL 增强功能》
《JavaScript 已进入第三个时代,未来将何去何从?》
《前端上传前预览文件 image、text、json、video、audio「实践」》
《深入细品 EventLoop 和浏览器渲染、帧动画、空闲回调的关系》
《推荐13个有用的JavaScript数组技巧「值得收藏」》
《36个工作中常用的JavaScript函数片段「值得收藏」》
《一文了解文件上传全过程(1.8w字深度解析)「前端进阶必备」》
《手把手教你如何编写一个前端图片压缩、方向纠正、预览、上传插件》
《JavaScript正则深入以及10个非常有意思的正则实战》
《前端开发规范:命名规范、html规范、css规范、js规范》
《100个原生JavaScript代码片段知识点详细汇总【实践】》
《手把手教你深入巩固JavaScript知识体系【思维导图】》
《一个合格的中级前端工程师需要掌握的 28 个 JavaScript 技巧》
《身份证号码的正则表达式及验证详解(JavaScript,Regex)》
《127个常用的JS代码片段,每段代码花30秒就能看懂-【上】》
《深入浅出讲解JS中this/apply/call/bind巧妙用法【实践】》
《干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)》
《面试中教你绕过关于 JavaScript 作用域的 5 个坑》
作者 | Derek Yeung
编辑 | Nodejs技术栈
转发链接:https://mp.weixin.qq.com/s/C3PUMDlQxbndar9oHXixWg
相关推荐
- 用Steam启动Epic游戏会更快吗?(epic怎么用steam启动)
-
Epic商店很香,但也有不少抱怨,其中一条是启动游戏太慢。那么,如果让Steam启动Epic游戏,会不会速度更快?众所周知,Steam可以启动非Steam游戏,方法是在客户端左下方点击“添加游戏”,然...
- Docker看这一篇入门就够了(dockerl)
-
安装DockerLinux:$curl-fsSLhttps://get.docker.com-oget-docker.sh$sudoshget-docker.sh注意:如果安装了旧版...
- AYUI 炫丽PC开发UI框架2016年6月15日对外免费开发使用 [1]
-
2016年6月15日,我AY对外发布AYUI(WPF4.0开发)的UI框架,开发时候,你可以无任何影响的去开发PC电脑上的软件exe程序。AYUI兼容XP操作系统,在Win7/8/8.1/10上都顺利...
- 别再说C#/C++套壳方案多了!Tauri这“借壳生蛋”你可能没看懂!
-
浏览器套壳方案,C#和C++有更多,你说的没错,从数量和历史积淀来看,C#和C++确实有不少方式来套壳浏览器,让Web内容在桌面应用里跑起来。但咱们得把这套壳二字掰扯清楚,因为这里面学问可大了!不同的...
- OneCode 核心概念解析——Page(页面)
-
在接触到OneCode最先接触到的就是,Page页面,在低代码引擎中,页面(Page)设计的灵活性是平衡“快速开发”与“复杂需求适配”的关键。以下从架构设计、组件系统、配置能力等维度,解析确...
- React是最后的前端框架吗,为什么这么说的?
-
油管上有一位叫Theo的博主说,React是终极前端框架,为什么这么说呢?让我们来看看其逻辑:这个标题看起来像假的,对吧?React之后明明有无数新框架诞生,凭什么说它是最后一个?我说的“最后一个”不...
- 面试辅导(二):2025前端面试密码:用3个底层逻辑征服技术官
-
面试官放下简历,手指在桌上敲了三下:"你上次解决的技术难题,现在回头看有什么不足?"眼前的候选人瞬间僵住——这是上周真实发生在蚂蚁金服终面的场景。2025年的前端战场早已不是框架熟练...
- 前端新星崛起!Astro框架能否终结React的霸主地位?
-
引言:当"背着背包的全能选手"遇上"轻装上阵的短跑冠军"如果你是一名前端开发者,2024年的框架之争绝对让你眼花缭乱——一边是React这位"背着全家桶的全能选...
- 基于函数计算的 BFF 架构(基于函数计算的 bff 架构是什么)
-
什么是BFFBFF全称是BackendsForFrontends(服务于前端的后端),起源于2015年SamNewman一篇博客文章《Pattern:BackendsFor...
- 谷歌 Prompt Engineering 白皮书:2025年 AI 提示词工程的 10 个技巧
-
在AI技术飞速发展的当下,如何更高效地与大语言模型(LLM)沟通,以获取更准确、更有价值的输出,成为了一个备受关注的问题。谷歌最新发布的《PromptEngineering》白皮书,为这一问题提供了...
- 光的艺术:灯具创意设计(灯光艺术作品展示)
-
本文转自|艺术与设计微信号|artdesign_org_cn“光”是文明的起源,是思维的开端,同样也是人类睁眼的开始。每个人在出生一刻,便接受了光的照耀和洗礼。远古时候,人们将光奉为神明,用火来...
- MoE模型已成新风口,AI基础设施竞速升级
-
机器之心报道编辑:Panda因为基准测试成绩与实际表现相差较大,近期开源的Llama4系列模型正陷入争议的漩涡之中,但有一点却毫无疑问:MoE(混合专家)定然是未来AI大模型的主流范式之一。...
- Meta Spatial SDK重大改进:重塑Horizon OS应用开发格局
-
由文心大模型生成的文章摘要Meta持续深耕SpatialSDK技术生态,提供开自去年9月正式推出以来,Meta持续深耕其SpatialSDK技术生态,通过一系列重大迭代与功能增强,不断革新H...
- "上云"到底是个啥?用"租房"给你讲明白IaaS/PaaS/SaaS的区别
-
半夜三点被机房报警电话惊醒,顶着黑眼圈排查服务器故障——这是十年前互联网公司运维的日常。而现在,程序员小王正敷着面膜刷剧,因为公司的系统全"搬"到了云上。"部署到云上"...
- php宝塔搭建部署thinkphp机械设备响应式企业网站php源码
-
大家好啊,欢迎来到web测评。本期给大家带来一套php开发的机械设备响应式企业网站php源码,上次是谁要的系统项目啊,帮你找到了,还说不会搭建,让我帮忙录制一期教程,趁着今天有空,简单的录制测试了一下...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 用Steam启动Epic游戏会更快吗?(epic怎么用steam启动)
- Docker看这一篇入门就够了(dockerl)
- AYUI 炫丽PC开发UI框架2016年6月15日对外免费开发使用 [1]
- 别再说C#/C++套壳方案多了!Tauri这“借壳生蛋”你可能没看懂!
- OneCode 核心概念解析——Page(页面)
- React是最后的前端框架吗,为什么这么说的?
- 面试辅导(二):2025前端面试密码:用3个底层逻辑征服技术官
- 前端新星崛起!Astro框架能否终结React的霸主地位?
- 基于函数计算的 BFF 架构(基于函数计算的 bff 架构是什么)
- 谷歌 Prompt Engineering 白皮书:2025年 AI 提示词工程的 10 个技巧
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- jpa框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- JAVA集合框架 (47)
- mfc框架 (52)
- abb框架断路器 (48)
- ui自动化框架 (47)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (55)
- ppt框架 (48)
- 内联框架 (52)
- cad怎么画框架 (58)
- ps怎么画框架 (47)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)
- oracle提交事务 (47)