关于前端架构:前端架构分层而治铁打的MV流水的C

为什么在web前端很少有人会提到分层架构,例如经典MVC架构,这是因为浏览器诞生之初就只是作为一个后端数据的GUI渲染器。也就是说整体来看,web1.0时代的整个web前端工程就是一个View层,而Model和Controller就是指后端,所以基本无需在web前端工程中去提什么MVC。 然而web生态倒退到明天,浏览器越来越弱小,赋能越来越多,甚至不亚于一个小型操作系统,这时候的Web前端早已不是当初简略的数据渲染器,随着PWA、小程序、快利用的推广,WebAPP曾经和传统的“富客户端”没什么两样了。那么在这种趋势下,如果咱们的Web前端架构还停留在View的时代,那么显然是掉队的。 View是最直观的、最原始的驱动源,解决了所有View的问题也就实现了WebApp的所有性能,前端开发人员的全副工作就是一个又一个的编写Component。正是因为这种以View为外围的架构思维,导致咱们只为解决表层问题而架构,不去思考顶层设计,这也是很多Web前端工程通过几轮迭代和人员转手后,很难持续保护上来的根本原因。 近年来涌现出一些优良的UI渲染框架,比方React/Vue/Anglar,它们很弱小,能够干很多分内和分内的事件。正因为如此,更造成了开发者的惰性,将所有逻辑都间接以最原始的形式写在View中,不论是Mode还是Controller,不论是业务逻辑、还是渲染逻辑、还是存储逻辑、还是通信逻辑、还是路由逻辑...所有各类逻辑叠加各种UI生命周期,全副都耦合在一个Component中。 从视图驱动到畛域驱动是时候扭转咱们的架构思维,从视图驱动升华到畛域驱动。尽管视图驱动是最直观也是最简略的一种架构模式,然而咱们不仅要解决问题,还要思考如何优雅的解决问题,这也好比是排版和设计的区别吧! 或者说,在我的项目横蛮成长的混沌初期你能够从下往上构建,所建即所得。但到了某个重构的工夫点,你还是得站在顶部进行下层设计。 当然,这里有个默认前提,那就是须要长期保护的中大型项目,小而快的我的项目无需谈各种架构模式...将UI框架拉下神坛UI不是工程的全副,UI的指摘只有2个:输出与输入,仅此而已。所以即使React/Vue再弱小,你也该当仅把它们当成一个GUI工具,相似于Java Swing和.Net WPF,而非整个工程React/Vue一把梭。 ~~ 敲黑板:UI只是指令的收集者、传播者、反馈者,而不该当成为指令的执行者。 利用的外围是业务逻辑当初问你一个问题: 如果没有UI,你的利用能够通过命令来驱动吗?你可能会怼我说,用户怎么可能会通过命令来应用我的利用,没有这样的业务场景,你这个假如是毫无意义的。然而,这种场景或者并不是为用户而设,而是为合作伙伴而设、为测试工具而设、为日志剖析而设、为今后的扩大和生态建设而设... 再问你一个问题: UI说:利用要改版,皮肤、交互、页面组织都要调整,要多久?产品说:把H5改改,做成小程序、APP吧,要多久?经理说:React人太难招了,要不咱们换成Vue吧,要多久?Leader说:Vue3进去了,咱们降级为最新版吧,要多久?架构师说:Svelte快到飞,咱们重构为Svelte吧,要多久?业务逻辑不变,仅调整UI和其运行平台,问你要多久?这个时候,如果你的全副逻辑都是耦合在某种特定的UI框架中的,那你只能是哀默之心大于死... UI逻辑与业务逻辑拆散从晚期的前后端一体化,到前面的前后端逻辑拆散,在到这里我重提UI逻辑与业务逻辑拆散,都只是顺着2件事件在做:让须要内聚的更内聚,让能够解耦的更涣散。 业务逻辑形象,UI逻辑具体:业务逻辑是形象的数据模型,而UI逻辑则是形象事物逐层泛化后的具体表现,它们两个自身就不再同一个Level上。业务逻辑通用,UI逻辑非凡:UI逻辑通常须要借助于某个具体的UI框架而表白,而UI框架通常都会与运行平台非亲非故,同时也会退出本人的各种各种限度、束缚、约定、魔术办法等。所以形象来说,前者是JS国家的普通话,而后者则是方言。业务逻辑直观,UI逻辑费解:UI渲染自身就是一件很简单的事件,其间波及到各种组件的各种生命周期、创立、销毁、更新、重绘等等、在加上各种优化伎俩造成的心智累赘。所以你会发现某些业务逻辑放在Redux或是Vuex等纯状态治理框架中,比应用组件外部的Hooks去保护更简略和直观。只管Hooks也能够模仿这些Flux框架,然而不迭前者有条理,比方测试、监控、剖析、回滚一个Action多容易,你测试、监控、剖析、回滚一个Hooks试试...业务逻辑稳固、UI逻辑多变:UI/UE太理性、太灵便了,带有很强的集体主观色调,常常会被优化、批改、甚至颠覆,不像业务逻辑那么稳固。这也是要将UI逻辑和业务逻辑离开的重要起因,如果你将业务逻辑和UI逻辑写在一起,你原本稳固的代码将受到不稳固代码的重大骚挠。铁打的MV流水的C当初提MVC是不是过期了?非也,MVC是其实一种最简略的哲学思想,Model代表形象事物,View代表具体事物,而Controller则是代表如何在形象事物和具体事物之间泛化。所以MVC是一种思维,而不是特指某种框架,过期的只会是框架,而非思维。 近年来各种演变如:MVP、MVVM、MVI等等,其实都是在围绕C做文章,最终的目标无非就是更好的隔离M和V,也就是千方百计拆散UI逻辑和业务逻辑。 Flux架构其实也是一种MVC,而Redux/Vuex/Pinia、以及自己的框架Elux,都能够看作它的变种。上面咱们就看看如何利用Flux架构来拆散UI逻辑与业务逻辑: 从State到ModelMVVM利用中充斥着状态,有的用来形容Component的外部情况,它与Component唇齿相依,追随Component诞生和销毁,这就是咱们常说的ComponentState。\还有一些状态,用来形容业务状态,与具体哪个Component没有间接关系,不存在复用与销毁,咱们能够称它为Model。 ComponentState 是UI逻辑,该当封装在Component外面,外界也无需晓得。Model 是业务逻辑,反映整个APP的情况,与Component无关,该当由Flux框架来对立治理。从Event到Action用户通过UI界面产生的人机交互事件,咱们习惯叫"Event事件",而"Event事件"背地的业务目标咱们能够叫"Action动作",它们一个是因一个是果,一个是表一个本。 解决 Event 的 Handler 是UI逻辑,该当写在UI组件中解决 Action 的 Handler 是业务逻辑,该当写在Controller外面举个具体的例子吧:"SubmitLoginForm|提交登录表单",通常要实现如下逻辑: 验证输出是否无效验证以后用户是否曾经登录申请后端API,并期待返回如果胜利,保留用户信息,并跳回原页面如果失败,提醒谬误,并留在原地这是一个业务动作,因为它能够不依赖哪个具体UI而运行,用户可能通过“onClick事件”点击登录按钮来触发,也可能通过“onKeyPress”按下回车键来触发,甚至你能够间接让用户通过“Login命令”来触发。 所以“onSubmitLoginForm()”该当写在Controller而非UI组件中。\UI组件中只有"onLoginButtonClick()"或"onEnterKeyPress()",而它们往往也就一句话,就是Dispatch一个Action来触发Controller中的“onSubmitLoginForm()” 将业务逻辑移出UI组件,这样UI层就变薄了,回归到了它的实质:只负责收集业务动作,不负责解决它。 改进Flux框架传统的Flux框架也有痛点: 全局中心化治理导致逻辑过于集中;单实例、不销毁容易造成信息累积爆炸;DispatchAction机制过于简略,不适宜解决前因后果的长流程业务。Elux正是针对以上痛点进行了改进: 尽管保持全局中心化治理,但Elux提出“微模块”的概念,将利用拆分成独立自治的一个个“微模块”,每个微模块仅解决本人畛域内的事件。不再单实例,每次路由变动都会产生一个新的空白Store,而后从新筛选有用的状态挂载,相似一种垃圾回收机制。提出了ActionBus的概念,让Action作为Model中的事件来播送。让Action的解决链条具备“协程”机制,更好的协同各业务动作之间的关联。Elux践行的分层而治正是因为遵循了轻UI、重Model的设计思维,让Elux能够挂接React/Vue等各种不同的UI框架,它们曾经变得没那么重要了。 正式因为拆散了UI逻辑和业务逻辑,让Elux能够用一种工程模式开发Web(浏览器)、Micro(微前端)、SSR(服务器渲染)、MP(小程序)、APP(手机利用)。 此致!欢送交换:https://eluxjs.com 最初也想跟大家交换一下前端“微模块”的话题:从"微前端"到“微模块”

