SDK性能自测小技巧

初始化耗时统计
利用时间差:

%title插图%num

通过这种方式可以较准确的得到SDK的初始化耗时。

内存消耗统计
获取内存信息方法

方法一:使用命令

adb shell dumpsys meminfo -d <process-name>

方法二:使用IDE工具

打开logcat,选中侧边栏的系统信息,选择Memory Usage:

%title插图%num

内存信息

示例场景:SDK初始化

SDK初始化前:

%title插图%num

SDK初始化后:

%title插图%num

 

内存消耗计算规则

这里我们只关注Pss Total,取前后Total之差:

20388 – 17317 = 3071 kB

流量消耗统计
流量相关的状态数据存储在/proc/uid_stat/<UID>/目录下,其中<UID> 表示apk对应的UID。

获取UID
方法一: 查看进程

%title插图%num

UID = 10000 + 539 = 10539

注:Java程序的UID从10000起。

获取流量数据

示例:初始化前后的流量

因为初始化前没有任何网络请求操作,所以系统还没有任何对应UID的流量数据,我们点击按钮初始化之后再看的话就有流量数据了:

%title插图%num

采集到前后两次流量数值后,即可计算得到总的流量消耗:

初始化流量消耗 = 1479 + 497 = 1976 bytes ≈ 1.93kb

线程数统计
运行Demo之后,打开Android Studio monitor

%title插图%num

选中进程,然后Update Threads:

初始化前:

%title插图%num

初始化后:

%title插图%num

可以通过前后新增的线程来判断哪些是SDK初始化后的开的线程,从截图来看Bugly常驻线程有5个。

Android多模块构建合并aar解决方案

前段时间,我在实现gradle多模块构建遇到一个问题,以前我们基本上是以jar包形式让开发者集成到工程中使用,但自从Android Studio中有了多module的概念,而我们的SDK也是分了多个模块进行构建的,但我们这里有个问题就是模块之间是相互关联的,不能针对每个模块单独打包,而每个module都会生成对应的aar,但并不会把依赖的module代码打进去,别问我为什么知道,你将aar后缀改为zip,然后反编译classes.jar就可以看到。所以我们这边就有了合并aar这样的一个需求,下面就告诉大家怎么来实现。

android-fat-aar

当时我遇到这个问题,就去github搜了一下,已经有人将合并aar的脚本开源出来了,开源地址如下:

https://github.com/adwiv/android-fat-aar

什么是aar?

什么是aar?它跟jar包有什么区别?它该怎么样使用?相信大家一定会有这些疑问。首先aar是针对Android Library而言的,你可以理解为IDE针对Android Library的打包,一个aar包含什么东西?
它的文件后缀名是.aar,它本身是一个zip文件,强制包含以下文件:

/AndroidManifest.xml
/classes.jar
/res/
/R.txt

另外,AAR文件可以包括以下可选条目中的一个或多个:

/assets/
/libs/name.jar
/jni/abi_name/name.so (where abi_name is one of the Android supported ABIs)
/proguard.txt
/lint.jar

具体看到这里看如何创建一个Android Library:
https://developer.android.com/studio/projects/android-library.html#aar-contents

jar包跟aar包有什么区别?
jar:只包含了class文件与清单文件,不包含资源文件,如图片等所有res中的文件。
aar:包含所有资源,class以及res资源文件全部包含。

如果你只是简单实用一些类库,你可以直接使用*.jar文件,而如果你想既想使用类库,又想实用资源,那么你就可以创建一个Android Library,使用它生成的*.aar文件。

jar文件的使用方式我们应该比较熟悉了,将它复制到工程的libs目录下,然后在gradle中添加以下脚本:

dependencies {
compile fileTree(include: [‘*.jar’], dir:’libs’)
}

aar文件使用同样需要复制到libs目录下,并按照以下方式集成:

repositories {
flatDir {
dirs’libs’
}
}

