应用 (app) 的技术周期 – 词焙的重构实践

作为造轮子专业户, 自己的一些小东西不可避免地重写了一次又一次, 不过在应用层面, 我接触到的架构上的重新设计案例并不多. 几个月之前, 我结束了词焙 0.x 版本的开发, 开始重新设计并实现 1.0 版本, 是一次有意思的经历.

应用的技术周期

对于词焙 (web app) 来讲, 应用从最初概念的提出, 需求设计, 代码实现, 到未来逐渐成熟, 我想会经过如下几个阶段.

  1. 最初的需求实现.
    这个过程中, 会有一个针对的架构设计, 这个设计或许满足最初的需求实现, 也考虑了扩展能力, 但由于思维的局限性, 通常会遗留一部分问题.
  2. 不断补充的需求与对应的实现.
    此时, 会暴露一些原有细节实现的问题, 但更棘手的是, 可能会发现原有架构难以满足进一步的改变了.
  3. 架构的重新设计与实现.
    在应用上线一段时间后, 将暴露的问题和新增的需求纳入考量, 重新设计应用架构.
  4. 新架构实现的重构.
    在实现新架构某一部分的过程中, 分析现有技术实现遇到的问题, 并进行这部分实现的重构.
  5. 接受新需求与对应的实现的检验.

目前词焙 1.0 处在第三个阶段, 并且已经发现了一些组件设计中不完善或者不合理的地方, 会在未来进行重构.

新版词焙的架构考虑

老版本的词焙设计之初, 是需要联网使用的, 但由于用户的强烈要求, 不得不将本来为联网使用设计的简单缓存结构硬改 (其实是重写) 为支持离线和同步的结构, 很多实现非常生硬, 难以维护. 另外在 UI 这一块儿, 一开始因为进度需要, 没有做好规划, 触摸手势及相关的事件管理也没有做好统一, 这些都成了新版词焙设计时重点考虑的地方.

使用 TypeScript

前后端全面改用 TypeScript 开发, 静态类型检查与对应的自动完成很能提高开发效率.

基于 Promise (ThenFail)

刚开始接触 Promise 的时候其实有点抵触, 但后来发现上手了也挺好用的. 但本着造轮子的精神, 写了自己的 ThenFail, 使用 TypeScript 开发, 也可以更方便地添加需要的 helper.

离线数据与同步 (Syncable, 面临重写, 未来将整理开源)

除了简单的数据同步外, 词焙中需要一个可以累积的东西以储存类似每天学习时间的数据. 但考虑到未来或许还会处理更复杂的数据合并, Syncable 为这一块儿预留了相关接口. 为了进一步增加该组件的一般性, 还添加了诸如只读数据, 被动 (按需) 同步等特性.

触摸与手势 (Touch Delegate)

上一版词焙实际上也使用了类似 Touch Delegate 的做法, 但是不支持多触点, 并且缺乏终止事件传播的机制 (因为没有统一的管理), 这些是重写这部分的原因. 与此同时, 保留了之前 Gesture Identifier 的模式, 方便手势扩展.

MVC 框架 (Drop, 面临重写)

相对 ThenFail 这么个 Promise 实现来讲,,, 自己搞一套 MVC 框架确实需要点超越时代的轮子精神在里面, 但也算实现了个愿望, 因为还在 2011 年的时候就一直想写这么个框架. 考虑到开发词焙的时间不是特别充裕, 但又耐不住想要写这么个东西, 这次实现的时候调整了原有的模板语法, 力求实现简单, 同时也尽量保留原有设计的精髓 (一般性). 但感觉歪打正着, 现在反倒更喜欢这个实现简单的语法, 而一般性也得到保留 (具体可以参看 Drop 中各种 Decorator 的实现).

不过将 Drop 应用到词焙开发上之后, 还是遇到了不少问题. 其中一些问题通过添加 Decorator (修饰器) 的形式巧妙地解决了, 另一些则还有待框架本身的改进, 比如对象字面量的支持 (实现时为了解析简便, 不允许为嵌套未转义的 { }), 对表达式的解析.

服务器角色的转变和重构

目前来讲, 词焙的服务器基本就是作为数据同步中心存在的 (其实之前改离线之后就已经是这样的功能了). 同时, 新的服务器端脚本也使用了 TypeScript 和 Promise, 结构更加清晰, 实现也更加优雅.

