前端资源动态渲染模式介绍之seed篇

前面两篇文章(前端资源动态渲染模式介绍之概览篇前端资源动态渲染模式介绍之combo篇)介绍了combo模式,今天重点介绍下seed模式,seed模式是一种利用js动态解析页面模块依赖,而且结合localstorage和combo 服务,实现的一种速度更快的加载方式。

seed模式特点

  • 结合打包工具,实现页面依赖管理,seedjs不需要维护整站(整个项目)的resourcemap,combo需要使用后台语言维护map.json
  • 结合localstorage,将模块缓存到ls,方便全站(单域名)下公用,下载过模块避免二次请求
  • 对于更新的,没有下载过的模块,拼成combo url,一次加载,避免多次请求

如何实现seed模式

要实现seed模式,要修改模块定义和引入函数,例如AMD中的definerequire,结合打包工具,实现静态资源依赖表resourcemap(例如fis中的map.json)的字段自定义

define函数进行改造

首先对define函数进行改造,增加参数传入md5:define(id, factory, md5) ,使其将factory源码和版本号存入localstorage,文件的md5值,可以结合打包工具实现,fis中的file对象有个方法是file.getHash() 可以获取md5值,这个值还需要存入resourcemap,用于比较缓存中的version和下发的resourcemap是否一致,如果不一致则需要重新拉最新版本。

localstorage中缓存的内容是factory的源码,加上version(hash):

//代码示例如下
//找到resourcemap中的id
var map = resouceMap[id];
//拼缓存的数据
var content = {
    version: map.hash,
    //code是factory的内容
    code: factory.toString()
}
localStorage[id] = JSON.stringify(content)

resourcemap的改造

这个很简单,增加个字段:hash

字段
id 唯一id
uri 线上cdn完整url
hash 文件md5
deps 依赖的模块

对文件进行改造

为了防止每个页面都是用全局的resourcemap(fis的map.json),对于单个页面文件需要输出自己的依赖关系表resourcemap,减少页面的大小,这部分工作也是通过打包工具实现的。

打包工具将每个页面的依赖关系遍历出来,然后输出到每个页面,例如fis3的做法是:http://fis.baidu.com/fis3/docs/lv3.html#%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E6%98%A0%E5%B0%84%E8%A1%A8

require函数的改造

require函数是获取模块依赖关系,没有的则加载模块,优先加载依赖的模块,等依赖模块加载完毕后,再遍历向上加载,保证模块代码执行的时候,该模块依赖的模块都已经加载完毕。

前端资源动态渲染模式介绍之combo篇

今天继续介绍前端资源动态渲染模式中的combo模式,combo模式是利用静态服务器的combo服务,结合静态分析页面使用的js或者css文件,然后动态输出combo url地址的方式。

静态资源combo服务

公司静态集群使用的是nginx服务,nginx有个concat模块可以将url进行打包。使用它之后,需要合并输出的静态资源需要在??两个问号后面加,逗号隔开,例如:

http://baidu.com??style1.css,style2.css
http://box.bdimg.com/life/js??script1.js,script2.js

当然这种合并的文件数也是有限的,如果超过默认或者设置的最大文件数,服务就会报错,可以通过修改nginx.conf的配置进行修改:

location /static/ {
    concat on;
    concat_max_files 20;
}

combo渲染模式

前文介绍过基本原理,现在就拿smarty模板的{%require name="life:js/demo"%}说下具体的代码实现步骤,其他语言参考即可,我们还有个node版本的,node我们采用了yog2框架,其实是一样的,如果是直接php,其实就是个引入的函数而已(从第二步开始)

  1. 执行{%require name="life:js/demo"%},进入smarty的扩展语法require标签的实现
  2. 实际执行的是 Resource.class的load函数:Resource::load('life:js/demo')
  3. load函数根据传入的id(life:js/demo),读取life-map.json(fis生成的静态资源表),根据id找到类似下面的内容:
    "life:js/demo":{
    "uri":"http://s.box.bdimg.com/lf/js/demo_defb566.js",
    "type":"js",
    "deps":["common:bdbox/template"],
    "rUri":"/static/lf/js/demo_defb566.js",
    "hash": "defb566"
    }
    

JavaScript sdk(jssdk)设计指南

今天看到一篇《javascript sdk设计指南》,内容篇幅比较多,很多实际是问题的枚举,但是信息量太大,所以我结合之前做微博开放平台和运营活动平台的经验,说下jssdk的设计和一些核心问题的解决方案。

一个jssdk一般是指提供给第三方人员使用的一段js,通过这个js实现一些平台化产品提供的服务,比如微博的jssdk。整个jssdk的设计有一下几个核心问题:

  • 代码如何被使用页面接入
  • 如何实现跨域通信
  • 如何实现优雅api的设计
  • 公共资源的使用
  • 代码组件化