August 3, 2022 · 1 min · jiezi

关于前端架构:智联招聘的Web模块扩展落地方案

传统模块化在利于开发的同时,存在诸多不便,例如: 以后端多页面工程场景中应用的组件库更新时,须要编译并上线所有应用到的页面,进步了上线老本,存在局部页面漏发或回滚时漏发带来的危险;当应用CDN资源管理组件库时,CDN资源更新能够实现浏览器端更新,然而服务器端渲染场景node端加载资源并执行无奈保障与浏览器端渲染中加载的CDN资源版本统一,会带来渲染差别导致从新渲染问题;当采纳打包工具提取公共资源时,打包工具的chunk提取局限于以后构建批次,若一个工程单次只构建局部页面,无奈实现工程内所有的公共资源被全副提取,因为各批次构建的公共资源hash值不同,会导致客户端渲染时加载CDN资源的效率大打折扣。致力于解决以上问题,咱们开发了web模块的概念,从架构的层面解决困扰业务的问题。 愿景基于已知问题,咱们的关注点有: 实现web模块独自编译上线,亦可独自回滚;web模块上线后所有应用模块的工程页面立刻失效;反对服务端渲染的同构工程。在理论业务场景中,咱们心愿通过web模块扩大解决多页面我的项目外部UI组件库、工具库(lodash,moment)、申请库(axios)等的跨页面资源共享,全量上线一次,后续更新共享资源只须要独自构建公布web模块扩大即可,公布后通过咱们提供的能力达到多页面实时更新共享资源的成果。 另外咱们心愿业务方应用时援用web模块扩大与援用其它资源语法统一,采纳原生的import或require,不引入非凡语法,防止减少业务线的学习老本。 实现思路此计划基于智联大前端Ada架构之上,能够到 《揭秘智联招聘的大前端架构Ada》理解,下文会间接应用Ada进行形容。 Ada从开发到构建应用的是清单服务进行解析渲染的,构建工具打包会生成一份清单文件,此文件蕴含本次打包的url及打包资源等信息。 利用清单的约定,咱们实现了微前端落地计划Widget,本次设计一样是针对清单进行一些约定实现运行时的动静加载。 首先咱们约定了一种新的工件类型,叫做“web模块扩大”,开发时须要写到固定的文件夹内,才会被脚手架辨认并编译。 同失常的入口文件一样,写到规定文件夹内的文件会被编译成独自的bundle文件,不便援用这里抉择只打包成一个bundle的形式,即js与css最多各只有一个文件。 到这里咱们就拿到了web公共模块,并写入到清单文件当中,拿axios举例如图: 接下来就是我的项目中的axios提取,既然是公共的web资源,所以须要将 import 略微批改一下,即: import axios from 'axios' // 原援用import axios from 'extensions/web-modules/axios' // 新援用此时须要做的就是把 'extensions/web-modules/axios' 这个门路变成一个动静加载的代码,各打包工具都提供了 Externals,利用这一个性能够自定义排除构建资源的加载形式。 拿webpack举例,简略实现如下: { externals: [ ({ request }, callback) => { // ... if (!matched) return callback() // 未match到web模块扩大门路间接返回 const url = path.posix.join(projectKey, WEB_MODULES_SCOPE, matched.name) // 实在URL门路 const name = target === 'node' ? `ada.webModules.require('${url}')` // 由Ada server外部实现此办法 : url return callback(null, name, scope) } ]}将动态门路援用变成动静获取,浏览器端应用window全局变量的形式,node端应用自实现require的形式。 ...

November 12, 2021 · 1 min · jiezi

关于前端架构:sass-项目前端架构

1.前端整体架构图备注: 如果公司技术栈是对立的Vue 或者 React, 绿色模块只有一个, 如果是传统旧我的项目倡议逐渐重构掉 2.表单配置化零碎架构图 3.表单配置化效果图 4.瞻望低代码平台, 通过配置化间接生成一个私有化我的项目

May 10, 2021 · 1 min · jiezi

关于前端架构:探索微前端的场景极限

本文次要介绍总结了一些基于 qiankun 的微前端利用场景与实际根底场景与路由绑定的形式渲染微利用通常状况下,咱们接触的最多的微前端的实际,是以 URL/路由 为维度来划分咱们的微利用,以 OneX 平台(蚂蚁金融云基于微前端架构打造的对立接入平台)为例: 接入这类平台的微利用,通常只须要提供本人的 entry html 地址,并为其调配一个路由规定即可。 这背地的实现则是基于 qiankun 的 registerMicroApps API,如: import { registerMicroApps } from 'qiankun';registerMicroApps([ { name: 'app1', container: '#container', entry: '//micro-app.alipay.com/', activeRule: '/app1' }])路由与利用绑定的形式简略直观,是微前端中最为常见的应用场景,通常咱们会用这种形式将一堆独立域名拜访的 MPA 利用,整合成一个一体化的 SPA 利用。 但这类场景也有本人的局限性: 因为URL/路由的 唯一性/排他性 的特点,这种形式只实用单实例场景需要微利用的调度都是由路由零碎来主动解决的,尽管省事然而碰到更简单的需要,如同一个路由下,依据不同的用户权限展现不同的微利用这类个性化诉求,须要写一些中间层代码来曲线救国利用挂载的容器节点等需提前准备好,不然碰到 动静/嵌套 路由这类状况,可能会因为路由 listener 监听触发的时序不确定,导致微利用无奈实现挂载的问题以组件的形式应用微利用qiankun 2.0 的公布带来一个全新的 API loadMicroApp,通过这个 API 咱们能够本人去管制一个微利用加载/卸载,这个形式也是 qiankun 2.0 的重磅个性: import { loadMicroApp } from 'qiankun';// do somethingconst container = document.createElement('div');const microApp = loadMicroApp({ name: 'app', container, entry: '//micro-app.alipay.com' });// do something and then unmount appmicroApp.unmout();// do something and then remount appmicroApp.mount();开发者能够在脱离路由的限度下,以更自在的形式去渲染咱们的微利用。基于 loadMicroApp API,咱们只须要做一些简略的封装,即能够相似组件的开发体验,实现微利用的接入,以 React 为例: ...

March 8, 2021 · 3 min · jiezi

我对前端架构师的定义

