xufei / blog

my personal blog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

后Angular时代二三事

xufei opened this issue · comments

commented

后Angular时代二三事

JavaScript框架/库一直就是百花齐放,最近几年更是层出不穷。回顾这几年,有两个最引人注目的东西,一个是Angular,一个是React。其中,Angular最火的时间是2013年中到2014年末,React从2014年中开始升温,然后又由于ReactNative等周边项目,导致关注度很高。

2014年末,Angular官方宣布了一个大新闻,要完全重写Angular 2.0。这个事情让很多想要使用Angular的人止步不前,也给很多人带来了困惑。

随后,Angular 2.0的开发者之一创建了新的框架Aurelia,整体思路上与Angular相似,有一些细节的差异。那么,我们应当如何看待这些框架呢?

为什么Angular 2重写

如果不是有重大原因,没有哪个开发者会做出彻底重写,产生很多不兼容变更的决定。对于Angular来说,它面临这么一些原因:

  • Web标准的升级,主要是Web Components相关标准和ECMAScript的后续版本
  • 自身存在的一些问题:性能,模块,过于复杂的指令等等

使用转译语言

也正是Angular 2.0那篇大新闻,使大家知道了AtScript这样的语言,它在TypeScript的基础上添加了注解等功能。

有很多语言可以转译成JavaScript,比如CoffeeScript,Dart,TypeScript等,从最近的一些事件来看,TypeScript可以算是JavaScript转译领域的最大赢家。

很多人可能会有这样的疑问:为什么我们要用这些东西,而不是直接编写原生的JavaScript?开发语言的选择,很大程度上反映了我们对JavaScript组件化方案抽象度的需求。

比如说,Angular中,可以使用TypeScript来写业务代码,React中,通过JSX来使用组件,这都是具有较高抽象度的方案,能够让业务代码变得更直观。

ES6

先不看这些转译语言,来看看ES6,它给我们带来了很多编程的便利,每一次这种语言细节的升级,都引入了一些好用的东西,所以我们当然是期望尽早使用它。但问题是,浏览器的支持程度总是落后的,如果用它写了,在很多浏览器上不支持,比如箭头函数:

this.removeTodo = function(todo) {
    this.todos = this.todos.filter(item => todo!=item);
};

所幸,我们有Babel这样的转换器,可以把这样的代码翻译成ES5代码,它的生成结果就是

this.removeTodo = function(todo) {
    this.todos = this.todos.filter(function(item) {
        return todo != item;
    });
};

这个例子并不明显,如果你使用class之类的东西,就能体会到更大的改变。虽然说class这些只是语法糖,但用起来还是很爽的,可以复用一些传统的设计模式之类。

对于那些只需支持ES5+的项目而言,现在开始选用ES6语法编写代码是非常合适的,因为我们有Babel这样的东西,我们可以享受ES6新语法带来的愉悦编程体验,而无需承担兼容风险。

ES6新语法有很多,想要在生产过程中更好地使用,可以参见百度ecomfe的这篇使用ES6进行开发的思考

TypeScript

Angular和Aurelia都支持TypeScript,可以直接使用TypeScript编写业务代码。如果选用这样的框架,个人建议直接使用TypeScript。

为什么在类似Angular这样的体系里,我要建议使用TypeScript呢,因为这么几个原因:

长期的兼容性更好

很可能在现在这个阶段,你的项目还需要面对一些不支持ES6的浏览器,所以不能直接写ES6代码,但有可能有一天,浏览器支持了,但你的代码还是老的,它基本上还在使用ES5编写,想要迁移到ES6比较麻烦,以后每次迁移都是痛苦的过程。TypeScript就是以生成JavaScript为目标的,所以如果你用它写,只需选择生成参数,比如生成es5,es6就可以了,就算以后es继续升级,也只要改个参数就完事。

编写体验更好

TypeScript为代码提示作了很多特殊优化,比如:

ele.on("click", function(e) {
    // 这里我们是不知道e上面有什么,在编写的时候得不到提示
});

但是如果使用TypeScript编写,因为这个e的类型确定,所以就能有提示。

使用这样的语言也能够更快让非前端方向的人参与项目。

工作流程与管控

Angular的整体方案,由于分层很清晰,在JavaScript代码中基本就是纯逻辑,这样的代码如果使用TypeScript编写,会更加精炼,更加清晰。

这几年,大家逐渐接受了一个现实,那就是:前端也是需要构建的,所以我们有grunt,gulp这样的构建工具。之前我们不愿意写转译语言,是因为其他环节不需要构建,为了一些语法糖而引入整个构建环节代价太大。现在,既然发布之前的构建环节不可缺少,使用转译语言也不过就是加一段配置而已,这个使用代价已经小很多了。

