angular开发总结

下面是XXX的HTML5版本开发总结。

开发思路

说好是MVVM了,当然和以前也页面为中心进行DOM操作的方式很不一样。这方面可以参考文章:StackOverFlow精彩问答赏析:有jQuery背景的开发者如何建立起AngularJS的思维模式?

开发经验就是,以数据为中心来考虑。我通常是对着高保真,先把所需要的数据结构先构思了,看看接口是如何和数据进行对接的。

不过,即使是以数据为中心的开发模式,逻辑与视图的完全分开几乎是不可能完成的任务,只能尽量的分离。我们需要操作的数据模型,不是真正意义的业务模型,而是视图模型(业务数据和视图数据的混合体)。如果实现上需要更偏重业务模型的话,可能要更多借助于resource的概念(ng也有提供这个),不过对于我们这种过程化的接口,意义不大。

关于Service

因为接口的原因,现在有的Service文件,按规范是一个接口对应一个,而且文件内容除了接口参数和响应结果获取,其他基本上是一样的。

根据接口数据的格式,我有时候会预先在service里边进行格式调整,例如一个带父子关系的列表数据,需要先整理为两层的数据结构,这个我会优先考虑在Service里处理。基本原则就是,如何让数据更贴近业务模型。

关于Controller

因为Service是一个很薄的一层,所以大多的页面流程控制,都是在Controller里边完成的。对于Controller如何组织视图模型,我通常是定义一个总的viewModel,来挂载所有的视图模型,大体上就是:

# 定义视图的数据模型
$scope.viewModel =
  acttypes: null #活动类型
  acts: {} #可选的活动列表数据
  act: null #选择的活动
  levels: [] #可选的档次
  level: null #选择的档次
  rewards: [] #可选的赠品、赠品包

这个做法,有个最明显的好处,就是不容易踩到ng-model的scope的坑,关于angular的scope,请参考文章 Understanding Scopes

关于ngmodel

像上面提到的,使用ngmodel,nginit这种会操作model的指令,需要注意scope的范围,一不小心就可能操作到不同的scope。具体原因请参考上述文章。有几个办法可以处理:

  • 使用$parent,但是对于多层次的scope无效。
  • 采用非原型类型的obj,可以避免scope的意外。如上述的viewModel

大多数情况在我们都是用的单向绑定,如果涉及ng-model这种双向绑定的话,有另外一个文章推荐 Using NgModelController With Custom Directives, 因为这篇文章很好的描述了angular对双向绑定的处理流程。

另外,如果使用html5的验证或新类型,也会有些问题,例如你使用number类型,输入非number类型的内容的话,model获取到的值就无效了。

关于性能

可以参考一下这位UC童鞋的文章 angular性能优化心得

基本上该说的都在上面的了。这里补充一下:

  • bindonce可以减少一些无谓的watch,主要用于ng-repeat上,目前还没使用上,但试过是可用的。
  • 在watch方法里边不能操作dom,并及时unwatch。操作dom对性能影响很大。
  • 像$interval,$timeout虽然有个invokeApply这个参数,却是无效的,这个问题在1.3.0beta14才修复,如果不需要变更模型,建议用原生的。
  • filter因为经常计算,所以要尽量的快,选择合适的数据结构很重要,例如对大列表进行筛选不如key/value的查找快。曾经做过字典结构的调整。
  • 目前项目没有lazy loading的问题,这迟早是需要考虑的,网上已有一些方案,借助了route的resolve特性来实现的。

复杂页面流程

  • 是分单个route还是多个route,主要考虑是否需要独立入口,因为一个route对应一个url。这个主要参考URL的设计原则。
  • 跨页面参数传递,如果参数少建议用路径参数,如果共享数据多,可以使用服务来数据共享。

复杂页面的处理,我主要考虑使用状态机来处理。现在使用statechart.js

关于状态机的介绍,请参考Statecharts and Angular.js

开发工具支持

项目涉及多种工具、概念,需要掌握他们的应用场景:

  • 基础设施 node
  • 包管理 npm
  • 模块化,require,sea,amd,cmd
  • 资源依赖,bower
  • 打包自动化,grunt
  • angular相关 插件batarang
  • 语言改进,coffeescript

特别说明关于coffeescript的使用,上述关于viewModel的代码就是coffeescript,大体上可以减少1/3的代码行。

调试的支持

chrome的调试工具很强大,能够模拟手机操作,基本可以解决大多数问题。