阅读原文: https://github.com/ruizhengyun/front-end-note/issues/8近一年多,一直在思考和实践如何做一名合格的架构师。下面从职责和要求两个维度来说说,部分会和前端TL(team leader)工作重叠。具体事项后续完善,望理解。遗漏之处,还请评论区提出,谢谢。 职责(对外输出)开发层面参与产品、测试用例评审参与前端开发,充分理解需求独立负责项目产品体验技术反推业务技术层面制定前端代码规范工程体系化:开发、构建、发布技术选型与设计web 性能优化新技术探索解决未知问题组件库规划与落地代码 review(CR)文档编写能力跨部门层面与产品协调工作并做约定与交互协调工作并做约定与设计协调工作并做约定团队分享技术与经验日常管理:对人、代码、文档等的管理任务安排团队协作乐于分享行业个人博客参与开源项目并贡献要求(具备能力)基础知识扎实的计算机基础知识及网络知识阅读英文文档无障碍技术扎实的基础知识扎实的数据结构知识良好编程习惯跨端开发能力熟练构建工具使用,比如 webpack至少了解一门框架(react、vue、angular)原理至少熟悉一门后端语言,比如 node、go、python、java、php、.net 等前端模块化知识容器通讯知识安全知识关注技术趋势并了解对一块或多块区域有深入认识经验大型项目优化经验5年(含)以上开发经验独立负责一个项目产品思维、交互思维和设计思维软实力沟通能力责任心团队协作能力自我管理能力对前端热爱,积极学习说明后续就要把上面????吹过的牛用文字摆事实讲道理了。

October 15, 2019 · 1 min · jiezi

前端架构有什么能做的

前端有架构吗?前端有架构模式吗? 架构是什么?软件架构,是一种为了解决复杂问题的通用模式。软件架构,是关于软件系统的一系列有层次的技术决策的集合。换句话来说,当我们讨论架构的时候,不能只讨论某某架构,而是要包含其实施,以及后期的维护。 因为: 一个无法上线的应用架构,算不上是好的软件架构一个没有人能完成开发的软件架构,算不上是可行的软件架构一个在现有的技术上不可行的架构,算不上是合理的软件架构诸如微服务,对于复杂的后端系统来说,是一种不错的『低耦合,高内聚』的实施。但是,它并不适合于大部分的小公司——复杂的架构不适合于简单的应用。而小公司也缺乏足够的人才,来实施一个复杂的系统,与此同时还需要有人能维护这个系统。 所以,当我们谈及软件架构的时候,说的是:有这么一些人,他/她们能按时、高质量(或者说有质量)完成这一个系统的设计——即有能力的个人。 PS:对于前端架构来说,这些人大概率会来自于看了本书的人,笑~ 前端架构拆解:四层次设计从笔者的角度来看,架构设计本身是分层级的,面向不同级别的人时,所展示的内容也是不一样的。如面对的是同一级别、更高一级别的架构师、技术人员,说的便是形而上学的东西,如微前端、前后端分离,并通过各种概念,如构建系统拆份,以抽象的方式介绍如何去设计。这些概念,对于接触过的程序员来说,也是相当好理解的。而当我们面对的是,经验略微丰富的程序员的时候,说的可就不是:去实现微前端这样的东西。而是需要落实到怎样去做这样的一件事。 在不同的时期,不同的阶段,我们考虑的架构相关的因素是不同的。按照这个思想,笔者将架构的设计分为了四个层级: 系统级,即应用在整个系统内的关系,如与后台服务如何通讯,与第三方系统如何集成。应用级,即应用外部的整体架构,如多个应用之间如何共享组件、如何通信等。模块级,即应用内部的模块架构,如代码的模块化、数据和状态的管理等。代码级,即从基础设施来保障架构实施。 对应的层级与实施模式,如下图所示: ![前端四个层级]() 系统内架构在设计前端架构的时候,首先考虑的是应用在整个系统中的位置——它与系统中的其它子系统是怎样的。这种关系包含了架构上的关系、业务上的关系,以及它们之间的协作机制。对于前端应用来说,这一部分的子系统包含了: 其它前端应用。侧重于关注如何与这些应用交互,诸如交互、通讯等。对接的后台服务。关注于如何与后台服务进行通信,诸如权限、授权、API 管理等。若是系统间的数据通信,如与后台服务之间的交互,那么只需要规定好数据通信、数据传递的机制即可。这一类的通讯机制,不仅仅包含了前后端分离架构的 API 设计,还包含了各类的数据传递,诸如 OAuth 跳转的 Token 验证。除此,对于传统的多页面应用来说,也需要关注于其中的数据传递,如 Cookie 作为用户凭据等等。 为此,对于前端开发人员来说,关于与后端间的关系,我们所要考虑的主要因素是前后端分离架构的设计。 前后端分离架构。(详见《前端架构:从入门到微前端》)微前端架构。(详见《前端架构:从入门到微前端》)而后,我们还需要考虑到前端的客户端展现形式。在大前端的背景之下,它可能是以 PC Web 应用、移动 Web 应用、混合移动应用(结合 Cordova 构架)、混合桌面应用(结合 Electron 框架)、原生移动应用(结合 React Native)等,具体选择何一种技术,取决于我们在之前调查的利益相关者的需求。 当我们做完上述的三个核心的架构决策之后,就需要考虑一下应用的部署架构。不同的客户端形式,或者需要服务端渲染,会在某种程度上影响到前端应用的部署,但是总的影响并不是太大。往往只需要通过反向代理的配置,就可以完成部署的配置。若是与后台服务不在一个域,则需要考虑支持跨域请求或者是后台做一层代码。 在有了这些基本的架构设定,便可以往下继续设计下一层级的应用架构。 应用级架构应用级架构,指的是单个应用与外部应用的关系,如微服务架构下的多个应用的协作。它可以是一个团队下的多个前端应用,又或者是一个组织内的前端应用。其在组织中所处的位置,也进一步决定了我们所需要设计的架构方案。 若是从相关的定义上来看,它与系统级应用存在一定的交集。但是,笔者将其视之为系统级架构的进一步细化。如在系统内架构的层级里,我们定义了微前端架构,而具体的实施细则会放在各个应用中实现的。而诸如应用间的数据如何交换,而不同的应用又有各自不同的实现,则会在这个层级里定义了相应的接口。 与此同时,当我们维护了多个前端应用,必然会去考虑在这些应用之间,复用代码、共享组件库、统一设计等,以减少相应的工作量。为此,在这个层级里,我们会考虑下面的一些架构相关的设计内容: 脚手架。(详见《前端架构:从入门到微前端》)模式库。(详见《前端架构:从入门到微前端》)设计系统。(详见《前端架构:从入门到微前端》)与此同时,在这个层级里,我们决定选择什么前端框架,进行相关的技术选型。 模块级架构模块级架构,便是深入单个应用内部,更细致的设计应用内部的架构。它所涉及的部分,便是在日常开发中,我们经常接触到的主要部分。我们所做的便是制定一些规范,又或者是更细致的架构设计。这部分的内容,会在我们开始业务编码之前进行设计,在敏捷软件开发中,它称之为 迭代 0/Sprint 0/Iteration 0。相关的内容有: 模块化。(详见《前端架构:从入门到微前端》)组件化。(详见《前端架构:从入门到微前端》)除此,对于不同的框架,还涉及到一些框架特定的模式与架构设计,它们会在一定程度上影响单个应用的架构。对于不同的框架来说,所需要涉及的范围都有所不发。如在 Angular 框架中,不需要操心相关的模式,只需要掌握框架定义的规范即可,如使用 Service 来保存应用的状态,使用 Pipe 来处理数据等。而在 React 框架中,则需要设计状态和数据流的管理方式,为此便需要诸如 Flux 或者 Redux 这样的状态管理方案。 代码级:规范与原则当我们真正编码的时候,考虑的架构因素便是更低层级的内容。这部分的架构设计,便称为代码级的架构设计,它关注于实践过程中的相关规范和原则。这部分的内容相当的多,并且繁琐。它包含了以下的内容,但是又不限于下述的部分: 开发流程。(详见《前端架构:从入门到微前端》)代码质量及改善。(详见《前端架构:从入门到微前端》)规范而非默契。(详见《前端架构:从入门到微前端》)除此,在日常的开发中,还需要注重代码的可维护——简单的代码更容易读性和维护。笔者维护一个 Scala 项目的过程中,便是深有体会——越是写得越抽象的代码,越难以维护。诸如函数式编程是一个好的东西,但是好的东西也容易被烂用,导致人们不喜欢这个东西。 小结买, 买,买 ——节选自《前端架构:从入门到微前端》 ...