Angular这样的解决方案,所面向的多数都是重量级产品,这些产品本身就会有构建环节,也基本上会使用IDE,所以,使用TypeScript的代价不大。

当项目变大的时候,我们会面临很大的管理成本,比如对代码的分析,结构调整,模块依赖关系梳理等,在TypeScript上面做,会比在JavaScript上面做更有优势。

最近几年前端领域“工程化”这个词被说得太多,但其实绝大部分说的都只是“工具化”。早在Visual Studio 2005中,就存在很多Factory插件,举例来说,一个普通项目的工作流程可能是这样:

  • 使用ER图设计模型结构
  • 一键生成数据库表结构和存取过程
  • 一键生成数据库访问层和实体定义代码
  • 一键生成Web Service接口
  • 根据WSDL,一键生成客户端的调用接口
  • 剩下的就是做界面,调用这些接口了

比如说我们做到一半,需要变更模型,也只是需要在ER图那边修改,然后依次一键变更过来。很多时候我们也会有代码的目录调整,批量更名,如果使用约束较强的语言,这部分可靠性会更高。

组件化与路由

如果用过angular 1.x,会对它的路由机制印象深刻。有复杂业务需求的人一般都不会使用内置的ng-route,而是会使用第三方的ui-router,这两者的核心差别是子路由的定义。

比如:

A界面有两个选项卡,分别B,C,如果我们想要:

app.html#a/b
app.html#a/c

这样的多级路由,在ng-route中想要定义,就比较麻烦,而在ui-router中,允许使用嵌套的ui-view指令,可以比较方便地支持这一功能。

在这两种方式下,路由都是全局配置的,但我们考虑在全组件化的场景下,组件的嵌套会受到这种路由配置的制约。比如,本来我们只是期望把某个组件嵌入到另外一个组件中,就能完成功能,但为了路由,不得不额外在全局路由配置的地方,加一个配置,而且每当组件层级发生变更的时候,这个配置都需要改,这就大幅拖累了我们组件体系的灵活度。

为此,我们可能会期望把路由配置放在每个组件中,比如说,组件A定义自己的路由为a,组件B的路由为b,组件C的路由为c,无需额外的配置,当B和C放在A中作为选项卡的时候,上面那两条路由会自动生效。

在Angular的新路由机制中,就是这样处理的,这也是Angular 2.0和Aurelia的共同路由机制。在这种机制下,如果有一天我们在另外一个更高层的组件D中,引入了组件A,那路由就会自己变成类似:

app.html#d/a/b
app.html#d/a/c

这个是非常灵活的,这对于我们构建一个全组件化的系统很有利,另外,这实际上实现了路由的动态配置。

当然,对这个问题,也是有争议的,因为路由不再集中配置,很难有一个地方能查看所有的路由状况了。

此外,由于在Angular 2和Aurelia中都凸显了组件的概念,组件的生命周期被引入了,比如说,组件的四个状态:

  • 创建前
  • 创建
  • 销毁前
  • 销毁

这些跟路由进行配合,可以把我们的加载过程,前置、后置条件过程都整理得很清楚。

指令与Web Components

最近,越来越多的人开始关注Web相关标准的推进,在HTML这个方面,最重要的标准就是Web Components,它主要是提供扩展HTML元素的能力(Custom Elements)。

HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.

这一段来自Angular的官方介绍。扩展HTML的词汇,是Angular的一种愿景,在这个里面,除了包含对元素的扩展,还有属性(Attribute)。

很多时候,仅仅有元素的扩展,是不足以满足需求的。举例说,让某个按钮闪烁,我们有两种方式实现:

  • 创建一种可以闪烁的按钮
  • 创建一种可以闪烁的行为

其中,前者是特定的解决方案,创建一个自定义元素<blink-button></blink-button>可以达到目的,但闪烁这个动作可以是一种通用行为,我们可能需要让图片闪烁,让链接闪烁,让各种元素都能闪烁,把这种行为扩展到不同的元素上。

如果用jQuery,我们可能会写:

$.fn.blink = function(options) {
    // 这里对DOM进行处理,添加闪烁功能
};

然后在使用的时候:

$('.some-element').blink();

如果说有自定义属性,可能我们就只要写:

<span blink>aaa</span>
<a blink>aaa</a>
<button blink>aaa</button>

借助数据绑定,还可以把blink绑定到一个变量上,由这个变量动态控制是否闪烁。

<div blink="hasNewMessage">aaa</div>