dependencies {
compile(name:’your aar’, ext:’aar’)

多模块构建合并aar

这个是本文的重点,我们可以再每个module下的build/outputs/aar下找到编译生成的*.aar文件。

步骤1:
将gradle文件’fat-aar.gradle’到你的项目目录,然后apply:

apply from: ‘fat-aar.gradle’

步骤2:定义嵌入的依赖关系
你需要修改你以前依赖项,并将compile更改为embedded,作为你想要合并的aar。使用例子如下:

%title插图%num

通过以上的方式你可以将多个module生成的aar合成一个,大家可以新建一个demo工程来测试下

%title插图%num

如何开发一个App(Android)

前言
本篇博客从开发的角度来介绍如何开发一个Android App,需要说明一点是,这里只是提供一个如何开发一个app的思路,并不会介绍很多技术上的细节,从整个大局去把握如何去构思一个app的开发,让你对独立开发一款app的时候有个理解,如果有说的不对的地方,欢迎大家拍砖留言讨论。

开发环境
Android应用层使用的开发语言是Java,自然需要用到Java运行环境,无论你在Window是系统还是Mac系统都需要安装JDK,并且配置它的环境变量,不懂什么叫环境变量的或者不知道怎么配的,请利用好自己的百度技能自行学习。

%title插图%num

开发工具
开发工具,Android因为历史原因,前面很长一段时间使用的是Eclipse,我们要开发Android应用的时候需要另外安装Google为它开发的插件ADT,Eclipse这个开源软件很优秀,也很专业,但仅仅只是使用它来开发Android就显得有点鸡肋了,如果没有对Eclipse进行优化的话,会很卡很卡,后面Google实在不愿意寄人篱下,就专门为我们开发者开发了目前很受广大开发者推崇的Android Studio,现在2.0预览版也出来啦,大伙赶紧去下吧。

%title插图%num

模拟器
Android自带的模拟器一直广受诟病,实在是太卡太慢了,对比人家IOS模拟器是那么的流畅,Android开发者直喊好苦逼啊,不过还好后面出了第三方比原生流畅n倍的模拟器Genymotion,大家可以去下一个个人版的够你平时开发测试用了。*好的办法还是使用真机测试,毕竟真机才是真实的环境。

%title插图%num

Android核心基础
前面是准备工作,想开发一款Android app,你*起码要掌握Android的核心基础知识,针对初学Android的童鞋,想一口吃掉一个胖子是不可能的,还是得把基础打牢之后才能完成独立的开发,Android入门不难,学完基础你只是具备开发app的基本条件,想做一个优秀的app还有很长一段路要走,经验是靠不断实践堆出来的,找一套系统的课程去认真学习一下,在线教育的资源很多,比如慕课网,*客学院都有很多不错的学习资源,童鞋自己择优来学习即可。

推广:http://edu.csdn.net/course/detail/545(笔者的课程)

产品开发流程
正常的互联网开发app的流程大致如下:
– 产品规划,定产品方向
– 需求调研,产出需求文档
– 需求评审,修订需求文档
– 产品狗画app线框图提供给射鸡师
– 射鸡师根据线框图设计视觉稿
– 程序猿根据视觉稿搭建UI框架
– 程序猿根据需求文档开发功能
– 测试媛编写测试用例,根据排期进行测试
– 程序猿修复回归测试反馈的bug,提交beta版
– 测试通过,提交给运营喵发布到渠道上线

上面是笔者的经验总结,可能有不太准确的地方,但大致整个流程是这样,我们开发者要清楚自己在整个产品中充当的角色,明白自己的工作职责即可。

快速搭建项目
Android比没有想象那么难,只是刚开始要学习的东西多,一下子消化不了才会比较茫然,笔者写这篇博客就是想帮助大家整理一下开发思路。

快速搭建项目也算是一项技能,而这项技能只能由你自己来完善,刚开始做开发的时候一定会有很多重复性的工作,如果没有意识去提高自己的开发效率的话,可能你的十年工作经验代表的只是一年的经验用了十年而已。

那要怎么做,笔者提供一个自己总结的,仅供参考:
– 定开发规范
– 搭建UI框架
– 选用开发库集成(或自造轮子)
– 第三方服务集成(视情况而定)

定开发规范
一个项目一般不会只有一个人独立开发,就算是独立开发,我们也应该定一下开发规范,一般会有什么样的规范?
– 命名规范
– 代码规范
– 代码管理规范

命名规范
命名规范包括:
– 项目命名
– 包命名
– 类命名、成员变量命名
– 资源文件命名
我们做每一个产品都会有相应的命名,不要用中文来命名,*好用能表达中文意思的英文来表示,例如CSDN客户端,可以用CSDNClient作为命名,我们创建项目的时候可以以它作为Application name。
可以看看以前写的这篇文章:
http://blog.csdn.net/wwj_748/article/details/42347283

代码规范
代码规范视语言而定,开发android使用的是Java,自然要按照Java的编码规范进行编码,比如命名采用驼峰命名法,编码的时候也要符合Android的开发规范,比如UI线程不做任何耗时的操作,像网络请求、数据库操作都需要放到子线程中去做,只有UI的刷新才在UI线程中做,像这些规范,可以根据自身在项目遇到的问题来定,定规范的好处就是减少踩坑的几率,提高开发的质量。

代码管理
对于一个经常更新迭代的产品,不可能由头到尾不变,这个时候我们需要对代码进行分支管理,*好使用git代码仓库对代码进行管理,作为一个合格的开发者连git都不用实在说不过去,还用svn的童鞋赶紧放弃它投入git的怀抱,它的好处是不言而喻的,自己去体会。

搭建UI框架
搭建UI框架需要我们根据产品的导航模式来设计,市场上常用的导航模式有如下图几种:

%title插图%num

我们的app如果不出意外一定是其中的一种导航模式,一般线框图出来我们就应该知道即将要开发的app长什么样子,开发者不必等视觉稿和素材出来才开始动工,我们先大致搭个架子,等视觉稿出来之后我们再做调整。

选用开发库
一般我们app涉及到的库会有:
– UI框架(比如下拉刷新PullToRefresh、侧滑菜单Slidingmenu)
– 网络请求库(比如okhtttp、AndroidAsyncHttp、Volley)
– 数据操作库(比如GreenDao、Ormlite)
– 图片缓存框架(比如Universal-Imageloader)
– 数据解析库(比如Gson)

之所以要选用这些库,肯定是为了避免重复造轮子,在开发效率的角度来说,选用优秀的开源库能大大缩短开发周期和提高开发效率,但从个人提升角度来看的话,我们可能就成了一个只会用API的程序猿了,如果想提升的话,造轮子或者分析这些优秀的源代码是一个不错的途径。

第三方服务集成
我们开发app的时候,肯定会遇到一些需求,比如推送的需求、自动升级、数据统计、社会化分享、用户反馈等等,然而对于一个刚起步的企业或者个人开发者的话,全都要自己去开发的话,那岂不是累死,像推送这种有一定的技术门槛,能做好都能成立一家公司了,所以选用一些第三方服务是一个可选之举。如果说你以后做大了,用第三方怕不好控制,那就自己做呗,有钱任性招兵买马就自己做,谁叫咱有钱呢。

前面这些东西开发一个app够了,开发出来能不能用还得有靠谱的测试,有没有crash,操作流不流畅,体验好不好才会有用户去用。这里不从产品的角度去评判一个app的好与坏,程序员要考虑的是从代码层面、性能层面去让我们的app变得更好。

云测
我们开发完毕之后,需要给测试工程师进行基本的功能需求测试,他们传统的做法就是根据事先写好的测试用例来做回归测试,再把测试出来的bug反馈给工程师,工程师再去修bug,但这样实在是太不靠谱了,有时候我们太在意功能而忽略了一些更重要的东西,那就是体验,给用户*直接的感受就是你这个app够不够惊艳,够不够流畅,用户可能根本就不在乎你这个功能做的有多牛逼。所以我们更应该从非功能性方向去做测试,我们的目的是让用户用的爽,而不是加一些乱七八糟的功能。那怎么测非功能性的一些因素,这里就要提到『云测』这个东西,因为现在设备太多了,如果公司要买一堆设备来做测试,那得多少成本,况且设备更新得太快,你根本就跟不上,所以就有了云测这个东西,它是一个云测试平台服务,提供了一大批主流机型,我们就直接省去购买设备的成本,还能得到完善的测试报告。

再来说一下它的好处:
– 终端云,省去测试设备购买租赁成本
– 高效率 节省测试人员成本及时间
– 包含兼容性测试、性能测试、功能测试
– 操作简单、详细测试报告生成

这么多好处,你在缺少测试工程师的时候,不去尝试那实在说不过去。

打包上线
前面的开发环节、测试环节都没问题之后,你离实现一个app的完整开发就不远了,正常的互联网公司,会把签名打包的apk给到运营,交给他们去写文案,上传到应用渠道,让渠道给我们去首发和推广。如果是个人开发者,这些工作就得我们自己做了。

总结
本篇博客从整个app开发流程上去给大家梳理了一遍,没有讲太多技术上的东西,但理解app流程对自己把握产品很有益处,虽然我们只是一个小小的开发者,但如果你有追求,哪天轮到你去负责一个产品的时候,你就应该对开发的每一个环节都了如指掌,因为出了问题,你也知道怎么针对性的去解决。笔者虽然只是一个小小的开发者,但也乐于思考,很多时候不太愿意被别人牵着鼻子走,因为我们的人生就应该把握在自己手里。

使用Vue.js开发微信小程序:开源框架mpvue解析

前言
mpvue是一款使用Vue.js开发微信小程序的前端框架。使用此框架,开发者将得到完整的 Vue.js 开发体验,同时为H5和小程序提供了代码复用的能力。如果想将 H5 项目改造为小程序,或开发小程序后希望将其转换为H5,mpvue将是十分契合的一种解决方案。

目前,mpvue已经在美团点评多个实际业务项目中得到了验证,因此我们决定将其开源,希望更多技术同行一起开发,应用到更广泛的场景里去。github项目地址请参见mpvue 。使用文档请参见 http://mpvue.com/。

为了帮助大家更好的理解mpvue的架构,接下来我们来解析框架的设计和实现思路。文中主要内容已经发表在《程序员》杂志2017年第9期小程序专题封面报道,内容略有修改。

小程序开发特点
微信小程序推荐简洁的开发方式,通过多页面聚合完成轻量的产品功能。小程序以离线包方式下载到本地,通过微信客户端载入和启动,开发规范简洁,技术封装彻底,自成开发体系,有Native和H5的影子,但又*不雷同。

小程序本身定位为一个简单的逻辑视图层框架,官方并不推荐用来开发复杂应用,但业务需求却难以做到精简。复杂的应用对开发方式有较高的要求,如组件和模块化、自动构建和集成、代码复用和开发效率等,但小程序开发规范较大的限制了这部分能力。为了解决上述问题,提供更好的开发体验,我们创造了mpvue,通过使用Vue.js来开发微信小程序。

mpvue是什么
mpvue是一套定位于开发小程序的前端开发框架,其核心目标是提高开发效率,增强开发体验。使用该框架,开发者只需初步了解小程序开发规范、熟悉Vue.js基本语法即可上手。框架提供了完整的 Vue.js 开发体验,开发者编写Vue.js代码,mpvue 将其解析转换为小程序并确保其正确运行。此外,框架还通过 vue-cli 工具向开发者提供quick start 示例代码,开发者只需执行一条简单命令,即可获得可运行的项目。

为什么做mpvue
在小程序内测之初,我们计划快速迭代出一款对标 H5 的产品实现,核心诉求是:快速实现、代码复用、低成本和高效率… 随后经历了多个小程序建设,结合业务场景、技术选型和小程序开发方式,我们整理汇总出了开发阶段面临的主要问题:

组件化机制不够完善
代码多端复用能力欠缺
小程序框架和团队技术栈无法有机结合
小程序学习成本不够低
组件机制:小程序逻辑和视图层代码彼此分离,公共组件提取后无法聚合为单文件入口,组件需分别在视图层和逻辑层引入,维护性差;组件无命名空间机制,事件回调必须设置为全局函数,组件设计有命名冲突的风险,数据封装不强。开发者需要友好的代码组织方式,通过 ES 模块一次性导入;组件数据有良好的封装。成熟的组件机制,对工程化开发至关重要。

多端复用:常见的业务场景有两类,通过已有 H5 产品改造为小程序应用或反之。从效率角度出发,开发者希望通过复用代码完成开发,但小程序开发框架却无法做到。我们尝试过通过静态代码分析将 H5 代码转换为小程序,但只做了视图层转换,无法带来更多收益。多端代码复用需要更成熟的解决方案。

引入 Vue.js:小程序开发方式与 H5 近似,因此我们考虑和 H5 做代码复用。沿袭团队技术栈选型,我们将 Vue.js 确定为小程序开发规范。使用 Vue.js 开发小程序,将直接带来如下开发效率提升:

H5 代码可以通过*小修改复用到小程序
使用 Vue.js 组件机制开发小程序,可实现小程序和 H5 组件复用
技术栈统一后小程序学习成本降低,开发者从 H5 转换到小程序不需要更多学习
Vue.js 代码可以让所有前端直接参与开发维护
为什么是 Vue.js?这取决于团队技术栈选型,引入新的选型与统一技术栈和提高开发效率相悖,有违开发工具服务业务的初衷。

mpvue 的演进
mpvue的形成,来源于业务场景和需求,*终方案的确定,经历了三个阶段。

*阶段:我们实现了一个视图层代码转换工具,旨在提高代码首次开发效率。通过将H5视图层代码转换为小程序代码,包括 HTML 标签映射、Vue.js 模板和样式转换,在此目标代码上进行二次开发。我们做到了有限的代码复用,但组件化开发和小程序学习成本并未得到有效改善。

第二阶段:我们着眼于完善代码组件化机制。参照 Vue.js 组件规范设计了代码组织形式,通过代码转换工具将代码解析为小程序。转换工具主要解决组件间数据同步、生命周期关联和命名空间问题。*终我们实现了一个 Vue.js 语法子集,但想要实现更多特性或跟随 Vue.js 版本迭代,工作量变得难以估计,有永无止境之感。

第三阶段:我们的目标是实现对 Vue.js 语法全集的支持,达到使用 Vue.js 开发小程序的目的。并通过引入 Vue.js runtime 实现了对 Vue.js 语法的支持,从而避免了人肉语法适配。至此,我们完成了使用 Vue.js 开发小程序的目的。较好地实现了技术栈统一、组件化开发、多端代码复用、降低学习成本和提高开发效率的目标。

mpvue设计思路
Vue.js 和小程序都是典型的逻辑视图层框架,逻辑层和视图层之间的工作方式为:数据变更驱动视图更新;视图交互触发事件,事件响应函数修改数据再次触发视图更新,如图1所示。

%title插图%num
图1: 小程序实现原理
鉴于 Vue.js 和小程序一致的工作原理,我们思考将小程序的功能托管给 Vue.js,在正确的时机将数据变更同步到小程序,从而达到开发小程序的目的。这样,我们可以将精力聚焦在 Vue.js 上,参照 Vue.js 编写与之对应的小程序代码,小程序负责视图层展示,所有业务逻辑收敛到 Vue.js 中,Vue.js 数据变更后同步到小程序,如图2所示。如此一来,我们就获得了以 Vue.js 的方式开发小程序的能力。

Vue代码
– 将小程序页面编写为 Vue.js 实现
– 以 Vue.js 开发规范实现父子组件关联

小程序代码
– 以小程序开发规范编写视图层模板
– 配置生命周期函数,关联数据更新调用
– 将 Vue.js 数据映射为小程序数据模型

并在此基础上,附加如下机制
– Vue.js 实例与小程序 Page 实例建立关联
– 小程序和 Vue.js 生命周期建立映射关系,能在小程序生命周期中触发 Vue.js 生命周期
– 小程序事件建立代理机制,在事件代理函数中触发与之对应的 Vue.js 组件事件响应

这套机制总结起来非常简单,但实现却相当复杂。在揭秘具体实现之前,读者可能会有这样一些疑问:

要同时维护 Vue.js 和小程序,是否需要写两个版本的代码实现?
小程序负责视图层展现,Vue.js的视图层是否还需要,如果不需要应该如何处理?
生命周期如何打通,数据同步更新如何实现?
上述问题包含了 mpvue 框架的核心内容,下文将仔细为你道来。首先,mpvue 为提高效率而生,本身提供了自动生成小程序代码的能力,小程序代码根据 Vue.js 代码构建得到,并不需要同时开发两套代码。

Vue.js 视图层渲染由 render 方法完成,同时在内存中维护着一份虚拟 DOM,mpvue 无需使用 Vue.js 完成视图层渲染,因此我们改造了 render 方法,禁止视图层渲染。熟悉源代码的读者,都知道 Vue runtime 有多个平台的实现,除了我们常见的 Web 平台,还有 Weex。从现在开始,我们增加了新的平台 mpvue。

生命周期关联:生命周期和数据同步是 mpvue 框架的灵魂,Vue.js 和小程序的数据彼此隔离,各自有不同的更新机制。mpvue 从生命周期和事件回调函数切入,在 Vue.js 触发数据更新时实现数据同步。小程序通过视图层呈现给用户、通过事件响应用户交互,Vue.js 在后台维护着数据变更和逻辑。可以看到,数据更新发端于小程序,处理自 Vue.js,Vue.js 数据变更后再同步到小程序。为实现数据同步,mpvue 修改了 Vue.js runtime 实现,在 Vue.js 的生命周期中增加了更新小程序数据的逻辑。

事件代理机制:用户交互触发的数据更新通过事件代理机制完成。在 Vue.js 代码中,事件响应函数对应到组件的 method, Vue.js 自动维护了上下文环境。然而在小程序中并没有类似的机制,又因为 Vue.js 执行环境中维护着一份实时的虚拟 DOM,这与小程序的视图层完全对应,我们思考,在小程序组件节点上触发事件后,只要找到虚拟 DOM 上对应的节点,触发对应的事件不就完成了么;另一方面,Vue.js 事件响应如果触发了数据更新,其生命周期函数更新将自动触发,在此函数上同步更新小程序数据,数据同步也就实现了。

mpvue如何使用
mpvue框架本身由多个npm模块构成,入口模块已经处理好依赖关系,开发者只需要执行如下代码即可完成本地项目创建。

# 安装 vue-cli
$ npm install –global vue-cli
# 根据模板项目创建本地项目,目前为内网地址
$ vue init ‘bitbucket:xxx.meituan. com:hfe/mpvue-quickstart’ –clone my- project
# 安装依赖和启动自动构建
$ cd my-project
$ npm install
$ npm run dev

执行完上述命令,在当前项目的 dist 子目录将构建出小程序目标代码,使用小程序开发者工具载入 dist 目录即可启动本地调试和预览。示例项目遵循 Vue.js 模板项目规范,通过Vue.js 命令行工具vue-cli创建。代码组织形式与 Vue.js 官方实例保持一致,我们为小程序定制了 Vue.js runtime 和 webpack 加载器,此部分依赖也已经内置到项目中。

针对小程序开发中常见的两类代码复用场景,mpvue 框架为开发者提供了解决思路和技术支持,开发者只需要在此指导下进行项目配置和改造。我们内部实践了一个将 H5 转换为小程序的项目,下图为使用 mpvue 框架的转换效果:

%title插图%num

图2: H5 和小程序转换效果
将小程序转换为H5:直接使用 Vue.js 规范开发小程序,代码本身与H5并无不同,具体代码差异会集中在平台 Api 部分。此外并不需明显改动,改造主要分如下几部分:

将小程序平台的 Vue.js 框架替换为标准 Vue.js
将小程序平台的 vue-loader 加载器替换为标准 vue-loader
适配和改造小程序与 H5 的底层 Api 差异
将H5转换为小程序:已经使用 Vue.js 开发完 H5,我们需要做的事情如下:

将标准 Vue.js 替换为小程序平台的 Vue.js 框架
将标准 vue-loader 加载器替换为小程序平台的 vue-loader
适配和改造小程序与 H5 的底层 Api 差异
根据小程序开发平台提供的能力,我们*大程度的支持了 Vue.js 语法特性,但部分功能现阶段暂时尚未实现。

%title插图%num

表1: mpvue 暂不支持的语法特性
项目转换注意事项:框架的目标是将小程序和 H5 的开发方式通过 Vue.js 建立关联,达到*大程度的代码复用。但由于平台差异的客观存在(主要集中在实现机制、底层Api 能力差异),我们无法做到代码 100% 复用,平台差异部分的改造成本无法避免。对于代码复用的场景,开发者需要重点思考如下问题并做好准备:

尽量使用平台无的语法特性,这部分特性无需转换和适配成本
避免使用不支持的语法特性,譬如 slot, filter 等,降低改造成本
如果使用特定平台 Api ,考虑抽象好适配层接口,通过切换底层实现完成平台转换
mpvue *佳实践
在表2中,我们对微信小程序、mpvue、WePY 这三个开发框架的主要能力和特点做了横向对比,帮助大家了解不同框架的侧重点,结合业务场景和开发习惯,确定技术方案。对于如何更好地使用 mpvue 进行小程序开发,我们总结了一些*佳实践。

使用 vue-cli 命令行工具创建项目,使用Vue 2.x 的语法规范进行开发
避免使用框架不支持的语法特性,部分 Vue.js语法在小程序中无法使用,尽量使用 mpvue 和 Vue.js 共有特性
合理设计数据模型,对数据的更新和操作做到细粒度控制,避免性能问题
合理使用组件化开发小程序,提高代码复用率

%title插图%num
表2: 框架使用特点对比
结语
mpvue 框架已经在业务项目中得到实践和验证,目前正在美团点评内部大范围使用。mpvue 来源于开源社区,饮水思源,我们也希望为开源社区贡献一份力量,为广大小程序开发者提供一套技术方案。mpvue 的初衷是让 Vue.js 的开发者以低成本接入小程序开发,做到代码的低成本迁移和复用,我们未来会继续扩展现有能力、解决开发者的诉求、优化使用体验、完善周边生态建设,帮助到更多的开发者。

*后,mpvue 基于 Vue.js 源码进行二次开发,新增加了小程序平台的实现,我们保留了跟随 Vue.js 版本升级的能力,由衷的感谢 Vue.js 框架和微信小程序给业界带来的便利。

用Vue开发小程序的框架

为什么是 Vue.js?这取决于团队技术栈选型,引入新的选型对统一技术栈和提高开发效率相悖,有违开发工具服务业务的初衷。

mpVue 的演进

mpVue 的形成,来源于业务场景和需求,*终方案的确定,经历了三个阶段。

  • *阶段:我们实现了一个视图层代码转换工具,旨在提高代码首次开发效率。通过将 H5 视图层代码转换为小程序代码,包括 HTML 标签映射、Vue.js 模板和样式转换,在此目标代码上进行二次开发。我们做到了有限的代码复用,但组件化开发和小程序学习成本并未得到有效改善。
  • 第二阶段:我们着眼于完善代码组件化机制。参照 Vue.js 组件规范设计了代码组织形式,通过代码转换工具将代码解析为小程序。转换工具主要解决组件间数据同步、生命周期关联和命名空间问题。*终我们实现了一个 Vue.js 语法子集,但想要实现更多特性或跟随 Vue.js 版本迭代,工作量变得难以估计,有永无止境之感。
  • 第三阶段:我们的目标是实现对 Vue.js 语法全集的支持,达到使用 Vue.js 开发小程序的目的。并通过引入 Vue.js RunTime 实现了对 Vue.js 语法的支持,从而避免了人肉语法适配。至此,我们完成了使用 Vue.js 开发小程序的目的。较好地实现了技术栈统一、组件化开发、多端代码复用、降低学习成本和提高开发效率的目标。

mpVue 设计思路

Vue.js 和小程序都是典型的逻辑视图层框架,逻辑层和视图层之间的工作方式为:数据变更驱动视图更新;视图交互触发事件,事件响应函数修改数据再次触发视图更新,如图 1 所示。

%title插图%num

图 1 小程序实现原理

鉴于 Vue.js 和小程序一致的工作原理,我们思考将小程序的功能托管给 Vue.js,在正确的时机将数据变更同步到小程序,从而达到开发小程序的目的。这样,我们可以将精力聚焦在 Vue.js 上,参照 Vue.js 编写与之对应的小程序代码,小程序负责视图层展示,所有业务和逻辑收敛到 Vue.js 中,Vue.js 数据变更后同步到小程序,如图 2 所示。如此一来,我们就获得了以 Vue.js 的方式开发小程序的能力。为此,我们设计的方案如下:

%title插图%num

图 2 mpVue 实现原理

Vue 代码:

  1. 将小程序页面编写为 Vue.js 实现;
  2. 以 Vue.js 开发规范实现父子组件关联。

小程序代码:

  1. 以小程序开发规范编写视图层模板;
  2. 配置生命周期函数,关联数据更新调用;
  3. 将 Vue.js 数据映射为小程序数据模型。

并在此基础上,附加如下机制:

  1. Vue 实例与小程序 Page 实例建立关联;
  2. 小程序和 Vue 生命周期建立映射关系,能在小程序生命周期中触发 Vue 生命周期;
  3. 小程序事件建立代理机制,在事件代理函数中触发与之对应的 Vue 组件事件响应。

这套机制总结起来非常简单,但实现却相当复杂。在揭秘具体实现之前,读者可能会有这样一些疑问:

  1. 要同时维护 Vue.js 和小程序,是否需要写两个版本的代码实现?
  2. 小程序负责视图层展现,Vue.js 的视图层是否还需要,如果不需要应该如何处理?
  3. 生命周期如何打通,数据同步更新如何实现?

上述问题包含了 mpVue 框架的核心内容,下文将仔细为你道来。首先,mpVue 为提高效率而生,本身提供了自动生成小程序代码的能力,小程序代码根据 Vue.js 代码构建得到,并不需要同时开发两套代码。

Vue.js 视图层渲染由 Render 方法完成,同时在内存中维护着一份虚拟 DOM,mpVue 无需使用 Vue.js 完成视图层渲染,因此我们改造了 Render 方法,禁止视图层渲染。熟悉源代码的读者都知道 Vue RunTime 有多个平台的实现,除了我们常见的 Web 平台,还有 Weex。从现在开始,我们增加了新的平台 mpVue。

再看第三个问题,生命周期和数据同步是 mpVue 框架的灵魂,Vue.js 和小程序的数据彼此隔离,各自有不同的更新机制。mpVue 从生命周期和事件回调函数切入,在 Vue.js 触发数据更新时实现数据同步。小程序通过视图层呈现给用户、通过事件响应用户交互,Vue.js 在后台维护着数据变更和逻辑。可以看到,数据更新发端于小程序,处理自 Vue.js,Vue.js 数据变更后再同步到小程序。为实现数据同步,mpVue 修改了 Vue.js RunTime 实现,在 Vue.js 的生命周期中增加了更新小程序数据的逻辑。

而用户交互触发的数据更新则是通过事件代理机制完成。在 Vue.js 代码中,事件响应函数对应到组件的 method 方法,Vue.js 自动维护了上下文环境。然而在小程序中并没有类似的机制,又因为 Vue.js 执行环境中维护着一份实时的虚拟 DOM,这与小程序的视图层完全对应。我们思考,在小程序组件节点上触发事件后,只要找到虚拟 DOM 上对应的节点,触发对应的事件不就完成了么。Vue.js 事件响应如果触发了数据更新,其生命周期函数更新将自动触发,在此函数上同步更新小程序数据,数据同步就实现了。

mpVue 如何使用

mpVue 框架本身由多个 npm 模块构成,入口模块已经处理好依赖关系,开发者只需要执行如下代码即可完成本地项目创建。

安装 vue-cli

$ npm install –global vue-cli

根据模板项目创建本地项目,目前为内网地址,暂稳开放

$ vue init ‘bitbucket:xxx.meituan.com:hfe/mpvue-quickstart’ –clone my-project

安装依赖和启动自动构建

$ cd my-project

$ npm install

$ npm run dev

执行完上述命令,在当前项目的 dist 子目录将构建出小程序目标代码,使用小程序开发者工具载入 dist 目录即可启动本地调试和预览。

示例项目遵循 Vue.js 模板项目规范,通过 Vue.js 命令行工具 vue-cli 创建。代码组织形式与 Vue.js 官方实例保持一致,我们为小程序定制了 Vue.js RunTime 和 Webpack 加载器,此部分依赖也已经内置到项目中。

针对小程序开发中常见的两类代码复用场景,mpVue 框架为开发者提供了解决思路和技术支持,开发者只需要在此指导下进行项目配置和改造。

将小程序转换为 H5

直接使用 Vue.js 规范开发小程序,代码本身与 H5 并无不同,具体代码差异会集中在平台 API 部分。此外无需明显改动,改造主要分以下几个部分:

  1. 将小程序平台的 Vue.js 框架替换为标准 Vue.js;
  2. 将小程序平台的 Vue-loader 加载器替换为标准 Vue-loader;
  3. 适配和改造小程序与 H5 的底层 API 差异。

将 H5 转换为小程序

已经使用 Vue.js 开发完 H5,则需要完成以下事宜:

  1. 将标准 Vue.js 替换为小程序平台的 Vue.js 框架;
  2. 将标准 Vue-loader 加载器替换为小程序平台的 Vue-loader;
  3. 适配和改造小程序与 H5 的底层 API 差异。

根据小程序开发平台提供的能力,我们*大程度地支持了 Vue.js 语法特性,但部分功能现阶段暂时尚未实现,具体见表 1。

%title插图%num

表 1 mpVue 暂不支持的语法特性

mpVue 框架的目标是将小程序和 H5 的开发方式通过 Vue.js 建立关联,达到*大程度的代码复用。但由于平台差异的客观存在(主要集中在实现机制、底层 API 能力差异),我们无法做到代码 100%复用,平台差异部分的改造成本无法避免。对于代码复用的场景,开发者需要重点思考如下问题并做好准备:

  1. 尽量使用平台无的语法特性,这部分特性无需转换和适配成本;
  2. 避免使用不支持的语法特性,譬如 slot、filter 等,降低改造成本;
  3. 如果使用特定平台 API,考虑抽象好适配层接口,通过切换底层实现完成平台转换。

mpVue *佳实践

在表 2 中,我们对微信小程序、mpVue、WePY 这三个开发框架的主要能力和特点做了横向对比,帮助大家了解不同框架的侧重点,结合业务场景和开发习惯,确定技术方案。对于如何更好地使用 mpVue 进行小程序开发,我们总结了一些*佳实践。

%title插图%num

表 2 框架主要能力及特性对比

  1. 使用 vue-cli 命令行工具创建项目,使用 Vue 2.x 的语法规范进行开发;
  2. 避免使用不框架不支持的语法特性,即有部分 Vue.js 语法在小程序中无法使用,尽量 mpVue 和 Vue.js 共有特性;
  3. 合理设计数据模型,对数据的更新和操作做到细粒度控制,避免性能问题;
  4. 合理使用组件化开发小程序,提高代码复用。

结语

mpVue 框架已经在业务项目中得到实践和验证,目前开发文档也已经就绪,正在做开源前的*后准备,希望能够为小程序和 Vue.js 生态贡献一份力量。mpVue 的初衷是希望让 Vue.js 的开发者以低成本接入小程序开发,其能力和使用体验还有待进一步的检验。我们未来会继续扩展现有功能、解决用户的问题和需求、优化开发体验、完善周边生态建设,以帮助到更多的开发者。

需要说明一下,mpVue 是通过 fork Vue.js 源码进行二次开发,新增加了 mp 平台的 Vue.js 实现,我们保留了跟随 Vue.js 版本升级的能力,希望未来能够实现更好的能力增强,*后感谢 Vue.js 框架和微信小程序对业界带来的便利

WORD中编辑页码方法大全

1.把光标定位在第1页末,执行“插入/分隔符/下一页/确定”命令;

2.执行“视图/页眉和页脚”命令,分别将光标定位在页脚,单击“页眉/页脚”工具栏上的“链接到前一个”按钮;

3.执行“插入/页码”命令;

4.在“页码”对话框中,单击“格式”按钮;

5.在“页码格式”对话框中,“起始页码”设置为“1”;

6.单击“确定”、“确定”完成页码插入。

 

(二)在Word文档中怎么自动生成目录

(1)转入大纲视图:视图—大纲。

(2)把光标放置在每一个需要记录到目录的标题前面,然后设置每个标题的级别(在任务栏)。

(4)切换到页面视图:视图—页面。

(5)把光标放到目录页。然后:插入—引用—索引和目录—目录—确定,即可生成目录。

(6)如果标题或页码发生了变化,可以在目录页的任意处点击右键,更新域就可以了。

 

(三)

问:WORD里边怎样设置每页不同的页眉?如何使不同的章节显示的页眉不同?

答:分节,每节可以设置不同的页眉。文件——页面设置——版式——页眉和页脚——首页不同

问:请问word中怎样让每一章用不同的页眉?怎么我现在只能用一个页眉,一改就全部改了?

答:在插入分隔符里,选插入分节符,可以选连续的那个,然后下一页改页眉前,按一下“同前”钮,再做的改动就不影响前面的了。简言之,分节符使得它们独立了。这个工具栏上的“同前”按钮就显示在工具栏上,不过是图标的形式,把光标移到上面就显示出”同前“两个字来了

问:如何合并两个WORD文档,不同的页眉需要先写两个文件,然后合并,如何做?

答:页眉设置中,选择奇偶页不同/与前不同等选项

问:WORD编辑页眉设置,如何实现奇偶页不同? 比如:单页浙江大学学位论文,这一个容易设;双页:(每章标题),这一个有什么技巧啊?

答:插入节分隔符,与前节设置相同去掉,再设置奇偶页不同

问:怎样使WORD文档只有*页没有页眉,页脚?

答:页面设置-页眉和页脚,选首页不同,然后选中首页页眉中的小箭头,格式-边框和底纹,选择无,这个只要在“视图”——“页眉页脚”,其中的页面设置里,不要整个文档,就可以看到一个“同前”的标志,不选,前后的设置情况就不同了。

问:如何从第三页起设置页眉?

答:在第二页末插入分节符,在第三页的页眉格式中去掉同前节,如果*、二页还有页眉,把它设置成正文就可以了

●在新建文档中,菜单—视图—页脚—插入页码—页码格式—起始页码为0,确定;

●菜单—文件—页面设置—版式—首页不同,确定;

●将光标放到*页末,菜单—文件—页面设置—版式—首页不同—应用于插入点之后,确定。

第2步与第三步差别在于第2步应用于整篇文档,第3步应用于插入点之后。这样,做两次首页不同以后,页码从第三页开始从1编号,完成。

问:WORD页眉自动出现一根直线,请问怎么处理?

答:格式从“页眉”改为“清除格式”,就在“格式”快捷工具栏*左边;选中页眉文字和箭头,格式-边框和底纹-设置选无

问:页眉一般是———,上面写上题目或者其它,想做的是把这根线变为双线,WORD中修改页眉的那根线怎么改成双线的?

答:按以下步骤操作去做:

●选中页眉的文字,包括*后面的箭头

●格式-边框和底纹

●选线性为双线的

●在预览里,点击左下小方块,预览的图形会出现双线

●确定

▲上面和下面自己可以设置,点击在预览周围的四个小方块,页眉线就可以在不同的位置

 

 

在Word文档中设置页码并不是一件难事。首先打开“插入”菜单并选定“页码”,从“页码”对话框中选择需要的选项。页码虽小,但编辑起来却比较困难。由于页码是强行插入的,很难像文档那样进行编辑。怎样才能对文档中的页码进行编辑呢?以下简便的操作方法不妨一试。 

一、如何让页码靓起来

1.在菜单栏依次点击“插入”、“页码”,弹出“页码”对话框,根据需要选好位置与对齐方式,按下“页码”对话框的“确定”按钮,文件页码已经设好。但这时的页码只是孤零零的一个数字,如果想让你的页码变靓,还需进一步的加工。

2.在文件的任一页码 处双击鼠标左键,弹出“页眉页脚”对话框,单击页码处,你会发现页码被一“斜线框”包围,这时你就可以对“页码”进行编辑了,把你喜爱的装饰符号填入斜线 框内(注意:不要随便改动页码,只需填入符号即可),按下“页眉页脚”对话框的“关闭”按钮。文件的所有页码都出现了所填的符号,变得更靓了。

二、如何实现一页纸内设置两个页码

曾经遇到过这样的尴尬,打印了一份材料,想把它排成32开本双面印,由于选择B5纸型分栏法编辑,无法在一页纸内设置两个页码。那么,如何才能在一页纸内设置两个页码呢?

1.在菜单栏依次点击 “文件”、“页面设置”,弹出“页面设置”对话框,选择“纸型”标签设置纸张类型,选择好比要求纸张大一倍的纸型(32开本选择16开纸型,16开本选择 8开纸型,依次类推,也可设为自定义纸张),方向设为横向;再选择“页边距”标签设置页边距,选中“拼页”复选框,这时页边距显示为上、下、内侧、外侧, 根据自己的需要设置好页边距,按下“页面设置”对话框的“确定”按钮。

2.根据自己的需要,按照正常插入页码的方法,给文件加上页码,打印输出后,一页纸内就出现了两个页码(注意:打印纸必须符合所选择的纸型)。

三、如何实现一页纸内打印两个不连续的页码

1.依照方法二所讲,设好页面设置与页码。

2.根据需要,任意“ 拼页”。如,想把第32页与第1页打印在一张纸上(你的文件必须够长),你就可以这样做:在菜单栏依次点击“文件”、“打印”,弹出“打印”对话框,选中 “页码范围”单选框,输入“32,1”,中间用半角逗号隔开,按下“打印”对话框的“确定”按钮,输出后,就可以看到一张纸上设有两个页码了。如果你想一 次输出多张,可以在输入页码数值时,按照所拼页依次输入(如32,1, 2,29,28,3……)。当然,你可以不拘一格,任意组合(注意:进行拼页时, 先输入的页码在左,后输入的页码在右)。

四、如何在一个文档内编辑多个文件的页码设置

一般在一个文档内只编 辑一个文件,这样的文件设置页码比较简单,只要插入页码,就会按照顺序依次排列。如果一个文档内编辑多个文件,插入页码时,也会按顺序依次排列。这样,第 二个文件及其以下文件的页码就会与*个文件的页码依次顺排,而不是从*页开始重新排页。如何才能实现每一个文件都从*页开始排列页码呢?

1.在每一个文件结束处插入一个分节符。具体操作为:在菜单栏点击“插入”、“分隔符”,弹出“分隔符”对话框,“分节符类型”设为“下一页”,按下“分隔符”对话框的“确定”按钮。

2.选中要设置页码的 文件,然后在菜单栏点击“插入”、“页码”,弹出“页码”对话框,点击“格式”,弹出“页码格式”对话框,把“页码编排”下的“起始页码”设为“1”,依 次按下“页码格式”对话框与“页码”对话框的“确定”按钮。所选中文件的页码就会从第1页开始编排。

五、如何删除已插入的页码

我们给文件加入页码以 后,有时需要把它去掉,如何才能去掉已经插入的页吗呢?双击文件的任一页码,弹出“页眉页脚”对话框,点击页码所在处,出现一“斜线框”包围的页码,按 Delete键,看见页码被删除后,按下“页眉页脚”对话框的“关闭“按钮,整个文档的页码都被删除了。

如何去掉分隔符

2008-05-25 23:37

 

 

分节符是我们在编辑文档页面格式时经常使用的一种格式标记,使用它可以很方便地将一篇文档分成若干小节分别进行页面的设置。但是当我们把分节符插入到文档中去以后,又想把它删除怎么办呢?

要知道,在通常我们所使用的页面视图或大纲视图中是看不到分节符的。其实只要单击“常用”工具栏上的〔显示/隐藏编辑标记〕按钮(如图1)就可以把它显示出来了。然后将鼠标定位于分节符前或选中分节符,按下“Del”键就可以了。

 

%title插图%num

图1

如果您的“常用”工具栏中看不到这个按钮,可以点击“常用”工具栏*右侧向下的三角箭头,然后选择“添加或删除按钮→常用”,并在展开的列表中选中“显示/隐藏编辑标记”就可以把它添加到工具栏中了。

%title插图%num

图2

以上操作在Word 2003中可以轻松实现。如果Word版本较低,可能在列表中找不到“显示/隐藏编辑标记”命令。只要点击“工具→自定义”菜单命令,打开“自定义”对话 框。点击“命令”选项卡,在左侧的“类别”列表中选择“视图”,在右侧的“命令”列表中选中“全部显示”,再将此命令拖到“常用”工具栏中就行了

word—工具—选项—视图—格式标记—可选分隔符。把勾去掉。

深入理解JavaScript系列:设计模式之构造函数模式

介绍

构造函数大家都很熟悉了,不过如果你是新手,还是有必要来了解一下什么叫构造函数的。构造函数用于创建特定类型的对象——不仅声明了使用的对象,构造函数还可以接受参数以便*次创建对象的时候设置对象的成员值。你可以自定义自己的构造函数,然后在里面声明自定义类型对象的属性或方法。

基本用法

在JavaScript里,构造函数通常是认为用来实现实例的,JavaScript没有类的概念,但是有特殊的构造函数。通过new关键字来调用定义的否早函数,你可以告诉JavaScript你要创建一个新对象并且新对象的成员声明都是构造函数里定义的。在构造函数内部,this关键字引用的是新创建的对象。基本用法如下:

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= function () {
return this.model + "走了" + this.miles + "公里";
    };
}