总结

总体而言, 这次架构的重新设计自己还是比较满意的, 也产出了几个实用并且通用的组件. 我想之后会把这些经验和组件打包一下, 做成一套 web app 的解决方案. 当然, 在此之前, 还得完成具体实现的重构, 并且经得住需求的考验.

Disable Scroll Bouncing Effect of WebBrowser Control on Windows Phone 8

Just another story happens when developing WordsBaking.

First, the basis. If you don’t have any element in your web page that requires overflow style being auto or scroll, “-ms-touch-action: none;” under “html” selector should work well . Actually it works all the time in Internet Explorer, but in a WebBrowser control, if there’s something like a list for which you may need that style, the solution becomes tricky.

I spent tons of hours and tried tons of ways hoping figure out a solution, and luckily, found one.

That is the good news, but the bad news is, this solution doesn’t seem to be elegant (though it works perfectly so far).

The first thing that I found might be useful is that this bouncing effect happens only when your finger touches the elements that already have their contents at the top/bottom or both. So, the first solution I thought might work was to add a pointerdown (MSPointerDown) event listener on the element, and determine whether its content has reached the top or bottom. Unfortunately, it doesn’t work well.

Later I read about an article shows a solution on suppressing scrolling and zooming in WP7 WebBrowser control, I can’t say that it works (on WP8), but it helps.

Combining these two parts (and this is why I think it’s not elegant enough), here’s the solution:

C# Part

private void mainBrowser_Loaded(object sender, RoutedEventArgs e) {
    // here we are using a library named Linq to Visual Tree.
    // http://www.scottlogic.com/blog/2010/03/04/linq-to-visual-tree.html
    var border = mainBrowser.Descendants().Last() as Border;
    border.ManipulationDelta += border_ManipulationDelta;
    border.ManipulationCompleted += border_ManipulationCompleted;
}

void border_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) {
    mainBrowser.InvokeScript("onmanipulationcompleted");
}

void border_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) {
    var status = mainBrowser.InvokeScript("onmanipulationdelta") as string;
    if (status == "top" || status == "both") {
        if (e.DeltaManipulation.Translation.Y > 0) {
            e.Handled = true;
        }
    }
    if (status == "bottom" || status == "both") {
        if (e.DeltaManipulation.Translation.Y < 0) {
            e.Handled = true;
        }
    }
}

JavaScript Part

window.manipulationTarget = null;

window.onmanipulationdelta = function () {
    if (!window.manipulationTarget) {
        return '';
    }

    var target = window.manipulationTarget;

    var top = target.scrollTop == 0;
    var bottom = target.scrollTop + target.clientHeight == target.scrollHeight;

    return top ? bottom ? 'both' : 'top': bottom ? 'bottom' : '';
};

window.onmanipulationcompleted = function () {
    window.manipulationTarget = null;
};

// and you'll need to make calls to every elements
// with overflow auto or scroll with this:
function preventBouncing(ele) {
    // using jQuery.
    ele.on('MSPointerDown pointerdown', function (e) {
        window.manipulationTarget = this;
    });
}

And good luck fellows~

Let's Again Talk About IE (Internet Explorer)

As an amateur but the same time professional front-end engineer, one of the most common things in my life is to press F5 in different browsers.

When this word was not “standardized” as it is now, I was used to pressing that button in several versions of IE, Chrome, Firefox, Safari, and, if I was in a really great mood, Opera. So I hadn’t suffer less than anyone. Especially from IE 6/7.

Now I basically test only in IE 8+ and Chrome. If the project is platform targeted, even better. There has been a lot of scoffs about IE  on compatibility and performance these years, which make me sort of sorry. People would always be fond of the new and tired of the old, even though it is the old one which brings the bright start to all these wonderful things.

IE 6 in my opinion is a great browser. It is out of date but It was well-designed and ignited many features in modern browsers. Actually, tons of newly added features in HTML5 or CSS3 can be imitated in IE 6, a browser which was released 12 years ago. And many earlier IE-only features have become the standards.

The earlier versions of IE is buggy and perform slow, but they perfectly served the days when it was released.

Well I am not trying to ask people not to give it up, but just hoping we can keep the respect, which this browser deserves, to it in its last days.

