Facebook’s challenges are applicable to any very complex websites with many developers. Or any situation where CSS is bundled into multiple files and loaded asynchronously, and often loaded lazily.
——@vjeux
将 Facebook 换成 Tencent 同样适用。
同行们是怎么解决的?
- Shadow DOM Style
Shadow DOM 的样式是完全隔离的,这就意味着即使你在主文档中有一个针对全部 <h3>
标签的样式选择器,这个样式也不会不经你的允许便影响到 shadow DOM 的元素。
举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<body> <style> button { font-size: <span class="number">18</span>px; font-family: <span class="string">'华文行楷'</span>; } </style> <button>我是一个普通的按钮</button> <div></div> <script> <span class="keyword">var</span> host = document.querySelector(<span class="string">'div'</span>); <span class="keyword">var</span> root = host.createShadowRoot(); root.innerHTML = <span class="string">'<style>button { font-size: 24px; color: blue; } </style>'</span> + <span class="string">'<button>我是一个影子按钮</button>'</span> </script> </body> |
这就很好地为 Web Component
建立了 CSS Namespace 机制。
- Facebook: CSS in JS
http://blog.vjeux.com/2014/javascript/react-css-in-js-nationjs.html
比较变态的想法,干脆直接不要用 classname,直接用 style,然后利用 js 来写每个元素的 style……
例如,如果要写一个类似 button:hover
的样式,需要写成这样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<span class="keyword">var</span> Button = React.createClass({ styles: { container: { fontSize: <span class="string">'13px'</span>, backgroundColor: <span class="string">'rgb(233, 234, 237)'</span>, border: <span class="string">'1px solid #cdced0'</span>, borderRadius: <span class="number">2</span>, boxShadow: <span class="string">'0 1px 1px rgba(0, 0, 0, 0.05)'</span>, padding: <span class="string">'0 8px'</span>, margin: <span class="number">2</span>, lineHeight: <span class="string">'23px'</span> }, depressed: { backgroundColor: <span class="string">'#4e69a2'</span>, borderColor: <span class="string">'#1A356E'</span>, color: <span class="string">'#FFF'</span> }, }, propTypes: { isDepressed: React.PropTypes.bool, style: React.PropTypes.object, }, render: <span class="keyword">function</span>() { <span class="keyword">return</span> ( <button style={m( <span class="keyword">this</span>.styles.container, <span class="comment">// 如果压下按钮,mixin压下的style</span> <span class="keyword">this</span>.props.isDepressed && <span class="keyword">this</span>.styles.depressed, <span class="keyword">this</span>.props.style )}>{<span class="keyword">this</span>.props.children}</button> ); } }); |
几乎等同于脱离了 css,直接利用 javascript 来实现样式依赖、继承、混入、变量等问题……当然如果我们去看看 React-native 和 css-layout,就可以发现,如果想通过 React 打通客户端开发,style 几乎成了必选方案。
我们的方案
我们期望用类似
Web Component
的方式去写 Component 的样式,但在低端浏览器根本就不支持Shadow DOM
,所以,我们基于 BEM 来搭建了一种 CSS Namespace 的方案。
我们的 Component 由下面 3 个文件组成:
- main.html 结构
- main.js 逻辑
- main.css 样式
可参考:https://github.com/miniflycn/Ques/tree/master/src/components/qtree
可以发现我们的 css 是这么写的:
1 2 3 4 5 6 7 8 9 |
.<span class="variable">$__title</span> { margin: <span class="number">0</span> auto; font-size: <span class="number">14</span>px; cursor: <span class="keyword">default</span>; padding-left: <span class="number">10</span>px; -webkit-user-select: none; } <span class="comment">/** 太长忽略 **/</span> |
这里面有长得很奇怪的.$__
前缀,该前缀是我们的占位符,构建系统会自动将其替换成 Component 名,例如,该 Component 为 qtree,所以生成结果是:
1 2 3 4 5 6 7 8 9 |
.qtree__title { margin: <span class="number">0</span> auto; font-size: <span class="number">14</span>px; cursor: <span class="keyword">default</span>; padding-left: <span class="number">10</span>px; -webkit-user-select: none; } <span class="comment">/** 太长忽略 **/</span> |
同样道理,在 main.html
和 main.js
中的对应选择器,在构建中也会自动替换成 Component 名。
这有什么好处呢?
- 基于路径的 Namespace,路径没有冲突,那么在该项目中 Namespace 也不会冲突
- Component 可以任意改名,或复制重构,不会产生任何影响,便于 Component 的重构和扩展
- Component 相对隔离,不会对外部产生影响
- Component 非绝对隔离,外部可以对其产生一定影响
xmlhttprequest2.0 可以支持文件上传. 这东东很方便, 但是在实际使用中碰到了一些问题. 这里记录下.
从 https://developer.chrome.com/devtools/docs/remote-debugging 我们可以知道在 android 4.4+可以通过在 apk 中使用下面的代码开启 webview 的 chrome 远程调试
1 2 |
WebView.setWebContentsDebuggingEnabled(<span class="keyword">true</span>); |
但我们开发中接触的 apk 往往是第三方的,没谁会为我们开启 webContentsDebuggingEnabled。而 Xposed 能强制做到这一点
Xposed
Xposed 能够勾住 (Hook) Android 应用程序对象的方法,实现 AOP,一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<span class="keyword">public</span> <span class="keyword">class</span> WebViewHook <span class="keyword">implements</span> IXposedHookLoadPackage { <span class="comment">// handleLoadPackage 会在android加载每一个apk后执行</span> <span class="keyword">public</span> void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { <span class="comment">// 可以从lpparam中获取当前apk的名字</span> <span class="keyword">if</span> (! lpparam.packageName.equals(<span class="string">"com.tencent.mobileqq"</span>)) { <span class="keyword">return</span>; } XposedBridge.log(<span class="string">"WebViewHook handleLoadPackage: "</span> + lpparam.packageName); <span class="comment">// 勾住 WebView 所有的构造器</span> XposedBridge.hookAllConstructors(WebView.<span class="keyword">class</span>, <span class="keyword">new</span> XC_MethodHook() { @Override <span class="keyword">protected</span> void beforeHookedMethod(MethodHookParam param) throws Throwable { <span class="comment">// 打开webContentsDebuggingEnabled</span> XposedHelpers.callStaticMethod(WebView.<span class="keyword">class</span>, <span class="string">"setWebContentsDebuggingEnabled"</span>, <span class="keyword">true</span>); XposedBridge.log(<span class="string">"WebViewHook new WebView(): "</span> + packageName); } }); } } |
上面的代码可以为 QQ 打开 WebView 的 webContentsDebuggingEnabled
Xposed 工作原理可以参考文档: https://github.com/rovo89/XposedBridge/wiki/Development-tutorial
没有详细的 API 页面, 因为 API 也就几个,可以查看源代码: https://github.com/rovo89/XposedBridge/tree/master/src/de/robv/android/xposed
馋图
拿来主义
1、需要 android 4.4+ Root 手机
2、安装 Xposed 框架
3、已开启 QQ WebView 的 Apk: webviewdebughook.Apk
原文地址:http://www.toptal.com/nodejs/top-10-common-nodejs-developer-mistakes
原文作者:MAHMUD RIDWAN
转载此译文请注明原文及译文出处,如译文有翻译不当之处还请各位看官指出。
自 Node.js 面世以来,它获得了大量的赞美和批判。这种争论会一直持续,短时间内都不会结束。而在这些争论中,我们常常会忽略掉所有语言和平台都是基于一些核心问题来批判的,就是我们怎么去使用这些平台。无论使用 Node.js 编写可靠的代码有多难,而编写高并发代码又是多么的简单,这个平台终究是有那么一段时间了,而且被用来创建了大量的健壮而又复杂的 web 服务。这些 web 服务不仅拥有良好的扩展性,而且通过在互联网上持续的时间证明了它们的健壮性。
然而就像其它平台一样,Node.js 很容易令开发者犯错。这些错误有些会降低程序性能,有些则会导致 Node.js 不可用。在本文中,我们会看到 Node.js 新手常犯的十种错误,以及如何去避免它们。
回想最早年资源版本控制,是不是类似如下
1 2 3 |
<script src="a.js?t=20140404"></script> <script src="a.js?v=1.1.0"></script> |
这是一个最常见的古老的版本控制方式,简单直观易用,缺点就是我们常常会因为缓存因为覆盖造成发布外网时出现资源同步问题。对于没啥访问量的小型站点折腾折腾也就过了,如果项目很大访问很大,一但出现问题波及很广你自然少不了被领导被上级抓去捡肥皂的命运。
为什么要用 sass
在选择 sass 之前,我们先说下为什么要使用 CSS Preprocessor。
大概两年前,CSS Preprocessor 其实没有这么热,而了解 sass,less,stylus 的人也还没那么多(当时三者占比 less 还是拥有绝对优势的),但很多时候就是那么 duang 的一下,然后改变就发生了,就如 html5&css3,仿佛一夜之间就遍地开花。当然这其中质变肯定是有道理值得去说道说道的。下面我们一起来对比下 css 和 CSS Preprocessor(以 sass 为例),了解下其中的优劣。
CSS 无层级嵌套机制
因为 css 无嵌套机制,所以造成层级方面的阅读及折叠方面极为不便,如下代码,使用 scss 就能更好的管理代码层级关系
1 2 3 4 5 6 7 8 9 |
<span class="comment">// css</span> .<span class="keyword">parent</span>{} .<span class="keyword">parent</span> .child{} <span class="comment">// scss</span> .<span class="keyword">parent</span>{ .child{} } |
css 本身缺少变量机制
举个最简单的例子,每个站点都有个主色,如果没有变量的话,我们只能每次使用都拷贝颜色,当然也有神人是可以把颜色的六位数记住,但多数肯定是记不住。下面以文本色及链接色为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<span class="comment">// css</span> body{ color:<span class="comment">#333;</span> } a{ color: <span class="comment">#188eee;</span> } .dark a{ color: <span class="comment">#333;</span> } .dark a:hover{ color: <span class="comment">#188eee;</span> } |
有了变量呢,那就简单了,直接定义一个变量,然后需要的时候调用变量即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<span class="comment">// scss</span> <span class="variable">$textColor</span>: <span class="comment">#333 !default;</span> <span class="variable">$lickColor</span>: <span class="comment">#188eee !default;</span> body{ color:<span class="variable">$textColor</span>; } a{ color: <span class="variable">$lick</span>-color; } .dark a{ color: <span class="variable">$textColor</span>; } .dark a:hover{ color: <span class="variable">$lickColor</span>; } |
注:css 变量已经正在开发中,现在的火狐其实已经支持最新的 css 变量了,而且比所有的 CSS Preprocessor 都好用,感兴趣的同学可以去尝个鲜。
@import 不是我们所期望的功能
随着业务功能增加及复杂性增强,多人员合作及组件开发模式是必然的。而现有的 CSS 的 @import
与我们所要的 @import
不是一个概念。为了表示两者的区别,我们直接在 page.scss
中导入一个 css 文件和一个 scss 文件:
page.scss
1 2 3 4 5 6 |
@import <span class="string">"reset.css"</span>; @import <span class="string">"mod-a"</span>; p{ background: <span class="comment">#0982c1;</span> } |
_mod-a.scss
1 2 3 4 5 6 |
<span class="comment">//_mod-a.scss</span> <span class="comment">//-------------------------------</span> .hello { color: <span class="comment">#eee;</span> } |
最终编译出来的 page.css 文件:
1 2 3 4 5 6 7 8 |
@import <span class="string">"reset.css"</span>; .hello { color: <span class="comment">#eee;</span> } p{ background: <span class="comment">#0982c1;</span> } |
可以看到,@import "reset.css"
没有发生改变,而 moda-a 的 scss 文件则被合进了 page.css
,这才是我们需要的结果,需要的时候调用想用的 scss 文件,然后最终合并到一个 css 文件中。
对可重用的代码缺少重复使用机制
css 对于相同或相似的代码,除了一遍遍的拷贝复制或组合申明之外,不可以定义一些规则或函数,去简单重复使用,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<span class="comment">// 组合申明</span> .center-block, .container{ margin-left: auto; margin-right: auto; } .container{ margin-bottom: <span class="number">20</span>px; width: <span class="number">1200</span>px; } <span class="comment">// 拷贝使用</span> .fixed-top{ position: fixed; left: <span class="number">0</span>; right: <span class="number">0</span>; top: <span class="number">0</span>; } .fixed-bottom{ position: fixed; left: <span class="number">0</span>; right: <span class="number">0</span>; bottom: <span class="number">0</span>; } |
而使用 scss 之后则如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<span class="comment">// %,解析后组合申明样式</span> %center-block{ margin-left: auto; margin-right: auto; } .center-block{ @extend %center-block; } .container{ @extend %center-block; margin-bottom: <span class="number">20</span>px; width: <span class="number">1200</span>px; } <span class="comment">// @mixin, 解析后拷贝样式 </span> @mixin fixed(<span class="variable">$pos</span>: <span class="number">0</span>) { position: fixed; left: <span class="number">0</span>; right: <span class="number">0</span>; @<span class="keyword">if</span> <span class="variable">$pos</span> == bottom { bottom: <span class="number">0</span>; } @<span class="keyword">else</span> { top: <span class="variable">$pos</span>; } } .fixed-top{ @<span class="keyword">include</span> fixed; } .fixed-bottom{ @<span class="keyword">include</span> fixed(bottom); } |
除此之外,CSS Preprocessor 还有条件判断,循环等高大上的东西,这些都是 css 目前不具备的,当然 CSS 也正在一步步变化,为更好而革新,相信在不久的将来,CSS 也会 duang 的一下,给你眼前一亮。
说完为什么要选择 CSS Preprocessor,接下来我们说下为什么选择 sass 吧。
其实几个 CSS Preprocessor 的基本功能都差不多,都能胜任日常的开发,但如果是做基础的 css 框架及组件开发的话还是 sass 略强点。
- sass 的语法不容易混淆,@mixin,%,@function 定义各种用途,很清楚明白
- 原先被人诟病的 sass 的变量机制也完善了,!default 变量和!global 变量双剑合璧,解决一切所需。
- 自从 map 类型数据出现后,sass 处理数据方面更加突出。
- sass 的函数多多,应有尽有,各种选择器函数,颜色函数,判断条件,循环函数等,是你构建基础框架的得力助手
总之,就目前来说 sass 是个很好的选择。当然也许有一天 less 或其他的会超越它,或者直接到了某一天 css 本身就有了这些功能,根本不需要这些 CSS Preprocessor。而所有这些都是有极可能的。
为什么要抛弃 compass
用雕爷的一个字评价 compass 就是——学习成本比较高,更新太慢,东西太多,实用的却很少。
作为以 sass 为基础构建的 css 框架,compass 还是非常优秀的,其思想及设计都值得借鉴。但是鉴于它的更新频率及里面的 css 代码,还是不得不吐槽下:
跟不上 sass 的更新节奏
sass 之所以能够在 2 年内反超 less,成为现在的首选,就是因为从版本 3.2.0 之后,不断更新,开发并优化更好的功能,吸引更多的关注。而 compass 却迟迟跟不上 sass 的脚步,严重影响 sass 的体验。
跟不上 css 的脚步
看下 compass 的重置,html5 的几乎没有,现在框架几乎都是 normalize+reset 的天下了;再看下其 inline-block 的 mixin 居然还有 display: -moz-inline-stack;
,虽然穿着的是 sass 前沿的华衣,走的却是怀旧风格,怎么着都有点诡异。
CSS3 mixin
相信很多人用 compass 是奔着这烦人的 css3 前缀来的,可是弱弱的说句,它也过时了,现在都是基于 can i use 的数据来自动生成前缀或兼容了,各大自动化工具如 grunt/gulp 都有其相应的插件 autoprefixer,就算是不用这些自动的前缀,也有很多专门针对 css3 前缀的 scss 文件供调用,如 css3-scss
sprite 自动生成雪碧图
当然还有更大部分使用者是朝着这个功能来的,如果你仅是为了使用这个功能呢,替代的工具同样有的是,同样配置下自动化工具生成 sprite 分分钟搞定。
所以你为什么还在坚持使用 compass?
最后问题来了,如果选择了 sass,抛弃了 compass,用哪个做基础的框架比较合适?
请听下回分解。
背景
当前网速越来越快,但是随着网页内容越来越丰富,其实我们打开网页的速度并未得到什么提升,相反,过多的内容会导致网页打开速度变慢。于是,出现了一些性能优化的方法。
1. 合并文件,如 css,js 等
2. 将 js 文件放在文档的底部
3. 将服务器部署到离用户近的地方,如 cdn 技术
4. 缓存技术
5. 负载均衡
6. 文档直出
等等
有这么多通用的方式能够适用于我们的产品中,每一种实现的技术难度不大,当我们都应用了这些技术,发现网站性能依然不那么乐观的时候,会考虑到一些非常规、适用于某些特定场景的优化技术
另一种思路
Facebook 的研究科学家 changhao jiang 提出了一个优化方案,名字叫做 bigpipe,应用了此项技术的 facebook 首页,访问速度提升一倍。它极大的提升了 fackebook 的性能。它是怎么做到的。
传统的一个打开页面的步骤
- 浏览器发送 HTTP 请求
- 服务器接收到 HTTP 请求,解析请求,从存储层拉取数据,拼接 HTML,发回一个 HTTP 响应
- 这个请求通过网络传输到浏览器
- 浏览器解析接收到的数据,构造 DOM 树,下载 CSS 和 JavaScript
- 浏览器下载了 CSS 之后,开始解析 CSS,渲染页面
- 下载 JavaScript 之后,开始解析 JavaScript,执行 JavaScript
Bigpipe 的思路
- Request parsing:服务器解析和检查 http request
- Datafetching:服务器从存储层获取数据
- Markup generation:服务器生成 html 标记
- Network transport : 网络传输 response
- CSS downloading:浏览器下载 CSS
- DOM tree construction and CSS styling: 浏览器生成 DOM 树,并且使用 CSS
- JavaScript downloading: 浏览器下载页面引用的 JS 文件
- JavaScript execution: 浏览器执行页面 JS 代码
看着 bigpipe 的步骤和普通的方式没什么区别,其实,它只是其中一个模块(pagelet)的流程而已。而一个页面是可以分解成 N 个模块,多个模块以流水线式的方式运行
demo
一个 node 实现的 demo,实现基本的分块的思想
模拟服务器部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
http.createServer(<span class="keyword">function</span>(request, response) { <span class="comment">// Write the document</span> response.writeHead(<span class="number">200</span>, {<span class="string">"Content-Type"</span> : <span class="string">"text/html"</span>}); response.write(<span class="string">'<!DOCTYPE html>'</span>); response.write(<span class="string">'<head><script type="text/javascript">function arrived(id,text) { var b=document.getElementById(id); b.innerHTML = text; }</script>'</span>); response.write(<span class="string">"</head><body><div>Progressive Loading"</span>); <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">6</span>; i++) { response.write(<span class="string">"<div id='"</span> + i + <span class="string">"'>"</span> + i + <span class="string">"</div>"</span>); } response.write(<span class="string">"</div>"</span>); <span class="keyword">var</span> down = <span class="number">6</span>; <span class="keyword">for</span> (i = <span class="number">0</span>; i < <span class="number">6</span>; i++) { http.get(<span class="string">"http://localhost:2000/?id="</span> + i, <span class="keyword">function</span>(res) { res.on(<span class="string">'data'</span>, <span class="keyword">function</span>(chunk) { response.write(chunk, <span class="string">'binary'</span>); }); res.on(<span class="string">'end'</span>, <span class="keyword">function</span>() { console.log(<span class="string">"down"</span>+down) <span class="keyword">if</span>((--down )== <span class="number">0</span>) { response.end(); } }) }); } response.write(<span class="string">"</body></html>"</span>); }).listen(<span class="number">8080</span>); |
模拟请求的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
http.createServer(<span class="keyword">function</span>(request, response) { <span class="comment">// Some delay upto upto 2 seconds</span> <span class="keyword">var</span> delay = Math.round(Math.random() * <span class="number">2000</span>); setTimeout(<span class="keyword">function</span>() { <span class="keyword">var</span> params = url.parse(request.url, <span class="keyword">true</span>); <span class="keyword">var</span> id = params.query.id; response.writeHead(<span class="number">200</span>, {<span class="string">"Content-Type"</span> : <span class="string">"text/html"</span>}); <span class="keyword">var</span> content = <span class="string">"<span>Content of Module "</span> + id + <span class="string">"</span>"</span>; response.write(<span class="string">"<script>"</span> + <span class="string">"arrived('"</span> + id + <span class="string">"', '"</span> + content + <span class="string">"');"</span> + <span class="string">"</script>"</span>); response.end(); }, delay); }).listen(<span class="number">2000</span>); |
一个框架
https://github.com/bigpipe/bigpipe
BigPipe is a radical new web framework for Node.JS. The general idea is to decompose web pages into small re-usable chunks of functionality called Pagelets and pipeline them through several execution stages inside web servers and browsers. This allows progressive rendering at the front-end and results in exceptional front-end performance.
框架会有些重,小型开发场景下可以考虑自己实现
存在的问题
1.seo
设置 useagent,爬虫来的时候给完整页面(有淘宝同学说这里存在 seo 问题,是否有 seo 问题还待确认,因为本身页面是在一个请求内完成)
前言
随着 Web 前端开发由 PC 转向 Mobile,作为前端工程师,除了需要去学习掌握移动端的新特性外,还需要面对大量移动端特有的难题,其中之一就是——怎么调试移动端页面? 针对调试的问题,现在已经有一些很好的解决方案,比如:weinre、chrome remote、手 Q 浏览器 Inspector,不过这些方案的门槛都较高,需要满足一些额外的条件。目前,在日常的开发调试工作中,打日志依然是一种常见的调试手段,通过日志内容来判断当前页面运行的状态,而日志的输出形式又有很多种,有 img 请求、dialog 弹窗、alert 等等,但这些输出方式都不能很好的满足我们的需要,甚至存在一些问题,所以 Rosin 诞生了。
关于 Rosin
Rosin 是一个 Fiddler 插件,它能接收页面中的 console 的输出,将内容持久存储在本地,并展现在 Fiddler 面板。 如果你的项目是通过 Fiddler 代理来开发调试手机页面,那么 Rosin 将会是你的好帮手。
项目地址: http://alloyteam.github.io/Rosin/
- AppCan 是 HTMl5 移动开发应用平台,支持跨平台,可以简单、快速、高效开发移动应用。
- 倡导 Hybrid App 开发。
- 为 HTML5 开发提供底层 Native 交互能力。
主要优势
- 跨平台
- 原生体验,引入 Native UI 控件和交互
- 模拟调试:提供模拟器和调试工具
- 多窗口机制,秒杀 phoneGap
- 一键打包
- …
###开发工具 IDE
AppCan IDE 基于 Eclipse 定制,支持跨平台、本地打包、模拟调试、真机同步等功能
模拟调试
左边部分是手机的一些模拟器。
中间部分是手机屏幕,支持横竖屏切换和分辨率选择。
右边是 chrome 调试窗口,这里内置了 chromium。
本地打包
支持将 web 应用直接打成 APK(android)安装包。
真机同步
将应用生成 “AppCan 调试中心” 模式的安装包,使用手机安装,确保手机和 pc 在同一个网络内。手机上安装成功后,打开 app,输入 pc 的 IP,登录成功后,可以看到应用的 List,打开调试的应用,可以 pc 上 IDE 的控制台上看到请求。
- 打开手机上的 “AppCan 调试中心” APP,输入 pc 的 ip 地址,登录,即可看到应用的 List。
- 手机上打开应用后,pc 上 IDE 控制台看到调试信息
插件 API
AppCan 提供了丰富的底层插件,包括网络通讯,界面布局,功能扩展,第三方 SDK 等能力。API 文档可以参考 [这里], 覆盖了移动开发应用中的常用场景。(http://newdocx.appcan.cn/index.html?templateId=315)。
JS SDK
AppCan 将常用的 UI 和功能性模块封装成了一个开发库,开发者只需要引入 appcan.min.js 即可,另外把对 backbone, zepto, underscore 的依赖也打包在基础库中了
JS SDK 文档参考这里
###总结
AppCan 是一种基于 H5 实现跨平台 App 开发的解决方案,封装了一些常用的 UI 组件供 js 调用,提供了实现一整套流程的 IDE,包括调试,打包等。
1 2 3 4 5 6 7 |
function* generateNaturalNumber() { var i = 0; while(i <= 100) { yield i; i++; } } |