先说第一个问题

代码如何被使用页面接入

这个问题涉及到几个小问题需要讨论:

  • 命名空间
  • 样式冲突
  • 版本维护
  • appid等参数的传入

命名空间

在「命名空间」部分,需要做到不污染环境,保护好自己,即不要对本来的页面造成命名的破坏,只是用一个命名空间,又要考虑到第三方页面的复杂性,防止跟错综复杂的命名空间冲突。

要做到这点,需要我们在命名空间命名的时候多注意下,尽量不要使用业内通用的命名方法,比如驼峰,名字尽量起的怪一些,偏一些,一般,要么使用_开头(甚至多个),要么使用项目代号这些不太被别人想到的名字,嗯,我记得有人命名空间用av,很好呀!

还有一种方式是动态的命名空间,在url中带上namespace=xxx,本节结束后面会统一给出示例

样式冲突

除了js命名空间问题,如果jssdk带有UI组件,那么还需要考虑css的样式冲突问题,这里不用多说,记住以下几点:

  • 一些复杂的widget可以使用iframe方式引入
  • 不使用id
  • 使用带前缀的class命名,前面用一个class最好包裹
  • 自己做reset!
  • 跟js相关的class要有特殊的约定(比如_J-xxx )或者使用data-id代替

其实利用sass、less这些预编译语言很容易

例如下面的代码:

$name: avUI;

.#{$name}__dialog{
    @include reset();
    .#{$name}__dialog__header{
        color: white;
    }
}

版本维护

版本维护的目的是保证代码最新,功能最全,而不用每次做了升级,通知所有使用的第三方开发者把自己页面的代码挨个更换。所以这里版本维护不应该暴漏给使用者,比如在url使用版本号,到了2.0版本,通知使用方替换,这是不合理的,总有些公司或者人不配合的。最好的方法是设计的时候就要考虑到这个问题。

一般有两种比较好的方式:

  • 小拖大,动拖静:即第三方引入的js是一个动态的,或者没有缓存没有cdn的,然后由它带出后面的cdn
  • 隔段时间动态创建script

推荐使用「小拖大,动拖静」,后面介绍组件化也要使用这个方式来按需加载代码

小拖大,动拖静

核心代码示例

(function(){
    .....
    var url = '最新版本cdn的地址';
    load(url);
}())

隔段时间动态创建script

代码示例:

(function () {
    var s = document.createElement('script');
    s.type = 'text/javascript';
    var t = +new Date;
    t -= %864E5;
    s.src = 'http://xxx.com/sdk.js?t='+t;
    var x = document.getElementsByTagName('script')[0];
    x.parentNode.insertBefore(s, x);
  })();

前端资源动态渲染模式介绍之概览篇

在手百我们使用了一种动态管理静态资源的方式,在开发中通常打包工具在打包构建的时候根据页面依赖将所有的js或者css打成一个app.js或者app.css,这样的打包方式我们称之为「静态打包」,因为只能在项目发布构建的过程中一次性将页面用到的js或者css打包成一个文件,上完线之后就不能在拆分。

这种「静态打包」的方式有个弊端:如果在多个view的页面,是不能够充分利用不同页面之间的公用代码(模块)。于是加强版就是在线统计一个网站(项目)模块依赖和公用的代码,然后设置一个阈值,利用统计的数据,分析出多个页面高频使用的公共代码,再下次上线的时候,将这部分代码打包在一起。这种方式百度内部也有类似的解决方案,但是这种方案也没有充分发挥出细粒度模块的作用,而且实现相对来说比较麻烦,需要长期数据统计支持。

今天我介绍下手百中我们实现的一种动态管理静态资源的方式,我将它称之为「前端资源动态渲染模式」,简称「渲染模式」。这种方式是基于手百现有业务出发的,解决了手百不同平台(安卓、ios、winphone)的代码差异,而且充分利用缓存、combo服务,做到可配方便调试的目的。

前端资源动态渲染模式特点

代码永远只写一份,不需要编写特殊逻辑

我们实现的渲染模式,对于代码开发者来说,不需要做特殊的逻辑编写,只需要按照正常编码要求来写代码,剩下的就交给打包工具和上线流程,对于初学者不需要任何的学习门槛,而且支持本地静态资源和外部静态资源混用。

能够根据不同平台,不同后端业务逻辑,动态打包不同代码

后端业务逻辑不通,则需要的js或者css文件不通,之前的做法是一个js里面包含了所有的代码逻辑,现在只需要如下代码:

{% if($isiOS) %}
    {%require name="life:ios/invoke.js"%}
{%else%}
    {%require name="life:android/invoke.js"%}
{% endif%}

