Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
去啊无线前端的一天
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
ningzbruc
February 01, 2016
Programming
180
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
去啊无线前端的一天
去啊无线前端的一天
ningzbruc
February 01, 2016
More Decks by ningzbruc
See All by ningzbruc
如何写出一个优秀的开源库
ningzbruc
0
66
React & Component
ningzbruc
0
45
阿里旅行去啊H5首页总结&Promise
ningzbruc
0
270
KISSY.Base - all about that Base
ningzbruc
0
130
Hammer.js
ningzbruc
1
340
淘宝旅行门票iPad版开发
ningzbruc
0
140
Travel on KISSY mini
ningzbruc
0
200
Events
ningzbruc
2
130
Why YUI3
ningzbruc
0
190
Other Decks in Programming
See All in Programming
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.5k
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
190
New "Type" system on PicoRuby
pocke
1
900
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
540
jQueryをバージョンアップする前に使いたいjQuery Migrate
matsuo_atsushi
0
470
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
230
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
780
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
220
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
160
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
170
肥大化するレガシーコードに立ち向かうためのインターフェース分離と依存の逆転 / JJUG CCC 2026 Spring
hirokunimaeta
0
550
不変条件と整合性境界—ビジネスが決める設計判断と実現パターン / Invariants and Consistency Boundaries
nrslib
13
3.9k
Featured
See All Featured
Everyday Curiosity
cassininazir
0
230
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
390
Why Our Code Smells
bkeepers
PRO
340
58k
How to train your dragon (web standard)
notwaldorf
97
6.7k
HDC tutorial
michielstock
2
710
Leo the Paperboy
mayatellez
7
1.8k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.9k
How STYLIGHT went responsive
nonsquared
100
6.2k
Context Engineering - Making Every Token Count
addyosmani
9
960
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
590
Designing for Performance
lara
611
70k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Transcript
㣁冠┮䷐䥥⃡Ⰺ D[夯䔺
ň撮宦Ⅷ䠖史猾撮㢑㜎㇛䚐⭤↡℩䥥揞㤐⻰ECUG猳ʼn 09:30
None
None
None
工具集 • git - 版本管理/代码发布 • node - 本地服务/开发工具 •
yo - 项目脚手架 • grunt - 项目构建 • bower - 组件安装 • sass - CSS编译 • generator-clam - 去啊前端脚手架
None
mods: 公用模块 pages: 项目页面 widgets: 公用组件
None
generator-clam ✓ 项目初始化 ✓ 本地调试 ✓ 构建代码 ✓ 发布页面
就是这个feel...倍儿爽~
ň㋌ℱテ∽猾庅䨀ㅓ䀜ʼn 09:40
None
None
None
‧ 日历 ‧ 筛选 ‧ 排序 ‧ 轮播 ‧ 滚动列表
‧ 自动完成 ‧ 横竖屏检测 ‧ … ✓ 组件丰富 ✓ 兼容性强 ✓ 体积轻量 ✓ 体验完美 ✓ 应用广泛 mpi组件列表
‧ 日历 ‧ 筛选 ‧ 排序 ‧ 轮播 ‧ 滚动列表
‧ 自动完成 ‧ 横竖屏检测 ‧ … ✓ 组件丰富 ✓ 兼容性强 ✓ 体积轻量 ✓ 体验完美 ✓ 应用广泛 mpi组件列表
核心代码库 • kissy mini - 基础库 • jsbridge - 与客户端交互
• mpi_css - 样式库 • mtop - 无线标准数据平台 • mpi - 组件库
{ "directory":"./", "shorthand_resolver":"http://gitlab.alibaba-inc.com/ {{{owner}}}/{{{package}}}.git" } ➜ widgets git:(master) ✗ bower
install kissy=kissy/m ➜ widgets git:(master) ✗ bower install mpi/mtop 安装组件 • .bowerrc 项目初始化时会自动安装常用组件~
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html
/** * @fileoverview pages/search/index.js */ KISSY.add(function (S, Base) { var
Search = Base.extend({ initializer: function() { console.log('search'); } }); return Search; }, { requires: ['base'] }); 还是熟悉的味道 还是原来的配方~
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html
TMS http://daily.h5.taobao.org/wvdoc/awp/PageTags.html <!--TMS:/rgn/mytaobao_bk.html,utf-8,41:TMS--> <!--HTTP:http://****.html,(utf-8|gbk):HTTP--> 线上页面会将此标签替换成TMS区块内容
MTop.getApi(apiName, apiVersion, data, params, success, failure); MTop • waptest -
日常环境 • wapa - 预发环境 • m - 线上环境 根据URL来获取不同环境的数据
ň奞䐗㓲㺵扤哋⋂猾∧㓲㏔抹㤐槡屢廤庶⃬⃡ʼn 10:30
for mtop $ grunt demo proxy 本地代理 调试服务
for mtop $ grunt demo proxy 本地代理 调试服务
None
dev.m.taobao.com
None
dev.m.taobao.com
None
None
✓ 本地服务 ✓ TMS区块处理 ✓ Juicer数据模拟 ✓ 线上代码调试 ✓ 更多…
$ grunt online 本地未压缩文件 h5.m.taobao.com
$ grunt online 本地未压缩文件 h5.m.taobao.com
前端可以在海滩边晒太阳边写代码
ň浥椔∝䍌猾慢▉榟勱猾➒⇍㩆⃡⚲✈ʼn 14:00
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html
构建 • copy - 拷贝文件至build目录 • kmc/kmb - 生成模块依赖关系表(map.js) •
combohtml - 合并请求与公用模块(mods) • uglify - 压缩JS代码 • cssmin - 压缩CSS代码 • replace - 替换版本号 $ grunt build
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <link
tag="combocss" href="http://g.tbcdn.cn/??trip/h5-test/0.1.22/widgets/ mpi_css/mpi-min.css,trip/h5-test/0.1.22/pages/search/index-min.css" rel="stylesheet" /> <script tag="combojs" src="http://g.tbcdn.cn/??trip/h5-test/0.1.22/widgets/ kissy/build/mini-all-min.js,trip/h5-test/0.1.22/widgets/jsbridge/index-min.js,trip/h5- test/0.1.22/widgets/mtop/index-min.js,trip/h5-test/0.1.22/config-min.js,trip/h5-test/ 0.1.22/map-min.js"></script> <meta name="pageid" content="on181.h5-test/pages/search/index.html"> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> $ grunt build 构建可发布代码
代码发布 • grunt daily - 替换html资源host为日常 • grunt prepub -
发布日常/预发代码 • grunt publish - 压发布线上代码 g.tbcdn.cn -> g.assets.daily.taobao.net
页面发布 AWP管理平台
页面发布流程 1. 新建页面(填写页面信息) 2. 编辑页面(拷贝html内容) 3. 生成资源列表(监控资源变化) 4. 点击保存预览(检查是否有问题) 5.
提交发布单(性能验收) 6. 点击发布(正式上线)
页面发布流程 1. 新建页面(填写页面信息) 2. 编辑页面(拷贝html内容) 3. 生成资源列表(监控资源变化) 4. 点击保存预览(检查是否有问题) 5.
提交发布单(性能验收) 6. 点击发布(正式上线) 5-10 min/page
页面发布流程 1. 新建页面(填写页面信息) 2. 编辑页面(拷贝html内容) 3. 生成资源列表(监控资源变化) 4. 点击保存预览(检查是否有问题) 5.
提交发布单(性能验收) 6. 点击发布(正式上线) x10 老子要抓狂了!
awpp
awpp 一键发布, 爽!
ň∽ℛ䥥┮䷐猾㌈▀叞⇹◷厐㤐㊦⃮⛐⻲䥥ʼn 14:05 X
为什么 ‧网络速度慢 ‧手机性能差
去啊用户 旅行途中 (别说3G了, 2G网还灭了俩格)
https://speakerdeck.com/lijing00333/css-selector-performance
None
None
怎么做
怎么做 Everything you’ve learned still applies 学到的知识不会过时
https://speakerdeck.com/ningzbruc/mobile-h5-xing-neng-you-hua
客户端3.7性能优化 • 先展示框架 • 样式放在头部 • 脚本放在底部 • 合并/减少请求 •
异步加载脚本 • 删减冗余代码 • 优化图片质量 • 完善客户端缓存机制
None
None
None
None
旅行目的地页面 加载时间 0s 35s 70s 105s 140s 去哪儿iOS 旅行iOS 2
0.5 140 65 140 63 140 9 首屏框架展示 首屏功能可用 首屏全部展示 二次加载 WTF?
大部分客户端在onload之后展示页面 而请求阻塞onload事件
Get.loaded(getCombo([ 'trip/h5-fsticket/widgets/libs/juicer.js', 'trip/h5-fsticket/pages/search/tms.js' ]), function() { //渲染⻚页⾯面 window.TicketTMS.render(); }, !ksdebug,
null, 30); Hack 把所有请求放到onload事件之后
机智的少年
None
4s?
4s? faster?
4s? faster? how about 0.5s?
4s? faster? how about 0.5s? but why?
去啊用户 旅行途中 (别说3G了, 2G网还灭了俩格)
None
• 快速开发 • 冲量引流 • 多端适配 • 产品创新 • 速度慢
• 体验差 • 转化低 少年, 该醒醒了!
ň屢℩悲㉸㧕㋌猾屢℩㹜㉸㧕㋌猳ʼn 16:00
弱网无缝秒出 在追求极致用户的体验上,要么零分,要么满分!
如何实现秒出 • 后端渲染?(每次都需要发起请求) • OPOA?(首页无法秒出, 不便于统计) • 本地缓存+增量更新?(缓存容易被清掉) • AppCache?(功能不稳定,
bug多)
如何实现秒出 • 后端渲染?(每次都需要发起请求) • OPOA?(首页无法秒出, 不便于统计) • 本地缓存+增量更新?(缓存容易被清掉) • AppCache?(功能不稳定,
bug多) ✓ 离线包!!
如何实现离线包
如何实现离线包
放下偏见 我们需要更亲密地合作
高举Hybrid大旗 • 不是H5要挑战Native体验 • 而是Hybrid要挑战Native体验 • 借助Native的力量, 实现弱网无缝秒出 • Hybrid
!== Page + Webview + Bridge
None
离线包关键功能 • 预装载(发版时提前装载H5离线包) • 动态更新(离线包更新时可以静默推送至用户客户端) • 虚拟域(与线上页面保持一致, 方便数据获取与环境切换) • 桥的增强(充分利用Native的功能,
比如mtop请求) • TMS解析(正确解析页面中的TMS区块标签) • 埋点(客户端拦截)
None
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <!--HTTP:http://trip.taobao.com/go/rgn/trip/act-818-preferential.php,gbk:HTTP--> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html 离线包中如何处理
离线包TMS • 异步jsonp请求? • 如何插入HTML区块? • 如何解决代码依赖问题? • 如何做到同样的区块适用于线上与离线环境? •
如何插入第三方区块并且不阻塞业务流程?
tms-offline-parser 页面 代理区块 区块 包装内容
<!--HTTP:extra.php?async,gbk:HTTP--> <script id="tms_fragment_1">get_tms_fragment("http://proxy.php?src=extra.php", "gbk", "tms_fragment_1");</script> handle_tms_fragment(function() { /*<div>TMS内容</div>*/ }, 2);
function handle_tms_fragment(fn, id) { var content = getComment(fn.toString()); //<div>TMS内容</div> $('tms_fragment_' + id).replaceWith(content); } 异步TMS获取
<!--HTTP:data.php,gbk:HTTP--> <script src=“http://proxy.php?src=data.php&id=tms_fragment_1“></script> <tms id="tms_fragment_1"></tms> handle_tms_fragment(function() { /*<div>TMS内容</div>*/ }, 2);
function handle_tms_fragment(fn, id) { var content = getComment(fn.toString()); //<div>TMS内容</div> $('tms_fragment_' + id).replaceWith(content); } 同步TMS获取
<!--HTTP:data.php,gbk:HTTP--> <script src=“http://proxy.php?src=data.php&id=tms_fragment_1“></script> <tms id="tms_fragment_1"></tms> handle_tms_fragment(function() { /*<div>TMS内容</div>*/ }, 2);
function handle_tms_fragment(fn, id) { var content = getComment(fn.toString()); //<div>TMS内容</div> $('tms_fragment_' + id).replaceWith(content); } 同步TMS获取 保证依赖关系 阻塞页面渲染
万事俱备... 测试结果: 两秒的白屏...
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="../../widgets/mpi_css/mpi.css" /> <link rel="stylesheet" href="./index.css"/> <!--Global JavaScripts--> <script src="../../widgets/kissy/build/mini-all.js"></script> <script src="../../widgets/jsbridge/index.js"></script> <script src="../../widgets/mtop/index.js"></script> <script src="../../config.js"></script> <script src="../../map.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <tms id="tms_fragment_1"></tms> <script src="http://proxy.php?src=data.php&id=tms_fragment_1"></script> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> • pages/search/index.html 即使是读取本地文件 客户端也存在IO性能 阻塞渲染
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="index_combo.css" /> <!--Global JavaScripts--> <script src="index_combo.js"></script> </head> <body> <h1>Demo</h1> <!--TMS--> <tms id="tms_fragment_1"></tms> <script src="http://proxy.php?src=data.php&id=tms_fragment_1"></script> <script> KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); </script> </body> </html> 合并请求, 减少IO的消耗 • pages/search/index.html
合并请求... 测试结果: 一秒的白屏... (即使是把TMS去掉)
提示一下下
None
<!DOCTYPE html> <html lang="zh"> <head> <title>Demo</title> <meta charset="utf-8" /> <!--Global
StyleSheets--> <link rel="stylesheet" href="index_combo.css" /> </head> <body> <h1>Demo</h1> <!--TMS--> <tms id="tms_fragment_1"></tms> <script> MT.Get.rendered([ 'index_combo.js', 'http://proxy.php?src=data.php&id=tms_fragment_1' ], function() { KISSY.use('h5-test/pages/search/index', function(S, Search) { new Search(); }); }); </script> </body> </html> 页面渲染之后再发起请求
window.alitrip_render_finished_callback = function() { executeCallbacks(document, 'rendered'); }; • mpi/get/index.js 客户端通知更准确的渲染时间
延迟请求... 测试结果: 秒了!! 秒了!!
WHAT ABOUT THE CODE?!
None
离线包构建/调试 • grunt build_offline - 构建 • grunt offline -
本地调试
Demo
ň廆⑺❉㖆Ⅷʼn 17:00
None
工具, Hybrid, 性能
None
None
None
None
ň冩Ⅿ㖃┑㽺⚲Ⅷ猾㧪↡℩屢棏䥥⛸獑猳ʼn 18:00
THIS IS THE END