- 平台化设计思维
一、概述 作为一个码农,在成长过程中总是有各种各样的挑战,面对这些挑战很对人又不知该如何解决。而作为成长方向上为数不多的有光明前途的平台设计者,可以拥有更多的“机会”,也是团队中可以指引整体发展方向的“引导者”,在组织中拥有“重要”的地位。 当然先成长为一个优秀的“开发者”对升级成为一个平台设计者会更有帮助,以前的文章也有探讨过研发思维的建设,传送门在这里 码农进阶中的思维变化。 回归正题,多数程序员成长过程中经常会遇到不知怎么将一个很好的东西变成高大上的产品的问题,或者说不知道该更方便的提供/邀请给其他人使用,这里介绍平台化设计思维或者说方向,可以给大家提供一个思路。 二、定义 平台:它是一个基础设施,可以提供 “资源”、“交流”、“服务”等内容,并制定完善的规则,使各种角色的用户可以制作或者获取到特定的内容。 三、如何拥有平台化思维 3.1、为啥要做“平台” 这里我们先探讨“如何拥有”平台化思维,让我们先有个潜意识的就会往那边想,至于“如何设计”,我们下一章节讨论。 为何我这里说“我们先有个潜意识“,是因为我发现日常工作中很多能力强的小伙伴其实都有不错的”研发能力“、”效能思维“、”行动能力“等,然后工作中遇到问题后开发出来了一个”工具“提升了自己的开发效率或者沟通效率,但是却止步于此,比如一个调试工具、一个多端编译工具、一个上传工具等,这些“工具”都有一个普遍的特点,那就是:“单向服务无组织规则”、“功能共享使用者却无法约束”、“生产的数据不共享”、“即使开源也得不到发展”,因此我们要做“平台化”,建立一个基础设施,作为一个“平台”,上面的用户(内容提供者、内容使用者)可以有机的组织到一起,使服务更加流畅、用户各司其职、信息共享更加方便、发展更加迅速,那么如何建立这种“平台化思维”呢,我这里有一个简单的方法-“两步七问”,即: 第一步:规划,时刻谨记我们需要成长和成果,因此我们需要一些更耀眼的“内容” ●要不要功能更强大一些; ●要不要推广更广泛一些; ●要不要做成独一无二(一定范围内); 第二步:落地,判断能不能做成“平台” ●可以共享使用功能和数据吗? ●可以让更多人来自主“生产”内容吗? ●需要统一管理用户和使用规则吗? ●需要有更多人来一起发展吗? 以上要时刻提醒、锻炼自己,形成一定的思考习惯,在做之前先“思考”,不然遗忘或者没这个想法,那自己设计开发的东西就发展不起来,甚至可能启发其他人并被占先; 3.2、平台化思维如何建立 上面的3.1小章节只是用来让大家时刻谨记“要不要”和“是否适合“的,接下来我们思考平台要做成什么样,也就是“平台化”的“战略目标”; 这里简单画了一个思维导图,大家看起来可以更加直观。 [图片] 3.2.1、定位 对应图里的”解决什么问题“,也就是我们这个”平台“是用来做啥的,是个”工具平台“、”电商平台“,还是”管理平台“、”娱乐平台“,有了目标才能规划功能,这里简单罗列了几个方向。 需求:范围较广,比如来自产品、研发、测试等等; 效率:提高服务、内容生产的效率,如商城、后台管 理等; 降本:降低了”工作内容“的成本,如开源社区、工具类平台等; 3.2.2、角色 角色即使用此平台的所有构成者的身份,粗略划分为“内容使用者”、“内容提供者”、“中心化服务”,真实用户又可以划分的更加细致,比如后台管理员、会员等级1、会员等级2、普通会员等 3.2.3、组织 组织即平台通过一定的“规则”使所有使用者、内容形成一定的规范,可以形成功能闭环,不同部分各司其职,共同组成了一个“平台”。我们可以这样假设,现在有一堆人、有不同的需求,但是“人”能够去哪、“看”到什么东西、“制作”出什么东西、如何“交流”,这都是零散的、独立的,是个无效、无意义的“组合”,只有通过一定的“规则”“组织”起不同的部分,提供一个平面上,“用户”可以到不同的“地方”,“看”或者“制作”不同的“内容”,这样才能性能一个有效的“系统”。所以一个“平台”需要提供两方面内容:“制定规则”、“提供基础服务”。 3.2.4、发展 一个“平台“若不发展,迟早要没落,要想发展,那就需要提供“更多功能”吸引“更多人”来使用,“更多人”来提供“更多功能”。简单理解就是用户更多、开发更多,那么从一个平台来说,在不谈主动推广,只探讨自然生长的情况下,如果做到“更多人”来开发呢?现在有个比较好的模式就是“生态”,开放出去一部分能力,让更多的人基于“开放”去制作更多的“内容”和”功能“,类似“插件”。这些”插件“越来越多,功能越来越强大,使用的人也就越来越多。 四、如何设计一个平台 上面已经讲了“战略”,接下来我们思考“战术”,即一个平台该“怎么做” 4.1、公式 [图片] 为了方便大家的使用,简单总结了一个公式(大家可以一起来完善它)如上,这里简单介绍一下: ●“角色A和B”:用户 ●“可以”:权限 ●“动作”:规则 ●“内容X”:数据 ●“越来越多、很多”:生态 ●整体表达:需求 4.2、思维导图 为了方便大家理解,我们还是画个图 [图片] 4.2.1、定位 思考方向主要有: ●用途,提供给什么人什么服务,大方向上制定目标 ●发展方向,细分市场,提前做好规划 ●竞争对手,对比竞对,思考优势劣势 4.2.2、角色 尽可能多的争取到更多的用户,划分出各种角色,后续可方便规划“需求”、“规则” 4.2.3、规则 可分为如下的内容(粗略): ●用户管理:包含登录、注册、权限等 ●内容管理:包含制作、存储、删除、获取等 ●数据管理:包含统计等 ●业务逻辑:包含使用规则、功能服务逻辑、流程逻辑等 4.2.4、生态 “插件”的设计规则: ●准入规则:登录、权限等 ●开放规则:“平台”基础服务、规范、兼容等 ●制作规则:开发、调试、发布、定价等 ●容器规则:跟平台的”融合“逻辑等 ●使用规则:购买、打烊、过期等 ●管理规则:禁用、删除、推广、分账等 ●其他:安全、审核、文档等 五、举个栗子🌰 5.1、背景 笔者曾经接到过一个C端消息通知的需求,要求是服务端推送消息给C端,我们可选择的是自研和购买第三方服务,经过调研对比了:功能、性能、成本、兼容性等多个方面打算自研,那么很自然而然的想到要实现这个需求,我们可以建一个服务提供生成消息、发送消息、存储消息等等,然后实现一个客户端sdk,用于接收消息和处理消息。需求实现了,皆大欢喜了,然而我们是否就可以满足了?结项了? 接下来让我们用今天讨论的平台化设计思维再来思考一遍。 5.2、战略思考 两步七问,规划和落地: ●要不要功能更强大一些 ○当前的需求确实简单,一推一收即可,将来会不会有黑名单、群发、根据特定的产品范围发、是否有c端向服务端发送的消息?可以跟产品探讨一下。更多的功能是否就可以有更多的沉淀、更多的收获、跟多的发展? ○- 要 ●要不要推广更广泛一些 ○当前是一个产品功能在用,公司或集团下有没有其他部门也有类似的需求?更多人使用我们的价值产出是否就更大,甚至将来作为isv开放给外部企业使用,未来的“钱途”是否也会更好? ○- 要 ●要不要做成独一无二(一定范围内) ○是否可以设计成一个统一的系统,不同的人可以划定一个“私有区域”,可以管理只跟”自己“相关的内容,比如成本、消息管理、权限管理?然后让大家都来使用,完全“垄断”某一个范围?ps:垄断的好处就不用说了吧~ ○- 要 ●可以共享使用功能和数据吗? ○不同部门的产品同样有此类需求,达到“总成本”降低 ○可以 ●可以让更多人来自主“生产”内容吗? ○不同的需求需要生成不同的“消息” ○可以 ●需要统一管理用户和使用规则吗? ○多团队使用时,需要管理不同的成本单元用户;也需要管理c端不同权限以及名单的用户和使用规则 ○需要 ●需要有更多人来一起发展吗? ○可以开放一些插件(消息处理方向),比如针对特定消息内容可以类型转换、展示不同交互,让不同的团队自己实现自己的“功能“,减少本团队的人力成本 ○不确定 结论:有做成平台的必要性 5.3、战术思考 根据公式: {“越来越多”} {“角色A”} {“可以“} 通过平台 {“动作”} {“很多”} {“角色B”} {“动作”} 的 {“内容X”} 思考如下: 1、定位:面向公司内部的需求,实现客户端、服务端之间的消息通信,将来可以开放给其他外部企业付费使用,简单易接入、兼容各个平台、功能完善定位于基本的消息处理并深度挖掘业务使用场景(抛弃如视频、语音等场景)、费用比竞对低、稳定安全有保障; 2、角色:不同研发、不同消息生产者、不同消息接收者、其他功能扩展开发者; 3、规则:设计一个平台,包含:运营管理系统、后台管理系统、用户端sdk/消息页等,囊括的逻辑有,用户身份相关和权限管理、消息产生发送接收流程、组织间“数据”独立和组织管理、数据(消息)的流转保存统计、成本管理等; 4、生态:即插件的设计规则,如接口定义、上传和使用规则、审核和更新流程等 最后,通过相关的“产品”设计、“UI”设计、“架构”设计、“流程”设计、“开发”设计,逐步实现整个平台。 六、最后 还是那句话,我们每个人都需要成长,从最初写一个“方法”的代码、到写一个“功能”、实现一个“模块”、跟一个”项目“、设计一个”架构“、搭建一个”平台“,这一个又一个的阶段有不同的目标也有不同的思维模式,我们要不停的改变、不停的成长、不停的思考才能应对不同的问题或者目标,一种思维模式也需要慢慢养成一种习惯。 好了,我的分享结束,希望能对大家有用,如有问题请及时与我反馈,我们一同进步~😄
2023-12-20 - 小程序调试新方案——使用WeConsole监控console/network/api/component/storage
[图片] 一、背景与简介 在传统的 PC Web 前端开发中,浏览器为开发者提供了体验良好、功能丰富且强大的开发调试工具,比如常见的 Chrome devtools 等,这些调试工具极大的方便了开发者,它们普遍提供查看页面结构、监听网络请求、管理本地数据存储、debugger 代码、使用 Console 快速显示数据等功能。 但是在近几年兴起的微信小程序的前端开发中,却少有类似的体验和功能对标的开发调试工具出现。当然微信小程序的官方也提供了类似的工具,那就是 vConsole,但是相比 PC 端提供的工具来说确实无论是功能和体验都有所欠缺,所以我们开发了 weconsole 来提供更加全面的功能和更好的体验。 基于上述背景,我们想开发一款运行在微信小程序环境上,无论在用户体验还是功能等方面都能媲美 PC 端的前端开发调试工具,当然某些(如 debugger 代码等)受限于技术在当前时期无法实现的功能我们暂且忽略。 我们将这款工具命名为[代码]Weimob Console[代码],简写为[代码]WeConsole[代码]。 项目主页:https://github.com/weimobGroup/WeConsole 二、安装与使用 1、通过 npm 安装 [代码]npm i weconsole -S [代码] 2、普通方式安装 可将 npm 包下载到本地,然后将其中的[代码]dist/full[代码]文件夹拷贝至项目目录中; 3、引用 WeConsole 分为[代码]核心[代码]和[代码]组件[代码]两部分,使用时需要全部引用后方可使用,[代码]核心[代码]负责重写系统变量或方法,以达到全局监控的目的;[代码]组件[代码]负责将监控的数据显示出来。 在[代码]app.js[代码]文件中引用[代码]核心[代码]: [代码]// NPM方式引用 import 'weconsole/init'; // 普通方式引用 import 'xxx/weconsole/init'; [代码] 引入[代码]weconsole/init[代码]后,就是默认将 App、Page、Component、Api、Console 全部重写监控!如果想按需重写,可以使用如下方式进行: [代码]import { replace, restore, showWeConsole, hideWeConsole } from 'weconsole'; // scope可选值:App/Page/Component/Console/Api // 按需替换系统变量或函数以达到监控 replace(scope); // 可还原 restore(scope); // 通过show/hide方法控制显示入口图标 showWeConsole(); [代码] 如果没有显式调用过[代码]showWeConsole/hideWeConsole[代码]方法,组件第一次初始化时,会根据小程序是否[代码]开启调试模式[代码]来决定入口图标的显示性。 在需要的地方引用[代码]组件[代码],需要先将组件注册进[代码]app/page/component.json[代码]中: [代码]// NPM方式引用 "usingComponents": { "weconsole": "weconsole/components/main/index" } // 普通方式引用 "usingComponents": { "weconsole": "xxx/weconsole/components/main/index" } [代码] 然后在[代码]wxml[代码]中使用[代码]<weconsole>[代码]标签进行初始化: [代码]<!-- page/component.wxml --> <weconsole /> [代码] [代码]<weconsole>[代码]标签支持传入以下属性: [代码]properties: { // 组件全屏化后,距离窗口顶部距离 fullTop: String, // 刘海屏机型(如iphone12等)下组件全屏化后,距离窗口顶部距离 adapFullTop: String, } [代码] 4、建议 如果不想将 weconsole 放置在主包中,建议将组件放在分包内使用,利用小程序的 分包异步化 的特性,减少主包大小 三、功能 1、Console 界面如图 1 实时显示[代码]console.log/info/warn/error[代码]记录; [代码]Filter[代码]框输入关键字已进行记录筛选; 使用分类标签[代码]All, Mark, Log, Errors, Warnings...[代码]等进行记录分类显示,分类列表中[代码]All, Mark, Log, Errors, Warnings[代码]为固定项,其他可由配置项[代码]consoleCategoryGetter[代码]产生 点击[代码]🚫[代码]按钮清空记录(不会清除[代码]留存[代码]的记录) [代码]长按[代码]记录可弹出操作项(如图 2): [代码]复制[代码]:将记录数据执行复制操作,具体形式可使用配置项[代码]copyPolicy[代码]指定,未指定时,将使用[代码]JSON.stringify[代码]序列化数据,将其复制到剪切板 [代码]取消置顶/置顶显示[代码]:将记录取消置顶/置顶显示,最多可置顶三条(置顶无非是想快速找到重要的数据,当重要的数据过多时,就不宜用置顶了,可以使用[代码]标记[代码]功能,然后在使用筛选栏中的[代码]Mark[代码]分类进行筛选显示) [代码]取消留存/留存[代码]:留存是指将记录保留下来,使其不受清除,即点击[代码]🚫[代码]按钮不被清除 [代码]取消全部留存[代码]:取消所有留存的记录 [代码]取消标记/标记[代码]:标记就是将数据添加一个[代码]Mark[代码]的分类,可以通过筛选栏快速分类显示 [代码]取消全部标记[代码]:取消所有标记的记录 [图片] 图 1 [图片] 图 2 2、Api 界面如图 3 实时显示[代码]wx[代码]对象下的相关 api 执行记录 [代码]Filter[代码]框输入关键字已进行记录筛选 使用分类标签[代码]All, Mark, Cloud, xhr...[代码]等进行记录分类显示,分类列表由配置项[代码]apiCategoryList[代码]与[代码]apiCategoryGetter[代码]产生 点击[代码]🚫[代码]按钮清空记录(不会清除[代码]留存[代码]的记录) [代码]长按[代码]记录可弹出操作项(如图 4): [代码]复制[代码]:将记录数据执行复制操作,具体形式可使用配置项[代码]copyPolicy[代码]置顶,未指定时,将使用系统默认方式序列化数据(具体看实际效果),将其复制到剪切板 其他操作项含义与[代码]Console[代码]功能类似 点击条目可展示详情,如图 5 [图片] 图 3 [图片] 图 4 [图片] 图 5 3、Component 界面如图 6 树结构显示组件实例列表 根是[代码]App[代码] 二级固定为[代码]getCurrentPages[代码]返回的页面实例 三级及更深通过[代码]this.selectOwnerComponent()[代码]进行父实例定位,进而确定层级 点击节点名称(带有下划虚线),可显示组件实例详情,以 JSON 树的方式查看组件的所有数据,如图 7 [图片] 图 6 [图片] 图 7 4、Storage 界面如图 8 显示 Storage 记录 [代码]Filter[代码]框输入关键字已进行记录筛选 点击[代码]🚫[代码]按钮清空记录(不会清除[代码]留存[代码]的记录) [代码]长按[代码]操作项含义与[代码]Console[代码]功能类似 点击条目后,再点击[代码]❌[代码]按钮可将其删除 点击[代码]Filter[代码]框左侧的[代码]刷新[代码]按钮可刷新全部数据 点击条目显示详情,如图 9 [图片] 图 8 [图片] 图 9 5、其他 界面如图 10 默认显示 系统信息 可通过[代码]customActions[代码]配置项进行界面功能快速定制,也可通过[代码]addCustomAction/removeCustomAction[代码]添加/删除定制项目 几个简单的定制案例如下,效果如图 11: [代码]import { setUIRunConfig } from 'xxx/weconsole/index.js'; setUIRunConfig({ customActions: [ { id: 'test1', title: '显示文本', autoCase: 'show', cases: [ { id: 'show', button: '查看', showMode: WcCustomActionShowMode.text, handler(): string { return '测试文本'; } }, { id: 'show2', button: '查看2', showMode: WcCustomActionShowMode.text, handler(): string { return '测试文本2'; } } ] }, { id: 'test2', title: '显示JSON', autoCase: 'show', cases: [ { id: 'show', button: '查看', showMode: WcCustomActionShowMode.json, handler() { return wx; } } ] }, { id: 'test3', title: '显示表格', autoCase: 'show', cases: [ { id: 'show', button: '查看', showMode: WcCustomActionShowMode.grid, handler(): WcCustomActionGrid { return { cols: [ { title: 'Id', field: 'id', width: 30 }, { title: 'Name', field: 'name', width: 70 } ], data: [ { id: 1, name: 'Tom' }, { id: 2, name: 'Alice' } ] }; } } ] } ] }); [代码] [图片] 图 10 [图片] 图 10 四、API 通过以下方式使用 API [代码]import { showWeConsole, ... } from 'weconsole'; showWeConsole(); [代码] replace(scope:‘App’|‘Page’|‘Component’|‘Api’|‘Console’) 替换系统变量或函数以达到监控,底层控制全局仅替换一次 restore(scope:‘App’|‘Page’|‘Component’|‘Api’|‘Console’) 还原被替换的系统变量或函数,还原后界面将不在显示相关数据 showWeConsole() 显示[代码]WeConsole[代码]入口图标 hideWeConsole() 隐藏[代码]WeConsole[代码]入口图标 setUIConfig(config: Partial<MpUIConfig>) 设置[代码]WeConsole[代码]组件内的相关配置,可接受的配置项及含义如下: [代码]interface MpUIConfig { /**监控小程序API数据后,使用该选项进行该数据的分类值计算,计算后的结果显示在界面上 */ apiCategoryGetter?: MpProductCategoryMap | MpProductCategoryGetter; /**监控Console数据后,使用该选项进行该数据的分类值计算,计算后的结果显示在界面上 */ consoleCategoryGetter?: MpProductCategoryMap | MpProductCategoryGetter; /**API选项卡下显示的数据分类列表,all、mark、other 分类固定存在 */ apiCategoryList?: Array<string | MpNameValue<string>>; /**复制策略,传入复制数据,可通过数据的type字段判断数据哪种类型,比如api/console */ copyPolicy?: MpProductCopyPolicy; /**定制化列表 */ customActions?: WcCustomAction[]; } /**取数据的category字段值对应的prop */ interface MpProductCategoryMap { [prop: string]: string | MpProductCategoryGetter; } interface MpProductCategoryGetter { (product: Partial<MpProduct>): string | string[]; } interface MpProductCopyPolicy { (product: Partial<MpProduct>); } /**定制化 */ interface WcCustomAction { /**标识,需要保持唯一 */ id: string; /**标题 */ title: string; /**默认执行哪个case? */ autoCase?: string; /**该定制化有哪些情况 */ cases: WcCustomActionCase[]; } const enum WcCustomActionShowMode { /**显示JSON树 */ json = 'json', /**显示数据表格 */ grid = 'grid', /** 固定显示<weconsole-customer>组件,该组件需要在app.json中注册,同时需要支持传入data属性,属性值就是case handler执行后的结果 */ component = 'component', /**显示一段文本 */ text = 'text', /**什么都不做 */ none = 'none' } interface WcCustomActionCase { id: string; /**按钮文案 */ button?: string; /**执行逻辑 */ handler: Function; /**显示方式 */ showMode?: WcCustomActionShowMode; } interface WcCustomActionGrid { cols: DataGridCol[]; data: any; } [代码] addCustomAction(action: WcCustomAction) 添加一个定制化项目;当你添加的项目中需要显示你自己的组件时: 请将 case 的[代码]showMode[代码]值设置为[代码]component[代码] 在[代码]app.json[代码]中注册名称为[代码]weconsole-customer[代码]的组件 定制化项目的 case 被执行时,会将执行结果传递给[代码]weconsole-customer[代码]的[代码]data[代码]属性 开发者根据[代码]data[代码]属性中的数据自行判断内部显示逻辑 removeCustomAction(actionId: string) 根据 ID 删除一个定制化项目 getWcControlMpViewInstances():any[] 获取小程序内 weconsole 已经监控到的所有的 App/Page/Component 实例 log(type = “log”, …args) 因为 console 被重写,当你想使用最原始的 console 方法时,可以通过该方式,type 就是 console 的方法名 on/once/off/emit 提供一个事件总线功能,全局事件及相关函数定义如下: [代码]const enum WeConsoleEvents { /**UIConfig对象发生变化时 */ WcUIConfigChange = 'WcUIConfigChange', /**入口图标显示性发生变化时 */ WcVisableChange = 'WcVisableChange', /**CanvasContext准备好时,CanvasContext用于JSON树组件的界面文字宽度计算 */ WcCanvasContextReady = 'WcCanvasContextReady', /**CanvasContext销毁时 */ WcCanvasContextDestory = 'WcCanvasContextDestory', /**主组件的宽高发生变化时 */ WcMainComponentSizeChange = 'WcMainComponentSizeChange' } interface IEventEmitter<T = any> { on(type: string, handler: EventHandler<T>); once(type: string, handler: EventHandler<T>); off(type: string, handler?: EventHandler<T>); emit(type: string, data?: T); } [代码] 五、后续规划 优化包大小 单元测试 体验优化 定制化升级 基于网络通信的界面化 weconsole 标准化 支持 H5 支持其他小程序平台(支付宝/百度/字节跳动) 六、License WeConsole 使用 MIT 协议. 七、声明 生产环境请谨慎使用。
2021-07-14