这个是动态逻辑解析后的执行,所以编译阶段的静态打包是完全解决不了这个问题的。

结合combo服务或者localstorage做缓存优化

利用静态资源的combo服务和浏览器的localstorage存储,我们可以把渲染模式进一步优化,最简单的方式是combo渲染模式,动态分析完页面依赖之后,将url拼接成combo url一次输出

学习iOS:使用CocoaPods管理iOS包依赖

iOS的生态中CocoaPods是包依赖管理工具,等同于node生态中的npm,OSX中的brew,linux的apt-get、yum这些工具,下面介绍下CocoaPods的安装和基本使用方法

安装

首先要有ruby(Mac自带哦~),然后执行gem

sudo gem install cocoapads
pod setup

这时候估计会报:Operation not permitted - /usr/bin/xcodeproj错误,需要执行下面的操作:

 mkdir -p $HOME/Software/ruby
$ export GEM_HOME=$HOME/Software/ruby
$ gem install cocoapods
[...]
1 gem installed
$ export PATH=$PATH:$HOME/Software/ruby/bin
$ pod --version
1.0.0

我是修改了.bash_profile(zsh修改.zshrc),将export GEM_HOME=$HOME/Software/ruby放进去,把$HOME/Software/ruby/bin加入PATH。这时候在再执行gem install cocoapads 就不会报错了。

pod setup在执行时,会输出Setting up CocoaPods master repo,但是会等待比较久的时间。这步其实是 Cocoapods 在将它的信息下载到 ~/.cocoapods目录下,如果你等太久,可以试着 cd 到那个目录,用du -sh *来查看下载进度。你也可以参考下面的 使用 cocoapods 的镜像索引 一节的内容来提高下载速度。

使用 CocoaPods 的镜像索引

所有的项目的 Podspec 文件都托管在https://github.com/CocoaPods/Specs。第一次执行pod setup时,CocoaPods 会将这些podspec索引文件更新到本地的 ~/.cocoapods/目录下,这个索引文件比较大,有 80M 左右。所以第一次更新时非常慢,笔者就更新了将近 1 个小时才完成。
一个叫 akinliu 的朋友在 gitcafe 和 oschina 上建立了 CocoaPods 索引库的镜像,因为 gitcafe 和 oschina 都是国内的服务器,所以在执行索引更新操作时,会快很多。如下操作可以将 CocoaPods 设置成使用 gitcafe 镜像:

pod repo remove master
pod repo add master https://gitcafe.com/akuandev/Specs.git
pod repo update

将以上代码中的 https://gitcafe.com/akuandev/Specs.git 替换成 http://git.oschina.net/akuandev/Specs.git 即可使用 oschina 上的镜像。

使用CocoaPads管理iOS依赖

CocoaPads的基本命令是pod,常用的方法如下

  • 初始化:进入项目路径 pod init
  • 安装:pod MODULE_NAME
  • 安装特定版本的依赖:pod MODULE_NAME, 版本,例如:pod 'Objection', '0.9'
  • 更新 pod update
  • 搜索 pod search MODULE_NAME

Podfile介绍

pod init的时候会在你项目中创建一个Podfile 文件,很类似npm的 package.json,如果一个项目中已经有了Podfile, 就可以直接进入该目录然后执行 pod install,就会安装上Podfile中的依赖包

Podfile.lock介绍

执行pod install之后会生成Podfile.lock放在目录中,为了保证每次执行pod install的版本一致性,所以建议把Podfile.lock放入版本库维护,如果需要更新某个包的版本,执行pod update,则Podfile.lock文件会发生变化,记得提交到版本库

参考文章

手机百度移动适配切图解决方案介绍

我们知道移动开发上面有个设备像素比:window.devicePixelRatio,现在在开发页面的时候,一定会在head中添加个viewportmeta类似下面的代码:

<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">

但是随着手机屏幕越来越大,于是页面会遇见下面的问题,手机屏幕越大,右边留白越多,字体越小也不清晰,影响体验:

iPhone5页面
iPhone6 Plus页面

介绍下REM

rem是以document.documentElement(即<html>标签)的font-size为基准的,举例说明:

  • html的font-size:10px
  • 那么1rem = 10px

手百Rem切图方案

为了切图方便,我们手百使用了Rem切图,首先类似淘宝的flexible方案,会在页面head中引入一个flexible.js

手机百度localstorage细粒度缓存介绍

写在前面

拖了一年多的文章,终于开始慢慢补上了。。。上周末整理了下hexo的模板,本周末写了2015年的第一篇文章。