不过真机、webview之类的调试仍然是个大问题。当然方案也是有的,如google官方的android+chrome方案、weinre的方案。 不过,我页面做的少,实在没什么经验可以发表的。

其他

自己对css并不熟悉,主要关注逻辑编写,个人认为和web打交道,还是得多理解http协议,浏览器原理。

xml格式须知

着重介绍与项目使用相关的xml知识

常见节点

  • 虽然xml节点是可以带属性的,但是我们项目中使用的通常没有带属性。
  • 常见节点有带子节点的节点(非内容节点)、带文本的节点。
  • 协议中表示可选的节点,通常是指文本为空的节点,如,而不能不带。

节点的顺序

  • 非列表节点,通常是不需要限定顺序的,但不排除有一些SB系统处理不了,所以最好按协议顺序来。
  • 带列表节点,最好保证子节点按业务要求的顺序排列,即使有标示顺序的子节点。

命名空间

  • 通常是有命名空间的话,所有节点都会带,不带通常是不对的。
  • 命名空间是通过xmlns:n0=”http://www.gmcc.net/ngcrm/” 来进行别名的,理论上应该认URI而不是别名,但实际使用却常用别名来识别。
  • 解析时可以考虑忽略命名空间处理,例如xpath,可以用doc.selectNodes(“//*[local-name()=’Service’]”)找到Service节点。

编码格式

  • xml是一种自编码文本,是由第一行决定整个xml的编码格式。
  • 上面只是规范,但像http一样,总有系统不是这么玩的,需要双方协商和确认。
  • 有的xml就是没有第一行的编码描述,更是应该协商确认。

合法的值

  • 除了内置xml文本作为文本节点,或者明显可能特殊字符的文本(如密码,各种属性用某符号拼接),大多数情况不需要关注。
  • 和html一样,某些特殊字符是需要转移的,见下面的转义列表。
  • 如果不想转义,需要用<![CDATA[]]>括起来,需要注意的是,里边的内容需要保持不转义。
  • <![CDATA[]]>里边的内容是原封不动获取的,即使内容带了回车、空格,如果需要对内容二次处理的话,需要注意某些留空可能导致无法处理。
  • <![CDATA[]]>不能嵌套两次,只能用转义来规避。

常见的特殊字符

lt &lt; <(小于号)
gt &gt; >(大于号)
amp &  &(“and”符)
apos ' '(撇号或单引号)
quot " "(双引号)

2014读书笔记

ruby元编程

  • 作者Paolo Perrotta
  • 阅读于2014-04-22,大约5天
  • 适合有一定ryby/rails开发经验的同学,对认识ruby魔法很有帮助

最近看这本书,主要是跟别人谈起这本书,一时兴起又阅读一下,主要是复习复习。毕竟自己搞ruby/rails有好几个年头了,虽然现在主要以java为主,但是ruby还是我工作以外的首选。说回这本书,是系统介绍ruby魔法的魔法书,如果想了解一下那些神奇的库是怎么实现的,又或者想用ruby构建自己的DSL,这本书可以给你足够的知识点。放马去吧!

CSS实战手册

  • 作者David Sawyer McFarland
  • 阅读于2014-04-18,大约7天
  • 适合想系统学习点CSS的童鞋,书籍出版了有些年头,不过的确经典,强力推荐

最近在弄个网站,苦恼bootstrap水太深,想调整难度大,所以看看书找找感觉。 这个The Missing Manual系列还是比较出名的,特别是这本CSS实战手册,难度也不是大,但讲解得很系统化。 其实,这本书还出了新版,专注CSS3了。可惜的是,现在只有英文版,不是很适合我这种CSS初哥,不过下一阶段打算看看。 那么多概念,还是浮云呀,终究还是看基础扎不扎实。

Go语言编程

  • 作者许式伟、吕桂华(七牛云存储团队)
  • 阅读于2014-04-09,大约3天
  • 适合对Go语言刚兴趣、想一窥究竟的童鞋,内容一般、比较浅显,不是很适应英文的童鞋可以优先考虑

看这本书主要是冲着七牛云存储团队的名气去的,毕竟他们是国内第一个吃螃蟹的,据说90%以上的代码都是GO实现的。 另外,我最近用Go在做点东西,看从中看到对Go实践方式的解读。阅读之后,感觉内容偏浅,不如看官方文档。 不过里边有些例子还不错,适合有其他语言经验的童鞋阅读,可以通过对比其他语言的代码实现,加深印象。 总结一下,这书适合快速浏览,剩下的还是老老实实编程吧。

代码的未来

  • 作者松本行弘(Ruby之父)
  • 阅读于2014-04-06,大约3天
  • 适合轻松阅读、扩展知识面,不深入,可以读读

这本书和作者另外一本书”松本行弘的程式世界”感觉有些类似,都是以发表在某专栏的文章集合为主。 可能是由于篇幅问题,大多都是泛泛而谈,点到即止。阅读起来没有什么压力,如果有需要深入的话,就得自己去找资料了。 总体来说,我对这类书还是挺感兴趣的,可以挑选一些自己感兴趣的话题进行阅读,有助于开阔视野。

websphere+spring+jndi数据源配置

配置的步骤在下面简要说明:

  1. 增加基于spring的jndi数据源配置
  2. 在websphere上增加数据源配置
  3. 链接iiop地址

下面进行详细描述。

  • 基于spring的jndi配置文件

注意:如果是第二种方式,可以忽略第三部分”链接iiop地址”

第一种方式:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

+	<bean id="GlobalConfig"
+		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+		<property name="systemPropertiesMode">
+			<value>1</value>
+		</property>
+		<property name="searchSystemEnvironment">
+			<value>true</value>
+		</property>
+		<property name="ignoreUnresolvablePlaceholders">
+			<value>true</value>
+		</property>
+	</bean>

+	 <bean id="jtaTransactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
	 <tx:annotation-driven transaction-manager="jtaTransactionManager"  proxy-target-class="true" /> 
	 <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"/> 

+	<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
+		<property name="jndiName"><value>XXX</value></property>
+		<property name="resourceRef"><value>true</value></property>
+		<property name="jndiEnvironment"><props><prop key="java.naming.provider.url">${java_naming_provider_url}</prop></props></property>
+	</bean>  
</beans>
  • 在websphere上增加数据源配置

效果图

  • 链接iiop地址

在server的java进程定义里边配置jvm参数: -Djava_naming_provider_url=iiop://localhost:port/,另外,修改启动参数需要重启server才能生效。

效果图

其中每个server的port都是不一样的,具体的端口通过Application servers - serverX - Communications - Ports上查看,如下图:

效果图

jdk7编译的bug记录

过程

昨天在编译某个android项目的时候,发现dex打包出错。 后来检查发现编译生成的SplashScreenActivity$1.class格式出错。

后来经常测试,发现jdk6正常,jdk7不正常,包括最新的u51版本。

bug分析

相关的代码如下:

private void checkUpdate() {
    if (UpgradeInfo.isApkLocalExist(SplashScreenActivity.this, UpgradeInfo.getFilePath())) {
        updateInstall();
    } else if (AUTO_UPDATE) {
        initDialog();
        mUpgradeInfo = new UpgradeInfo();
        mGetVersionConfig = new GetVersionConfig(SplashScreenActivity.this,
                new CheckUpgradeHandler(), mUpgradeInfo, getUpgradeRequestParam());
        mGetVersionConfig.start();
    } else {
        // /没版本且不升级
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Intent i = new Intent(SplashScreenActivity.this, LoginActivity.class);
                startActivity(i);
                finish();
            }
        }, LOADING_TIME);
    }
}