Microsoft seems to have realized their mistakes on detaining the developing of IE these years and brings us the new IE 9/10/11. Compatibility issues have become minor since version 9 (also it brings us fully GPU acceleration, which is a really important event for web apps), and since version 10, IE finally caught up the steps of modern browsers.

IE now is almost ready for web apps which could be as great as native apps, and I am looking forward to the booming of mobile web apps in the next several years. 🙂

Some Thoughts on Touch-friendly Mobile Web Apps

These days I am working on a project related to English study as my startup (though part-time), due to the limited resources, I chose a native browser based solution, So that we’ll save some time for building only the single main part of the app which will run on multiple devices.

However, the app I want to build has to be user-friendly, and for an HTML based app on a mobile device, which means, relatively poorer performance, to make the user experience acceptable forms a challenge. Also, as the UX we designed, there would be some touch gestures, how to manage these gestures is also a core issue to figure out.

Luckily, both of these two issues are not tricky.

There are some transition animations during page switching, elements removing, etc. I tried to use something like margin (CSS), but it doesn’t work that well, the animation doesn’t look smooth. So I made some searches, an article says CSS 3 transform has even slower performance comparing to CSS properties like margin and the position stuffs. I believed it… But it turned to be wrong. After I randomly changed the page switching from margin to CSS 3 transform + transition, the animation runs like a charm! I can’t say it’s as good as native apps, but it’s far from acceptable.

I am using Lumia 1020 with IE 10 Mobile myself, and get a Xiaomi 2S with its built-in browser and Chrome Mobile. These browsers all perform well, especially IE 10 and Chrome Mobile. Also, I tested it on an iPhone 5 of my friend, Safari Mobile does a great job, too. That’s actually not a surprise, I wrote some web games in 2010 runs on my iPod touch 3 32G using CSS 3 animations, even with some 3D stuffs, and the game performed well.

As for gestures management, I wrote two classes respectively called GestureIdentifier and GestureDelegate and so far they works well. The idea is really simple but it really makes things much easier, hoping telling this will help some starters learn about the power of ideas which is far above code itself.

And also some quick notes:

About “meta viewport”, seems that some phones have a minimum valid width of 360 instead of 320, so any value below 360 will not work… T-T gotta make some changes to fit this new value…

About click and “tap”. Seems that if these is a click event (especially on an “a” tag), these will be a semi-transparent rectangle when you “tap”. But sometimes, it doesn’t look well… Luckily “tap” based on touch event would be a solution… As I got the two gesture classes, I can simply define a new GestureIdentifier called “tap” and write a simple rule like the path of touch has only one point. Solved.

Wishing this tool will soon be available to everyone!

[Update]

The touch event trick for preventing tap highlight works most of the time, but on IE Mobile there are still some conditions on which the semi-transparent rectangle (tap highlight) will show up, so I continued searching for solutions. Luckily, there are way better and more official ones:

For IE, there’s a meta item named “msapplication-tap-highlight”, and simply setting the content to “no” solves the problem.

<meta name="msapplication-tap-highlight" content="no" />

For Webkit (Chrome and Safari), there’s a style named “-webkit-tap-highlight-color”, and “transparent” is the answer.

html { -webkit-tap-highlight-color: transparent; }

浅谈Web程序设计中的面向数据的编程(DOP)

所以说, 趋势就是趋势, 并不在于谁提出来, 该有的东西自然便会有. 我不知道我们这一代人是否算是见证了DOP的诞生, 但至少在这些年, DOP被用得越来越多了.

说到这个, 首先想提两点. 一个是之前众所周知的变成模式, 面向对象编程(OOP). 对与我来说, 面向对象在我的代码中扮演了相当重要的角色. 它让程序内部的交流变得更加清晰, 提升了程序的可读性, 降低了出现bug的概率. 在有些应用上, 到目前还是不能替代的. 另一个是今后Web编程的发展, 我目测会有几个大方向: 1. 基于Canvas或CSS3的Web游戏. 2. 功能复杂的Web应用程序(如在线办公). 3. 以内容呈现与交互为主的Web页面(如SNS). 只所以想要提这两点, 是为之后将要说到的OOP的局限性, 和DOP的应用范畴做铺垫.

