Qianniu Application Platform(QAP)是阿里千牛官方推出的、功能丰富的开发者套件。底层weex,上层有SDK接口、rax库、以及rax的ui组件nuke
背景
2017年 由于整个技术方向切到qap平台,将原有的h5 web app 改造成以weex渲染。
demo
包大小优化原因
qap平台应用简单来说是多页应用,每个页面之间都是无关联的,借助于weex的navigator
进行跳转。对比web浏览器来说,就是多页面开发,每次跳转都是打开新的窗口页面。web浏览器是支持一个页面里面引入多个js的,但是weex现阶段只支持一个页面一个js)。
qap平台是将千牛应用的js打包成一个zip,通过后台服务上传,然后由千牛推送给用户,这就决定了我们必然要去优化这个zip包的大小。
同时用户在首次下载新版本包的时候,包越大越容易导致丢包,用户无法更新到最新包,用户网络不能一直保持良好状态。
一个例子
页面比较简单,核心代码如下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
35
36import {createElement, Component, render} from 'rax';
import {View,Text,Button,Link,Image,Modal,TextInput,TouchableHighlight} from 'nuke';
import st from './style.rxscss';
import QAP from 'QAP-SDK';
import WangWang from 'common/chatww';
import RCUserInfo from 'common/userInfo';
class Feedback extends Component {
constructor(props) {
super(props);
}
// 具体业务场景
render() {
return (
<View style={st.feedbackContentWrap}>
<View style={st.feedbackInputWrap}>
<TextInput
ref="myFb"
placeholder='吐槽、建议、改进、新功能统统写上来,我们会尽快联系您哒~'
multiline={true}
style={st.feedbackInput}
/>
</View>
<Button
style={st.singleSubmitBtn}
onPress={this.submit.bind(this)}
>
提交
</Button>
</View>
);
}
}
render(<Feedback/>);
这个页面依赖了rax, QAP-SDK, nuke, chatww, userInfo style.rxscss
,但是阿里的qap平台已经帮我们内嵌了rax, QAP-SDK, nuke
,我们在打包的时候就不需要打包了,剩下的依赖其实不算是很多,然而最终的打包之后的大小不理想。达到了38k。
再次分析
我们里面依赖了userInfo, 而userInfo依赖了fetch,fetch依赖了apiList,
而apiList是啥?
apiList分析
对于前后分离项目,前端这边会有一个api映射表,用于数据请求。这里apiList是所有页面的api映射的集合。
1 | var apiList = { |
随着项目越来越复杂,apiList 越来越复杂,在实际项目里面apiList 达到了30k多。
而在实际项目里面有超过80个chunk,每个chunk都会打包apiList,有人会说用CommonsChunkPlugin
抽离公共的js,但是现在weex还不支持一个页面多个js机制,所以就会出现80*30重复了很多。
拆分apiList
既然apiList冗余,那能不能在使用的时候,只要打包想要的呢?
方案1-更改请求写法
现有的方式1
2
3this.request({
api: 'XXX'
})
这种方式在原有的h5里面是可以的,全局共享apiList。
可以改成1
2
3import {XXX} from 'api'
XXX();
这样就能够按需加载api了,但是要大改整个请求,而且在每个页面都得更改,成本太大了。新的项目可以这样子,但是对于现有的项目改动成本大。
方案2-提前处理api
既然apiList很大,那就不用了,直接在使用的时候,把映射关系写到参数里面。
这个会带来很多问题,一个api在多个页面被使用了,一旦改动就要涉及到多个页面改动,给项目维护带来很大困难。
方案3-提前预处理api
改进方案2,还是按照之前的写法,在打包的过程中,提前把映射关系注入参数。
利用babel-plugin
将1
2
3
4
5request({
api: 'A0'
}, (result)=> {
console.log(result);
})
转成1
2
3
4
5
6
7
8
9request({
api: 'A0',
apio: {
server: 'rc',
url: 'https://mwdsp.superboss.cc/api/a0'
}
}, result => {
console.log(result);
});
就可以不需要引入apiList了。
这样就能够抛弃apiList了,最终通过这种方式优化了25%的体积。
进一步思考
如果有80个页面,每个页面用了userInfo,按照上面的思路,是不是最终会有80个userInfo?
其实不然,每个页面会有很多组件,如果有些组件里面是单独请求userInfo,也会做一次转化。最终是>=80
个。
那能不能进一步减少呢?
那就是要抽离重复代码,将userInfo封装成一个公共函数调用,最终webpack计算页面组件依赖关系的时候,只会计算出依赖userInfo这个公共函数,这样就能做到80次转化userInfo。
那能不能进一步减少呢?
这里就设计到qap平台的一些特性了,这个后面会在qap统一入口里描述,不是任何场景都适合。
问题
apiList 是动态对象,业务层存在动态调用。
在通过babel-plugin处理request的时候,是得知道真正调用的api的名字的。动态传参是无法转换的。
这里就需要手动改成静态调用。
总结
包的大小对于web应用,以及native应用一直都是难题;在业务复杂情况下,怎么做好包大小控制都是难题。
此次主要是对apiList进行优化,效果比较显著,在实际项目里包的大小降低了25%。
再次思考?
能不能计算出每个页面里面具体使用了哪些api,然后替换掉apiList,每个页面的apiList都是精简过后的,这样就不用转化了。
这个在统一入口实战里面有涉及到。