var tom= new Car("大叔", 2009, 20000);
var dudu= new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());

上面的例子是个非常简单的构造函数模式,但是有点小问题。首先是使用继承很麻烦了,其次output()在每次创建对象的时候都重新定义了,*好的方法是让所有Car类型的实例都共享这个output()方法,这样如果有大批量的实例的话,就会节约很多内存。

解决这个问题,我们可以使用如下方式:

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= formatCar;
}

function formatCar() {
return this.model + "走了" + this.miles + "公里";
}

这个方式虽然可用,但是我们有如下更好的方式。

构造函数与原型

JavaScript里函数有个原型属性叫prototype,当调用构造函数创建对象的时候,所有该构造函数原型的属性在新创建对象上都可用。按照这样,多个Car对象实例可以共享同一个原型,我们再扩展一下上例的代码:

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}

/*
注意:这里我们使用了Object.prototype.方法名,而不是Object.prototype
主要是用来避免重写定义原型prototype对象
*/
Car.prototype.output= function () {
return this.model + "走了" + this.miles + "公里";
};

var tom = new Car("大叔", 2009, 20000);
var dudu = new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());

这里,output()单实例可以在所有Car对象实例里共享使用。

另外:我们推荐构造函数以大写字母开头,以便区分普通的函数。

只能用new吗?