在Angular 1.x中,使用指令(directive)来实现自定义元素和自定义属性,这个东西设计得很复杂,所以不太容易上手,在2.0中,这一块改了。

在Angular 2和Aurelia中,使用很简单的标记来表明某个东西是自定义元素还是属性。

@customAttribute('blink')
@inject(Element)
export class Blink {
  element:any;

  constructor(element) {
    this.element = element;
  }
}
@customElement('my-calendar')
export class Calendar {
}

自定义属性的理念,在早期IE中实现的HTML Components中有很好的体现,它允许使用JavaScript编写DOM元素相关的代码,然后在css中作为行为附加到选择器上。

组件化与MVVM

对于大型Web应用来说,组件化是必须的,但是如何实现组件化,每个人都有自己的看法,所以组件化这个词就像**,法制一样,容易谈,难做。

我们所期望的组件化往往是这样:

我们期望的组件化

但实际上,很可能是这样:

现实中的组件化

实际在用组件,尤其UI组件的时候,会出现很多尴尬的地方,比如说同一个组件在不同场景下形态不一致,所以我们需要多个层次的组件复用级别。

在Angular 1.x中,组件化并不是一个很明确的概念,它的整体思路还是:逻辑层+模板层这样的概念,此外,有一些指令(directive),用于表达对HTML标签、属性的增强。

在2.0版本中,组件成为了一个很清晰的东西。一个常见的组件,包含界面模板片段和逻辑类两个部分。

如果我们经历过Angular 1.2之前的版本,可能会感受到controller的一些变化。比如说,之前我们写一个controller,可能是:

function TestCtrl($scope) {
    $scope.counter = 0;

    $scope.inc = function() {
        $scope.counter++;
    };
}

然后这样用:

<div ng-controller="TestCtrl">
    {{counter}}
    <button ng-click="inc()">+1</button>
</div>

在1.2之后,我们会这样写:

function TestCtrl() {
    this.counter = 0;

    this.inc = function() {
        this.counter++;
    };
}

然后这样用:

<div ng-controller="TestCtrl as test">
    {{test.counter}}
    <button ng-click="test.inc()">+1</button>
</div>

注意TestCtrl的实现,里面没有$scope了,这意味着什么呢?意味着这个“controller”已经不再是controller了,而是view model,这个部分的代码变得更加纯净,每有一个对应的界面片,就实例化一个出来与之对应绑定。

在Angular 2和Aurelia里面,HTML模板与视图模型被视为一体,当做一个组件,而Aurelia的灵活度更高,因为它尽可能地把额外的配置放在HTML模板中,所以视图模型变得更单纯,也存在复用价值了。

Aurelia跟Angular 2有不少细节差异,写法上大致的对比可以从这里看出:Porting an Angular 2.0 App to Aurelia

Angular支持使用pojo作为数据模型,这可以算是它的优点之一,这样,它对模型层的定义就比BackBone和Knockout简洁很多。

但是在2.0时代,我个人是倾向于预定义模型类型的,因为在MVVM这三层中,不宜过于淡化VM和M的分界,分清哪些东西是从属于模型的,哪些东西是从属于视图模型,在很多情况下都会很重要。这会影响我们另外一些工程策略,比如测试环节的处理方式。

在大型应用中,model应当与store视为一体,在比如数据的共享,缓存,防冲突,防脏等方面综合考虑,而view model可以不要考虑得这么复杂。

基于MVVM,我们可以在不同层级复用组件,可以把模板和视图模型当做一个整体复用,也可以只复用视图模型,使用不同的模板。在这一点上,Angular 2显然比Aurelia欠考虑。

代码的迁移

Angular的这次升级,最令人不满的是它的不兼容变更。这些变更很多方面来说,是无奈之举,因为前后的差距确实有那么大,想要短期平滑,就得在未来背负更重的历史负担。

但事实上,我们在很多场景下,比如企业应用领域,并没有比它更好的解决方案,所以这时候需要来看看如果想要作一个版本迁移,需要做哪些事情。

如果我们要做从Angular 1.x到2.0的代码迁移,相对最容易,也最值得做迁移的部分是数据模型,但这个问题说难也难,说简单也简单。

很多对分层理解不深的人,很可能把这个代码迁移想得过于复杂。但其实,一个规划良好的Angular 1.x工程,它的代码结构应该是非常有序的,什么东西放在模板里,什么东西放在controller,service,都是非常清楚的,而且,绝大多数controller和service中,是不应有DOM相关的代码的。

