项目采坑笔记
有段时间没写博客了,前段时间在看 Underscore 源码所以写的多了点,这段时间还是在忙自己的其他项目去了,还是有不少收获的。
Angular 视图过渡动画
之前使用 angular-promise-button
这个模块实现了按钮的自动变化,以前自己是用很多标志位来判断特别二。不仅如此,页面切换动画也是用标志位判断,这样就特别不好维护特别不优雅,上次重构的时候就把这些全部去掉了。但是问题来了,页面数据未到达时候页面就渲染肯定会造成视觉上的问题,怎么解决呢。
我们都想写一些应用很广的代码,比如指令,比如上面这个 angular-promise-button
模块等等。其实要解决上面的问题,也是几行代码就可以解决的事情了。
我所使用的是 Angular 的 ui-router。ngRoute 应该也差不多。
在 ui-router 中可以使用 resolve 达到在控制器初始化以及视图加载前确保数据到达。比如:
只有 resolve 中的全部方法执行完后,才会开始初始化控制和加载视图。这个数据如果在控制器或者视图中要使用,可以在控制器中进行依赖注入。例如上面这个我的控制器是这样写的:
resolve中的方法是阻塞页面进行的,这样就会带来问题了,如果数据请求比较久将导致网站停滞,我们这时候就希望可以有过渡动画出来。要达到全局过渡效果的作用,可以直接监听 $rootScope
中的三个状态即 $stateChangeStart
和 $stateChangeSuccess
以及 $stateChangeError
事件。例如上面这个例子中,当我们触发 me
这个 state 时,也就触发了 $rootScope 上的 $stateChangeStart
事件,当处理结束后将出发 $stateChangeSuccess
并加载视图, 处理失败就会触发 $stateChangeError
事件。代码如下:
入口页面,省去了其他代码,这里第一行就是视图,第二行是加载动画,通过ng-show来控制显示。第三行是引入导航栏,这个在后面会说下。
可以看到上面的代码中是监听了 $stateChangeStart
事件,然后获取目标 state 上的 resolve 方法,当 state 上的 resolve 方法全部结束后,$rootScope.loading
设置为 false,否则保持为 true。
当监听到 $stateChangeSuccess
或者 $stateChangeError
事件时,置 $rootScope.loading
为 false,退出过渡动画。在视图中可以使用 $root
得到 $rootScope
。
可以看到这里有很多参数,可见其功能是很强大的。
再看下上面这个第三行
结合上面的 JS 代码来看,我已经把 $state
注入到了 $rootScope
中了,之后我就可以使用 $state.current
来获取当前的视图状态和信息。我需要实现导航栏仅仅出现在我指定的页面中,下方按钮可以根据当前视图来激活。第一点可以通过给路由 state 补充变量比如我这里的 nav
来实现,需要导航栏的地方就设置 nav
为 true, 否则就不设置。第二点则可以利用 ui-sref-active
来实现。如下:
最后还有一个地方就是,下面这个代码
ui-view 的用法其实还不少,如果你有去注意的话,会看到这个视图在变化的时候其类名会变化,依次我们可以结合 angular-animate 来实现切换动画。注意这个切换动画是在视图加载后才开始的,和上面的不一样,如果同时使用,则会在上方过渡效果结束后触发。例如,我们可以实现淡入淡出:
所以上面其实说了好几点:
- 使用路由上的 resolve 来实现在控制器初始化前获取到需要的数据
- 监听 $rootScope 上的事件来实现 resolve 等待动画
- 在 ui-view 上通过使用 angular-animate 来实现视图的切换动画
- 通过 ui-sref-active 在当 ui-sref 和当前 state 一致时激活 active 类名
- 把 $state 注入到 $rootScope 达到在视图中获取 $state 用途
- 使用 $root 得到 $rootScope,利用 $root 获取 $rootScope 上的对象
静态资源自动发布七牛云
这个很简单啦,我使用了 gulp-qiniu 这个模块来实现,很简单,结合前面说的 gulp-usemin 就更完美了。
例如对上面 index.html 中的这一片段,我们要在开发环境中使用本地资源,而在线上环境则使用 CDN 资源。我们可以这样配置 gulpfile。
运行上面的任务后就会在 backend/app/templates
生成一个修改过的 index.html。对于上面的片段,处理之后是这样的:
前面的注释是给 usemin 使用的,所以要按照规范书写。
同时上面的任务也会把资源发布到七牛云上面,如果你还想指定文件头或者版本号之类的信息,可以参考 gulp-qiniu 的文档进行配置。
上面的方法有一个小问题就是还会在 backend/app/templates
生成一个 cdn.bookist.org
的文件夹,不过无所谓咯,放到 .gitignore 就好了。
至于图片和 css 都差不多吧,就不多说了。
微信坑逼
有段时间我的网站在微信显示部分地方是有问题的,但在手机浏览器和电脑都没任何问题,一开始以为是样式的问题。等到自己要去解决这个问题了,才知道是 js 的问题。
由于微信开发工具不支持 linux,然后我虚拟机的 win10 有不知道什么原因一直连接不到手机。所以就手动调试了,这边改一点,发布上去,然后等微信缓存没了看效果(微信恶心的缓存…我都叫用苹果的人帮我看,因为苹果可以刷新..)。就这样一步步看,最后定位了问题代码。中间过程就不描述了,直接看:
省略了无关代码,这是一个方法里面的部分代码,如果一个对象传入这个方法中,在微信会得不到返回值,在手机浏览器和电脑上都正常。
虽然这里使用了 ES6 的语法,但是我其实已经大量使用 let…of… 语句,项目并没有出现太大问题,就只是上面描述的一些地方异常而已。再者我使用了 babel 转码了。越想越觉得没道理啊。。。
最后虚拟机换了 win7 然后手机连接上了,打开了微信开发工具,调试微信 webview,报错 Symbol is not defined
。
果断控制台敲 Symbol,结果 Symbol is not defined
。
好吧,问题出来了,微信这个辣鸡不支持 Symbol…
但问题是我没有用 Symbol 这个东西啊,我瞄了 babel 一眼。
看下上面那段代码转码的结果:
什么鬼…算了,不看了,但是确定了是 babel 转码导致的。然后查了下资料:
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator, Generator, Set, Maps, Proxy, Reflect, Symbol, Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。
其实早有耳闻 polyfill 这个东西,但我想他一般是用在 IE 这种辣鸡浏览器上的,没想到微信 webview 这么不争气。
多打包一个 babel-polyfill 解决了这个问题。
坑比微信…
故事还没结束…
昨天发现网站并没有使用 http2,但是已经进行了设置了,最后查了很多资料原因好像是 openssl 的问题,但由于使用的是 docker,貌似不太好搞,但其实更换 nginx 版本就好了,原先是1.9版本,换成1.11-alpine 版就解决了,用 docker 更换 nginx 版本非常方便。额,具体原因不深究了,可能跟 alpine 这个字眼有关?不清楚…
http2 具备多路复用的特点,在 http1.1 中,并行传输文件是有限制的,因为用户端和服务端的最大连接数是有限制的,而连接的建立和销毁又会带来开销,所以在 http1.1 中对文件进行压缩合并是很有必要的。不过在 http2 就不需要这样做了,http2 可以在一条通道上传输多个文件,如果合并剩几个,就没法发挥并行传输的优势,而且文件太大,还会降低运输层的效率,即丢包或者乱序到达的影响。
我把网站改到了 http2 后,就不再进行文件合并了,转而可以大量使用 bootcss 的 CDN,bootcss 的 CDN 支持 http2,传输很快。至于上面没有的和自己写的,就发布到七牛云上面。恩,电脑上加载是变快了很多。
但是感觉不到微信加载变快…最后发现是微信不支持 http2…
好伤心…
最后我的方案是产生两个 css 文件和两个 js 文件,之所以是两个,因为一个是自己写的,经常变,另一个是用别人的,几乎不会变。
最后又发现微信好像支持 spdy… 心好累,算了,降了 nginx 版本开启 spdy 不理了。
总结:
- Angular 是一个大而全的框架,我觉得很强大很牛逼,越来越喜欢 Angular 了
- 使用 CDN 可以大大的加速,尽量使用 CDN
- 微信这个坑比我就不说了,不支持 Symbol 不支持 http2
- 能用 http2 就尽量用 http2
- 考虑浏览器兼容性,根据需要引入 babel-polyfill