正常情况下,Runnable匿名类会生成一个class,但是这里的AUTO_UPDATE被定义为static final,并且设置为true。 在这种情况下,jdk会优化掉后面的分支,但是jdk6不会生成class,但是jdk7会生成一个格式有误的class,反编译后如下:

class SplashScreenActivity$1
  implements Runnable
{
  public void run();
}

所以,在进行dex打包的时候,就会检测到不规范的class,进行报错。 我想,如果在web应用上的话,应该是不会有问题的,因为这个类没有机会被使用。

后来,我发现在其他項目中也有类似写法,但是编译不会有问题。如下所示:

if (UpgradeInfo.isApkLocalExist(LogoActivity.this,UpgradeInfo.getFilePath())) {
    // /检测到有新版本可以安装
    String msg = getString(R.string.dialog_check_version);
    DialogUtil.dialogForTwoButton(... , new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    //..
                }
            });
} else if (AUTO_UPDATE) {
    // ....
} else {
    ///没版本且不升级
    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            Intent i = new Intent(LogoActivity.this, OperatorLoginActivity.class);
            startActivity(i);
            finish();
        }
    }, LOADING_TIME);
}

故猜测,这个bug只出现在第一个不被使用匿名类身上(因为这个类前面还有个OnClickListener的匿名类)。 经过测试,发现也的确是存在这样的情况。

规避方式

乖乖使用jdk6,不要使用jdk7。