比如,service中是什么?主要是数据模型的存取,与服务端的交互,本地缓存,公共方法等,这些东西要迁移到2.0中,是很容易的,只是写法会稍有差别。

接下来往上看看,看这个所谓的controller。在2.0中,不再有controller,service这些东西的区分,一切都是普通的ES类,但是理念还是有的。比如一个含有视图的组件,它的逻辑部分就会是一个ES类,这个也就是视图模型,基本上也就对等于1.x中的controller。

比如最简单的todo:

function TodosCtrl() {
    this.todos = [];
    this.newTodo = {};

    this.addTodo = function() {
        this.todos.push(this.newTodo);
        this.newTodo = {};
    };

    this.removeTodo = function(todo) {
        this.todos = this.todos.filter(function(item) {
            return item != todo;
        });
    };

    this.remainingCount = function() {
        return this.todos.filter(function(item) {
            return item.finished;
        }).length;
    };
}

这代码很简单,就是给一个列表添加移除东西,假设我们要把这个代码移植到2.0,可以说基本没有代价,因为在2.0里你要实现这样的功能,也得这么写。

(注意,下面这段是Aurelia代码,并且不是使用ES6,而是使用TypeScript编写)

export class Todos {
    public todos: Array<Object> = [];
    public newTodo: Object = {};

    addTodo(): void {
        this.todos.push(this.newTodo);
        this.newTodo = { content: "" };
    }

    removeTodo(todo): void {
        this.todos = this.todos.filter(item => todo != item);
    }

    get remainingCount() {
        return this.todos.filter(item => item["finished"]).length;
    }
}

这么一看,好像也很容易迁移过去,多数情况下是这样,但这里面有坑。坑在什么地方呢?主要是手动添加变更检测的部分。变更检测是个复杂的话题,在本文中先不讲,后面专门写一篇来讲。

现在我们把逻辑层摆平了,来看界面层,这里主要有三个东西,一个是原先的指令,一个是普通的模板,还有一个是过滤器。

指令的问题好办,我们刚才提到的自定义元素,自定义属性,其实对使用者是没什么差别的,也就是实现的人要把代码迁移一下。

我个人并不赞同在一个业务型的项目中封装太多自定义元素,仅仅那种被称为“控件”的东西才有这个必要,其他东西可以直接采用模板加视图模型的方式,具体理由在前一篇的组件化之路中提到过。如果是按照这种理念去实现的业务项目,指令这块迁移成本也不算高。

过滤器也很好办,2.0 有同样类似的机制实现。

普通模板这边,绝大部分都是固定的工作量,比如ng-repeat,ng-click换个写法而已,里面有一些影响,但基本上是可以用批量转换去搞定的。

所以我们发现,迁移的成本并没有想象的那么大,为了更好地拥抱Web标准和更好的性能,这样的事情是比较值得去做的。

Angular与React

这两种东西代表着现代Web前端的两种方法论,前者是以分层和绑定为核心的大一统框架,后者提供了渲染模型多样化,带生命周期的多层组件机制。由于实现理念的不同,用它们分别开发同样的Web应用也会有很大差异。好比我们造一个仿生机器人,用Angular是先造完骨架,把基本运动功能调试完,然后加装肌肉等部件,最后贴皮肤,眉毛,头发,指甲;用React是先造出各种器官,肢体,然后再拼装。

方法论的事情那个很难说对错,只有看场景。比如亚洲农民跟美洲农民种地,理念肯定是不同的,因为他们面临的场景不同,比如亚洲种地普遍很精细化,美国种地很粗放。这也有些像React和Angular的差别。

我个人不赞同在框架的问题上有太多争论,因为天下武功,到底什么厉害,完全是看人的,一阳指在段正淳手里,只能算二流,到了南帝段智兴手里,可与降龙十八掌齐名。聚贤庄一战,乔帮主用最普通的太祖长拳,打得天下英雄落花流水。如果深刻理解了一个技术的优点和缺点所在,扬长避短,则无往而不利。

近年来,各框架是在互相学习的过程,但是每个东西到底有什么不同,最好还是列出需求,分别用代码体现。现在已经有todomvc这么一个库,用各种框架实现todo,但在我看来,这个需求还太小,不足以表达各自的优势。

我倡议,每个框架的熟练使用者能够选出一些典型场景,然后写一些demo,供更多的人学习对比之用。

Angular与未来

到目前为止,我们在浏览器中看到系统从规模来说都是中小型的,与传统桌面的大型软件们相比,还很幼小。比如Office的开发团队,千人以上的规模,无论是代码的架构,还是人员的分工协作,都可以算是伟大的工程。