July 5, 2019 · 1 min · jiezi

前端架构师亲述前端工程师成长之路的-N-问-及-回答

问题回答者:黄轶,目前就职于 Zoom 公司担任前端架构师,曾就职于滴滴和百度,毕业于北京科技大学。1. 前端开发问题 大佬,能分享下学习路径么,感觉天天忙着开发业务,但是能力好像没有太大提升,不知道该怎么充实自己 ? 解答 业务开发有没有痛点,能不能通过技术的手段解决 ?平时开发业务用到了哪些技术栈和周边的生态链,我是否对他们熟练掌握了,对他们的实现原理呢 ?平时开发遇到了 bug,调试了很久,能不能提升自己快速定位 bug,解决问题的能力 ?如果上面分配了一个需求,没有现成的轮子可以用,我是否可以快速造一个出来 ?如果使用第三方轮子出现问题,我能否能找到合适的解决方案,甚至参与共建 ?以上提到了这些问题,不妨问问自己,如果没有做的足够好,都是你可以提升的方向。问题 我想知道你为什么对前端这个职业(行业),总是保持一颗好奇心,每天都不停探索,每天保持学习进步,你是怎样坚持下来的呢 ? 就像医院里的医生(教授/专家),在这个行业刻苦钻研了大半辈子,怎样保持每天学习的这种精神 ?探索精神 ?并且长久坚持下去 ?为社会做出了非常多的贡献。 我知道你是以怎样的决心和毅力保持每天学习,不停探索前进 ? 解答 主要是兴趣驱动吧,对技术保持热情和好奇。另外就是成就感,当我 get 到某个新技能,解决了某个复杂的问题的时候会非常有成就感。工作前几年的时间是非常关键的,是成长空间大且精力最旺盛的阶段,一定要在这个阶段多学知识。学习是无止境的,尤大说过一句话我印象非常深刻 ”做脑力工作的人,往往钻研得越深,越发现自己的渺小和无知“ ,与君共勉。问题 最近拿到了滴滴出行的实习生 offer,我想问一下您对实习生 (或者说初步踏入 IT 行业的学生) 在融入部门和提升上有什么建议 ? 解答 实习生一定要多做业务,工作要积极主动,争取转正机会。另外,非常推荐去我之前的团队,现在是苗老板负责,你可以私下联系他喔~问题 感觉自己的 js 基础很薄弱啊,我想问如果想进大厂你指的基础具体一点到底指啥啊,我是一个非科班出身的求解呀 ? 解答 如果是应届生,大厂关注的是你的基础和潜力。如果是社招,大厂会关注你的经验和能力,以及潜力。如果你有心仪的大厂,不妨去看一下他们的招聘要求,以及关注一下他们对外输出的东西。非科班是一个劣势,那么你就务必要花时间去补一些计算机相关的理论知识,简历有需要亮点,最好能有一些技术输出,比如很多人会做博客、写系列文章、做有趣的项目等等。另外,最好的时机是等大厂缺人,招人名额多的时候去投简历,也可以多认识一些找内推机会。最后,一切的一切,都离不开硬实力,所以优先提升自己的硬实力,多花时间学习。问题 黄轶老师,你对于在项目中推行 BFF 模式有什么见解吗 ?希望你可以回答的略广一点,非常期待您的回答。 解答 BFF 在服务聚合上还是很有优势的吧,特别是微服务特别火的今天,前端只需要关注所需要的数据,不用关注底层提供这些数据的服务。我在滴滴和 Zoom 的时候都是这么玩的~问题 请问一下,你做兼职的话,一般是关注那些方面呢 ? 还有比较建议在哪些渠道寻找兼职做呢 ? 解答 主要关注的是性价比,因为牺牲了自己的业余时间,要么是多挣钱(很难),要么是提升能力。最好是熟人介绍,没有的话可以去水木论坛找找看(我曾经找到过),其它渠道没有经验,我就不推荐了。问题 黄老师,想问下你对于前后端数据交互的最佳实践的看法,ajax ?axios ?等等,有没有系统学习的推荐。 解答 前后端交互通常有 HTTP 和 WebSocket 2 种通讯方式,建议你首先系统的学习一下 HTTP 相关知识,推荐看 《HTTP 权威指南》或者是 《图解 HTTP》。另外你提到的 axios 只是对 Ajax 的封装,如果你想了解它的实现原理,正好前阵子我在慕课网做了一门课程《基于TypeScript从零重构axios》,学一遍后你会对 axios 的实现细节会了如执掌,同时也可以巩固不少 HTTP 相关的知识。问题 ...

June 21, 2019 · 3 min · jiezi

【手牵手】搭建前端组件库(一)