上面的例子对函数car都是用new来创建对象的,只有这一种方式么?其实还有别的方式,我们列举两种:

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
// 自定义一个output输出内容
    this.output = function () {
return this.model + "走了" + this.miles + "公里";
    }
}

//方法1:作为函数调用
Car("大叔", 2009, 20000);  //添加到window对象上
console.log(window.output());

//方法2:在另外一个对象的作用域内调用
var o = new Object();
Car.call(o, "Dudu", 2010, 5000);
console.log(o.output());

该代码的方法1有点特殊,如果不适用new直接调用函数的话,this指向的是全局对象window,我们来验证一下:

//作为函数调用
var tom = Car("大叔", 2009, 20000);
console.log(typeof tom); // "undefined"
console.log(window.output()); // "大叔走了20000公里"

这时候对象tom是undefined,而window.output()会正确输出结果,而如果使用new关键字则没有这个问题,验证如下:

//使用new 关键字
var tom = new Car("大叔", 2009, 20000);
console.log(typeof tom); // "object"
console.log(tom.output()); // "大叔走了20000公里"

强制使用new

上述的例子展示了不使用new的问题,那么我们有没有办法让构造函数强制使用new关键字呢,答案是肯定的,上代码:

function Car(model, year, miles) {
if (!(this instanceof Car)) {
return new Car(model, year, miles);
    }
this.model = model;
this.year = year;
this.miles = miles;
this.output = function () {
return this.model + "走了" + this.miles + "公里";
    }
}

var tom = new Car("大叔", 2009, 20000);
var dudu = Car("Dudu", 2010, 5000);

console.log(typeof tom); // "object"
console.log(tom.output()); // "大叔走了20000公里"
console.log(typeof dudu); // "object"
console.log(dudu.output()); // "Dudu走了5000公里"

通过判断this的instanceof是不是Car来决定返回new Car还是继续执行代码,如果使用的是new关键字,则(this instanceof Car)为真,会继续执行下面的参数赋值,如果没有用new,(this instanceof Car)就为假,就会重新new一个实例返回。

原始包装函数

JavaScript里有3中原始包装函数:number, string, boolean,有时候两种都用:

// 使用原始包装函数
var s = new String("my string");
var n = new Number(101);
var b = new Boolean(true);


// 推荐这种
var s = "my string";
var n = 101;
var b = true;

推荐,只有在想保留数值状态的时候使用这些包装函数,关于区别可以参考下面的代码:

// 原始string
var greet = "Hello there";
// 使用split()方法分割
greet.split(' ')[0]; // "Hello"
// 给原始类型添加新属性不会报错
greet.smile = true;
// 单没法获取这个值(18章ECMAScript实现里我们讲了为什么)
console.log(typeof greet.smile); // "undefined"

// 原始string
var greet = new String("Hello there");
// 使用split()方法分割
greet.split(' ')[0]; // "Hello"
// 给包装函数类型添加新属性不会报错
greet.smile = true;
// 可以正常访问新属性
console.log(typeof greet.smile); // "boolean"

总结

本章主要讲解了构造函数模式的使用方法、调用方法以及new关键字的区别,希望大家在使用的时候有所注意。

快速学习android开发

因为项目需要,8月中旬开始决定做安卓的程序,所以马上就开始学习安卓方面的开发知识,把*近的学习实践经历和大家分享分享。不要一开始就下载一大堆资料,视频,然后就不知道做什么了,要给自己定个目标,我要做什么?我怎么达到目标?

我不懂java,但是懂C#和C++,所以我没主张去单独学习java语言,如果你是个**初的新手,没啥语言基础,那你必须先看看java语言,不要很详细看,因为学习Android中,你也是在学习java。

1. 明确目标

没有目标的学习,会感觉到后面没什么成果,在1年前,我也打算学习android开发的,但是目的就是学习,到网上去下载很多学习的视频,然后把开发环境搭建起来,能把Helloworld运行起来,能打些log,Activity之间也能互相切换了,但是后面也就不了了之了,因为不知道学了要干什么。依葫芦画瓢的做了几个例子,因为里面的问题都是已经解决的,所以也没能深入的系统学习。

这次因为产品的需要,要做Android版本,要做的东西一开始就已经设计好了,见摇摇2选1安卓版本,刚开始也不知道里面有些什么技术难度,但是要做的目标已经明确了,而且也没有现成的,碰到问题就查资料,慢慢地解决,这样有的放矢,学习的效果非常好。既有现成的技术可以使用,又有些技术,需要查比较多的资料,这样记忆就比较深刻,所掌握的知识也比较系统。

接下来的一系列文章,我会把在开发摇摇2选1中遇到的问题,给大家详细讲讲,程序虽然小,但是五脏俱全,做Demo和做产品的要求完全不是一个级别,如果Android大牛感觉知识讲的比较浅,那可以绕道,毕竟我是从一个完全的新手开始的。

 

2. 了解安卓开发中比较困难的地方

学习一个新平台,就要知道此平台开发要面临的困难有哪些,不要做到*后,这些问题没有考虑,那就比较糟糕了。在网上搜索了下,安卓开发困难总结如何:

1)安卓系统版本比较多,各版本之间的兼容性是个问题,此为系统碎片。

2)安卓设备千变万化,设备难以统一,每个产品都成为独立,分散的Android碎片。