在大型系统中,组件化可以说是立足的基础,但怎样去实践组件化的**,是一个见仁见智的话题。

还是以Office为例,它除了提供图形化的操作界面,还提供了一套API,可以被VBA这样的嵌入语言调用。

比如说,我们可以在界面上选中一个工作表,然后在某行某列填入数据,也可以在VBA中使用这样的语句去达到同样的目的

这就意味着,对于同一种操作,存在多样化的外围接口。继续分析下去,我们会发现,存在一种叫做Office Object Model的东西,这也就是一个核心数据模型,我们所有的操作其实都是体现在这个模型上的,GUI和VBA分别是这个模型的两个外围表现。

所以可以想象,如果Office的测试团队想要测试功能是否正确,他是有两条路要走:

  • 通过VBA这么一个相对简单直接的方式,去调用OOM上的方法和属性,然后再次通过VBA去验证结果
  • 通过GUI上类似录屏的操作,去模拟人的一些操作,然后,通过VBA或者是界面选取的方式验证结果

从这里可以大致感受到,当系统越复杂的时候,独立的模型层越重要,因为必须保持这一层的绝对清晰,才能确保整个系统是正确而稳定的。层层叠加,单向依赖,这使得软件正确性的验证过程变得更加可控。

在业务系统中,又存在另外一些问题。以我曾经从事过的电信行业软件系统为例,整个运营与业务支撑系统由若干个子系统构成,比如:

  • 资源管理,管理卡、号、线等资源
  • 营业系统,负责对外营业
  • 计费与结算
  • 运维与调度,负责人员权限考核调度等
  • 相关的内部管理系统

这些系统基本都已经Web化,如果我们要探讨它们的组件化方式,必须作相当深远的考虑,因为,还可能出现终极杀手——比如呼叫中心系统。

大家打客服电话的时候,有没有注意到,客服人员可以操作的东西,是超过了前台营业员的,这也就说明他实际上能够操作以上某几个系统。可是我们也没有发现他在切换多种功能的时候,花太多时间,说明其实他有一个高度集成的界面入口。

这就来了问题了,如果这里的多数功能是集成其他系统的组件所致,那都该是一些什么样的组件啊?

小结

篇幅所限,不在本文中讨论这些问题。抛出这样的问题来,是为了让大家察觉,在很多不为人知的地方,存在很值得思考的东西。一些新的Web标准是为了解决Web系统的大型化,应用化,但仅仅以这些标准本身而言,还是存在一定的不足,需要更深刻的改变。

我们期望Angular2和Aurelia为代表的新型框架能够给这些领域带来一些灵感,互相碰撞,解放更多人的生产力。

总而言之:

“I think we agree, the past is over.” – George W. Bush

commented

这篇是为Segmentfault 3周年活动前端场准备的,有些仓促,因为最近太忙了……demo稍后整理

nice shot!

最近在做一个内部的叫筋斗云的项目,第一次真正在项目中用angular. 于jq相比,第一次使用angular更多的是一种思维的转变,思维转变了接下来就易懂的多...

good job ! 👍

commented

👍

commented

thanks


有思考就有收获

👍

thx!学习

最近都在搞scrat, 没关注ng, 看起来ng2的语法似乎稳定了,下个月抽时间可以考虑看看ng2

期待 Angular 2 和 TypeScript 方面的进展.

关于 React 的对比, 最近注意到一个点, 就是当我们后端工程师跟我讨论组件的时候,
他熟悉 Rails 和 Node 方面的做法, 对组件的理解应该说是更接近面向对象,
比如说一个 field 内容封装组件, 就暴露方法获取和改变 field 具体数据..
跟他讨论过程中我意识到 React 完全不同, 不赞成从外界获取和操作组件内部数据了
而是传给组件属性, 传给组件修改属性的方法.. 这个思路跟以往的大不相同
我对 Angular 当中做法不够明确. 不过这个套路和 jQuery 时代已经完全不一样了

关于引入编译流程

这个毫无疑问,编译这一环节现在肯定都会有, 关键是在开发阶段 “是不是要引入发布级”的编译. 比如我们现在其实也会实用browserify + commonjs 来编写较小的单页应用(4人-的项目), 但是大型工程还是会选择AMD 的方式。 两方面考虑。

  • 构建成本,时间真的应该是考虑因素了
  • 团队成员的学习成本
  • 我觉得是工具本身就应该独立出框架, 而不是没有工具就没法使用这套框架了, 毕竟现在IDE很弱,大部分还是得靠命令行

##关于React 和 Angular

