-
Notifications
You must be signed in to change notification settings - Fork 762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
浴火重生的Angular #9
Comments
感谢博主翻译。 |
对官方公布细节的翻译在这里:#8 |
Nice! |
脏检查和 |
@LiuJi-Jim getter这个的问题在哪里啊,我没明白,getter里面本来就是不该放逻辑的……这种文本的转换应当是通过filter去做啊,因为如果你需要在模型里做getter的转换,基本上一定是给视图用的,所以用filter很合适。假如模型中还存在别的转换,那就是动用$watch的时候,属于在模型内部的关联。 web components这段,可能你没明白我意思,我举个例子: 你有一个组件 <foo bar="123"></foo> bar属性用于接收一个数字,然后把组件内部的某个东西创建这个数字这么多个,并且显示出来,这本身不会有任何问题,但考虑一个场景,我想把这个数字动态传入,这个数字从哪里来呢?从外层作用域的某个变量中来,如果是普通元素,是不是你要写: <div ng-controller="TestCtrl">
<input type="number" ng-model="a"/>
<span ng-bind="a"></span>
<foo bar="{{a}}"></foo>
</div> 你看,a的值可以从输入框传入span并显示,但是传不到组件的bar里。如果你想里面扫里面的,那么,组件在解析插值表达式的时候,从哪里去获取这个a?所以这个问题的关键在于外层世界与组件的传值,别的是没有问题的,其他时候大家都各自做自己的事好了。 样式这块,碰到web components的时候,还是需要重新规划,思路跟之前的大有不同,我想想。 |
@xufei 我对filter的看法是它应该是高度可复用的util类型的东西,比如urlencode、camelCase这种就是filter,但上面我说的那个例子,或者 对于 |
@LiuJi-Jim 在Angular里面,是可以把filter看作数据getter的装饰器的,它的职责其实就是干这些事。 绑定是有问题的。 这个绑定实现不了,原因是angular不知道组件有bar属性,它必须先穷举,必须预先知道元素上有哪些可插值属性,然后挨个去解析,还要区分找到的是属性还是方法。就算找到了bar,也没法知道它是不是要插值。就算知道它是要插值的,在计算出真实结果传入之前,已经把整个插值表达式传到组件内部了,一个不合法字符串。 所以问题都在内外传递这一步上。 |
Polymer基础层使用了以下技术:
你们所说的问题就是MDV吧,慢慢等吧,否则自己实现扫描器 |
支持Polymer,未来的趋势!!! |
我对angular不是很熟,但是对于它解决的问题大概清楚,有点疑问: 为什么angular不是扫描元素的attributes,读到bar,然后根据值 在计算出表达式结果之前,传入组件内部的值是不合法内容。这不算问题吧,现在的angular+html元素也有这个问题。所以angular在逻辑完成之前会先隐藏内容,计算完成后再更新属性,这时候的属性是合法的,控件对应地更新状态就好了。 |
Polymer还没碰过,周末看一下先。 |
@RubyLouvre 正美同学的“不要让数据绑定接触到Web Components”是什么意思? 按说你是区分不出一个自定义元素和html本身元素的,所以机制应该是一视同仁的。 当然你可以说我根据自定义元素的规则(带有 - 的)来区分。可是别忘了还有 type extension 的情况(尽管我觉得type extension这个名字有点问题),就是对现有元素的增强,如 |
@hax 他的意思是:不要让绑定扫描机制跨越组件内外。我举个例子: 之前Angular里面的元素指令,如果我们造这么个元素Panel,把它放在一个div中: <div ng-controller="TestCtrl">
<Panel></Panel>
</div> 比如这个TestCtrl是外层的视图模型,里面的所有变量,其实可以在Panel的内部实现中用于绑定。 但是如果用Web Components来实现这个自定义元素Panel,这个事情是非常困难的,所以干脆就不要想这些事,组件外的东西限制到组件外,组件内的东西如果有绑定,内部自行通过一个公共类处理。 但我的疑问在于,假如恰好在组件直接使用的时候,要从外部带入一个值,怎么办? 比如TestCtrl里面有变量a和b,我只是想这样: <div ng-controller="TestCtrl">
<Panel title="{{a + b}}"></Panel>
</div> 这个从使用者的角度,并未破坏Web Components的封装性,因为没有干涉内部实现,所以应当还是允许比较好,否则,自定义元素与原生元素的行为就不一致了。 |
@RubyLouvre “自行通过公共类处理”是什么意思?让组件自己import相关的model?这不是break了封装和复用性嘛。 我理解中组件必然需要数据绑定。因为可以把组件理解为增强性的form controls,所以是最需要数据绑定的部分。 |
@hax 他意思是,内外各自绑各自的,但是如果像我上面举的例子,那个表达式只能让外层去解析,内层能拿到表达式,拿不到执行上下文,还是绑不了 |
浴火是了,重生倒未必,我有几点不太同意楼主:
所以,还能叫 ng,只是利用了一些语言上的新特性来重写一次 angular。从目前的文档来看,浴火的是:class 取代了 controller,干掉了 $scope,annotation 取代了 module。但是骨架仍然 1.x 的形状,重生是未必。 |
感谢,文章翻译得好啊,后面的讨论也有趣。谢谢上面各位。 |
nice |
nice. |
还打算学Angular,Angular2出来Angular是不是就没用了,还有必要学Angualr么? |
大家的讨论很精彩,赞一个。 |
学习了~原来ng2的变革如此之大! |
ng2已经弃用AtScript改用TypeScript了 |
@xufei 已经说得很清楚了,angular1是不能动态加载组件的 @nighca 比如: <div id="container" ng-controller="myController">
<p class="text-center bg-info" ng-hide="testValue">加载中...</p>
<p ng-show="testValue">testValue: {{testValue}}</p>
<hello data-some-val="{{testValue}}"></hello>
</div> 指令hello的属性值这样传进去就报错了。所以这时只能通过事件广播的方式对指令进行更新,非常不方便 |
谷歌把angular毁了。angular本不配称框架,现有知识的敏捷利用的价值让一代备受追捧,但真不是框架,也没想。谷歌非要强加自己天下一统舍我其谁的个性,完蛋。重写也是个自恋的说法,不得不扔出DI,显示性编程,设计模式里的古怪名字来,一团乱麻,又一个Java类语言。本质问题是异步不是主要问题,service根本就是evil。数据本地化才是app的王道,而所谓的稀释依赖不过是自找麻烦,web app那些需求全世界都知道,你多做点,开发人就少做点。占领市场对谷歌才是最重要的,就跟安卓一样,哭的是码农们,跟着这么个利益驱动,没有品味的大佬,永远痛苦抑郁。同样是框架,ember2年前就明白核心问题。完全是好比苹果和安卓的情况。也难怪,ember的创始人就是来自苹果。 |
啥玩意都不能免俗,临近秋天大家都要吃起羊肉了。。es6、组件化都是当下趋势,与时俱进,蛮好的,各位可以马上玩起来 |
再谈angularJS数据绑定机制及背后原理—angularJS常见问题总结 |
浴火重生的Angular
Angular团队近期公布了他们对2.0版本的一些考虑,很详尽,很诚恳,我读了好几遍,觉得有必要写点东西。
对于一个运行在浏览器中的JavaScript框架而言,最喜欢什么,最害怕什么?是标准的变动。那么,放眼最新的这些标准,有哪些因素会对框架产生影响呢?
这几点,我是按照影响程度从大到小排列的。下面逐条来说:
module
早期的JavaScript在模块定义方面基本没有约束,但作为框架来说,不按照某种约定的方式去写代码,就会导致一盘散沙,所以各家都自己搞一套,大部分还是各不兼容的,这是历史原因造成的,不能怪罪这些框架。最近几年,为了解决这些问题,人们又发明了AMD,CMD,以及各种配套库和工程库。好不容易有些框架向它们靠拢了,可是module又来了。
这真是没完。我知道很多人是对module有意见的,我自己也觉得有些别扭,但我坚信一个道理:有一个可用但是稍微别扭的标准,比没有标准要好得多。所以,不管怎样,既然他来了,就得想办法往上靠。不靠拢标准的后果是什么?非常严重,因为现在的Web是加速发展的,浏览器只要过了一个升级瓶颈,后面的发展快得出奇。不要看现在还有这么多老旧浏览器,很可能你睡一觉起来突然发现已经基本没人用了,到那时候,不紧跟标准的框架就很惨了,瞬间就边缘化了。
我们来看看Angular原先的module设计,如果是在五年前,它刚起步的时候看,可能觉得还可以,现在再看,问题就比较多了。Angular现有版本的module,其实跟我们在ES中看到的module不是一个概念,更像C#里面的namespace,它的各种controller,service,factory才是真的模块。
我认为现有版本的这块,有几个不好的地方:
所以,Angular 2.0中,把这一块彻底改变了,使用ES6的module来定义模块,也考虑了动态加载的需求。变动很大,很多人有意见,但我是支持他们的。这件事不得不做,即使现在不做,将来也还是要做。毛主席教导我们:革命不彻底,劳动人民就要吃两茬苦,受两茬罪。在现在这个时代,如果还继续用非标准的模块API,基本等于找死,所以它需要改变。
Web Components
为什么Web Components也能带来这么大的影响呢,因为它同样会造成断代升级,也就是说,你非完全跟着它的路不可,没有选择。
Web Components标准本身同样是个见仁见智的话题,在本文中我不评价,它作为标准,既然来了,大家当然要往上靠。一个致力于大规模Web前端开发的现代框架,不考虑Web Components是完全不可想象的。那么,怎么去靠拢它呢?
Web Components提供了一种封装组件的方式,对外体现为自定义标签,对内体现为Shadow DOM,可以定义自己的属性、事件等,这样问题就来了。
我们看当前版本的Angular,能看到ng-click之类的扩展元素属性,那么,他为什么要写成ng-click?是因为这是对原生click的一层封装和转换,同理,如果我的原始事件不是click,是另外一个名字,你当然也得跟着加一个,不然针对这种东西的操作就玩不下去。所以说,其实它是给每个有价值的原生事件都写了扩展。
这就有问题了,你能这么做的原因是,你预先知道有这么一些元素,这么一些事件,也知道这些元素上的这些事件是什么行为,如果全是自定义的,他不告诉你,你急死也不知道,怎么办?所以这一块必须重新设计。
同理,属性也是这样,之前像img的src,就有一个ng-src,如果没这个,你设置在上面的表达式就会被当成真的url,先去加载一次,显然是不对的。所以,设置在ng-src上,它等表达式解析出结果了,再把结果设置到src去。如果是用Web Components扩展的自定义组件,它不知道你有哪些属性,就搞不下去了。
所以,Angular 2.0团队在这一块还很纠结,需要很多探索,很多权衡才能找到一种能接受的方式。
后来在这一块,我跟@RubyLouvre 探讨了一下,他的观点是不要让数据绑定接触到Web Components,也就是说,不让扫描进入“暗世界”,我想了想,觉得也有道理,只是这样Web Components跟原生元素就要区别对待了。
或者对所有Web Components使用同一个壳子再次封装?感觉还是很怪。
observe
当前版本的Angular使用脏检测的方式来实现数据的关联更新,这种机制有一定优点,但缺点也非常明显。
在其他语言中要监控数据的变更,很多在语言层面上有get和set,一般都是从这个角度入手,有不少JavaScript框架也是从这个方面做下去的,Angular不是。
Angular的脏检测很有特色,它采用的是新旧值比对的方式,也就是说,对每个可变动的模型,保存上一次的值,然后通过手动,或者是封装事件调用检测,一遍又一遍地刷新模型,直到稳定,或者超出容忍限度。
为什么这里面会有不稳定现象呢?我举个简单的例子,这是伪代码,仅供演示:
这里面几条语句不是真的赋值,是用来表示:每当a变化了,b就跟着变,然后c也跟着变,那我们在这里就要创建两个监控,一个是对a的监控,在里面给b赋值,一个是对b的监控,在里面给c赋值。
好了,比如有人给a赋了个新值,我们一个脏检测循环下来,b增加了1,c也跟着增加了,好像没什么问题。那我们怎么知道整个模型稳定了呢?很简单也很无奈,再运行脏检测一次,这次a没变,所以另外两个也不变了,跟上一次检测之后的结果一样,所以就认为它稳定了。
这里我们看到,不管你怎样,只要变过数据,至少要跑两次脏检测。为什么说至少呢,因为我们这种情况刚好把坑给绕过了,来改下代码:
没改什么,只是把两条监控语句互换了,这个结果就不对了。为什么呢,比如a赋值为1之后,第一遍结果是这样的:
这里讨厌的是c的监控语句先执行了,但b还没有变,可是我们当时是不知道的。然后,我们想看看模型稳定了没有,就再检测一次。所谓的检测,其实是两个步骤:把所有监控语句跑一遍,对比本次结果与上次的差异。
那么,这次变成了:
第二轮结束。模型稳定了吗?其实已经稳定了,但是代码是不知道的,它判断稳定的依据是,本次结果与上次相同,可事实是不同的,所以它还得继续跑。
第三次跑完,终于跟第二次结果一样了,于是他认为模型稳定了,开始把真正的值拿出去用了。
所以这个过程的效率在很多种情况下偏低,但好在他这个变更不是实时的,而是通过某些东西批量触发,所以也还凑合。另外有些框架,是每次对数据赋值了就去立刻更新关联值,这当数据结构比较复杂的时候,这样比较高效。
Object.observe与之相比,在定义监控的时候比较直观一些,而且,基于set get的绑定框架,有些会在原始数据的原型上定义一些“私有”方法,相比来说,observe这种方式从数据的外部视角来处理变更,更合理一些。
Angular 2.0的数据绑定机制应该会使用observe重写,可以期待这个方面有较大的提升。
但不管什么绑定方式,都是有坑的。我知道读者中有不少坏人,你们看到这里肯定想到很多坏主意了,比如刚才的脏检测,有没有办法把这个过程搞死?很容易,我帮你写个简单的:
这个代码死循环了,形成了监控闭环。所以,在Angular里面发现循环到一定量的时候,就会觉得它停不下来,终止这个循环。在其他技术实现的绑定框架中,同样要解决此类问题,所以监控到变更的时候,也不是直接拿去应用。
promise
很奇怪啊,我一直喜欢promise这种编写异步代码的方式。可能我对它的喜好来自一些背景,比如说,做可视化组件编程。这个可视化的意思是指通过拖拽配置,配置逻辑流程(注意,不是拖UI)。
比如说,流程细到方法的粒度,每个步骤映射到一个方法,然后拖拽这些步骤,配置出执行流程。这里面有个麻烦就是异步,比如说,某个方法异步了,那就麻烦了,因为在一个纯拖动的配置系统中,如果你还要让他手工调整什么东西甚至改代码的话,这个事情基本就白做了。
所以你看,promise在这里优势很大。每一个有异步倾向的方法,我都让它返回promise,甚至为了一致性,不异步的方法也这么干,每个方法的入参出参都是map,让promise带着,是不是就很好了?
以上是我个人见解,可忽略,谢谢。
那么,在Angular 2.0中promise有什么影响呢?
回顾Angular 1.x版本,在其中已经可以看到很多promise的身影,只是那时候用了$q,一个小型的promise实现。在2.0中,promise的使用将更加广泛,因为更多的东西是异步的了,比如新的路由系统。
promise本身是很容易被降级的,在原生不支持它的浏览器中也很容易搞出一个polyfill来。
这个事情在我个人看来是很喜闻乐见的。
Angular 2.0除了作出符合标准的改进,还有一些提升的方面:
依赖注入
Angular大量使用了依赖注入。在JavaScript里面怎样做依赖注入呢?比如这段代码:
a跟b这两个模块都要注入进来。对于依赖注入系统而言,首先要知道注入什么,比如这里,至少要先知道a和b是什么,怎么知道呢?很多框架都用一种方式,就是先把foo这个待注入函数toString,这就取得了函数定义的文本,然后使用正则表达式提取参数名。
这个办法可行,但不可靠,它害怕压缩。随便什么压缩工具,肯定认为形参名是没用的,随手就改成a或者b了,这样你连正确的模块名都找不到了。那怎么办呢,只能老土一些:
这样总可以了吧?
这样写起来还是有些折腾,而且运行时的数据也不够完全,所以Angular 2.0很激进地引入了一种类似TypeScript的语言叫AtScript,支持类型和注解,比如它的这个例子:
一些配置信息就可以搞在注解里,类型信息也就丰富了,然后这代码编译成ES6或者5,多么美好。更美好的是,2.0借助这种语言,可能把原来的指令、控制器之类的东西统一成组件,使用普通ES6 class加注解的方式来编写它们的代码,消除原来那么多复杂冗余的概念。
其实还有很多改进点,比如路由等等,没法一一列出了,感兴趣的可以查阅Angular 2.0已经流出的文档,或者查阅它的github库。
小结
Angular 2.0这次的规划真是脱胎换骨,看了介绍文档,简直太喜欢了,之前我考虑过的所有问题都得到了解决。这一次版本跟之前有太大变化,从旧版本迁移可能是个难题,不过相对它所带来的改进,这代价还是值得的。勇于革自己的命,总比被别人革命好,期待Angular的浴火重生!
The text was updated successfully, but these errors were encountered: