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
ningzbruc
February 01, 2016
Programming
190
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
140
Hammer.js
ningzbruc
1
340
淘宝旅行门票iPad版开发
ningzbruc
0
140
Travel on KISSY mini
ningzbruc
0
210
Events
ningzbruc
2
130
Why YUI3
ningzbruc
0
190
Other Decks in Programming
See All in Programming
【やさしく解説 設計編 #1】「ドメイン駆動」と「実装駆動」ってなに? 〜設計の考え方を、たとえ話で学ぼう〜
panda728
PRO
1
140
AI がコードを書く時代における新卒エンジニアの仕事風景 (2026) / New Graduate Engineers in the Era of AI Coding (2026)
sushichan044
0
240
php-fpmのプロセスが枯渇した日-調査・対処・そして本当にやるべきだったこと-
shibuchaaaan
0
150
The Bowling Game- From Imperative to Functional Programming - Part 1
philipschwarz
PRO
0
350
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
550
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
180
ビデオ通話が繋がる0.2秒で何が起きているのか
supurazako
2
150
Go言語とトイモデルで学ぶTransformerの気持ち / fukuokago23-transformer
monochromegane
0
140
仕様書を書く前にハーネスを作る - Agent Native開発は「探索を速く、判定を固く」
gotalab555
2
460
<title><a id="</title>君はこのHTMLをパースできるか"></a></title> #雑LT_study
pizzacat83
0
110
SREの積み重ねがAI駆動開発のガードレールになった ― 7つの実践/SRE Guardrails The 7
tomoyakitaura
8
5.6k
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
320
Featured
See All Featured
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
For a Future-Friendly Web
brad_frost
183
10k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.1k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
430
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
230
sira's awesome portfolio website redesign presentation
elsirapls
0
310
GraphQLとの向き合い方2022年版
quramy
50
15k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
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