我和飞哥的意见很一致, 入门而言肯定是模板型的更容易入门, vd对于用户是透明的,然而diff却发生在这一层。 但传统mvvm的数据却是用户真实可触碰。 这两者的使用成本不言而喻。 关键是我越来越觉得或者怀疑的是:

其实现在很多鼓吹React的人, 根本就没有尝试过传统的mvvm方案。站队、押宝性质的去接触这个社区, 满嘴一些高级术语和词汇

Angular 和 React 带来的理念都是 颠覆性的, 我觉得两者后面肯定会共存下去, 使用场景根本不是谁取代谁的冲突

关于webcomponent

最大的优势是生命周期, 这个我在昨天会议中也和你提到了, polymer 或许可以绕过, 但是custom element是绕不过的。 好在完全组件化抽象后的框架输出的内容其实很容易注册成一个custom element. 比如regularjs的一个实验性扩展,就几十行代码 https://github.com/regularjs/customelements-register ,这个angular1.0不好处理, 但是2.0 应该也容易做到。

关于路由

ng-route肯定是不够用的, 这个我同意, 关于new-router倒是后续可以仔细关注下。

集中式和动态注册,其实各有优劣, 组件本身不涉及路由配置,你就可以让这个组件参与到多个路由节点, 最好的办法是用户自主选择, 比如我之前的stateman 其实就同时支持动态注册和集中式管理,

还有一个昨天你有个点, 貌似不在文章里

就是组件本身不适合过度封装? 还给了小马的尾巴的例子。

其实我很赞同了, 因为组件和模块不同的地方是, 组件的模板(或view)是有结构的, 而模块暴露的接口是可以适度重复的(你给的例子其实更适合用来说明模块,有重复但是仍然可用),但是组件不一样,结构只要有细微的不满足, 那整套模板就是不可用的。

所以我们可以安全封装可以冗余的业务逻辑(或vm上的方法)继承下来,因为不涉及到view, 更接近模块的定义, 比如我们内部的ListView其实也是没有template结构的, 而只有业务逻辑封装, 因为模板的变化太大了。

关于模板的封装, 我想到的有两种方式
一种方式是 include, 这个也是你提前定义好哪些可能会被替换, 比如一个modal弹窗的内容区, 显然应该留好这个placeholder
第二种方式可以直接微调模板, 这个有完整parse流程的regularjs就有优势了, 我可以提供hook给用户,直接操作结构化后的AST. Angularjs这种就很难做

结尾

写的有些凌乱, 也算一个记录。

未完待续...

commented

@jiyinyiyong angular也不赞成从外部对组件进行操作,但web components这个东西的理念是跟他的理解很接近的。所以,不管react还是angular,都会在web components上面再来一次抽象,隔离开发者对它的直接操作。

polymer应该更适合你同事的理解

commented

@leeluolee 有几段文字和插图没补上,刚加上了

飞哥,赞!

这图片很费心思啊 😅

学习学习~

TypeScript 用起来有java的语法特点,但是貌似多态搞不了,还有很多待了解

对于大型Web应用来说,组件化是必须的,但是如何实现组件化,每个人都有自己的看法,所以组件化这个词就像**,法制一样,容易谈,难做。

实际在用组件,尤其UI组件的时候,会出现很多尴尬的地方,比如说同一个组件在不同场景下形态不一致,所以我们需要多个层次的组件复用级别。

在Angular 1.x中,组件化并不是一个很明确的概念,它的整体思路还是:逻辑层+模板层这样的概念,此外,有一些指令(directive),用于表达对HTML标签、属性的增强。

组件化

实际开发中, 合理规划组件的粒度十分困难。 必须承认, 我们团队开发的组件整体质量还不高, 大家都在摸索中 。

但由于分工css 与 js 岗位职责的分离 ,这些质量参差不齐的组件,在缺少文档, 测试, 一致的命名规范的情况下, 为CSS 样式的工程化管理带来很大的挑战。 与此同时, 在团队合作中, 还有产品经理、 UI 后端等各个岗位的配合上也存在问题。

可以说, web component 还不成熟。 无论是技术上 还是 人才上。

