百度小程序
Last updated
Last updated
百度小程序的开发api,架构设计和微信小程序基本一致
为了提升整体性能,充分利用手机的多CPU性能: 1. 把逻辑层与渲染层分离,分别位于不同的运行容器 2. 异步请求都由native来执行
逻辑层
逻辑层就是对开发者所暴露的api,有APP, Page,布局文件,其中的App。Page都是两个函数
App()函数的处理: 直接创建App对象,全局唯一对象
Page()函数的处理: 保存到Map中,不会马上构建Page对象,当导航到页面时,才会真正创建Page对象
渲染层
使用MVVM框架san来渲染界面
在编译期间把小程序标签转化为san框架所支持的标签
为每个小程序页面,创建对应的san框架下Page组件,PageComponent的template就是swan.xml转译后的内容
渲染层与逻辑层交互
渲染层接收用户的交互事件,由统一的函数处理后,通过消息总线传递到逻辑层的Page对象,再调用对应的函数
逻辑层依据用户操作,执行业务操作,修改data数据,通过消息总线传递到渲染层的组件里,San.Page组件会自动更新界面
目录结构
app.js的源码
index.js的源码
index.swan的源码
编译之后
app.js的源码
index.swan.js的源码
编译总结:
对template进行转换:
标签转换:bind:tap ===> on-bindtap
事件包装:eventHappen(‘tap’, $event, ‘add’, ‘’, ‘bind’)
对App.js进行包装,提升效率,减少逐一加载流程
通过渲染模板,生成index.swan.js文件,提升渲染效率
用户点击跳转到小程序后:
Native的任务:
下载小程序.zip文件
启动两个web运行容器:
渲染层webview加载slaves.html
逻辑层jscore加载master.html
解析小程序app.json,发送’AppReady’事件
逻辑层master.js
监听’AppReady’事件,执行小程序的调起逻辑
```javascript
/**
监听客户端的调起逻辑 */ listenAppReady() { this.swaninterface.bind('AppReady', event => { console.log('master listener AppReady ', event); swanEvents('masterActiveStart'); // 给三方用的,并非给框架用,请保留 this.context.appConfig = event.appConfig; // 初始化master的入口逻辑 this.initRender(event); // this.preLoadSubPackage(); }); }
创建初始化页面的slave后,如果没有预加载,就加载小程序里的app.js文件(注意:是编译后的app.js文件),并发送’slaveLoaded’事件,通知渲染层开始渲染
```javascript
/**
初始化第一个slave
@param {Object} [initParams] - 初始化的参数
*/
pushInitSlave(initParams) {
....
// 创建初始化slave
this.initSlave = this.createInitSlave(initParams.pageUrl, this.appConfig);
// slave的init调用
this.initSlave
}
/**
初始化为第一个页面
*
@param {Object} initParams 初始化的配置参数
@return {Promise} 返回初始化之后的Promise流 */ Slave.init(initParams) { this.isFirstPage = true; return Promise .resolve(initParams) .then(initParams => { swanEvents('masterActiveInitAction'); if (!!initParams.preventAppLoad) { return initParams; } // const loadCommonJs = this.appConfig.splitAppJs // && !this.appConfig.subPackages // ? 'common.js' : 'app.js'; const loadCommonJs = 'app.js'; return loader .loadjs(${this.appRootPath}/${loadCommonJs}
, 'masterActiveAppJsLoaded') .then(() => { return this.loadJs.call(this, initParams); }); }) .then(initParams => { this.uri = initParams.pageUrl.split('?')[0]; this.accessUri = initParams.pageUrl; this.slaveId = initParams.slaveId; // init的事件为客户端处理,确保是在slave加载完成之后,所以可以直接派发 this.swaninterface.communicator.fireMessage({ type: slaveLoaded${this.slaveId}
, message: {slaveId: this.slaveId} }); return initParams; }); }
```
执行slave入栈后的生命周期函数this.initSlave.onEnqueue(); 在此函数里,会真正Page Instance,同时监听到渲染层准备好后,发送’initData’事件
```javascript /**
入栈之后的生命周期方法
*
@return {Object} 入栈之后,创建的本slave的页面实例对象
*/
onEnqueue() {
return this.createPageInstance();
}
/**
创建页面实例,并且,当slave加载完成之后,向slave传递初始化data
*
@return {Promise} 创建完成的事件流
*/
createPageInstance() {
if (this.isCreated()) {
}
swanEvents('masterActiveCreatePageFlowStart', {
});
const userPageInstance = createPageInstance(this.accessUri, this.slaveId, this.appConfig);
const query = userPageInstance.privateProperties.accessUri.split('?')[1];
this.setUserPageInstance(userPageInstance);
try {
}
catch (e) {
}
this.status = STATUS_MAP.CREATED;
console.log(Master 监听 slaveLoaded 事件,slaveId=${this.slaveId}
);
return this.swaninterface.invoke('loadJs', {
});
}
```
渲染层slave.js
监听’PageReady’事件,加载对应页面的文件:app.css,index.css,index.swan.js文件
```javascript
/**
监听pageReady,触发整个入口的调起
@param {Object} [global] 全局对象
*/
listenPageReady(global) {
swanEvents('slavePreloadListened');
// 控制是否开启预取initData的开关
let advancedInitDataSwitch = false;
this.swaninterface.bind('PageReady', event => {
swanEvents('slaveActiveStart', {
});
...
const appPath = event.appPath;
const pagePath = event.pagePath.split('?')[0];
const onReachBottomDistance = event.onReachBottomDistance;
...
let loadUserRes = () => {
};
// (event.devhook === 'true' ? loadHook().then(loadUserRes).catch(loadUserRes) : loadUserRes());
loadUserRes();
});
}
```
在每个页面编译后的xxx.swan.js文件里,会执行pageRender()函数,进行界面渲染,如此demo里的index.swan.js文件
global.pageRender()函数是在slave.js文件里定义的方法,其内部的逻辑就是创建对应的san框架里的Page组件,等待初始化数据过来后,再绑定到界面上
```javascript
/**
注册所有components(也包括顶层components -- page)
*/
registerComponents() {
...
global.pageRender = (pageTemplate, templateComponents, customComponents, filters, modules) => {
...
// 定义当前页面的组件
componentFactory.componentDefine(
);
swanEvents('slaveActiveDefineComponentPage');
// 获取page的组件类
const Page = global.componentFactory.getComponents('page');
// 初始化页面对象
const page = new Page();
swanEvents('slaveActiveConstructUserPage');
// 调用页面对象的加载完成通知
page.slaveLoaded();
swanEvents('slaveActiveUserPageSlaveloaded');
// 用于记录用户模板代码在开始执行到监听initData事件之前的耗时
global.FeSlaveSwanJsInitEnd = Date.now();
// 监听等待initData,进行渲染
page.communicator.onMessage('initData', params => {
}, {listenPreviousEvent: true});
...
};
...
}
```
当界面渲染后,发送’slaveAttached’事件,逻辑层执行onShow()生命周期函数
目录结构