JavaScript

使用高阶函数实现类的扩展设计

在不少框架中,都会对“扩展”这一概念有需求。所谓扩展,即一个可组合的组件,用于嵌入到目标的生命周期中,对目标的行为进行额外的处理使得目标拥有不同的表现。 一个非常简单的案例即日志的记录。通常框架自身并不会有业务相关的日志记录的功能,而业务代码也不希望混入并非业务逻辑的日志记录部分。那么使用一个扩展,在合适的点进行日志的收集和存储是很合适的设计。 在以往,比较流行的扩展通常有几种形式: Mixin形式。这种形式下扩展与目标形成完全的覆盖关系,属于暴力而简单的方法。 class Component { constructor({mixins}) { mixins.forEach(mixin => Object.assign(this, mixin)); } doWork() { // ... } } let logMixin

设计

聊一聊复用

复用是每一个工程师的追求,无论是设计还是编码我们最常说的除了“产品是SB”、“进度赶不上”外,恐怕就是“要好好复用”了吧。当一个应用或者一个系统日趋复杂的时候,我们总是希望其维护性不会线性地降低,而解决这一问题的方法上,就往往归结为简单的“复用”二字。 在与不少同学的沟通中,我发现有一部分工程师对“复用”的理解是有失偏颇的,在此针对比较容易出现的几类曲解进行一下解说。 复用的目的 我的论断很简单: 复用有很多的目的,但少写代码不应是其中一个。 注意这边说的是,少“写”代码不应该是复用的目的之一。所谓的“写”,是一个人所做出的行为,复用作为一种工程手段,其应该解决的是工程的问题,而不是人的问题。

杂想

另类MVC模式 - 优势及实现

继续大逆不道系列…… 在上一篇中,提出了一个另类的MVC模型,与经典MVC模型有一些不同,那么自然需要描述这样的另类模型有什么优势,又能在怎么样的场景中使用。 逻辑划分 正如上一篇所说,这种模式下,最大的优势莫过于逻辑的清晰划分。在该模式的作用下,每一个Action都只要处理真正与自己有关的逻辑及数据,而不需要关心一些“通用”的内容,因为这些通用内容也成了独立的Action。 例如,继续引用上一篇中的页面设计,根据经典的MVC模式,我们不得不在一个Action中准备所有数据: public ActionResult ViewPost(int id) { ViewBag.Friends = FriendsRepository.ByUser(CurrentUser); ViewBag.RecentVisitors = VisitorsRepository.

杂想

另类MVC模式 - 思考和雏形

这是一篇大逆不道的文章,其作用就是供大家娱乐以及批斗,因为此文所提及的思想,试图改变现有的著名模式MVC的结构,因此如果认为MVC优秀甚至完美的话,还请直接忽略此文,以免影响心情。 本文将提出一种类似MVC但又不完全是现有的经典MVC的模式,该模式仅基于HTTP的Web系统中对经典的MVC模式进行改造,其特点是将View前置,通过View的切分来切分逻辑,形成多次M-V-C交互,最终生成响应。 经典MVC模式 对于经典的MVC模式,虽然从表面上看完全是个“不需要解释”的问题,但是每个人的理解又不尽相同。在我的理解中,MVC模式可以用下面这张图来表达: 基于HTTP的Web系统里,在经典的MVC模式中,一个请求的处理过程大致分为: Controller处理原始请求,根据请求的数据与系统的配置,寻找到真正处理该请求的逻辑,称之为Action。 Action处理请求提交的数据,与Model进行交互,获取需要反馈的数据,并将这些数据按View的要求组装后交给View。 View根据Action递交的数据,

JavaScript

单页系统前端MVC设计 – MVVM模式

在上一篇中,Action已经可以获取所有需要的数据,下一步就是将数据与视图中的控件树进行关联,使得View模块可以获取、修改数据,并通过与Action的进一步交互来完成整个业务逻辑。 MVVM模式 在介绍本文的ValueStack和ModelDescriptor这两个在整个框架中意义非凡的组件之前,需要先简单地介绍一下MVVM模式。 这个MVC框架引入了MVVM模式,MVVM模式的全称是Model-View-ViewModel模式。该模式顾名思义地有三个部分组成: Model:即数据,所有可持久化和不可持久化的,与业务有着关系的内容都可以被称为数据。Model的数据是扁平的,为了区别下面的ViewModel,这里可以称之为FlatModel。 例如,一个系统有用户,以及用户发表的文章,期间是1:n的关系,那么Model就存在两条数据。其一为user,其二为postList,两者之间虽然有着“从属”的关系,但是在FlatModel这样“扁平”的结构中,

JavaScript

单页系统前端MVC设计 – 获取数据

一个单页系统中,由于浏览器始终不能离开当前页面,从而无法利用服务器端的动态页面(ASP、JSP、PHP等)将数据与页面结合起来,数据与表现相比单独的页面请求分离得更为彻底。其中数据获取的部分,通常使用的是AJAX的技术,利用XMLHttpRequest向服务器请求数据。 我在很多篇文章中提到过,现有互联网的大部分应用是数据驱动的,数据是整个业务的核心和关键所在。在框架的执行过程中,Action同样需要数据才可以驱动整个界面的渲染及交互,因此当Action进入自身的执行流程时,第一件事就是将自己需要的所有数据获取过来。 Action流程 Action从execute开始,到renderView绘制视图,并在之后与视图进行交互,其执行流程如下所述: 调用execute函数开始流程。 调用prepareData函数,该函数中利用DataProxy组件获取数据。 当所有数据请求完成后,会调用buildModel函数,该函数通过ValueStack和ModelDescriptor两个组件,将第2步中获取的数据组装成为更加结构化、可用于视图中的控件树的模型。 完成了模型的组装后,调用getRoot函数,

JavaScript

单页系统前端MVC设计 – ActionMapping过程

上一篇讲到通过对外暴露LocationManager组件,提供接口进行redirect等操作,可以形成一个Request对象交给Controller(即调用Controller的process函数)。 本篇主要讲述Controller如何处理一个Request,包括通过Request查找到对应的Action,并准备好上下文对象,交进而进入到Action的执行流程中。 Request对象 类似服务器端的MVC框架,Request对象将提供对一次请求中所能接触到的所有内容的封装,其会包含以下内容: url、path、query属性,对应window.location的href、pathname和search属性,表示整个请求资源符或者其中的一部分。 getParameter(key)函数,获取query中某个键对应的值。 getSession(key)函数,获取Session或者Session中某个键对应的值。 getAppContext(key)函数,获取AppContext或者AppContext中某个键对应的值。 Session对象及AppContext对象 在服务器端,

JavaScript

单页系统前端MVC设计 – 入口

这一篇讲作为整个MVC框架的入口的LocationManager和HashListener组件。 LocationManager和HashListener作为框架最外层的组件,提供了整个框架的入口,其中HashListener依赖于LocationManager,并且通过LocationManager才能够进入到框架的运行流程中。 在设计中,HashListener将会被设计成一个内部的组件,不会对外暴露,其所有可能提供的接口都通过LocationManager作代理,以保证对外开放的对象尽可能地少,形成更简明的API。 LocationManager 在接触该组件之前,先解释一下hash这个名词的概念。 在Web中,浏览器的地址栏中的URL通常可以分成几个部分,比如有一个简单的地址: http://www.some-where.com/list.html?page=2&size=20#item-3 上面的URL就能分为下面几个部分: 橙色:protocol,此处表示使用http协议。

JavaScript

单页系统前端MVC设计 – 总览

这是近期可能会接触的设计,对应的需求大致是: 单页系统,所有与后端交互通过AJAX进行。 每个模块、功能有自己的独立界面。 不同模块、功能对应着不同的URL,要求能通过URL直接定位到相关的模块或功能上。 模块间相互穿插,一个功能可能会突然被另一个模块引入,比如放在Dialog中。 对于上面的需求,我们讨论认为,对应的设计至少要能满足以下条件: 基于URL的可导航性和可访问性。 能够非常清晰、明确地划分模块及功能,划分上模块、功能相互独立。 可以方便地引入另一模块或功能的相关界面、交互。 基于此,虽然有着非常严重的过渡设计的嫌疑,最后我们还是选择了MVC的模式。当然这也是因为已经有过一个项目使用类似的模式,并且需要重用其中的一部分功能。 这一篇主要介绍整个设计的总体结构,以及一个请求的流程,首先下面是总体设计的总览图: 图很复杂,组件很多,所以说这根本就是个过渡设计的东西。

用户体验

从热力图上发现细节

百度统计的热力图功能上线了,cnbeta上一如既往的一片骂声,大概在那上回复的都不是什么真正的技术人员吧。 虽然不是同一个项目,但是统计小组为了这功能所付出的努力,也一直看在眼里,虽然热力图功能并不完善,甚至有着相当多的问题(仔细用一用就能发现了,和啥内部消息没有关系),但依旧不失为第三方统计中的一个创举。 确实热力图能反映很多细节和问题,但是发现这些也不是容易的事。本篇主要讲一讲我从百度统计官方空间给出的这张热力图中发现的一些内容。 这个应该是铁血社区的首页,某种程度上来说也是个门户了。接着针对热力图中我比较感兴趣的几个点说一说。 上方导航条 上方导航条每一项都是由4个汉字组成,从点击量来看,多数点击是落在第3个汉字上。 这大概算是网民的一种“中庸之道”的体现吧,不喜欢点头里,也不喜欢点尾部,倾向于中间偏后。 点击如此,是不是可以认为视觉上,一般第一时间也会落在第3个汉字上呢?如果是的话,那么导航条的设计就非常重要了,应当让第3个汉字起到突出全句的核心作用,

杂想

墙与非墙

有自己的房子也已经好几个月了,虽说现在去装修实在没意义,但是看着这个并不豪华甚至有些破旧的屋子,总免不了想一些日后的装修。 当一个人站在客厅的中央,客厅的墙上有着很大的镜子,让这个原本并不大的厅看上去无比宽广。就这样站在没有任何家具的客厅里,突然产生了一个奇妙的想法: 为什么,房子里要有墙呢? 我们不断遵从着社会的标准思想,将一个完整的房子用墙这样的东西隔成一个个小小的空间,客厅、餐厅、梳洗室、卧室……但是如果没有这样的分隔呢? 我的房子,会是一个完整的空间,没有任何的阻隔。在这一片空间之中,各种各样的家具用最合理的方式摆放,这一片用于休息,那一片用于进餐,一片与一片之间有着看上去非常适合的距离感,但是无论在哪里,我可以看到一切。 在这样的空间中,可以毫无阻碍地穿行。在一个角落享受热水的冲淋;在一个角落用餐之后享用甜点和悠闲的下午茶;在窗边看着太阳渐渐落下的金黄;

界面-数据-循环

数据,界面,循环 – 数据模型对界面的影响

上文介绍了数据建模的过程,最终得出一个含有Dataset、Data Item以及Operation的图状数据结构,其特点在于对Operation的关注,而不关注于常见的数据的属性、关系。 在数据建模中大量关注Operation及其之间的关系,就是为了让数据的模型可以有效地影响和指导界面的设计,从而形成“环”的两个部分的一条通路。 因此本文就介绍数据的模型如何指导界面的设计,以及从数据模型导出界面设计的一种方案。 导出跳转关系 在数据模型中,已经定义了针对各个Dataset以及Data Item的操作,同时也有了各个操作之间的关系。 还是使用上文中最后的数据模型: 首先可以假设,每一个操作,我们需要一个独立的界面模块来进行处理,比如针对上文的数据模型的定义,可以得出部分模块: 用户详情查看,User.R 文章列表,PostSet.L 文章详情查看,Post.

界面-数据-循环

数据,界面,循环 – 我是如何进行数据建模的

根据前言的介绍,设计、界面是一个相互影响的环,因此事实上从哪一点介入这个环并不重要,甚至完全取决与个人的喜好。对于个人而言,数据的建模方式也许可以说得更加精彩,因此就以“数据如何进行建模”作为这个环的切入点。 在小型应用的开发中,我是一个重数据而轻业务的人,也许很多注重领域业务的人会对此嗤之以鼻,但这确实是我经过不少项目之后的切身体会,并且也有一套自己的说法,具体的内容希望日后有机会可以写成博文进一步交流。 而在这里可以先不管业务、逻辑是否重要,事实上对于任何一个项目,数据的建模都是不可少的环节。在数据建模的过程中,我始终遵从一个原则:“数据的最终目的是与用户交互”,因此我对数据的建模是站在“交互”的角度出发的。 基本概念 首先,对于数据建模这一环节,会有几个下文中频繁使用的概念: Dataset:数据集,指一类数据的所有个体的集合。

界面-数据-循环

数据,界面,循环 – 前言

这会是一个系列文章,主要讲述在小型Web站点的设计与开发过程中,数据与界面之间如何相辅相承、相互影响。其中绝大部分内容(超过95%)来自于个人的观点,不尽正确,甚至可能和现有业界的部分观点存在着冲突和矛盾。 在我看来,对于一个小型的Web站点或者应用而言,界面是大门,而数据是核心,现有互联网上90%以上的小型Web应用都是以数据为驱动的。在这一类的应用中,数据与界面之间趋于一种不可隔离的状态,其主要体现在以下两点: 界面的作用是展现、修改数据,很少存在与数据无关的重业务型的界面。 任何数据,最终都需要通过界面才可以与用户进行交互,独立的数据没有太多意义。 因为数据与界面间紧密的关系,往往对于一个应用的设计,会采用两种方案进行设计: 自顶向下的设计方式,首先导出界面的框架,进而产生设计原型、交互原型,最终从界面上提取数据及数据间的关系,进而产生数据模型。