百度小程序

整体框架

百度小程序的开发api,架构设计和微信小程序基本一致

为了提升整体性能,充分利用手机的多CPU性能: 1. 把逻辑层与渲染层分离,分别位于不同的运行容器 2. 异步请求都由native来执行

baidu_mini_pro架构
  • 逻辑层

    1. 逻辑层就是对开发者所暴露的api,有APP, Page,布局文件,其中的App。Page都是两个函数

    2. App()函数的处理: 直接创建App对象,全局唯一对象

    3. Page()函数的处理: 保存到Map中,不会马上构建Page对象,当导航到页面时,才会真正创建Page对象

  • 渲染层

    1. 使用MVVM框架san来渲染界面

    2. 在编译期间把小程序标签转化为san框架所支持的标签

    3. 为每个小程序页面,创建对应的san框架下Page组件,PageComponent的template就是swan.xml转译后的内容

  • 渲染层与逻辑层交互

    1. 渲染层接收用户的交互事件,由统一的函数处理后,通过消息总线传递到逻辑层的Page对象,再调用对应的函数

    2. 逻辑层依据用户操作,执行业务操作,修改data数据,通过消息总线传递到渲染层的组件里,San.Page组件会自动更新界面

开发流程

编译

  1. 目录结构

    目录结构-src

  2. app.js的源码

  3. index.js的源码

  4. index.swan的源码

    编译之后

  5. 目录结构 目录结构-dist

  6. app.js的源码

  7. index.swan.js的源码

编译总结:

  1. 对template进行转换:

    • 标签转换:bind:tap ===> on-bindtap

    • 事件包装:eventHappen(‘tap’, $event, ‘add’, ‘’, ‘bind’)

  2. 对App.js进行包装,提升效率,减少逐一加载流程

  3. 通过渲染模板,生成index.swan.js文件,提升渲染效率

加载、启动、渲染

用户点击跳转到小程序后:

  1. Native的任务:

    • 下载小程序.zip文件

    • 启动两个web运行容器:

      • 渲染层webview加载slaves.html

      • 逻辑层jscore加载master.html

    • 解析小程序app.json,发送’AppReady’事件

  2. 逻辑层master.js

    • 监听’AppReady’事件,执行小程序的调起逻辑

      ```javascript

      /**

  3. 监听客户端的调起逻辑 */ 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', {

      });

      }

      ```

  4. 渲染层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()生命周期函数

reference

Last updated

Was this helpful?