在新旧交替之际, 怎么让前端开发拥抱新技术。 同时保证让团队各个环节合作顺畅, 项目稳步前进。 是我们值得思考的, 以下是我在angular1.x 中得实践经验:
  1. 减少组件的状态,子组件嵌套的深度不超过2层。
  2. 减少路由的状态, 不要过度使用ui-router, 尽量使用单路由, 嵌套深度不超过2层。
  3. 将controller与路由写在一起, 因为路由的所依赖的数据和实际业务无法分离(在controller中加载数据会导致页面闪烁,同时route 成功 hook 不正确。 同时导致应用有状态, 导致用户无法F5等等)。 综合权衡只有舍弃路由的全局配置管理, 将路由和业务层代码放在一起。(和angular2不谋而合)
  4. 为了规避1、2 损失的灵活性。 partial采用模板引擎管理(jade), 使用 extend include conditionals 等模板语言特性。 在构建期间生成真正angular使用的模板 。 这样提高性能, 增加复用性, 使用原生标签布局,减少学习、沟通成本。 进一步隔离样式 与 组件的, CSS 与 JS组件, 设计人员 与 系统业务
  5. directive, 使用动态 directive ; directive 中使用lodash template 模板预编译; 合理使用 compile 配置,在angular程序运行的不同周期, 动态改变模板。 减少不必要的双向绑定, 优化性能, 增加灵活性、复用性, 降低学习成本。

关于angular2

angular2 彻底摒弃了 angular1 中过于复杂概念, 拥抱标准。 更容易的总结出一套一致的最佳实践

同时使用typescript , 类型定义更清晰, 对IDE 更友好, 写代码应该更容易, 更健壮

期待。

去年尝试过 TypeScript,随着项目变大编译时间越来越慢,有时候甚至要等 2 分钟,加上编译检查过于严格,好多代码都不知道怎么写了,所以真正用起来的体验是超级不爽。。。

commented

现在的前端流行的做法都需要编译,但是编译会随着代码量增加而越来越慢。项目大之后如何分模块开发又是一个问题了。
目前正在开发一个超大型单页应用,重度交互应用。遵循commonJS规范开发,使用babel编译ES6语法,webpack按需加载js进行分包。现在的初次编译已达到2.5min,随着项目的变大,编译的问题也急需解决。不知道大家有什么好的方法。

commented

@nwind 项目变大这个问题不是很大,因为如果用它写全项目,我建议的方式是先规划后编码,引用的时候,把已确定的东西都先搞成类型文件,那部分的东西单独构建,这样,当某个人写自己模块的时候,依赖的东西基本都不需要编译,所以不至于特别慢。

至于说编译检查过于严格这事,确实带来不少学习成本,但我觉得如果纯逻辑代码的话,还好啊,在ng2或者aurelia的场景里,只用来写model和view model,应该不会这么可怕。

commented

@luqin 编译时间应该也可以算ts比es6的优势之一,因为可以引入预先定义的d文件,所以被依赖的东西不必每次都编译,每个业务开发者其实只是在编译自己的部分。

commented

学习!!

居然不提到 Vue,差评(逃

commented

@yyx990803 不熟悉的东西我实在不敢说。。。

commented

@xufei 我是一个Angular新手,看了这篇文章感觉获益良多。刚刚在看了Angular2的文档后,我有一些疑问:

  1. 我看到Angular2提供了ComponentView,看上去有点像React利用JSX创建virtual dom然后render到real element的方式(也许只是看上去像...),请问在Angular2中这样得到的组件是存在于内存中还是会成为页面上的一个real dom呢?
  2. 如果是内存中virtual dom,那么可以理解更新视图有性能提高,如果是渲染成一个real dom,那么Angular2是怎么优化dirty-check之后更新视图的操作呢?

我的见解可能比较浅薄,但是仍然希望您能给我解答一下

commented

11年开始使用angularjs,刚开始被它的双向绑定、路由、指令等功能吸引,后来随着项目越来越庞大,性能方面存在一定的问题,想尽各种方式进行优化,却不得其果。
angular2和react在性能方面的确有很大优势,目前正在考虑迁移过来

commented

angularjs 对百度的seo,一般用什么思路来实现呢?

commented

@joeylin 尝试一下Prerender,预渲染页面让搜索引擎来爬取和收录

commented

希望来一期React,:)

commented

之前一直用Angular,现在突然宣布2.0不向后兼容,Angular应该掉了很多粉。
现在个人比较倾向React这种职责单一的玩意儿,以后就算React新版本也不向后兼容,升级甚至替换的代价也要小很多。

commented

angular使用中,我遇到一个问题,我$watch('width')中修改height,$watch('height')中修改width, 最终产生一个错误, 大家遇到这种需求的时候,一般是怎么解决的呢?

commented

@joeylin 只要是在几步操作之内能收敛就行了,不能一直变下去。

比如说,你在width里面写:height = width * 2;
在height里面写:width = height / 2;

这样,无论是对哪个赋值,都能很快收敛回来,两个式子的结果都不再变化,尽管效率未必高,但结果是可以了。