手牵手搭建前端组件库本文梳理如何搭建和构建前端组件库.了解几个问题为何需要组件化?大部分项目起源都是源于业务方的各种各样的奇葩需求。随着公司的业务发展,公司内部开始衍生出很多的B2C系统、后台系统,前端部门也疲于应对越来越多同质化的项目,这些项目在很多基础模块层、源代码存在不小的相似,甚至存在相似的业务模块。笔者曾经所在的一个电商团队,前端成员基本每个人多做过登录注册、购物车、支付、微信登录…… 大量重复的业务代码。由于组内技术没有强制规范本质上相同的东西,重复的去code就显得浪费.分析这些问题发现:日渐增多的业务场景需求前端资源有限,无法支持所有项目的快速迭代公司内部诸多产品业务混乱、体验不统一于是开发底层的工具去服务不同业务就很有必要:设计一套公司内部的基础组件库支撑各个前端项目,提升项目和业务的可用性和一致性。一个前端团队拥有大量的业务场景和业务代码,相似的页面和代码层出不穷,如何管理和抽象这些相似的代码和模块,绝大多数团队会遇到这样的问题。 不断的拷代码? 修改代码?还是抽象成组件?显然后者更高效。所以在多项目存在高度的可控、底层依赖的情况下,前端实现组件库是最好的选择。组件化,又或者组件抽离的目的是为了功能共享方便维护,其能够带来的好处是少写代码,统一管理、统一维护。一套基础组件代码千锤百炼精而又精,从而起到快速支撑业务迭代,提升开发效率的目的。业务型组件库前端组件库百花齐放,antd、element ui这些基础组件库已经很强大,使用于各种业务场景。但是这些基础组件的粒度是基于单个交互,而在交互与产品之间隔着各种各样的模块和业务场景,产品的汇聚源于各种基础组件在业务逻辑的沾粘下集成为一个个项目,一个团队或多或少会有项目或模块存在功能、交互流程的重复、本质上的同质化。所以antd、element ui 这类组件库是基于单个非连续性的交互组件,一个组件代表着一次人机无副作用的操作与响应,其不思考实体、用户、终端的状态,最小化的暴露和响应组件内部状态。对于连续性的交互通常来说与特点的业务场景有关,存在诸多的外部依赖,目前都是在各个业务模块由用户(coder)自行编写。有没有一种方法解决连续性交互流程的共用问题?解决的办法是组件封装包含业务场景的连续性交互流程,利用组件化将内部依赖通过接口映射到外部。前端架构部门为业务部门提供业务型组件库能够有效提高开发效率.组件库设计思路组件是对一些具有相同业务场景和交互模式、交互流程代码的抽象,组件库首先应该保证各个组件的视觉风格和交互规范保持一致。组件库的 props 定义需要具备足够的可扩展性,对外提供组件内部的控制权,使组件内部完全受控。支持通过 children 自定义内部结构,预定义组件交互状态。保持组件具有统一的输入和输出,完整的API.组件库的开发我们需要考虑:组件设计思路、需要解决的场景组件代码规范组件测试组件维护,包括迭代、issue、文档、发布机制一个完整强大的组件库需要多方面努力,回归正题.使用到的基础技术vue cil 3npmwebpackrollup(v1.2.2)Demo下面就手把手搭建一个前端偏业务性的组件库。组件库包括:message 组件: 一个封装用于呈现后台通过 websocket 推送到前台页面的实时消息模块;pay 组件: 一个封装用于实现商品支付的模块share 组件: 一个封装用于实现商品、文章、视频在各社交平台分享的模块只抛出一个栗子,组件内部实现略这里注意组件抽取的粒度,组件的抽离以一个完整的连续性交互为目地。组件依赖数据、交互事件、控制权的暴露需要考虑全面,不同的上层业务部门都有自己对组件可配置的不同渴望。需要权衡,不能把配置化给捣鼓的永无止境到很难堪的局面。笔者曾经就参与一个项目的组件化,组件抽离的面目全非,各种依赖、环境、状态的配置,导致最后只有组件编写人员在看文档加回忆的情况下才能搞清楚其来龙去脉.从简单的开始1、初始化组件库目录创建一个空项目// 新建一个项目vue create qw-ui经过vue cil3初始化后的qw-ui目录:├─docs│ ├─public│├─src│ .gitignore│ babel.config.js│ package-lock.json│ package.json│ README.md│ vue.config.js│ 此时为了方便组件库的代码管理,将目录结构修改为:├─src // 用作示例 Demo│ ├─packages // 新增 packages 用于编写存放组件│├─lib // 新增 lib 用于存放编译后的输出文件│ .gitignore│ babel.config.js│ package-lock.json│ package.json│ README.md│ vue.config.js│ 目录结构可以更具需要调整.2、修改 vue.config.js 配置vue cli3 提供一个可选的 vue.config.js 配置文件。这个文件存在则他会被自动加载,所有的对项目和webpack的配置,都在这个文件中。修改 vue.config.js 配置的目的主要是:使 Demo 可访问,实现对 src目录的编译处理;提供对 package的编译、构建处理做以下两处修改:修改项目的入口entry 字段为项目入口入口修改使用 Vue CLI 3 的 page属性来配置:module.exports = { pages: { index: { // page 的入口 entry: ‘src/main.js’, // 模板来源 template: ‘public/index.html’, // 在 dist/index.html 的输出 filename: ‘index.html’ } }}添加对 packages 目录的编译处理packages 是我们后来新增的一个目录,默认是不被 webpack 处理的,所以需要通过添加配置对该目录的编译支持。新增编译处理目录,需要通过webpack的链式操作chainWebpack函数实现:module.exports = { pages: { index: { // page 的入口 entry: ’examples/main.js’, // 模板来源 template: ‘public/index.html’, // 在 dist/index.html 的输出 filename: ‘index.html’ } }, chainWebpack: config => { // packages和examples目录需要加入编译 config.module .rule(‘js’) .include.add(/packages/) .end() .include.add(/src/) .end() .use(‘babel’) .loader(‘babel-loader’) .tap(options => { // 修改它的选项… return options; }); }}执行 npm run vue-cli-service serve , 实现对Demo的访问.3、编写 packages 组件库创建一个 message组件创建组件在 packages 目录下,所有的单个组件都以文件夹的形式存储,这里创建一个目录 message 文件夹;在 message/ 目录下创建 src/ 目录存储组件源码,所有 message 依赖的除第三方资源都存放与该目录下;在 /message目录下创建 index.js` 文件对外提供对组件的引用示例代码:message/index.js 对外提供应用// message/index.jsimport message from ‘./src/message ‘message .install = function (Vue) { Vue.component(message .name, message )}export default message // message/src/message .js<template> <div class=“message”> <el-row class=“message-test”> <el-col :span=“12” class=“message-row”><p class=“text”>hello {{ message }}</p></el-col> <el-col :span=“6”> <img src="./st.png"/> </el-col> </el-row> </div></template><script>import ‘./index.scss’export default { name: ‘v-message’, // 申明组件的 name属性 props: { message: String }}</script>需要注意的是,组件 mesage 必须声明 name 属性,这个 name 就是组件的标签,如:<v-message><v-message/>packages/message目录结构如下:packages/message ├─index.js │ ├─src │ message.vue │ st.png // 组件依赖的图片 │ index.scss // 组件依赖的样式文件导出 packages 组件库修改 /packages/index.js 文件,整合所有组件,并对整个组件库进行导出:// 导入组件import hello from ‘./hello’// 存储组件列表const components = [ hello]// 定义 install 方法,接收 Vue 作为参数。如果使用 use 注册插件,则所有的组件都将被注册const install = function (Vue) { // 判断是否安装 if (install.installed) return // 遍历注册全局组件 components.map(component => Vue.component(component.name, component))}// 判断是否是直接引入文件if (typeof window !== ‘undefined’ && window.Vue) { install(window.Vue)}export default { // 导出的对象必须具有 install,才能被 Vue.use() 方法安装 install, // 以下是具体的组件列表 hello}到此,构建组件库的环境准好好了### 4、发布组件库到 npmpackages 目录的编译打包在 package.json的 scripts 字段中新增一下命令:“lib”: “vue-cli-service build –target lib –name kui –dest lib packages/index.js"vue cil3 提供了 库模式 来打包第三方库的开发,packages 的编译打包需要使用库模式–target: 构建目标,默认为应用模式。这里修改为 lib 启用库模式。–dest : 输出目录,默认 dist。这里我们改成 lib[entry]: 最后一个参数为入口文件,默认为 src/App.vue。这里我们指定编译 packages/ 组件库目录。在 vue cil3 库模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。配置好了后,执行编译命令:npm run lib稍后控制台输出,即编译完成: DONE Compiled successfully in 5988ms16:05:35 File Size Gzipped lib\kui.umd.min.js 8.08 KiB 4.55 KiB lib\kui.umd.js 17.78 KiB 7.31 KiB lib\kui.common.js 17.41 KiB 7.19 KiB lib\kui.css 0.10 KiB 0.10 KiB Images and other types of assets omitted. Total task duration: 8.71s ```package.json 配置name: 包名,该名字是唯一的。可在 npm 官网搜索名字。version: 版本号,每次发布至 npm 需要修改版本号,不能和历史版本号相同。description: 描述。main: 入口文件,该字段需指向我们最终编译后的包文件。keyword:关键字,以空格分离希望用户最终搜索的词。author:作者private:是否私有,需要修改为 false 才能发布到 npmlicense: 开源协议参考配置:{ “name”: “qw-ui”, “version”: “0.1.0”, “private”: false, “main”: “lib/kui.umd.min.js”, “description”: “qw-ui”, “keyword”: “qw-ui”, “author”:“luojh”, “scripts”: { “serve”: “vue-cli-service serve”, “build”: “vue-cli-service build”, “lint”: “vue-cli-service lint”, “lib”: “vue-cli-service build –target lib –name kui –dest lib packages/index.js” }}添加 .npmignore 文件发布时,只有编译后的 lib 目录、package.json、README.md才需要被发布。所以通过配置.npmignore文件忽略不需要提交的目录和文件。# 忽略目录examples/packages/public/# 忽略指定文件vue.config.jsbabel.config.js*.map# 本地文件.env.local.env..local# 日志文件npm-debug.logyarn-debug.logyarn-error.log# 编辑器缓存文件.idea.vscode*.suo*.ntvs**.njsproj*.sln*.sw*发布到 npm首先需要在 npm 官网上注册一个账号,通过 npm adduser 命令创建一个账户,或者在 npm 官网注册注册完成后在本地命令行中登录:npm login执行发布命令,发布到 npmnpm publishnpm 淘宝镜像不支持 publish 命令,如果设置了淘宝镜像,publish 前需将镜像设置会 npm :npm config set registry http://registry.npmjs.orgnpm publish时,本地cmd终端需通过管理员运行### 5.使用组件库安装发布的组件库:npm i qw-ui使用组件:# 在 main.js 引入并注册import qwui from ‘qw-ui’Vue.use(qwui)# 在组件中使用<template> <v-message message=” hello 333 :: 使用kui组件库"></v-message></template><script> export default { data () { return { } } }</script>完! ...