3)分辨率五花八门。一个产品,可能需要多个界面排版,人工消耗比较大。

%title插图%num

看到这张图,有没有头疼的感觉?

总结成一句话:Android的碎片化真是要来开发者的命。

 

3. 搭建开发环境

“巧妇难为无米之炊”,开发环境肯定是*件要做的事情,这类的文章已经很多了,我也不多说了,多说也就比较无聊了。感谢吴秦,也是博客园里的一员,他写的很详细了,见这里。

 

4. 查看网友总结的一些经验。

不是什么都查看,开发中遇到什么问题,就去查看什么问题,这样你查到的知识,马上就能深入的实践,这样知识就巩固了。

1)首先当然要看Android的开发文档,里面其实大部分的知识都有了,还有就是SDK自带的Samples。

2)博客园里搜索“Android开发”,会出来一大把,很多网友都是很系统的讲解了。

3)eoe android社区,里面有很多网友上传了现成的demo代码,里面很多都是模仿现在流行的产品的界面开发,很是不错。

 

5. 掌握调试方法

个人一直认为,调试技巧是开发中*重要的技能,如果调试技能比较差,不知道如何查找问题,那不会是个好的程序员,其技能也不会高到哪里去。

 Android做下来,感觉调试这块做的很不错了,这要感谢Eclipse IDE做的比较不错,但是Android的界面排版部分,真的不敢恭维,Eclipse时间用长了,占用内存真是大。有哪位高手能否告诉我下,你们界面排版是用什么的?

Android里,如果程序出了问题,有些是一下跟踪不到的,这时就要用到Log类了,Log类使用很简单,就不多说了。
 自己一个很有感触的经验,如果你真的打算做Android开发,那就买一个设备,这样能大大提高开发效率,模拟器有时用起来真是麻烦,而且开发的设备不要很好的,只要设备分辨率是主流的,传感器的支持*好全点就够了,国产的手机里,华为的还不错,或者到淘宝上去买个二手的,投资不大。真的买了投资,可能也会增加你学习Android的决心。

 

6. 程序框架

经过10多年的经历,认识到程序的框架对开发产品真的是起到举足轻重的作用。一个好的产品,如果有个好的程序框架,那真是事半功倍,可能开始所做的工作会比较多,很难看到令人激动的成果,但是相信我,*对值得。一开始,我也是比较注重程序界面开发,很快就开发出界面来,而且着实比较激动,因为有东西出来了,但是后面的事情,真是令人发狂,想从头再来,太迟了,离计划的时间比较近了,时间不允许,只有硬着头皮往上打补丁,硬着交付*个版本,第二个版本或者后面的版本,迟早是要重来的。所以在后面带领团队开发的时候,在开发前,都是要讨论程序框架,留出很大部分时间进行程序框架开发,及在开发中不断的改进。

刚接触Android,不知道用什么程序框架,所以查了很多资料,一开始看到一个程序框架,感觉很不错,所以一开始就使用了下面这个程序框架。

%title插图%num

开始这个框架用的蛮爽,简单介绍一下,主UI线程如果要做一个比较长时间的任务,创建一个任务,发送到任务队列中去,后台的Service不断的从任务队列中去取任务,然后交给线程处理,线程处理完后,通过消息(Message)发送到Handler里处理,然后Handler的handleMessage函数里回调到UI主线程中去。

此框架的缺点是比较麻烦。

1)首先这里有个Observer模式,每个Activity都要实现一个接口,然后注册到后台服务中去,这样后台服务才能把处理的结果回调到主线程中。

2) 每个事件都要组成一个任务,发送到任务队列。

3)后台线程先要取任务,然后处理,然后通过消息切换到主线程,然后又回到到相应的Activity中,中间有个3次判断当前是什么任务。

4)不能并非,任务只能一个接一个,虽然这个一般不会造成瓶颈。

因为摇摇2选1应用比较简单,所以后来感觉只要使用线程和Handler,通过消息就能处理,然后就查资料,发现已经有比较多的文章描述了这种方法,只是没提出这是一个框架,但是对摇摇2选1,我感觉已经足够了。下面就是主代码,分布在每个Activity中:

private void TaskThread()

{

Thread taskThread = new Thread()

{

@Override public void run()

{

//做耗时的任务

Message msg = Message.obtain();

msg.what = 1;

uiCallback.sendMessage(msg);

}

};

taskThread .start();

}

private Handler uiCallback = new Handler()

{

@Override public void handleMessage(Message msg)

{

if(msg.what == 1)

{

//在主线程中处理结果

}

}

};

 够用就好,至少我现在用着很爽。

 

7. 程序国际化

互联网让地球成为一个村,所以一开始也需要了解一下程序是如何做国际化版本的。不要一开始字符串满天飞,整个程序中都存在hard code,这样做,迟早是要吃亏的。

 

8.产品升级机制

一个方便的产品升级机制,对产品的成长很重要,android是一个开放平台,在这点上比iOS做的好的多,产品升级很方便。

 

此文章的目的就是对想学Android开发的人一个指引,因为我也就是这么走过来的,加上自己在软件开发上的一些经验,让新手少走弯路。有计划,有目标的学习,这是*好的方法。

Groovy基本语法(三)

Groovy中的循环
可以说,Groovy把Java循环结构折腾的面目全非,似乎主流脚本语音能用到的循环方式Groovy都给加了进去,实际上不同的循环语法和功能差异很小,这类玩意儿多了意义并不明显,例如我就比较喜欢for循环而不太爱用while循环,但并不是说while循环没有用处,虽然for循环完全可以涵盖while循环的所有功能。
Groovy的开发者目标很明确,让Groovy的语法甜的腻死你们!

package plswg.edu.groovy.basic

// groovy中可以使用传统的while循环或for循环

n = 0
while (n < 10) {
print n + ” ”
n++
}
println “”

for (i = 0; i < 10; i++) {
print i + ” ”
}
println “\r\n”

// groovy引入新风格的for循环

for (i in 0 .. 9) { // (i in 0 .. 9)表示循环从i=0开始,到i<=9时结束
print i + ” ”
}
println “”

// 利用数值类型可以也可以进行循环

// 利用整数的upto方法进行循环
i = 0
i.upto(9) { // 整数的upto方法可以启动一个循环, 循环体在一个闭包内执行
print it + ” ” // 在闭包内部,it关键字表示循环过程的整数值
}
println “; at the end of loop, the var i is ” + i // 循环结束后i的值仍是0,不会增加到9

// upto方法可以用在常量上
10.upto(19) { // 10.upto(20)表示从10开始循环到19结束
print it + ” ”
}
println “”

// upto方法中的整数是循环的起始点,time方法可以将整数作为循环的终止点
10.times { // 10.times表示从0开始循环到9结束
print it + ” ”
}
println “”

// step方法可以设置循环的步长值
0.step(11, 2) { // 0.step(11, 2)表示从0循环到11,每次循环值增加2
print it + ” ”
}
println “\r\n”

// 例子,利用groovy新语法输出一个99乘法表
// 方法1
for (i in 1 .. 9) {
for (j in 1 .. 9) {
z = i * j
print ((z < 10 ? ” ” + z : z) + ” “)
}
println “”
}
// 方法2
1.upto(9) {
x = it
1.upto(9) {
z = x * it
print ((z < 10 ? ” ” + z : z) + ” “)
}
println “”
}
// 方法3
9.times {
x = it + 1
9.times {
z = x * (it + 1)
print ((z < 10 ? ” ” + z : z) + ” “)
}
println “”
}
可以看到,Groovy对Java循环结构作了如下的修整:
对于for循环:除了传统三表达式的for循环和用于迭代的for each循环外,Groovy允许for循环遍历一个范围(Range),例如 for (i in 1..10),表示循环10次,i在1至10之间取值;
对于整数,Groovy增加了如下几个方法来进行循环:
upto:n.upto(m) 函数,表示循环 m- n 次,并且会有一个循环变量it,从n开始,每次循环增加1,直到m。循环体写在upto方法之后大括号中,表示一个闭包,在闭包中,it作为循环变量,值从a增长到n;
times:n.times 函数,表示循环n次,循环变量it从0开始到n结束。
step:n.step(x, y) 函数,表示循环变量从n开始到x结束,每次循环后循环变量增加y,所以整个循环次数为 (x – n) / y 次;
6、字符串

Groovy对字符串增加了一些新的特性,让字符串操作更加简单了:

package plswg.edu.groovy.basic

// groovy中对字符串的使用做了大量的简化

// 获取字符串中的字符
s = “Hello”
println s[0] // 输出’H’

// 遍历字符串中的所有字符
s.each {
print it + “, ” // 遍历字符串中的所有字符
}
println “”