你什么需求会这么写啊,详细说说,感觉逻辑没有理清楚……

commented

@xufei 我想让一个div的宽高,强制按比例缩放,比如增大width,height也同时增大,增大height,width也能同时按相应的比例增大。

commented

@xufei 对,这样的值,正常是可以的,我担心的是,在计算的时候值出现一些微小得差异,最终造成,无法收敛。

@joeylin Math.round? 1px内的误差应该可以接受。

赞,写的真好

学习了

commented

很多很好的思考啊,学习了。
关于Office Object Model这部分想法很形象,确实可以和Web类比,不过感觉应该类比的是DOM而不是MV*框架的Model层吧,毕竟除了Backbone其它主流框架的Model其实都没有很heavy。

另外最近很关注React带来的一些新**,然后看了你前几篇关于Angular的讨论,也理解了你对于组件化的意见,就像你在这篇文章最后suggest的那样,组件化的问题就在于复用带来的效率提升和组件构建成本之间的tradeoff,实际上是一个很难的决定。结合之前你在 MVVM时代的Web控件 ——基于AngularJS实现 里面提到的复用Display Logic和相关的一些实践,我想大概梳理一下我的想法。

感觉上对于UI(特指View)来讲,复用性已经不太是关注点了,因为不管是Layout还是Display Logic的复用性都不大,像Accordion算是一个,但实际上因为这个logic太基本,是不是值得复用也不好说(相较牺牲这种复用性而compose大一些的组件),而对于稍复杂一点的UI来说,复杂的state mutation和layout如果works as expectation还好,一旦出问题就比较抓狂,所以想来想去,还是可维护性最重要,不如把相关model和logic都放在一起方便debugging和testing。由此带来的潜在福利应该是很可观的。尽管好多人说MVVM这种对小型项目来说开发效率更高,但是我觉得其实就是遵循了传统后端的web架构**,大家已经熟悉并且接受,写起来没有**负担而已,真要说MVVM模式是否合适,我觉得还是要抱开放一点的心态,跳出套路进行比较。

比如PeteHunt在13JSConf上讲的就是Separation of technologies != Separation of concerns,最早说要把HTML/CSS和JS分离,最重要的是Design和Logic分离,这在web1.0时代是非常有效的,因为当时的static web还主要只是Layout和Style,不需要任何逻辑来支持动态效果。而Templating也是在这个大背景下,从后端渲染的MVC模式直接借鉴到前端的。不过在JS已经是前端主要语言,前端UI所需要的Display Logic的代码量非常庞大的现在,我们的UI已经无法避免要绑定几套不同的显示状态,还要带上underlying的logic handler,而这种时候还要试图分离HTML/CSS和JS就会很难受,MVVM里的VM做了大量后台的工作简化了data和display logic和view的binding,已经做到了declarative HTML as View,但是如果离开JS单看这个HTML template其实就有点无所适从,因为directives的behavior并无标准可循,要track back到JS那边才能完全理解。所以在Against CSS in JS里面KG会说

the mantra of React is to stop pretending the DOM and the JavaScript that controls it are separate concerns

这么想的话,React选择的激进的组件化风格就还是很有其现实意义的。而反思templating的**包袱的话,就是一定要强调HTML和JS分离,相反的结合JSX的话Design完全有能力对组件进行修改,这里就需要我们有一个open mind,接受Engineer和Designer可以是在相同环境中工作的可能性,这个时代,连我们的Designer都会一些JS了,与其让他在一个复杂的cascaded逻辑链或者CSS套用中去做改变,还不如让他在JSX中安心修改单个组件而不用担心side effect要好吧。不过这确实和公司的实力有关系,我认为FB有能力做更好的training给designer所以才能完全把React**执行下去吧。

React最重要的两个**就是VirtualDOM和JSX(embed markup/CSS in JS),Virtual DOM各大框架都可以adopt,而且个人觉得是React更重要更有价值的部分,但是第二点就和现在流行的MV_框架走的是不同的路,也许是时候考虑一下这种完全革新的架构了,React的设计者们从一开始就只关注UI,把UI作为driving force,才能够跳出一般的思路而把VIew单独拿出来进行思考,所以才能找到一种让开发更有效率的框架实现,而另一边,籍着这种disruptive的思路,Flux也可以跳出MV_的two-way binding的老路,提出一种完全不一样的pattern,让人耳目一新。个人认为也许Flux+React这种看似颠覆式的实现是next gen web的一个信号也说不定。

写的真好,赞! 👍

commented

版本已经迭代到rc4,那么现在新项目是时候选择angular2了?

commented

赞,很棒的。