那首先说说, 什么是DOP. 老实说, 我并非是从这个名字开始接触这个概念的, 而是从很多Web页面的改变上开始思考这个问题. 不过很自然地, 使用了同一个词语来描述这样同一个概念. 其实其中有一点YY的成分, 但我想也应该八九不离十.

如果说OOP让人们只用关心如何去调用一个功能, 而不用关心功能的实现的话, 那么DOP则只用人们去关心一个模块/对象所管理的数据, 而不用关心这些数据的改变会对其他模块/对象造成什么影响. 当然, 对自己的影响还是要关心的. 其实细想, 在一些高级编程语言中, 对象的属性就是一种简单地DOP模型. 但显然, 它是局限在某一个对象上的. 然而很多时候, 同一个数据在多个对象上是公用的, 这或许就是DOP和传统OOP一个很大的区别, 也是传统OOP的局限.

但说到这里, 大家可能也会有种想法, 认为DOP也是OOP. 我认为这种想法是正确的, DOP的最佳实践应该对象化/模块化, 只是与直接调用其他对象的方法不同, 通过数据来间接达到目的. 这一点来讲, 倒和面向事件的编程(EOP)有些相似, 但事件是瞬时的, 而数据则是可持续的. 或许DOP约等于OOP+EOP+Data?

近年来, 在很多网站中出现了Hash, 很多MVC框架也支持相应的技术, 极大地方便了复杂的无刷新页面的实现, 这应该算是典型的DOP. 不过如果仅仅是这样, 还只能说是Hash上的数据在和整个页面一个对象交互, 这就有点伤感了. 还好看到一些框架, 并非这样的如果, 比如淘宝的MagixJS似乎就不错. 不过很显然, DOP也有其局限性, 毕竟中间穿插了一个环节, 在效率上或许比不过传统的变成手段. 所以像Web游戏的有些部分则是不便于使用的, 但我想做做UI还是完全能够胜任.

按我的理解, 相对于OOP, DOP能进一步减小复杂程序的思维难度, 提高开发效率, 以及降低bug的概率, 应该是未来Web编程某些方面的趋势, 于是我也有自己的DOP框架计划, 只是准备在DOP的基础上, 增加强大的模板功能, 也许未来, 很多AJAX操作, 都只需要一个简单的模板了.

图片展示心得一二

这两天在做淘吧首页的重构, 有很多地方会有图片的展示. 图片之后会由运营上传, 所以无法硬性规定图片的规格. 于是写代码的时候, 则要加以注意, 防止因为图片的问题破坏了布局.

最开始我的做法很直接, 确定图片大小, 问题则是, 如果图片高宽比不对, 则会变形. 但考虑到运营通常会按照规则传图片, 所以其实也是可行的.

但是妙净大似乎不是很能接受这种做法, 于是给出了这边通用的一个解决方案, 可以实现任意大小图片的垂直居中, 并且过于大的图片也不会破坏布局, 缺点是结构层次太多:

<div><a><img /></a></div>

总共三层, 一方面觉得有点累赘, 一方面因为自己都是两层, 图片出现的位置很多, 要改也很麻烦, 后来想出了一个觉得不错的办法. 为了简洁, 我就直接把style写到HTML里了:

<a href=”#” style=”display: block; width: 120px; height: 100px; overflow: hidden;”>
<img alt=”vilic” src=”vilic.png” style=”display: block; margin: auto; height: 100px;” />
</a>

这段代码也很好理解, 将内外元素display: block; (当然, 对于外面的元素, 使用float, 或者是position: absolute;也是可以的), 外面的元素定高宽, 且overflow: hidden; 保证图片不会在过大的时候超出. 而对于img, 变为块级元素后, 使用margin: auto; 则可以在图片较小, 或者高宽比较大时水平居中. 而height: 100px; 与a元素等高, 宽度不定, 这样一方面可以限制图片的高度, 一方面使图片不至于变形.

所以总体来说, 这个方案除了在图片高宽比较小时, 显示会左对齐(即图片右边可能会被挡住), 其他时候, 都能比较完美地展现图片, 重要的是, 兼容性很好, 结构简单.

试一下吧~

补充下, 后来想到, 如果说把容器宽度设置得足够宽, 然后再调节margin或者其他东西, 让超出部分隐藏, 那么, 即可实现较宽的图片也能居中了.