February 26, 2019 · 3 min · jiezi

【手牵手】搭建前端组件库(二)

进阶组件库按需引入在目前,所有的组件会被打包进一个文件,组件库是一骨碌加载完所有组件,同时也会打包和加载多余的代码。对于小项目这样没有问题,但是当组件库越来越庞大、丰富,特别是像我们带业务逻辑的非js库,代码量会更大,如果不管不顾的一通加载完所有资源,后期肯定会带来业务方面的体验问题。所以首要的问题是实现源代码的按需引入,而按需引入的前提是实现源码包按独立组件分割和的拆分打包。代码分拆单个组件独立构建打包的思路就是给打包工具提供多个独立的入口,根据入口收集其所依赖的资源。一个组件入口产出一个文件webpack使用 webpack 配置多入口的方式来按模块拆分打包,每一个模块作为一个入口,与此同时产出对应的文件。webpack 的配置比较简单,不展开.实际构建 library 使用 webpack 有不小的缺点, 应为 webpack 产出后的文件中带有一层包裹代码,这种情况下在碎片化的组件库中反而会使打包体积变大,无法起到‘瘦身’的效果。如下的 webpack 包裹代码:/* 1 //*/ (function(module, webpack_exports, webpack_require) { ‘use strict’; / unused harmony export square / / harmony export (immutable) */ webpack_exports[‘a’] = cube; function square(x) { return x * x; } function cube(x) { return x * x * x; }});哪些额外的代码看着有点不那么清爽.rollup使用 rollup来打包 library,rollup相较于 webpack 在打包体积上会跟小,更加适合 .rollup 的特点:Tree Shaking: 自动移除未使用的代码, 输出更小的文件Scope Hoisting: 所有模块构建在一个函数内, 执行效率更高Config 文件支持通过 ESM 模块格式书写可以一次输出多种格式:模块规范: IIFE, AMD, CJS, UMD, ESMDevelopment 与 production 版本: .js, .min.js是驴是马拉出来溜溜全局安装 rollupnpm install –global rollup// orcnpm install –global rolluprollup 的迭代比较快,这里稍微留意一下 rollup 的版本,部分插件可能不兼容添加rollup.config.js在项目根目录下创建 rollup.config.js 文件:// rollup.config.jsexport default { input: ‘packages/index.js’, output: { file: ’lib/app.all.js’, format: ‘cjs’ }};input:构建入口format:编译打包格式file:编译后输出目录就这么简单,执行以下命令开始将装个 packages 构建构建为一个单文件rollup -c rollup.config.js添加 rollup 多文件构建Rollup 配置文件是一个标准 ES6 模块,默认到处一个对象,也可以到处一个对象用来构建多个模块// rollup.config.jsexport default [{ input: ‘packages/a.js’, output: { file: ’lib/app.a.js’, format: ‘cjs’ }},{ input: ‘packages/b.js’, output: { file: ’lib/app.b.js’, format: ‘cjs’ }}];packages 目录为组件库源码,相关模块不固定,不适宜写死。对于这个问题通过扫描目录获取所有模块,修改 rollup.config.js :// rollup.config.jsconst fs = require(‘fs-extra’);const path = require(‘path’);const packages = {};const dir = path.join(__dirname, ‘../packages’);const files = fs.readdirSync(dir);files.forEach(file => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { packages[file] =packages/${file}/index.js; }});function createRollupConfig (file, name) { const config = { input: file, output: { file: lib/index.js : lib/${name}/index.js, format: ’es’, name: name, sourcemap: true } } return config}const buildPackages = []for (let name in packages) { const file = packages[name] buildPackages.push(createRollupConfig(file, name))}export default buildPackages;这里打包文件的格式我们使用 es,es是指ES6.这个时候开始构建会报错,因为rollup还不能识别组件库中的 vue 样板代码、语法,同时我们的组件库并不是纯粹的js library, 也需要处理业务组件内引用的样式和图片、字体等。仅仅是使用 rollup 还不能实现我们的目的,需要借助一系列 rollup 插件来完成处理vue.vue文件的编译需要使用rollup-plugin-vue2插件:npm rollup-plugin-vue2 –save-dev处理样式样式处理可以使用 rollup-plugin-css-only插件。由于不喜欢笨拙的css,习惯了scss语法,就直接使用 scss,但其配置就相对要复杂一点。scss样式处理可以使用rollup-plugin-scss插件,为了灵活的处理样式我使用Postcss图片处理html中引入的图片在组件库部署后就无法正常访问了,这里使用 rollup-plugin-url插件将内嵌的图片编译为 base64 再直接放入 js 文件中。对于组件库中有较多大尺寸的图片建议直接将图片放入图片服务器,然后通过url 引入,避免打包文件过大的问题.加入 rollup 插件后的配置:// rollup.config.jsconst fs = require(‘fs-extra’);const path = require(‘path’);import vue from ‘rollup-plugin-vue2’import postcss from ‘rollup-plugin-postcss’import postcssScss from ‘postcss-scss’import autoprefixer from ‘autoprefixer’import base64 from ‘postcss-base64’import url from ‘rollup-plugin-url’import progress from ‘rollup-plugin-progress’import filesize from ‘rollup-plugin-filesize’;function isDir(dir) { return fs.lstatSync(dir).isDirectory();}const packages = {};const dir = path.join(__dirname, ‘../packages’);const files = fs.readdirSync(dir);files.forEach(file => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { packages[file] =packages/${file}/index.js; }});function createRollupConfig (file, name) { const config = { input: file, output: { file: lib/${name}/index.js, format: ’es’, name: name, sourcemap: true }, plugins: [ vue(), postcss({ extract: true, parser: postcssScss, plugins: [ base64({ extensions: [’.png’, ‘.jpeg’], root: ‘./packages/’, }), autoprefixer({ add: true }), ] }), url({ limit: 10 * 1024, //include: [’.svg’] }), progress(), filesize() ] } return config}const buildPackages = []for (let name in packages) { const file = packages[name] buildPackages.push(createRollupConfig(file, name))}export default buildPackages;到此可以运行 rollup -c rollup.config.js 打包,实现源代码按依赖关系和目录进行分拆打包:lib ├─message │ index.js | index.css | index.js.map ├─pay │ index.js | index.css | index.js.map ├─share │ index.js | index.css | index.js.map打包后的 packages/pay/index.js > lib/pay/index.js :// lib/pay/index.jsvar logo = “data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDE0IDc5LjE1Njc5NywgMjAxNC8wOC8yMC0wOTo1MzowMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTk2QkI4RkE3NjE2MTFFNUE4NEU4RkIxNjQ5MTYyRDgiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTk2QkI4Rjk3NjE2MTFFNUE4NEU4RkIxNjQ5MTYyRDgiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NjU2QTEyNzk3NjkyMTFFMzkxODk4RDkwQkY4Q0U0NzYiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NjU2QTEyN0E3NjkyMTFFMzkxODk4RDkwQkY4Q0U0NzYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5WHowqAAAXNElEQVR42uxda4xd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”;var message = {render: function(){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c(‘div’,{staticClass:“message”},[_c(’el-row’,{staticClass:“message-test”},[_c(’el-col’,{staticClass:“message-row”,attrs:{“span”:12}},[_c(‘p’,{staticClass:“text”},[_vm._v(“message “+_vm._s(_vm.message)+” – “+_vm._s(_vm.count))])]),_vm._v(” “),_c(’el-col’,{attrs:{“span”:6}},[_c(‘p’,[_vm._v("\n css backgroud image base64\n “)]),_vm._v(” “),_c(‘div’,{staticClass:“tops”}),_vm._v(” “),_c(‘hr’),_vm._v(” “),_c(‘p’,[_vm._v("\n html img 内嵌 image base64\n “)]),_vm._v(” “),_c(‘img’,{staticStyle:{“width”:“100px”},attrs:{“src”:_vm.imgUrl}})])],1)],1)},staticRenderFns: [], name: ‘x-message’, props: { message: String, }, data: function () { return { count: ‘2233hahaha’, imgUrl: logo } }, created: function () { console.log(’logo:’, this.imgUrl); }};message.install = function (Vue) { Vue.component(message.name, message);};export default message;//# sourceMappingURL=index.js.map构建整个组件库在支持按需引入的同时,如果还需支持完整引入整个组件库。则直接在 rollup 配置里加入一个完整的组件库入口rollup.config.js 最终配置// rollup.config.jsconst fs = require(‘fs-extra’);const path = require(‘path’);const pkg = require(”../package.json”)import vue from ‘rollup-plugin-vue2’import postcss from ‘rollup-plugin-postcss’import postcssScss from ‘postcss-scss’import autoprefixer from ‘autoprefixer’import base64 from ‘postcss-base64’import url from ‘rollup-plugin-url’import progress from ‘rollup-plugin-progress’import filesize from ‘rollup-plugin-filesize’;function isDir(dir) { return fs.lstatSync(dir).isDirectory();}const packages = {};const dir = path.join(__dirname, ‘../packages’);const files = fs.readdirSync(dir);files.forEach(file => { const absolutePath = path.join(dir, file); if (isDir(absolutePath)) { packages[file] =packages/${file}/index.js; }});const allScript = ${pkg.name}.allpackages[allScript] = packages/index.js;function createRollupConfig (file, name) { const config = { input: file, output: { file: name === allScript ? lib/index.js : lib/${name}/index.js, format: ’es’, name: name, sourcemap: true }, plugins: [ vue(), postcss({ extract: true, parser: postcssScss, plugins: [ base64({ extensions: [’.png’, ‘.jpeg’], root: ‘./packages/’, }), autoprefixer({ add: true }), ] }), url({ limit: 10 * 1024, //include: [’.svg’] }), progress(), filesize() ] } return config}const buildPackages = []for (let name in packages) { const file = packages[name] buildPackages.push(createRollupConfig(file, name))}export default buildPackages;产出的构建目录:lib ├─message │ index.js | index.css | index.js.map ├─pay │ index.js | index.css | index.js.map ├─share │ index.js | index.css | index.js.map index.js //完整组件库,包含所有组件 index.css index.js.map到这里构建部分完成,下一步,将构建后的lib目录发布到 npm:修改package.json version字段与上次不一样(如: 0.1.2),否则会提交失败修改package.json main字段为: lib/index.js发布:执行 npm publish按需引入组件组件库发布后,我们转入业务项目中npm 安装组件库,如:npm i qw-ui安装完成后,在项目node_modules文件夹下可以找到名为_qw-ui@0.1.1@qw-ui,即我们刚才发布的组件库.完整引入当我们想一次引入整个项目,而非单独引入每次组件时: 修改src/main.js,全局引入整个qw-ui,如:import Vue from ‘vue’import App from ‘./App.vue’import ElementUI from ’element-ui’;import ’element-ui/lib/theme-chalk/index.css’;import qwui from ‘qw-ui’ // 全局引入整个组件库import ‘qw-ui/lib/index.css’ // 全局载入样式Vue.config.productionTip = falseVue.use(ElementUI)Vue.use(qwui)new Vue({ render: h => h(App),}).$mount(’#app’)按需引入按需引入需要借助 babel-plugin-import,我们可以只引入需要的组件,以达到减小项目体积的目的.首先npm install babel-plugin-import –save-dev,然后再项目根目录上新建文件.babelrc.vue cli3 直接修改babel.config.js文件:// babel.config.jsmodule.exports = { presets: [ ‘@vue/app’ ], plugins: [[“import”, { “libraryName”: “qw-ui”, “customName”: (name) => { return ../lib/${name}/index; }, “style”: (name) => { return ${name}.css; } }]]}// src/main.jsimport Vue from ‘vue’import App from ‘./App.vue’import ElementUI from ’element-ui’;import ’element-ui/lib/theme-chalk/index.css’;import { message } from ‘qw-ui’ // 按需引入 message 组件Vue.config.productionTip = falseVue.use(ElementUI)Vue.use(message)new Vue({ render: h => h(App),}).$mount(’#app’)这时候已经启用了 babel-plugin-import ,插件会帮我们将import { message } from ‘qw-ui’转换成 import message from ‘qw-ui/lib/message’ 的写法。同时自动注入组件样式。~ 运行一下项目私有npm业务性组件库一般只适合于公司内部,组件或多或少的也涉及到代码安全和商业风险,所以将打包后的组件库发布到私有npm而不是发布到公网上的npm官网相对要安全很多.私有 npm 仓库可以让我们使用组件就像 npm 官方仓库里的包一样方便。私有 npm 仓库有以下一些特性:私有包托管在内部服务器或者单独的服务器上;可以同步整个官方仓库,也可以只同步需要的;下载的时候,可以让公共包走公共仓库,私有包走私有仓库;可以缓存下载过的包;对于下载,发布,有对应的权限管理。私有npm的搭建有多种方式,最简单的使用 git 仓库作为私有仓库.快速搭建和部署私有的 npm 包管理服务也可以使用 verdaccio对权限、安全性、稳定性有更高要求的可以使用 cnpmjs.org, cnpmjs.org 服务的搭建需要配合数据库使用.完! ...

