最近又把某Chrome扩展翻出来重写了一下.
原因大概一是依赖的某Polymer也到底1.0版本了,该切换下了.
二来,也是原来Proxy逻辑部分的kernel的有些华而不实的地方,用了比较多functional的形式写法.
其实早前也有过这个打算的.
当时Polymer的版本还是0.5吧.
简单bower了下,然后发现有些component的行为改了.
主要是当时用了个core-list吧.
最初的版本是不要求parent元素有height的,后来的版本要求了,于是一些地方就broken了.
然后简单翻了下,要fix的话,最简单的就是做个height限定.
不过这个终究不算是一个优雅的解决方法,毕竟分辨率这个是不定因素.
于是也就算了.
当时觉得这个改动还是挺儿戏的.
毕竟作为一个公共library,随意改行为终究不算什么太有原则的事情.
后来,当再npm update一下的时候,就觉得,这些其实不算什么.
毕竟,比起Node社区的某些工具链来说,至少core-list还保持了接口不变.
再后来,就是Polymer的1.x版本了.
也算正式版了.
看了下,基本上改动还是比较大的.
至少core-*的component基本上都改叫iron-*了.
倒是衍生的material design的paper-* component名字没怎么变.
虽然说实现也是基本改了.
毕竟,跟0.5甚至0.x时代来说,整个项目和某些哲学上的变化还是挺大的.
最明显的就是webcompoment这个东西算是独立出来了.
毕竟,当初还算是比较初级的阶段,浏览器支持不多.
基本是polyfill而非native的支持.
现在shadow dom基本算是常识了.
于是虽然保留了polyfill的部分,但至少从项目结构上来说,webcomponent不再是Polymer独有的东西.
对应的某些Polymer的某些template的语法变更了.
比如原来的custom-element改叫dom-module了.
template的functional的registration也由原来的Polymer(name,config)改为没那么多余的Polymer({is:name})了.
不过一个比较重要的改变应该是对templates的data binding部分吧.
开始的版本是可以随意bind对应model的某个attribute的,现在倒是限定了只能bind properties下面的东西.
而且在property的定义是一种显示的声明,得提供binding的一些行为和本身的类型之类的.
相对来说,这个改动使得binding的灵活性大为降低.
但原则上来说,倒是比较符合webcomponent的隔离哲学的.
毕竟,如果以component的思维和角度去看待这些templates的话,这种黑盒思路还是很有必要的.
不然的话,由于javascript本身语言比较随意,所以hack也能比较随意,带来的结果自然是component行为预期的不确定性了.
另外一个比较明显的改变是貌似取消了component的extends方式.
也就是说,component是没有父子继承关系了.
这点也不难理解,终究是黑盒的思路.
所以,要关掉一些可能更改行为的可能.
代价当然是灵活和所谓的复用性.
而所谓的"灵活"也只是特定条件下的术语.
毕竟,不能extends还能composite嘛.
所以并不算是一个实际的牺牲.
而复用性,在UI方面的话,composite就是解决方案.
有待解决的只是一些功能性的东西.
所以,这里就是behavior的mixin思路了.
这点其实是有些类似go之类语言的fucntion set的概念的.
实际上,这种设计很多地方确实就叫behavior.
就像某句著名的话.
"程序就是数据结构和算法".
其他一些改动就是对inline expression的evaluation.
这个也是比之前的较为严格了.
不再各种操作符.
原则上就是直接的property的direct mapping.
no more magic.
当然,也不是说现在的Polymer就是完全不能做hack做侵入的.
最简单的就是event/listener机制.
结合behavior的话,还是能够模拟一套能让外部侵入现有component,改变其行为的方法的.
只不过得考虑下怎么处理event driven的状态机而已.
还有一点改进是web-animation的部分.
之前的paper-* component系列的animation部分也是用这个重写了的.
特效部分也拆分为独立的neon-animation了.
也是顺应时代进步了的一个改动吧.
不过大体看了下,基本就是css3的transform的一些应用吧.
毕竟说到底material design的一个思路还是"平面UI立体交互吧".
所谓的paper和ink的关系.
所以基本上来说,paper也就是静态展示的部分基本上就是Google一贯风格的简约加上从微软metro吸收过来的视觉分块思路.
至于ink的动态部分.
一方面是分为UI状态变化.
一方面是用户交互的反馈.
前者的状态变化比较突出的就是hero这种联动的视觉块大小变化效果了.
通过变化做某种直觉引导的作用.
后者典型的就是ripple效果.
原则上来说,ripple类的效果也是做直觉引导的.
不过侧重点在于告诉用户交互已经发生.
而不是交互的变化结果.
这点大概就是material design为什么要用paper和ink来描述的原因了.
paper是书写/painting的效果/结果,ink是书写painting的动作及反馈.
所以Polymer的其中一个价值,至少是比较方便地自带material design的东西,可以直接用.
尤其考虑一些paper-style里的配色方案typography的话.
当然,除了这些算是比较好的点之外,1.x的Polymer用起来也还是有些地方比较tricky的.
不过,应该多数有two-way databing的都有这个问题.
即使bind的value update和paint的时间差的问题.
具体到Polymer来说,data change的observer的diff是在一个microtask或者说一个tick的最后,从一个pending的queue里取出来执行的.
因为Polymer实际上来说,还是以原则上300ms做一次check和update的.
所以,在每个300ms的time slice之后,是pop一些async/debounce的东西出来执行.
而pop的源里面其实至少包含了一个是data diff/change的处理,以及对应的dom modify.
所以,这里一个可能的案例就是,bind了一个array,然后array增加或者删除了元素.
然后,直觉上,应该是对应的dom节点应该也在这个function invoke的处理context/现场变更了.
而实际上是没有.
因为对dom的修改是在这个context之后.
更确切地说是在当前tick的microtask的lifecycle的最后才发生.
因此,该context的内容还是原来的.
要怎么解决呢?
一个解决方法就是去hack这个pending的queue.
假设实现不变的话,那么这里就只有一个queue.
而且dom changed应该是在queue的比较靠前的部分.
于是,既然有一个确定的position了,那么剩下的问题就是hack的点是需要在dom change前还是change后了.
对应的就是是在处理queue之前还是push到queue后面的地方.
当然,这算不上一个完美的解决方案.
毕竟这个依赖于Polymer的实现.
就像最近Go 1.5的某个更改一样.
原来的goroutine的scheudle的顺序是可估计的.
1.5之后,这个assumption不存在了,那自然要broken依赖于这个的很多东西.
不过无论怎么改,都解决不了实际上的bind value change和dom change是两个操作的事实,而不是一个atomic operraion.
除非就是什么时候浏览器暴露一个atomic的render hook了.
不过即使有,底层估计也还是会做一些batch或者类似tick的东西.
毕竟,这个世界很多时候其实是离散的.
instant是个很理想化的东西.
所以很多时候,很多东西其实是按照某种tick来计划安排的.
如人的秒.
如distributed system的versioning.
剩下的kernel方面的改动,其实多数是一些map/reduce/foreach等当时觉得好玩的写法的该写.
虽然这种functional的写法看起来可能阅读效果不错.
但实际上可能做的事情比较多余.
有些可以fast path的地方变得不太方便.
就像前段时间写Java 9的stram和lambda.
能用stream的地方就用stream,能写lambda就写lambda.
甚至还考虑写个基于Yarn的stream实现,写个基于socket的classloader基本就是分布式计算了.
不过后来想想,其实是种拿到新玩具的炫技心理吧.
有了新锤子,看什么都是钉子.
所谓的喜新厌旧.
没有评论:
发表评论