// 截取字符串
s1 = s[1..3] // 截取s字符串标号从1到3的3个字符,组成新的字符串赋予s1
// 该语法是String类的substring方法的简化
println s1

// 模板式字符串

n = 100
s1 = “The number n is ${n}” // ${n}表示将变量n的值放在字符串该位置
println s1

// 带格式的长字符串
// “””和”””之间的所有字符都会被算做字符串内容,包括// /*以及回车,制表符等
s = “””
大家好
欢迎大家学习Groovy编程
Groovy is a better Java
“””
println s

// groovy中单引号的作用

// 在不定义类型时,单引号也表示字符串
c1 = ‘A’
println c1.getClass().getName()

// 要明确的定义字符类型,需要给变量增加定义
char c2 = ‘A’
println c2.getClass().getName()

// 取消转义字符
s = ‘c:\\windows\\system’
println s
s = /c:\windows\system/ // 利用/字符串/定义的字符串
println s

// 字符串运算
s = “hello”
s = s + ” world” // +运算符用于连接字符串
println s

s -= “world” // -可以从字符串中去掉一部分
println s

s = s * 2 // *可以让字符串重复n次
println s

// 字符串比较
s1 = “Abc”
s2 = “abc”

println s1 == s2 ? “Same” : “Different” // 执行s1.equals(s2)
println s1 != s2 ? “Different” : “Same” // 执行!s1.equals(s2)
println s1 > s2 ? “Great” : “Less” // 执行s1.compareTo(s2) > 0
println s1 < s2 ? “Less” : “Great” // 执行s1.compareTo(s2) < 0
// s1 >= s2, s1 <= s2
println s1 <=> s2 == 1 ? “Same” : “Different” // 执行s1.compareTo(s2)
Groovy增加了对字符串的如下操作:
集合操作,Groovy将字符串看为字符的集合,可以通过 [n] 运算符直接访问字符串内的字符,也可以通过each循环遍历字符串的每一个字符;
截取子字符串的substring方法被简化为使用数值范围来进行截取,”hello”[1..3]表示截取字符串”hello”从下标为1到下标为3的部分,结果为 “ell”;
Groovy增加了一个新的字符串类型 GString,这种字符串可以进行格式化,在GString字符串中使用 ${变量},可以将该变量的值放入字符串的相应位置;
带格式的字符串,使用 “””字符串内容”””(连续的三个引号),这种字符串中可以包含直接输入的回车,TAB键,//或/*等字符,而这些在Java原本的字符串里,都必须通过转义字符来表示,例如只能用\n表示回车;
单引号问题,和Javascript和PHP类似,Groovy中无论是单引号还是双引号都表示是字符串类型,例如 ‘a’ 和”a”都是字符串类型,所以如果要确定存储一个 char 类型变量,就必须使用 char 类型定义强类型变量;实际上Groovy认为char类型并不是必须的,大部分时候字符串类型更方便一些;
用 / 包围的字符串,即 /字符串内容/,可以避免在字符串中使用转义字符,但 \n 字符不包含在内;
Java中对字符串的运算只有+运算,在Groovy中,字符串还可以使用 -运算 和 *运算,减法运算可以从一个字符串中删除一部分,乘法运算可以将一个字符串重复n次;
Groovy还为字符串加入了所有关系运算符,包括 ==, !=, >, <, >=, <=,这要归功于Groovy允许运算符重载,对于 == 和 !=,将调用String类的equals 方法,对于 >, >=, <, <=,将调用String类的compareTo方法;Groovy还增加了一个特殊的运算符<=>,这个运算符也会调用 compareTo 方法,返回 compareTo 方法的返回值;
Groovy对字符串增强了很多,使其更加方便使用,也更加符合主流动态语言的语法设定,当然这里面很多内容并不是简单的针对字符串的,例如Groovy的闭包,运算符重载等特性,都侧面的让Groovy字符串更加强大

Groovy基本语法(二)

静态import

我是*早在.net framework 3.5上使用类似import static这种语法的,用起来相当方便的说,但JDK一直没有引入这玩意儿,按道理说加上这么个小功能是很容易实现的。不过Groovy倒是带来来功能更强大的import static语法,用起来也很方便:

package plswg.edu.groovy.basic

// 将Integer类的MAX_VALUE静态成员变量引入
import static Integer.MAX_VALUE

// 将Double类的parseDoubl静态成员方法引入
import static Double.parseDouble

// 给静态引入赋予别名
import static Boolean.TRUE as T // 将Boolean.TRUE取名为T
import static Boolean.FALSE as F // 将Boolean.FALSE取名为F

// 给静态引入的方法赋予别名
import static Calendar.getInstance as now

// 代码中凡是用到MAX_VALUE, 指的都是Integer.MAX_VALUE
println MAX_VALUE

// 代码中凡是用到parseDouble, 指的都是Double.parseDouble
s = “123.123”
println parseDouble(s);

// 代码中凡是使用T的地方表示true,使用F的地方表示false
println T
println !T
println F

// 代码中凡是使用now的地方表示Calendar.getInstance
println now().get(Calendar.HOUR)
这玩意儿的主要作用就是避免重复书写哪些又臭又长的类名,随着JDK类库的不断增加,类名字是越来越长,像造句一样,import static恰好解决了这一问题,还是很贴心的。
5、分支结构语句

Groovy的结构化语句仍旧是三种:顺序结构、分支结构和循环结构,其中分支结构和循环结构增加了很多意想不到的功能,先看分支结构:
package plswg.edu.groovy.basic

// groovy中if条件分支的新规定

// groovy中,非0的数值可以表示true
int num = 0
if (num) {
println “True”
} else {
println “False”
}

println (++num ? “True” : “False”)

if (!!num) { // !!运算符可以将num转化为boolean类型
println “num=${num}为${!!num}”
}
println “”

// 引用为null时,表示false
Object o = null
if (o) {
println “True”
} else {
println “False”
}
o = new Date()
if (o) {
println “True”
} else {
println “False”
}
println “”

// 字符串为””时,表示false
String s = “”
if (s) {
println “True”
} else {
println “False”
}
s += “hello”
if (s) {
println “True”
} else {
println “False”
}
println “”

// 集合为空时,表示False
ArrayList array = new ArrayList()
if (array) {
println “True”
} else {
println “False”
}
array.add(100)
if (array) {
println “True”
} else {
println “False”
}
println “”

// groovy中switch选择分支的新规定

// switch对字符串类型的支持

print “Please input your words: ”
s = new BufferedReader(new InputStreamReader(System.in)).readLine()

switch (s) {
case “how are you”:
println “fine, thank you”
break;
case “how do you do”:
println “how do you do too”
break;
default:
println “nice to meet you”
}

// switch对集合和范围的支持
n = new Random().nextInt(101)
print “you got ${n} point, ”
switch (n) {
case [98, 99, 100]: // n为98, 99, 100这三个数字时
println “very good!!”
break;
case 90 .. 97: // n为90-97之间时
println “nice job!!”
break;
case 70 .. 89: // n为70-89之间时
println “good”
break;
case 60 .. 79: // n为60-79之间时
println “can be better”
break;
case {it > 0 && it < 60}: // n比0大且比60小时
println “too bad”
break;
case 0: // n为0时
println “nothing to say”
break;
}
首先,Groovy对boolean类型放宽了限制:
常量true和false分别表示“真”和“假”;
null表示false,非null表示true;
空字符串””表示false,非空字符串表示true;
0表示false,非0表示true;
空集合表示false,非空集合表示true;
好了,从这一角度上,Groovy确实和Javascript或者PHP非常相似了,用起来非常方便。
Groovy中对于switch语句的修改也是非常夸张的,这让我甚至有些怀疑Groovy中的switch是否还能达到O1的执行效率,因为Groovy的swtich表现的太像嵌套的if…else if了,在例子中,switch的case后面可以跟随如下几种玩意儿:

常量,在Groovy中,case后的常量包括数值型常量或字符串类型常量,方便很多了,JDK7中新增加了使用字符串常量的case语句,而在.net framework 1.1中早就有了,诸如Basic,PHP,Python,Javascript等脚本,更是一开始就支持字符串常量的case语句;
Range:即范围,语法 n1 .. n2 表示n1和n2之间的所有数值n(n1 <= n <= n2);
Collection:集合,语法 [a1, a2, a3, … , an],表示由中括号中的n个元素构成的集合,其中各个元素类型不必统一;
闭包:由 { 语句 } 构成的匿名函数体,这个后面会重点讲述,这里只要知道,一旦case之后的闭包返回true,则冒号之后的代码将会执行;
总之Groovy对分支语句的改造,让其更符合主流动态语言的特点,目前大家都在玩动态语言,就算强类型语言例如C#也在向动态语言方向靠拢,C++有泛型可以一直淡定,Java也有泛型,但那就不是个玩意儿。Groovy这下改的彻底,该有的全都有了!