February 26, 2019 · 4 min · jiezi

如何去设计前端框架能力?星巴克消息开放项目从0到1,从点到面的思考

本文由淘宝前端工程师罗嗣分享,主要讲述了作者在星巴克消息开放项目中的总结和思考,希望对大家有帮助,让业务分享更加有价值。摘要从满足星巴克项目需求单点出发,发散到从点到面的思考。从而总结了自己思考的基本流程(方法论)。从如下四个递进方面思考。业务拓展:拓展自有业务的边界,和其他业务合作共建,形成标准的能力透出, 合力共建。业务趋势:业务的特点和趋势是如何。技术可以如何储备来应对未来业务的变化。技术趋势:技术命题,技术趋势。选择适合的技术来解决现在的问题。保持技术对未来的弹性。需求问题:客观存在的事实,现在需求存在哪些问题,我们如何去帮助业务更加稳定,更加高效。本文提纲笔者从0到1构建一个IM前端系统,再从点到面思考整合突破原有的自有业务限制,尽量设计出的可扩展,可交互,甚至小而美的系统能力。本文会从如下几个方面去介绍。点:项目背景及需求难点(支付宝星巴克小程序入驻客服接待),以及现有的能力。面:需求做完反向思考,当前BC/CC遇到的问题及痛点,如何在同一个领域模型下做推动标准化能力。需求介绍项目背景客服接待能力由手淘消息平台和CCO团队合作共建,整体采用AMP+XSPACE的方案落地,AMP承接C端用户聊天界面,XSPACE承接B端聊天界面,同时接待又需要原有BC的聊天能力。星巴克客服接待两纵一横,底部需要对接不同的服务端,上层需要保证同一套UI来提升一致性体验。设计思路总体设计思想:设计分离出数据层和UI层,数据层和UI层以标准化协议对接。这样分层就可以解决当前业务遇到的问题,如下是当时需求的标准SDK事例点到面的思考星巴克客服消息接待开放是一种轻量级(H5形式)的客服接入能力。思考当前业务的问题是什么,如何改进,业务价值的意义等。 笔者会从如下几个方面去思考。原有H5旺旺由于历史原因有稳定性和体验的问题,这套方案能不能提供替换成原来的H5旺旺,同时对聊天接入统一收口(标准化组件)。从而达到更加稳定,更加的体验性。H5旺旺聊天可以投放到阿里系的其他端上(优酷,饿了嘛,拍卖等),甚至现在很多外投的广告业务。把H5聊天能力做强对淘宝的引流及成交都有很大的意义。同时集团里面还有小蜜作为客服聊天能力。能不能站在前端的角度思考整合输出。针对集团二方业务。需要定制个性化消息和UI能力,需要把SDK能力提供给他们去进行上层业务扩展,为保证他们低成本的接入需要提供基础能力,二方去扩展插件。同时工具链路上需要保证提高效率。生成闭环的开发环境,接入业务方只要关系自己的业务需求思考模型基于之前的背景和诉求,整体设计思路: 抽离UI层和数据层(模块),UI层和数据层基于Message实体进行标准化协议对接(桥梁)。工具链路垂直支持提高效能。 有如下几个方面衔接点:开放 UI组件 和 标准化SDK能力,让二方业务快速搭建,UI层 和 数据层之间用 标准化协议做桥梁连接在基础SDK上,会透出Context上下文(内部核心对象message,session,app)让业务去定制修改,业务方只需要去扩展插件。基于DEF脚手架体系提供相应工具链路支持,包括项目模板生成,项目测试,项目构建,完善可持续集成。业务价值在阿里做每件事情,需要明确这件事情的价值,这件事情投入产出比是多少。上文也陆续在提价值。 如图可以说明这件事价值实践方案上面几章介绍了项目背景,设计思路,思考模型和业务价值(PS:类似于论文前两章在介绍背景和理论知识)。这章主要是讲的项目实践。站在前端的角度,从四个方面去实践,并付相应代码地址。标准化协议: 由于消息领域模型是一致的,可以抽象出标准的 会话和 消息 格式。他是SDK和组件能力的桥梁SDK能力开放:提供标准化数据对接的能力,负责插件扩展能力。 业务入驻只需要开发业务相应的中间件(插件)。例如:各自业务的编解码模块,登录模块,消息处理模块组件能力开放:提供标准化的聊天能力组件。例如聊天入口接入标准化组件工具链路支撑:基于DEF脚手架体系,开发了def-kit-tbms套件。支撑项目全链路开发领域模型(标准化协议)为了达到消息标准化能力,需要把基本概念和接口达成一致。梳理两个基础概念: 会话 和 消息。会话conversation: 它是指AB通讯之间维持的一种关系,它是消息存储的载体消息message: 消息是一个对话的基本组成部分, 根据业务分为两大块消息,会话内消息和系统通知消息。会话内消息又可以分为基本消息和自定义消息。会话类型即时通讯 SDK 的核心概念「会话」,即 Conversation。我们将单聊和群聊(包括聊天室)的消息发送和接收都依托于 Conversation 这个统一的概念进行操作。会话属性备注id会话IDscene场景to聊天对象,账号或者群IDupdateTime会话更新时间unread未读数lastMsg此会话的最后一条消息custom扩展Json字符串消息类型IM SDK内的消息可以分为两类:会话内消息和系统通知消息。会话内消息只能出现并展示在聊天界面里,一般是应用内的一个用户发给另一个用户(或群组/聊天室)的消息,例如文本消息、图片消息都属于会话内消息。:会话内消息类型备注文本消息消息内容为普通文本图片消息消息内容为图片URL地址、尺寸、图片大小等信息语音消息消息内容为语音URL地址、时长、大小、格式等信息视频消息消息内容为视频文件的URL地址、时长、大小、格式等信息文件消息消息内容为文件的URL地址、大小、格式等信息,格式不限地理位置消息消息内容为地理位置标题、经度、纬度信息通知消息自定义消息可以用于消息接入扩展。 例如卡片消息,红包消息等。自定义消息**通知消息属于会话内的一种消息,用于会话内通知和提示场景。例如:群名称更新、某某某退出了群聊等。**会话和消息标准化格式标准化协议标准化会话格式标准化消息格式SDK能力开放SDK的设计参考了Koajs的设计原理(底层微创新了下)。Koajs的中间件思路: 中间件对于一次请求来处理,context分别集成了request和response对象, 同理可以映射成对一条收发消息的处理,面向切面的编程方式。。 在context中会集成message(消息),session(会话),app(如用户,初始化sdk信息等其他信息)。整个项目通过lerna进行了包管理,用Typescript写了SDK,并做了充分的单元测试,大家可以放心使用。整个项目分为了如下几个模块:@ali/tbms-compose: 函数组合模块,用于@ali/tbms-middlware服务@ali/tbms-middleware: 中间件模块@ali/tbms-util: 通用函数分装:如promise同步执行队列,mtop请求,event事件系统@ali/tbms-sdk: 消息标准化基础SDK,可以让业务扩展,补充插件测试说明:对底层支持的SDK都做了充分的单元测试,保证稳定性。后续版本更新提供差异性修改的检查使用事例组件能力开放由于需要多端投放,某些二方应用支持weex能力。从而选择了RAX技术方案。再在H5表现下对单聊做性能优化,现阶段完成聊天入口的统一接入组件,上层的组件在陆续完善中(完成度20%)。@ali/rax-tbms-chatwater tbms-components工具链路支撑基于DEF脚手架体系,开发了def-kit-tbms套件。提供项目全链路开发支撑。这个项目后续的项目搭建都采用standard-dev脚手架生成项目目录。例如:tbms-toolkit,tbms-packages总结这是一次完整的一个项目从0到1,从点到面的思考过程,建立模型到付诸于实践。从完成业务需求(需求做什么)到帮助业务成长(我能做什么)的思考过程。虽然只是站在前端角度在思考问题,但是方法论相信可以通用。后续Action改善原来的H5旺旺,使之更加稳定和更好的体验性。同时对聊天接入统一收口(标准化组件和标准化接入SDK)。Flag:利用业余时间,一月中旬前第一版本里程碑发布未完待续有什么IM相关的需求都可以联系我@罗嗣,共建标准化和生态。本文作者:罗嗣阅读原文本文为云栖社区原创内容,未经允许不得转载。

December 19, 2018 · 1 min · jiezi