- 快速开始
- JSX 介绍
- JSX+ 介绍
- 组件
- 事件处理
- 样式设置
- Hooks 介绍
- Driver 介绍
- 容器差异
- 工程介绍
- 目录结构
- 应用配置
- 应用入口
- 生命周期
- 路由管理
- 简介
- 语法约束
- Hooks
- 多端组件开发
- API 使用
- 静态资源引用
- 项目构建配置
- 页面配置
- 使用原生项目配置文件
- Rax 与小程序代码混用
- FAQ
- 更新日志
- 简介
- 环境变量与 Framework
- Weex Style 支持表
- Weex 组件
- Weex 模块
- 页面降级
- JS Service
- Bundle 解析
- 事件与手势
- 事件通信
- Weex 国际化
- Weex 常见问题
- 简介
- Document
- App Shell
- 代码分离
- 保存至桌面
- 渲染节点快照
- 预加载和预渲染
- 页面保活
- 缓存控制
- PHA 介绍
- 快速开始
- 编码指南
- 数据请求
- FaaS 接入: Now
- FaaS 接入: FC
- 与 Node 应用集成
- 数据请求
- 异步编程
- Rax 错误码
- 简介
- Lite 工程
- 云端一体化工程
- 插件配置
- 插件开发
- 插件简介
- build-plugin-rax-app
- build-plugin-rax-component
- build-plugin-rax-multi-pages
- build-plugin-rax-ssr
- build-plugin-rax-compat-react
- rax-plugin-app 0.1.0 升级
- 调试 Web
- 调试小程序
- 加载性能优化
- 渲染性能优化
- 从 Rax 0.x 升级
- 从 React 迁移
- API 概述
- render
- hydrate
- createPortal
- findDOMNode
- setNativeProps
- getElementById
- unmountComponentAtNode
- createElement
- cloneElement
- createFactory
- isValidElement
- Children
- memo
- Fragment
- createRef
- forwardRef
- useState
- useEffect
- useLayoutEffect
- useContext
- useRef
- useCallback
- useMemo
- useReducer
- useImperativeHandle
- PropTypes
- version
- ActionSheet
- Background
- Keyboard
- Animation
- Transition
- Toast
- Alert
- Confirm
- Loading
- Navigate
- ChooseImage
- Image
- Request
- Network
- File
- Env
- Device
- Clipboard
- AppState
- AsyncStorage
- Accelerometer
- 组件概述
- Text
- View
- TextInput
- Link
- Icon
- Image
- Picture
- Video
- ScrollView
- RecyclerView
- Waterfall
- Embed
- Countdown
- Canvas
- RefreshControl
- Slider
- Modal
- Weex JS Service
- Rax 长列表最佳实践
- 如何减小 Bundle Size
- Rax 0.x 开发工具
- Native 知识扫盲
- iOS 无障碍
- Rax 性能最佳实践
- 从零上手 Rax
- Rax v0.6 组件体验升级
- Rax v0.5 建立服务体系
- Rax v0.3 跨端生态建设
- Rax v0.2 基础能力建设
- 2016 淘宝双促中的 Rax
- Why Rax?
如何减小 Bundle Size
本文试图描述在rax体系下,减少js bundle size的常规与非常规手段。阅读本文你可能会了解到一下知识点:
1. 为什么要减少bundle size?
2. 减少bundle size的常规手段有哪些?
3. 如何做到模块更新与页面发布的分离?
4. 有没有终极大招?
也许你也有新的思路,欢迎提供,同时也欢迎致力于为服务『中小业务』开发者,提升开发效率与体验,探索新技术及解决方案落地的同仁,加盟『Rax叨叨团』。
1. 为什么要减少bundle size ?
Bundle这个词语频繁出现在前端视野应该是有了打包工具之后。打包工具的出现完全改变了前端的开发方式,工程化也至此迅猛发展。webpack在这个过程中几乎一骑绝尘,成为首选的打包工具。 在Rax体系下,默认会把所有资源都打到一个js bundle中,即使你写一个Hello world那么默认打出的包也有60KB(minifiy), 从淘系来讲,一般业务的平均包大小都在250KB - 300KB, 那么减少bundle size 的原因我大致归纳为以下几点:
1.1. 减少网络传输大小
减少网络传输大小,不仅仅是减少网络传输时间,更重要的是节省公司的cdn带宽,你可要知道带宽支出可是占整个公司的不小的一部分,当然,作为普通开发者没有关注过这些,“大手大脚”习惯了,不当家不知柴米贵。
1.2. 减少首次(屏)渲染时间,提升体验
这也是Rax开发者首要关注的点。weex下,受限当前的渲染模式:js parse ->执行->渲染,而js 引擎parse 时间与bundle size 大小是成正比的
1.3. 来自程序员的自我追求
这一点就比较“玄乎”了,如何用最少的代码,写出符合业务需求的代码,难道不应该是我们的一贯追求么?
2. 减少bundle size 的常规手段有哪些?
大家或多或少已经知道了很多常规的手段,比如:
1. 手动减少重复代码,这一点推荐《clean code》这本书。也就是在自己书写代码的时候,养成良好的习惯,比如当重复的逻辑使用两次以上,就要考虑封装成函数等等。
2. 减少一些工具函数比如lodash的使用,合理使用es6,7已经提供的方法。
3. 避免不同版本的包重复引入,因为像webpack这种会把不同版本的包认为是不同的模块都会打进去,这一块。
4. 去掉rax , 不再打进bundle,这个可以显著减少大小。
5. js service
6. tree shaking
7. Code spliting
我打算重点说说,4,5, 6,7 这四点。
2.1 把rax 在bundle中去掉
把rax 打进bundle 是一个比较遥远的历史问题了, 那个时候,rax 还没有内置,所以就需要依靠把rax打进bundle,这在当时也没有问题, 但现在遇到的问题就是:
weex下已经内置(通过下面所说的js service),打进去真是多此一举。
web下会额外加载framework 约等于加载两份。
有明白人可能会问:如果有些手淘版本没有内置rax怎么办?这个比例已经很小了(找下统计数据TODO),另外weex也会主动帮你降级的。所以放心使用。
可以使用的是webpack的话可以使用 externals 字段来排除。
适用客户端:手淘,天猫。
推荐指数:
困难程度:*
2.2 js service
JS service 和 Weex 实例在 JS runtime 中并行运行。Weex 实例的生命周期可调用 JS service 生命周期。目前提供创建、刷新、销毁生命周期。
大促通过js service 实现的内置方案,显著减少了200KB左右的大小。
推荐指数:***
困难程度:***
2.3 tree shaking
Tree shaking 应该是rollup这个打包工具的作者提出的,用于剔除『没有用到』的『模块导出』。也就是说它的应用是模块级别的。而能使用Tree shaking 的一个重要基础就是模块都是ES6 Modules, 因为ES6 Modules 有如下的特点:
- 只能作为模块顶层的语句出现,不能出现在 function 里面或是 if 里面。
- import 的模块名只能是字符串常量,不能更改。
- 在模块初始化的时候所有的 import 都必须已经导入完成。 更多的原理,可以自行搜索。 这项技术在webpack 2上已经支持了, 但是,但是,我们的builder还是1.x,也许这里面有很多历史原因我们的webpack与社区的webpack产生了脱节,那就给我们的坡哥一点时间,慢慢升级@上坡。 相关链接:
- Tree-shaking versus dead code elimination
- 如何评价 Webpack 2 新引入的 Tree-shaking 代码优化技术?
- Tree shaking in webpack
推荐指数:***
困难程度:
2.4 code splitting
代码分割,按需加载,也是一套很成熟的技术了。 本质是webpack在打包的时候异步(webpackJSONP)加载,但weex下默认使用不了code splitting:
不管是jsonp也好,require.js, kissy, sea.js 也好本质都是在『运行时』动态创建script来『加载』和『执行』js的。
weex 下没有script标签,要使用就需要hack。
实现这个的技术关键是:
- 下载: 其实就是发起http请求了,可以用fetch,ajax
- 执行: new Function or eval
其实webpack 2 还有个System.import 更强大一些,but,我们还是1.x. 不过已经够用了。
这个问题的缺点或者说解决不了下面说的模块与页面主bundle的发布分离的问题(因为这些还是需要和主bundle一起build,虽然不build一起了)。
推荐指数:
困难程度:**
3. 如何做到模块的更新与页面发布的分离?
假设有以下几种场景:
- 不定期的运营投放,只在某些时间才会用到某些模块,那需要打进bundle 吗?
- 动态模块场景:第三方业务提供的模块的发布与更新
- 页面是native的,内嵌的weex模块
- 可怜的zcache到达率,发布一次回到解放前。 其实在众多的搭建体系里面已经有类似的技术了,那就是编写模块,提取依赖,并把各个依赖发布到cdn,然后把各个模块combo后加载并执行。 下面这个图是我们充值中心首页用到的技术,实现了上面我说的模块的发布与页面的发布分离,当然,额外的可能需要有个配置的地方。 关键技术点:
- 相关模块包括依赖的模块需要都是rax 体系的模块
- 解析依赖(deps.json)并去除重复依赖
- combo cdn url
- 下载
- 执行
3.1 开发rax模块
3.2 解析依赖并combo url
3.3 下载并执行
4. 有没有终极大招?
有,别写代码。 [认真脸~]
缩减代码体积是一个长期的过程,同时可能还有其它方式或者方法,如果你知道并且有实践,欢迎补充。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论