从14年开始,手机百度就开始支持localstorage的细粒度缓存,配合inline渲染模式使用,在2G慢速网站将页面的js和css嵌入到script和style标签,然后将源码存到localstorage,第二次访问的时候从localstorage读取,提高页面访问速度。

细粒度localstorage方案

传统的localstorage缓存,流程图如下:
传统的localstorage缓存

最大的缺点是:需要页面渲染之后,读取localstorage缓存内容,然后二次拉取没有缓存或需要更新的资源

手机百度前端工程化之路

本文将围绕我半年来在移动前端工程化做的一些工作做的总结,主要从 localstorage缓存和版本号管理模块化静态资源渲染方式 三个方面总结手机百度前端一年内沉淀的解决方案,希望对大家移动开发有所帮助。

一年前存在的问题

可能因为之前项目节奏紧,人力不足原因,一部分phper承担了前端的工作,于是暴漏了一些问题。

粗暴的一刀切

从第一次在厂子写代码开始,就被前辈告诉移动页面,所以的静态资源都要内嵌,即写在scriptstyle内,这样的好处是,网络情况不好的时候,减少http请求。因为2G等网络不稳定的情况下,多开一个http请求,对手机资源消耗是巨大的,比如我们在手机信号不好的地方,访问网络,耗电量会急剧增高。

但是随着3G,甚至4G的普及,实际统计显示,手机百度上2G用户不到30%,所以上面提到的这种一刀切的方案是不妥的。

不成规矩

第二个问题是没有规范和模块化的问题。大家写码都是 意识流 ,除了都是用zepto.js之外,没有沉淀下模块。碰到以前写过的代码,都是ctrl+c + ctrl+v。这种粗放的方式,虽然可以暂时解决问题,但是当出现之前的一段代码不能满足需求的时候(比如新版app发布,之前的代码需要做兼容和升级),需要遍历所有的代码,挨个修改,麻烦!

kindle paperwhite2 越狱后优化

上篇已经说了怎么越狱,因为我的pw2是日版的,发现很耗电,删除了很多书了,还是三四天就开始出没电报警了。通过越狱后可以关掉索引,这样耗电量就小好多了,到现在一周了,电量还没消耗掉50%(最近一天看书2小时左右),这对于我来说已经很满意了。

ssh登录kindle

按照上篇操作,越狱之后 ,在搜索输入框内输入;711 就可以看到自己现在wifi的ip。

生成SSH密钥

为了每次方便,我使用了puttygen.exe生成了ssh密钥。操作跟github的一样,打开puttygen.exe,点击Generate 然后鼠标在key下面来回晃动,生成一个key,将生成的key复制,保存为:authorized_keys。点击save private key,将生成的密钥保存在硬盘上,例如:c:\private-key-for -kpw.ppk

usb连上kindle,放在usbnet/etc文件夹下。

SSH连接kindle

按照上篇中【安装USBNetwork】操作,打开wifi连接ssh。使用;711查询出kindle在现在网络的ip。

打开winscp,新建一个ssh会话,例如:sftp://192.168.xxx.xxx,用户名是root,端口22,用户密钥文件,选择上面保存的ppk地址,例如c:\private-key-for -kpw.ppk

然后点击登录,就可以登录kindle了。

关闭索引

kindle的索引是不停的在后台建立的,当书籍比较多或者书比较大的情况下,一般会碰见卡索引的问题,这时候需要关闭索引了,减少耗电!个人认为索引是比较鸡肋的功能。因人而异哈~

ssh连上kindle之后,执行

kindle paperwhite2 5.4.2越狱

买了kindle有一段时间了,发现电池并没有官方说的那么给力,我的pw2是从日本托朋友代购的,发现充电满了一天就差不多掉1/3的样子,曾经打电话给国内的亚马逊寻求帮助,但是国内亚马逊以国内还没上线为由没给我处理,要我自己到日亚提问?!

上网查了下,听说关掉索引会解决这个问题,于是我就一直琢磨这怎么越狱。

kindle越狱工具

我的越狱工具是从mobileread下载的:http://www.mobileread.com/forums/showthread.php?t=186645

kindle越狱

首先解压上面下载的kindle-jailbreak.zip越狱工具,得到三个文件:

  • bridge.sh
  • jb.sh
  • updatejb$(cd mnt && cd us && sh jb.sh).bin

将这个这三个文件copy到kindle的根目录下,然后断开USB连接,返回kindle[主页],点击[菜单]中的[设置],进入设置页面之后,再点击[菜单]选择[更新设备],这时候会执行bin文件,过一会儿之后,你就会在这个界面下面看到

JAILBREAK

就表示已经越狱成功了

安装启动器KUAL

安装插件之前需要安装KUAL作为启动器。启动器下载地址:http://www.mobileread.com/forums/showthread.php?t=203326