├── .gitignore
├── README.md
├── package.json
├── src
├── master
│ ├── app
│ │ ├── index.js
│ │ └── life-cycle.js
│ ├── index.js
│ ├── navigator
│ │ ├── history.js
│ │ ├── index.js
│ │ ├── slave-common-parts.js
│ │ └── slave.js
│ ├── page
│ │ ├── index.js
│ │ ├── life-cycle.js
│ │ ├── page-prototype.js
│ │ └── slave-events-router.js
│ └── proccessors
│ │ └── api-proccessor.js
├── slave
│ ├── component-factory
│ │ └── index.js
│ └── index.js
└── utils
│ ├── code-process.js
│ ├── communication
│ └── index.js
│ ├── data.js
│ ├── events-emitter.js
│ ├── index.js
│ ├── loader.js
│ ├── module.js
│ ├── path.js
│ ├── splitapp-accessory.js
│ └── swan-events
│ └── index.js
├── test
├── package.json
├── src
│ ├── main.js
│ ├── native
│ │ ├── component
│ │ │ ├── page.js
│ │ │ └── view
│ │ │ │ └── index.js
│ │ ├── index.js
│ │ └── utils
│ │ │ ├── events-emitter.js
│ │ │ ├── loader.js
│ │ │ └── swan-events
│ │ │ └── index.js
│ └── wxs
│ │ ├── helloworld
│ │ ├── app.css
│ │ ├── app.js
│ │ ├── app.json
│ │ └── pages
│ │ │ ├── component
│ │ │ ├── index.css
│ │ │ ├── index.js
│ │ │ ├── index.swan.js
│ │ │ └── index.wxml
│ │ │ └── text
│ │ │ ├── text.css
│ │ │ ├── text.js
│ │ │ ├── text.swan.js
│ │ │ └── text.wxml
│ │ └── wechat-app-demo
│ │ ├── app.css
│ │ ├── app.js
│ │ ├── app.json
│ │ ├── image
│ │ ├── arrowright.png
│ │ ├── fix
│ │ │ ├── bgm01.png
│ │ │ ├── bgm02.png
│ │ │ ├── bgm03.png
│ │ │ ├── bgm11.png
│ │ │ ├── bgm12.png
│ │ │ ├── bgm13.png
│ │ │ ├── bgm14.png
│ │ │ ├── bgm15.png
│ │ │ └── scroll-view.png
│ │ ├── icon64_appwx_logo.png
│ │ ├── pause.png
│ │ ├── play.png
│ │ ├── plus.png
│ │ ├── record.png
│ │ ├── resources
│ │ │ ├── arrow.png
│ │ │ ├── kind
│ │ │ │ ├── canvas.png
│ │ │ │ ├── content.png
│ │ │ │ ├── form.png
│ │ │ │ ├── interact.png
│ │ │ │ ├── map.png
│ │ │ │ ├── media.png
│ │ │ │ ├── nav.png
│ │ │ │ └── view.png
│ │ │ └── pic.jpg
│ │ ├── stop.png
│ │ ├── trash.png
│ │ ├── wechat.png
│ │ └── wechatHL.png
│ │ └── page
│ │ └── component
│ │ ├── component-pages
│ │ └── wx-view
│ │ │ ├── wx-view.css
│ │ │ ├── wx-view.html
│ │ │ └── wx-view.js
│ │ ├── index.css
│ │ └── index.js
└── webpack.config.js
├── webpack.base.conf.js
└── webpack.test.conf.js
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | package-lock.json
3 | node_modules/
4 | dist/
5 | output/
6 | **/.DS_Store
7 | test/coverage/
8 | .idea
9 | test/node_modules/
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # swanvue-core
5 |
6 | 基于百度小程序swan-core,替换其中的MVVM框架san,使用vue的小程序框架。
7 |
8 | 由于百度小程序只开源了js部分,对于native部分没有开源,此项目的目标就是把未开源的部分还原回去,界面渲染框架使用vue
9 |
10 | ## 思路
11 |
12 | 1. mock native部分代码,运行swan-core代码
13 | 2. 渲染部分使用vue替换san
14 | 3. 添加自定义组件
15 | 4. 渲染与逻辑分离,创建两个web运行环境,native实现渲染与逻辑通信
16 | 4. 实现编译脚本,对小程序代码进行转译
17 |
18 | ## build
19 | ```shell
20 | // 编译出master, slave
21 | npm run build:test
22 |
23 | // 运行测试项目
24 | cd test
25 | npm start
26 | ```
27 |
28 | ## 技术交流群
29 |
30 | 群名称:swanvue-core-交流群
31 |
32 | 群 号:733617647
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "swanvue-core",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "build:test": "webpack --config webpack.test.conf.js"
7 | },
8 | "author": "",
9 | "license": "MIT",
10 | "dependencies": {
11 | "vue": "^2.6.6"
12 | },
13 | "devDependencies": {
14 | "babel-core": "^6.26.3",
15 | "babel-loader": "^7.1.5",
16 | "babel-plugin-istanbul": "^5.1.0",
17 | "babel-plugin-transform-class-properties": "^6.24.1",
18 | "babel-plugin-transform-decorators-legacy": "^1.3.5",
19 | "babel-plugin-transform-object-assign": "^6.22.0",
20 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
21 | "babel-preset-env": "^1.7.0",
22 | "copy-webpack-plugin": "^4.6.0",
23 | "jasmine": "^3.3.1",
24 | "jasmine-core": "^3.3.0",
25 | "karma": "^2.0.5",
26 | "karma-chrome-launcher": "^2.2.0",
27 | "karma-jasmine": "^2.0.1",
28 | "karma-webpack": "^3.0.5",
29 | "npm": "^6.8.0",
30 | "webpack": "^3.12.0",
31 | "webpack-merge": "^4.2.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/master/app/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | mixinLifeCycle
3 | } from './life-cycle';
4 | import {getAppInfo} from '../../utils';
5 | import swanEvents from '../../utils/swan-events';
6 |
7 | /**
8 | * 绑定app的环境相关事件
9 | *
10 | * @param {Object} [appObject] - app对象的实例
11 | * @param {Object} [swaninterface] - swaninterface小程序底层接口
12 | */
13 | const bindLifeCycleEvent = (appObject, swaninterface, lifeCycleEventEmitter) => {
14 | const appEventsToLifeCycle = ['onAppShow', 'onAppHide', 'onAppError', 'onPageNotFound'];
15 |
16 | appEventsToLifeCycle.forEach(eventName => {
17 | lifeCycleEventEmitter.onMessage(eventName, messages => {
18 | // 筛选出本次的onShow的对应参数
19 | let event = messages[0] ? messages[0].event : messages.event;
20 | if (appObject[`_${event.lcType}`]) {
21 | appObject[`_${event.lcType}`]({
22 | event,
23 | appInfo: getAppInfo(swaninterface, true),
24 | type: event.lcType
25 | });
26 | }
27 | }, {
28 | listenPreviousEvent: true
29 | });
30 | });
31 |
32 | swaninterface
33 | .bind('onLogin', event => {
34 | appObject['_onLogin']({
35 | event,
36 | appInfo: getAppInfo(swaninterface, true),
37 | type: event.lcType
38 | });
39 | });
40 | swanEvents('masterPreloadInitBindingEnvironmentEvents');
41 | };
42 |
43 |
44 | /**
45 | * 获取所有的app操作方法(App/getApp)
46 | *
47 | * @param {Object} [swaninterface] - swan底层接口
48 | * @param {Object} [appLifeCycleEventEmitter] - app的数据流
49 | * @return {Object} 所有App相关方法的合集
50 | */
51 | export const getAppMethods = (swaninterface, appLifeCycleEventEmitter, lifeCycleEventEmitter) => {
52 | let initedAppObject = null;
53 |
54 | const getApp = () => initedAppObject;
55 |
56 | const App = appObject => {
57 | // 将初始化之后的app对象,返回到上面,getApp时,可以访问
58 | // 获取app的相关信息,onLaunch是框架帮忙执行的,所以需要注入客户端信息
59 | const appInfo = getAppInfo(swaninterface, true);
60 | // global.monitorAppid = appInfo['appid'];
61 | // try {
62 | // global.rainMonitor.opts.appkey = appInfo['appid'];
63 | // global.rainMonitor.opts.cuid = appInfo['cuid'];
64 | // } catch (e) {
65 | // // avoid empty state
66 | // }
67 | initedAppObject = mixinLifeCycle(appObject, appLifeCycleEventEmitter);
68 | bindLifeCycleEvent(initedAppObject, swaninterface, lifeCycleEventEmitter);
69 |
70 | // 触发launch事件
71 | swanEvents('masterOnAppLaunchHookStart');
72 | initedAppObject._onAppLaunch({
73 | appInfo,
74 | event: {},
75 | type: 'onAppLaunch'
76 | });
77 | swanEvents('masterOnAppLaunchHookEnd');
78 | return initedAppObject;
79 | };
80 |
81 | return {
82 | App,
83 | getApp
84 | };
85 | };
86 |
--------------------------------------------------------------------------------
/src/master/app/life-cycle.js:
--------------------------------------------------------------------------------
1 | import {processParam} from '../../utils/index';
2 |
3 | const lifeCyclePrototype = {
4 |
5 | /**
6 | * 生命周期的函数中接收到的参数处理函数
7 | *
8 | * @param {Object} [data] - 待处理的数据
9 | * @return {Object} 处理后的数据
10 | */
11 | _lifeCycleParamsHandle(data) {
12 | const appInfo = data && data.appInfo || {};
13 |
14 | // 取出 appInfo 中的 path, query, scene, shareTicket
15 | let result = ['path', 'query', 'scene', 'shareTicket']
16 | .reduce((prev, cur) => {
17 | prev[cur] = appInfo[cur] || '';
18 | return prev;
19 | }, {});
20 |
21 | // 如果是从小程序跳转来的,则增加引用信息 referrerInfo
22 | appInfo.srcAppId && (result.referrerInfo = appInfo.referrerInfo);
23 |
24 | return result;
25 | },
26 |
27 | /**
28 | * onShow生命周期的参数的处理
29 | * @param {Object} data - 待处理的数据
30 | * @return {Object} 处理后的传给开发者的参数
31 | * @private
32 | */
33 | _onAppShowLifeCycleParamsHandle(data) {
34 | const result = this._lifeCycleParamsHandle(data);
35 | const appInfo = data && data.appInfo || {};
36 |
37 | // 对于 onShow,传递entryType 及 appURL信息,以增加场景触发标识参数
38 | // {string} entryType 值同showBy,有'user' | 'schema' | 'sys' 标识onShow的调起方式,'user'通过home前后台切换或者锁屏调起,'schema'是通过协议调起,'sys'为默认值(未覆盖到的打开场景)
39 | // {string=} appURL showBy为schema时存在,为调起协议的完整链接
40 | if (appInfo.showBy) {
41 | result.entryType = appInfo.showBy;
42 | if (appInfo.showBy === 'schema') {
43 | result.appURL = appInfo.appURL;
44 | }
45 | }
46 | return result;
47 | },
48 |
49 | /**
50 | * 向事件流中发送生命周期消息
51 | *
52 | * @param {Object} [eventName] - 生命周期事件名称
53 | * @param {Object} [e] - 事件对象
54 | */
55 | _sendAppLifeCycleMessage(eventName, e) {
56 | this._appLifeCycleEventEmitter.fireMessage({
57 | type: 'ApplifeCycle',
58 | params: {
59 | eventName,
60 | e
61 | }
62 | });
63 | },
64 |
65 | /**
66 | * appLaunch生命周期,在app启动时即自执行
67 | *
68 | * @param {Object} [params] - appLaunch的生命周期函数
69 | */
70 | _onAppLaunch(params) {
71 | try {
72 | processParam(params.appInfo);
73 | this.onLaunch && this.onLaunch(this._lifeCycleParamsHandle(params));
74 | }
75 | catch (e) {
76 | console.error(e);
77 | }
78 | this._sendAppLifeCycleMessage('onLaunch', {
79 | e: params.appInfo
80 | });
81 | },
82 |
83 | /**
84 | * appShow生命周期,在app启动/前后台切换时触发
85 | *
86 | * @param {Object} [params] - appShow生命周期参数
87 | */
88 | _onAppShow(params) {
89 | try {
90 | processParam(params.appInfo);
91 | this._sendAppLifeCycleMessage('onPreShow', {e: params.appInfo});
92 | this.onShow && this.onShow(this._onAppShowLifeCycleParamsHandle(params));
93 | }
94 | catch (e) {
95 | console.error(e);
96 | }
97 | this._sendAppLifeCycleMessage('onShow', {
98 | e: params.appInfo
99 | });
100 | },
101 |
102 | /**
103 | * appHide生命周期,在app前后台切换时触发
104 | *
105 | * @param {Object} [params] - appHide生命周期参数
106 | */
107 | _onAppHide(params) {
108 | try {
109 | processParam(params.appInfo);
110 | this.onHide && this.onHide(this._lifeCycleParamsHandle(params));
111 | }
112 | catch (e) {
113 | console.error(e);
114 | }
115 | this._sendAppLifeCycleMessage('onHide', {
116 | e: params.appInfo
117 | });
118 | },
119 |
120 | /**
121 | * appError生命周期,在app生命周期内,如果发生错误,即触发
122 | *
123 | * @param {Object} [params] - appError生命周期的参数
124 | */
125 | _onAppError(params) {
126 | this.onError && this.onError(params.event);
127 | this._sendAppLifeCycleMessage('onError', {
128 | e: params.appInfo
129 | });
130 | },
131 | };
132 |
133 | export const mixinLifeCycle = (appObject, appLifeCycleEventEmitter) => {
134 | return Object.assign(appObject, lifeCyclePrototype, {
135 | _appLifeCycleEventEmitter: appLifeCycleEventEmitter
136 | });
137 | };
--------------------------------------------------------------------------------
/src/master/index.js:
--------------------------------------------------------------------------------
1 |
2 | import {getAppMethods} from './app';
3 | import {Navigator} from './navigator';
4 | import EventsEmitter from '../utils/events-emitter';
5 | import {Page, slaveEventInit} from './page';
6 | import {define, require} from '../utils/module';
7 | import {apiProccess} from './proccessors/api-proccessor';
8 | import Communicator from '../utils/communication';
9 | import swanEvents from '../utils/swan-events';
10 |
11 | export default class Master {
12 | constructor(context, swaninterface, swanComponents) {
13 | swanEvents('masterPreloadStart');
14 | // this.handleError(context);
15 | this.swaninterface = swaninterface;
16 | this.swanComponents = swanComponents;
17 | this.pagesQueue = {};
18 | this.navigator = new Navigator(swaninterface, context);
19 | this.communicator = new Communicator(swaninterface);
20 | swanEvents('masterPreloadCommunicatorListened');
21 | //
22 | // this.swanEventsCommunicator = new EventsEmitter();
23 | // this.virtualComponentFactory = new VirtualComponentFactory(swaninterface);
24 | // this.extension = new Extension(context, swaninterface);
25 | //
26 | // // perfAudit data hook
27 | // this.perfAudit = {};
28 | //
29 | // 监听app、page所有生命周期事件
30 | this.bindLifeCycleEvents();
31 | // // 监听所有的slave事件
32 | const allSlaveEventEmitters = slaveEventInit(this);
33 | //
34 | this.pageLifeCycleEventEmitter = allSlaveEventEmitters.pageLifeCycleEventEmitter;
35 | //
36 | // 装饰当前master的上下文(其实就是master的window,向上挂方法/对象)
37 | this.context = this.decorateContext(context);
38 | //
39 | // this.openSourceDebugger();
40 | // // 监听appReady
41 | this.listenAppReady();
42 | // // 适配环境
43 | // this.adaptEnvironment();
44 | // // 解析宿主包
45 | // this.extension.use(this);
46 | swanEvents('masterPreloadGetMastermanager');
47 | swanEvents('masterPreloadEnd');
48 |
49 | }
50 |
51 | /**
52 | * 监听客户端的调起逻辑
53 | */
54 | listenAppReady() {
55 | this.swaninterface.bind('AppReady', event => {
56 | console.log('master listener AppReady ', event);
57 |
58 | // if (event.devhook === 'true') {
59 | // if (swanGlobal) {
60 | // loader.loadjs('./swan-devhook/master.js');
61 | // } else {
62 | // loader.loadjs('../swan-devhook/master.js');
63 | // }
64 | // }
65 | swanEvents('masterActiveStart');
66 | // 给三方用的,并非给框架用,请保留
67 | this.context.appConfig = event.appConfig;
68 | // 初始化master的入口逻辑
69 | this.initRender(event);
70 | // this.preLoadSubPackage();
71 | });
72 | }
73 |
74 | /**
75 | * 初始化渲染
76 | *
77 | * @param {Object} initEvent - 客户端传递的初始化事件对象
78 | * @param {string} initEvent.appConfig - 客户端将app.json的内容(json字符串)给前端用于处理
79 | * @param {string} initEvent.appPath - app在手机上的磁盘位置
80 | * @param {string} initEvent.wvID - 第一个slave的id
81 | * @param {string} initEvent.pageUrl - 第一个slave的url
82 | */
83 | initRender(initEvent) {
84 | // 设置appConfig
85 | this.navigator.setAppConfig({
86 | ...JSON.parse(initEvent.appConfig),
87 | ...{
88 | appRootPath: initEvent.appPath
89 | }
90 | });
91 | swanEvents('masterActiveInitRender');
92 |
93 | // 压入initSlave
94 | this.navigator.pushInitSlave({
95 | pageUrl: initEvent.pageUrl,
96 | slaveId: +initEvent.wvID,
97 | root: initEvent.root,
98 | preventAppLoad: initEvent.preventAppLoad
99 | });
100 |
101 | this.appPath = initEvent.appPath;
102 | swanEvents('masterActivePushInitslave');
103 | }
104 |
105 | /**
106 | * 绑定生命周期事件
107 | */
108 | bindLifeCycleEvents() {
109 | this.lifeCycleEventEmitter = new EventsEmitter();
110 | this.swaninterface.bind('lifecycle', event => {
111 | console.log('master listener lifecycle', event);
112 | this.lifeCycleEventEmitter.fireMessage({
113 | type: event.lcType + (event.lcType === 'onShow' ? event.wvID : ''),
114 | event
115 | });
116 | });
117 | }
118 |
119 | /**
120 | * 装饰当前的上下文环境
121 | *
122 | * @param {Object} context - 待装饰的上下文
123 | * @return {Object} 装饰后的上下文
124 | */
125 | decorateContext(context) {
126 | Object.assign(context, this.getAppMethods());
127 | context.masterManager = this;
128 | context.define = define;
129 | context.require = require;
130 | // context.swaninterface = this.swaninterface; // 远程调试工具的依赖
131 | context.swan = this.decorateSwan(Object.assign(this.swaninterface.swan, context.swan || {}));
132 | // context.getCurrentPages = getCurrentPages;
133 | // context.global = {};
134 | context.Page = Page;
135 | //
136 | // context.Component = this.virtualComponentFactory
137 | // .defineVirtualComponent.bind(this.virtualComponentFactory);
138 | //
139 | // context.Behavior = this.virtualComponentFactory
140 | // .defineBehavior.bind(this.virtualComponentFactory);
141 | //
142 | swanEvents('masterPreloadDecorateContext');
143 | return context;
144 | }
145 |
146 | /**
147 | * 将导出给用户的swan进行封装,补充一些非端能力相关的框架层能力
148 | * 后续,向对外暴露的swan对象上,添加框架级方时,均在此处添加
149 | *
150 | * @param {Object} [originSwan] 未封装过的,纯boxjs导出的swan对象
151 | * @return {Object} 封装后的swan对象
152 | */
153 | decorateSwan(originSwan) {
154 | return apiProccess(originSwan, {
155 | swanComponents: this.swanComponents,
156 | navigator: this.navigator,
157 | communicator: this.communicator,
158 | pageLifeCycleEventEmitter: this.pageLifeCycleEventEmitter,
159 | appLifeCycleEventEmitter: this.appLifeCycleEventEmitter,
160 | swanEventsCommunicator: this.swanEventsCommunicator,
161 | // hostShareParamsProccess: this.extension.hostShareParamsProccess.bind(this.extension),
162 | swaninterface: this.swaninterface
163 | });
164 | }
165 |
166 | /**
167 | * 获取所有App级相关的方法
168 | *
169 | * @return {Object} 用户App的操作相关方法集合
170 | */
171 | getAppMethods() {
172 | this.appLifeCycleEventEmitter = new EventsEmitter();
173 | return getAppMethods(
174 | this.swaninterface,
175 | this.appLifeCycleEventEmitter,
176 | this.lifeCycleEventEmitter
177 | );
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/master/navigator/history.js:
--------------------------------------------------------------------------------
1 |
2 | import swanEvents from '../../utils/swan-events';
3 |
4 | export default class History {
5 | constructor(initSlaves = []) {
6 | this.historyStack = initSlaves;
7 | swanEvents('masterPreloadCreateHistorystack')
8 | }
9 | /**
10 | * 将一个slave推入栈中
11 | *
12 | * @param {Object} slave 推入栈中的slave
13 | * @return {number} 推入后栈新的长度(同array.push)
14 | */
15 | pushHistory(slave) {
16 | return this.historyStack.push(slave);
17 | }
18 |
19 | /**
20 | * 根据所给出的delta退栈
21 | *
22 | * @param {number} delta 退栈的层数
23 | * @return {Array} 退出的所有slave
24 | */
25 | // popHistory(delta = 1) {
26 | // let poppedSlaves = [];
27 | // for (let i = 0; i < delta; i++) {
28 | // const currentHistoryPage = this.historyStack.pop();
29 | // poppedSlaves.push(currentHistoryPage);
30 | // }
31 | // return poppedSlaves;
32 | // }
33 |
34 | /**
35 | * 替换栈顶的slave
36 | *
37 | * @param {object} slave 要被替换至栈顶的slave
38 | * @return {number} 推入后栈新的长度(同array.push)
39 | */
40 | // replaceHistory(slave) {
41 | // this.historyStack.pop();
42 | // return this.pushHistory(slave);
43 | // }
44 |
45 | /**
46 | * 获取栈顶的delta个元素
47 | *
48 | * @param {number} delta 获取的栈顶元素个数
49 | * @return {Array} 栈顶的delta个元素
50 | */
51 | getTopSlaves(delta = 1) {
52 | return this.historyStack.slice(-1 * delta, this.historyStack.length);
53 | }
54 |
55 | /**
56 | * 获取整个栈
57 | */
58 | // getAllSlaves() {
59 | // return this.historyStack;
60 | // }
61 |
62 | /**
63 | * 退栈到某一条件(到某一个slaveid的slave)
64 | *
65 | * @param {number} slaveId 退至的slave的id
66 | * @param {Function} everyCb 每退一个时候的回调
67 | */
68 | // popTo(slaveId, everyCb) {
69 | // if (this.historyStack.some(item => item.isTheSlave(slaveId))) {
70 | // while (this.historyStack.length !== 0
71 | // && !this.getTopSlaves()[0].isTheSlave(slaveId)
72 | // ) {
73 | // const topSlave = this.historyStack.pop();
74 | // topSlave.close();
75 | // everyCb && everyCb(topSlave);
76 | // }
77 | // }
78 | // }
79 |
80 | /**
81 | * 清空栈,并逐个调用栈中的析构方法(close)
82 | *
83 | */
84 | // clear() {
85 | // try {
86 | // this.historyStack.forEach(slave => slave.close());
87 | // }
88 | // catch (e) {
89 | // console.error('析构出现错误:', e);
90 | // }
91 | // this.historyStack = [];
92 | // }
93 |
94 | /**
95 | * 判断栈中是否有某一个slave
96 | *
97 | * @param {number} slaveId 判断是否存在的slave的id
98 | * @return {boolean} 判断某一slave是否存在
99 | */
100 | // hasTheSlave(slaveId) {
101 | // return this.historyStack.some(item => item.isTheSlave(slaveId));
102 | // }
103 |
104 | /**
105 | * 从history栈中获取slave
106 | * @param {string} [slaveId] slave的id或者slave的uri
107 | * @param {boolean} [getSuperSlave] 获取slave的泛类型,还是具体的slave
108 | * @return {Object} slave对象
109 | */
110 | seek(slaveId, getSuperSlave) {
111 | const superSlave = this.historyStack
112 | .find(item => item.isTheSlave(slaveId));
113 | return getSuperSlave ? superSlave : superSlave && superSlave.findChild(slaveId);
114 | }
115 |
116 | /**
117 | * 对于页面栈中的所有节点进行遍历
118 | *
119 | * @param {Function} fn 每次遍历调用的函数
120 | */
121 | // each(fn) {
122 | // this.historyStack.forEach(slave => {
123 | // fn && fn(slave);
124 | // });
125 | // }
126 | }
127 |
--------------------------------------------------------------------------------
/src/master/navigator/index.js:
--------------------------------------------------------------------------------
1 | import Slave from './slave';
2 | import History from './history';
3 | import splitAppAccessory from '../../utils/splitapp-accessory';
4 | import {pathResolver, parseUrl} from '../../utils';
5 | import swanEvents from '../../utils/swan-events';
6 |
7 | export class Navigator {
8 | constructor(swaninterface, context) {
9 | this.history = new History();
10 | this.swaninterface = swaninterface;
11 | this.context = context;
12 | }
13 | setAppConfig(appConfig) {
14 | // 第一次从客户端获取到appConfig
15 | this.appConfig = appConfig;
16 | }
17 |
18 | /**
19 | * 初始化第一个slave
20 | * @param {Object} [initParams] - 初始化的参数
21 | */
22 | pushInitSlave(initParams) {
23 | // Route事件监听开启
24 | // this.listenRoute();
25 |
26 | swanEvents('masterActiveCreateInitslave');
27 |
28 | // 根据appConfig判断时候有appjs拆分逻辑
29 | // 如果包含splitAppJs字段,且不分包,则为拆分app.js
30 | // if (this.appConfig.splitAppJs && !this.appConfig.subPackages) {
31 | // splitAppAccessory.allJsLoaded = false;
32 | // }
33 |
34 | // 创建初始化slave
35 | this.initSlave = this.createInitSlave(initParams.pageUrl, this.appConfig);
36 |
37 | // slave的init调用
38 | this.initSlave
39 | .init(initParams)
40 | .then(initRes => {
41 | swanEvents('masterActiveCreateInitslaveEnd');
42 | // 入栈
43 | this.history.pushHistory(this.initSlave);
44 | swanEvents('masterActivePushInitslaveEnd');
45 | // 调用slave的onEnqueue生命周期函数
46 | this.initSlave.onEnqueue();
47 | swanEvents('masterActiveOnqueueInitslave');
48 | });
49 | }
50 |
51 | /**
52 | * 产生初始化的slave的工厂方法
53 | *
54 | * @param {string} initUri 初始化的uri
55 | * @param {Object} appConfig 小程序配置的app.json中的配置内容
56 | * @return {Object} 一个slave或slaveSet
57 | */
58 | createInitSlave(initUri, appConfig) {
59 | let tabBarList = [];
60 | try {
61 | tabBarList = appConfig.tabBar.list;
62 | }
63 | catch (e) {}
64 | const initPath = initUri.split('?')[0];
65 | const currentIndex = tabBarList.findIndex(tab => tab.pagePath === initPath);
66 | const swaninterface = this.swaninterface;
67 | // if (tabBarList.length > 1 && currentIndex > -1) {
68 | // splitAppAccessory.tabBarList = tabBarList;
69 | // return new TabSlave({
70 | // list: tabBarList,
71 | // currentIndex,
72 | // appConfig,
73 | // swaninterface
74 | // });
75 | // }
76 | return new Slave({
77 | uri: initUri,
78 | appConfig,
79 | swaninterface
80 | });
81 | }
82 |
83 | /**
84 | * 跳转到下一页的方法
85 | *
86 | * @param {Object} [params] - 跳转参数
87 | * @return {Promise}
88 | */
89 | navigateTo(params) {
90 | params.url = this.resolvePathByTopSlave(params.url);
91 | this.preCheckPageExist(params.url);
92 | const {url, slaveId} = params;
93 | const {appConfig, swaninterface} = this;
94 | const newSlave = new Slave({
95 | uri: url,
96 | slaveId,
97 | appConfig,
98 | swaninterface
99 | });
100 | // TODO: openinit openNext 判断有问题
101 | return newSlave.open(params)
102 | .then(res => {
103 | const slaveId = res.wvID;
104 | // navigateTo的第一步,将slave完全实例化
105 | newSlave.setSlaveId(slaveId);
106 | // navigateTo的第二步,讲slave推入栈
107 | this.history.pushHistory(newSlave);
108 | // navigateTo的第三步,调用slave的onEnqueue生命周期函数
109 | newSlave.onEnqueue();
110 | return res;
111 | })
112 | .catch(console.log);
113 | }
114 |
115 | /**
116 | * 将传入的path以页面栈顶层为相对路径
117 | * @param {string} path - 需要解析的相对路径
118 | * @return {string} 解析后的全路径
119 | */
120 | resolvePathByTopSlave(path) {
121 | if (/^\//g.exec(path)) {
122 | return path.replace(/^\//g, '');
123 | }
124 | const topSlaveUri = this.history.getTopSlaves()[0].getUri().replace(/[^\/]*$/g, '');
125 | const uriStack = pathResolver(topSlaveUri, path, () => {
126 | console.error(`navigateTo:fail url "${path}"`);
127 | });
128 | return uriStack.join('/').replace(/^\//g, '');
129 | }
130 |
131 | /**
132 | * 前端预检查是否页面在配置项中
133 | *
134 | * @param {string} [url] - 跳转url
135 | * @return {boolean} 是否存在
136 | */
137 | preCheckPageExist(url) {
138 | let parsed = parseUrl(url);
139 | url = parsed.pathname;
140 | // 如果pages中存在该页面,则通过
141 | if (this.appConfig.pages.includes(url)) {
142 | return true;
143 | }
144 |
145 | // 有使用Component构造器构造的页面,则通过
146 | if (masterManager
147 | && masterManager.pagesQueue
148 | && Object.keys(masterManager.pagesQueue).includes(url)
149 | ) {
150 | return true;
151 | }
152 |
153 | // 获取分包中的path
154 | let subPackagesPages = [];
155 | this.appConfig.subPackages
156 | && this.appConfig.subPackages.forEach(subPackage => {
157 | // 此处兼容两种配置
158 | let pages = subPackage.pages.map(page =>
159 | (subPackage.root + '/' + page).replace('//', '/')
160 | );
161 | subPackagesPages = subPackagesPages.concat(pages);
162 | });
163 |
164 | // 如果分包的pages中存在该页面,则通过
165 | if (subPackagesPages.includes(url)) {
166 | return true;
167 | }
168 |
169 | // 不通过,走路由失败的逻辑
170 | this.handleNavigatorError(parsed);
171 | return false;
172 | }
173 |
174 | /**
175 | * 调用用户自定义onPageNotFound方法
176 | *
177 | * @param {string} [parsed] - 跳转url
178 | */
179 | handleNavigatorError(parsed) {
180 | let app = this.context.getApp();
181 | app && app._onPageNotFound({
182 | appInfo: this.context.appInfo || {},
183 | event: {
184 | page: parsed.pathname,
185 | query: parsed.query,
186 | isEntryPage: false
187 | },
188 | type: 'onPageNotFound'
189 | });
190 | }
191 |
192 | }
--------------------------------------------------------------------------------
/src/master/navigator/slave-common-parts.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file the slave's common constant variable or functions
3 | * @author houyu(houyu01@baidu.com)
4 | */
5 | export const STATUS_MAP = {
6 | INITED: 1,
7 | CREATING: 2,
8 | CREATED: 3,
9 | CLOSED: 0
10 | };
11 |
--------------------------------------------------------------------------------
/src/master/navigator/slave.js:
--------------------------------------------------------------------------------
1 | import {STATUS_MAP} from './slave-common-parts';
2 | import {createPageInstance, getInitDataAdvanced} from '../page';
3 | import {getParams, loader, executeWithTryCatch} from '../../utils';
4 | import splitAppAccessory from '../../utils/splitapp-accessory';
5 | import swanEvents from '../../utils/swan-events';
6 |
7 | export default class Slave {
8 | /**
9 | * Slave构造函数
10 | *
11 | * @param {string} uri slave的uri
12 | * @param {string} slaveId slave的唯一标识
13 | * @param {Object} navigationParams slave的唯一标识
14 | * @param {Object} swaninterface slave使用的swan-api
15 | */
16 | constructor({
17 | uri,
18 | slaveId = null,
19 | appConfig = {},
20 | swaninterface = {}
21 | }) {
22 | this.uri = uri.split('?')[0];
23 | this.accessUri = uri;
24 | this.slaveId = slaveId;
25 | this.status = STATUS_MAP.INITED;
26 | this.appConfig = appConfig;
27 | this.swaninterface = swaninterface;
28 | this.userPageInstance = {};
29 | this.appRootPath = appConfig.appRootPath;
30 | /* globals masterManager */
31 | this.loadJsCommunicator = masterManager.communicator;
32 | }
33 |
34 | /**
35 | * 初始化为第一个页面
36 | *
37 | * @param {Object} initParams 初始化的配置参数
38 | * @return {Promise} 返回初始化之后的Promise流
39 | */
40 | init(initParams) {
41 | this.isFirstPage = true;
42 | return Promise
43 | .resolve(initParams)
44 | .then(initParams => {
45 | swanEvents('masterActiveInitAction');
46 | if (!!initParams.preventAppLoad) {
47 | return initParams;
48 | }
49 | // const loadCommonJs = this.appConfig.splitAppJs
50 | // && !this.appConfig.subPackages
51 | // ? 'common.js' : 'app.js';
52 | const loadCommonJs = 'app.js';
53 | return loader
54 | .loadjs(`${this.appRootPath}/${loadCommonJs}`, 'masterActiveAppJsLoaded')
55 | .then(() => {
56 | return this.loadJs.call(this, initParams);
57 | });
58 | })
59 | .then(initParams => {
60 | this.uri = initParams.pageUrl.split('?')[0];
61 | this.accessUri = initParams.pageUrl;
62 | this.slaveId = initParams.slaveId;
63 | // init的事件为客户端处理,确保是在slave加载完成之后,所以可以直接派发
64 | this.swaninterface.communicator.fireMessage({
65 | type: `slaveLoaded${this.slaveId}`,
66 | message: {slaveId: this.slaveId}
67 | });
68 | return initParams;
69 | });
70 | }
71 |
72 | /**
73 | * 根据app.js拆分标识加载业务逻辑
74 | *
75 | * @param {Object} params 加载业务逻辑的参数
76 | * @return {Promise} 返回初始化之后的Promise流
77 | */
78 | loadJs(params) {
79 | return new Promise(resolve => {
80 | // 如果有分包,且有子包的话
81 | // if (this.appConfig.splitAppJs && !this.appConfig.subPackages) {
82 | // this.loadFirst(params)
83 | // .then(() => resolve(params));
84 | // }
85 | // // 如果没有分包,有root的时候,加载app.js
86 | // else if (!!params.root) {
87 | // loader.loadjs(`${this.appRootPath}/${params.root}/app.js`)
88 | // .then(() => resolve(params));
89 | // }
90 | // // 如果均没有,则直接返回
91 | // else {
92 | resolve(params);
93 | // }
94 | });
95 | }
96 |
97 | /**
98 | * 加载首页业务逻辑, 首屏渲染完成利用之前的通信方式sendLogMessage, 节省一次通信
99 | *
100 | * @param {Object} params 加载首屏页面业务逻辑的参数
101 | * @return {Promise} 返回初始化之后的Promise流
102 | */
103 | loadFirst(params) {
104 | this.loadJsCommunicator
105 | .onMessage('slaveAttached', event => {
106 | return +event.slaveId === +this.slaveId
107 | && this.loadPages(params);
108 | }, {once: true});
109 |
110 | const firstPageUri = params.pageUrl.split('?')[0];
111 | if (splitAppAccessory.tabBarList.length > 0) {
112 | let pageUriList = splitAppAccessory.tabBarList
113 | .map(tab => {
114 | let pageUri = tab.pagePath.split('?')[0];
115 | return pageUri;
116 | });
117 | if (pageUriList.indexOf(firstPageUri) < 0) {
118 | pageUriList.push(firstPageUri);
119 | }
120 | return Promise
121 | .all(pageUriList.map(pageUri => {
122 | return loader.loadjs(`${this.appRootPath}/${pageUri}.js`);
123 | }));
124 | }
125 | else {
126 | return loader.loadjs(`${this.appRootPath}/${firstPageUri}.js`);
127 | }
128 | }
129 |
130 | /**
131 | * 加载其它所有pages业务逻辑
132 | *
133 | * @param {Object} params 加载首屏页面业务代码的参数
134 | */
135 | loadPages(params) {
136 | Promise
137 | .all([
138 | loader.loadjs(`${this.appRootPath}/pages.js`)
139 | ])
140 | .then(() => {
141 | splitAppAccessory.allJsLoaded = true;
142 | typeof splitAppAccessory.routeResolve === 'function'
143 | && splitAppAccessory.routeResolve();
144 | });
145 | }
146 |
147 | /**
148 | * 入栈之后的生命周期方法
149 | *
150 | * @return {Object} 入栈之后,创建的本slave的页面实例对象
151 | */
152 | onEnqueue() {
153 | return this.createPageInstance();
154 | }
155 |
156 | /**
157 | * 判断slave当前状态
158 | *
159 | * @return {boolean} 当前状态
160 | */
161 | isCreated() {
162 | return this.status === STATUS_MAP.CREATED;
163 | }
164 |
165 | /**
166 | * 将slave实例与用户的page对象进行绑定,一实例一对象,自己管理自己的页面对象
167 | * userPageInstance为用户(开发者)定义的页面对象
168 | *
169 | * @param {Object} userPageInstance 开发者设定的页面的生成实例
170 | */
171 | setUserPageInstance(userPageInstance) {
172 | this.userPageInstance = userPageInstance;
173 | }
174 |
175 | /**
176 | * 创建页面实例,并且,当slave加载完成之后,向slave传递初始化data
177 | *
178 | * @return {Promise} 创建完成的事件流
179 | */
180 | createPageInstance() {
181 | if (this.isCreated()) {
182 | return Promise.resolve();
183 | }
184 | swanEvents('masterActiveCreatePageFlowStart', {
185 | uri: this.uri
186 | });
187 | const userPageInstance = createPageInstance(this.accessUri, this.slaveId, this.appConfig);
188 | const query = userPageInstance.privateProperties.accessUri.split('?')[1];
189 | this.setUserPageInstance(userPageInstance);
190 |
191 | try {
192 | swanEvents('masterPageOnLoadHookStart');
193 | userPageInstance._onLoad(getParams(query));
194 | swanEvents('masterPageOnLoadHookEnd');
195 | }
196 | catch (e) {
197 | // avoid empty state
198 | }
199 | this.status = STATUS_MAP.CREATED;
200 | console.log(`Master 监听 slaveLoaded 事件,slaveId=${this.slaveId}`);
201 | return this.swaninterface.invoke('loadJs', {
202 | uri: this.uri,
203 | eventObj: {
204 | wvID: this.slaveId
205 | },
206 | success: params => {
207 | swanEvents('masterActiveCreatePageFlowEnd');
208 | swanEvents('masterActiveSendInitdataStart');
209 | userPageInstance.privateMethod
210 | .sendInitData.call(userPageInstance, this.appConfig);
211 | swanEvents('masterActiveSendInitdataEnd');
212 | }
213 | });
214 | }
215 |
216 | /**
217 | * 判断当前slave是否某一特定slave
218 | *
219 | * @param {string} tag 表示某一slave的特殊标记uri/slaveId均可
220 | * @return {boolean} 是否是当前slave
221 | */
222 | isTheSlave(tag) {
223 | return this.uri.split('?')[0] === ('' + tag).split('?')[0]
224 | || +this.slaveId === +tag;
225 | }
226 |
227 | /**
228 | * 在当前slave中查找slave,对于普通slave来讲,获取的就是自己
229 | *
230 | * @return {Object} 当前slave实例
231 | */
232 | findChild() {
233 | return this;
234 | }
235 |
236 | /**
237 | * 页面打开逻辑open分支判断
238 | *
239 | * @param {Object} navigationParams 配置项
240 | * @return {Object} 打开页面以后返回对象
241 | */
242 | open(navigationParams) {
243 | return new Promise(resolve => {
244 | if (splitAppAccessory.allJsLoaded) {
245 | resolve();
246 | }
247 | else {
248 | splitAppAccessory.routeResolve = resolve;
249 | }
250 | })
251 | .then(() => this.openPage(navigationParams));
252 | }
253 |
254 | /**
255 | * 页面真正打开的执行逻辑
256 | *
257 | * @param {Object} navigationParams - 打开页面参数
258 | * @return {Object} 打开页面以后返回对象
259 | */
260 | openPage(navigationParams) {
261 | this.status = STATUS_MAP.CREATING;
262 | let {data, componentsData} = getInitDataAdvanced(navigationParams.url);
263 | return new Promise((resolve, reject) => {
264 | this.swaninterface.invoke('navigateTo', {
265 | ...navigationParams,
266 | initData: {data, componentsData},
267 | ...{
268 | success: res => {
269 | executeWithTryCatch(
270 | navigationParams.success,
271 | null,
272 | 'success api execute error'
273 | );
274 | resolve(res);
275 | },
276 | fail: res => {
277 | executeWithTryCatch(
278 | navigationParams.fail,
279 | null,
280 | 'fail api execute error'
281 | );
282 | reject(res);
283 | },
284 | complete: res => {
285 | executeWithTryCatch(
286 | navigationParams.complete,
287 | null,
288 | 'complete api execute error'
289 | );
290 | }
291 | }
292 | });
293 | })
294 | .then(res => {
295 | if (res.root) {
296 | return loader
297 | .loadjs(`${this.appRootPath}/${res.root}/app.js`)
298 | .then(() => res);
299 | }
300 | return res;
301 | })
302 | .then(res => {
303 | this.slaveId = res.wvID;
304 | return res;
305 | });
306 | }
307 |
308 | /**
309 | * 获取当前slave的开发者实例
310 | *
311 | * @return {Object} 开发者的slave实例
312 | */
313 | getUserPageInstance() {
314 | return this.userPageInstance;
315 | }
316 |
317 | /**
318 | * 设置slave的id
319 | *
320 | * @param {string} slaveId slave的客户端给出的id
321 | * @return {Object} 当前slave的操作实例
322 | */
323 | setSlaveId(slaveId) {
324 | // 如果新的slaveid与之前的slaveid不相等,证明本slave重新被创建,则进行一次重置
325 | if (+this.slaveId !== +slaveId) {
326 | this.status = STATUS_MAP.CREATING;
327 | }
328 | this.slaveId = slaveId;
329 | return this;
330 | }
331 |
332 | }
--------------------------------------------------------------------------------
/src/master/page/index.js:
--------------------------------------------------------------------------------
1 | import {Data, getParams, deepClone, getAppInfo} from '../../utils';
2 | import {getPagePrototypeInstance} from './page-prototype';
3 | import EventsEmitter from '../../utils/events-emitter';
4 | import SlaveEventsRouter from './slave-events-router';
5 | import swanEvents from '../../utils/swan-events';
6 |
7 | const pageLifeCycleEventEmitter = new EventsEmitter();
8 |
9 | /**
10 | * slave的绑定事件初始化
11 | *
12 | * @param {Object} [master] - master全局变量实例
13 | * @return {Object} - 所有slave事件的事件流集合
14 | */
15 | export const slaveEventInit = master => {
16 | const slaveEventsRouter = new SlaveEventsRouter(
17 | master,
18 | pageLifeCycleEventEmitter
19 | );
20 | slaveEventsRouter.initbindingEvents();
21 | return {
22 |
23 | pageLifeCycleEventEmitter
24 | };
25 | };
26 |
27 | /**
28 | * 初始化页面实例,附带页面原型,页面的创建函数
29 | *
30 | * @param {Object} [pageInstance] - 页面实例
31 | * @param {string} [slaveId] - 页面的slaveId
32 | * @param {string} [accessUri] - 页面的URL(带query)
33 | * @param {Object} [masterManager] - 小程序的全局挂载实例
34 | * @param {Object} [globalSwan] - 页面的swan接口对象(开发者用的那个swan)
35 | * @param {Object} [appConfig] - 页面配置对象(app.json中的配置内容)
36 | * @return {Object} - 创建页面实例
37 | */
38 | const initUserPageInstance = (pageInstance, slaveId, accessUri, masterManager, globalSwan, appConfig) => {
39 | const swaninterface = masterManager.swaninterface;
40 | const appid = getAppInfo(swaninterface).appid;
41 | slaveId = '' + slaveId;
42 |
43 | // 获取page的原型方法单例,防止对每个page都生成方法集合
44 | const pagePrototype = getPagePrototypeInstance(masterManager, globalSwan, pageLifeCycleEventEmitter);
45 | const [route, query] = accessUri.split('?');
46 | // merge page的原型方法
47 | Object.assign(pageInstance,
48 | {
49 | privateProperties: {
50 | slaveId,
51 | accessUri,
52 | raw: new Data(pageInstance.data),
53 | // share: new Share(swaninterface, appid, pageInstance, appConfig.pages[0])
54 | },
55 | route,
56 | options: getParams(query)
57 | },
58 | pagePrototype
59 | );
60 | swanEvents('masterActiveInitUserPageInstance', pageInstance);
61 |
62 | return pageInstance;
63 | };
64 |
65 | /**
66 | * 克隆page对象方法
67 | *
68 | * @param {Object} [pagePrototype] - 开发者的页面原型
69 | * @return {Object} - 克隆出的页面实例
70 | */
71 | const cloneSwanPageObject = (pagePrototype = {}) => {
72 | let newSwanObject = {};
73 | pagePrototype.data = pagePrototype.data || {};
74 | newSwanObject.data = JSON.parse(JSON.stringify(pagePrototype.data));
75 | Object.keys(pagePrototype).filter(pageKey => pageKey !== 'data')
76 | .forEach(pageKey => {
77 | if (!isWriteProtected(pageKey)) {
78 | newSwanObject[pageKey] = deepClone(pagePrototype[pageKey]);
79 | }
80 | else {
81 | console.error(`关键字保护:${pageKey} is write-protected`);
82 | }
83 | });
84 | return newSwanObject;
85 | };
86 |
87 | // Page对象中,写保护的所有key
88 | const isWriteProtected = pageKey => {
89 | const protectedKeys = [
90 | 'uri', 'setData', 'getData', 'shiftData',
91 | 'popData', 'unshiftData', 'spliceData',
92 | 'privateMethod', 'privateProperties'
93 | ];
94 | return false;
95 | };
96 |
97 | // 当页面打开时(即slave加载完毕,通知master时)master可以将页面对应的page对象进行实例化
98 | export const createPageInstance = (accessUri, slaveId, appConfig) => {
99 | swanEvents('masterActiveGetUserPageInstance');
100 | // 过滤传过来的originUri,临时方案;后面和生成path做个统一的方法;
101 | const uriPath = accessUri.split('?')[0];
102 | const userPageInstance = initUserPageInstance(
103 | cloneSwanPageObject(global.masterManager.pagesQueue[uriPath]),
104 | slaveId,
105 | accessUri,
106 | global.masterManager,
107 | global.swan,
108 | appConfig
109 | );
110 | return userPageInstance;
111 | };
112 |
113 | // 优化redirectTo和naviagteTo时的性能
114 | // 提前在调用路由端能力之前把initData准备好
115 | // 减少一次单独发送initData的端能力调用
116 | export const getInitDataAdvanced = accessUri => {
117 | swanEvents('master_active_get_salve_data_advanced');
118 | const uriPath = accessUri.split('?')[0];
119 | // 如果是在子包中的页面,直接返回,这种情况不做处理
120 | if (!global.masterManager.pagesQueue[uriPath]) {
121 | return {
122 | data: {},
123 | componentsData: {}
124 | };
125 | }
126 | // 因为获取页面原型的方法是单例,因此这里提前调用没有副作用
127 | const pagePrototype = getPagePrototypeInstance(global.masterManager, global.swan, pageLifeCycleEventEmitter);
128 | // 现获取页面本身的data
129 | let data = global.masterManager.pagesQueue[uriPath].data || {};
130 | // 再获取自定义组件里面的data
131 | // let componentsData = pagePrototype
132 | // .privateMethod
133 | // .getCustomComponentsData(global.masterManager.pagesQueue[uriPath].usingComponents);
134 | let componentsData = {};
135 | return {
136 | data,
137 | componentsData
138 | };
139 | };
140 |
141 | /**
142 | * 暴露给用户的 Page 方法
143 | *
144 | * @param {Object} [pageObj] - 开发者的page原型对象
145 | * @return {Object} - 开发者的page原型对象
146 | */
147 | export const Page = pageObj => {
148 | const uri = global.__swanRoute;
149 | const usingComponents = global.usingComponents || [];
150 | const pageProto = {data: {}};
151 | global.masterManager.pagesQueue[uri] = {...pageProto, ...pageObj, uri, usingComponents};
152 | return global.masterManager.pagesQueue[uri];
153 | };
--------------------------------------------------------------------------------
/src/master/page/life-cycle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file 单个页面的生命周期管理
3 | * @author houyu(houyu01@baidu.com)
4 | */
5 | import swanEvents from '../../utils/swan-events';
6 |
7 | // const customComponentPageLifetimes = ['onShow', 'onHide'];
8 |
9 | /**
10 | * 触发页面中自定义组件的pageLifetimes (show|hide)
11 | *
12 | * @param {Object} customComponent - 当前自定义组件实例
13 | * @param {string} type - 事件类型
14 | * @param {Object} params - 页面onLoad的参数
15 | */
16 | // const pageLifetimesExecutor = (customComponent, type, params) => {
17 | // if (customComponentPageLifetimes.includes(type)) {
18 | // const eventType = type.replace(/^(on)/, '').toLowerCase();
19 | // customComponent.pageLifetimes
20 | // && customComponent.pageLifetimes[eventType]
21 | // && customComponent.pageLifetimes[eventType].call(customComponent, params);
22 | // }
23 | // };
24 |
25 | /**
26 | * 自定义组件page化, 生命周期触发
27 | *
28 | * @param {Object} pageInstance - 页面实例
29 | * @param {string} type - 事件类型
30 | * @param {Object} params - 页面onLoad的参数
31 | */
32 | // const customComponentLifeCycleExecutor = (pageInstance, type, params) => {
33 | // const customComponents = pageInstance.privateProperties.customComponents;
34 | // if (customComponents) {
35 | // for (let customComponentId in customComponents) {
36 | // const customComponent = customComponents[customComponentId];
37 | // // 触发onLoad时自定义组件还没有创建好, 将触发onLoad时机放到onReady前
38 | // // 保持与page级生命周期(目前page未修复前onLoad -> onReady -> onShow)同时序
39 | // if (pageInstance._isCustomComponentPage
40 | // && pageInstance.route === customComponent.is
41 | // && type === 'onReady'
42 | // ) {
43 | // customComponent.onLoad
44 | // && customComponent.onLoad.call(customComponent, pageInstance._onLoadParams);
45 | // }
46 | //
47 | // pageLifetimesExecutor(customComponent, type, params);
48 | //
49 | // // 自定义组件当做页面使用时, 其生命周期的处理, 只有page化的该自定义组件享有页面级生命周期(methods内)
50 | // pageInstance._isCustomComponentPage
51 | // && pageInstance.route === customComponent.is
52 | // && customComponent[type]
53 | // && customComponent[type].call(customComponent, params);
54 | // }
55 | // }
56 | // };
57 |
58 | /* eslint-disable fecs-camelcase */
59 | const lifeCyclePrototype = {
60 |
61 | /**
62 | * onLoad生命周期,在页面入栈后既开始执行,在页面展现前既开始执行
63 | *
64 | * @param {Object} [params] - 页面onLoad的参数
65 | */
66 | _onLoad(params) {
67 | try {
68 | this.onLoad && this.onLoad(params);
69 | this._onLoadParams = params; // 给自定义组件page化使用
70 | }
71 | catch (e) {
72 | console.error(e);
73 | }
74 | this._sendPageLifeCycleMessage('onLoad', params);
75 | },
76 |
77 | /**
78 | * onReady生命周期,在页面渲染完成,并通知master之后执行
79 | *
80 | * @param {Object} [params] - 页面onReady的参数
81 | *
82 | */
83 | _onReady(params) {
84 | try {
85 | this.onReady && this.onReady(params);
86 | // customComponentLifeCycleExecutor(this, 'onReady', params);
87 | }
88 | catch (e) {
89 | console.error(e);
90 | }
91 | this._sendPageLifeCycleMessage('onReady', params);
92 | },
93 |
94 | /**
95 | * onShow生命周期,在页面展现出来后,但还未渲染前执行(或页面从后台切到前台,则执行)
96 | *
97 | * @param {Object} [params] - onShow生命周期的参数
98 | */
99 | _onShow(params) {
100 | try {
101 | this._sendPageLifeCycleMessage('onPreShow', params);
102 | this.onShow && this.onShow(params);
103 | // customComponentLifeCycleExecutor(this, 'onShow', params);
104 | }
105 | catch (e) {
106 | console.error(e);
107 | }
108 | swanEvents('pageSwitchEnd', {
109 | slaveId: this.privateProperties.slaveId,
110 | timestamp: Date.now() + ''
111 | });
112 | this._sendPageLifeCycleMessage('onShow', params);
113 | },
114 |
115 | /**
116 | * onHide生命周期,在页面切换到后台,不在当前界时触发
117 | *
118 | * @param {Object} [params] - onHide生命周期的参数
119 | */
120 | _onHide(params) {
121 | this.onHide && this.onHide(params);
122 | // customComponentLifeCycleExecutor(this, 'onHide', params);
123 | this._sendPageLifeCycleMessage('onHide', params);
124 | },
125 |
126 | /**
127 | * onUnload生命周期,页面被卸载时执行(页面退栈)
128 | *
129 | * @param {Object} [params] - onUnload的生命周期参数
130 | */
131 | _onUnload(params) {
132 | this.onUnload && this.onUnload(params);
133 | this._onHide();
134 | // customComponentLifeCycleExecutor(this, 'onUnload', params);
135 | this._sendPageLifeCycleMessage('onUnload', params);
136 | },
137 |
138 | /**
139 | * onForceReLaunch生命周期,小程序面板点重启时(强制relauch)
140 | *
141 | * @param {Object} params - onForceReLaunch的生命周期参数
142 | */
143 | _onForceReLaunch(params) {
144 | this.onForceReLaunch && this.onForceReLaunch(params);
145 | this._sendPageLifeCycleMessage('onForceReLaunch', params);
146 | },
147 |
148 | /**
149 | * 页面下拉刷新时执行
150 | *
151 | * @param {Object} [params] - 页面发生下拉刷新时的参数
152 | */
153 | _pullDownRefresh(params) {
154 | this.onPullDownRefresh && this.onPullDownRefresh(params);
155 | // customComponentLifeCycleExecutor(this, 'onPullDownRefresh', params);
156 | this._sendPageLifeCycleMessage('onPullDownRefresh', params);
157 | },
158 |
159 | _onTabItemTap(params) {
160 | const proccessedParams = [].concat(params)[0];
161 | this.onTabItemTap && this.onTabItemTap(proccessedParams);
162 | // customComponentLifeCycleExecutor(this, 'onTabItemTap', params);
163 | this._sendPageLifeCycleMessage('onTabItemTap', params);
164 | },
165 |
166 | // _share(params) {
167 | // this._sendPageLifeCycleMessage('beforeShare', params);
168 | // // 分享不需要清除之前postMessage过来的数据
169 | // this.privateProperties.share.shareAction(params)
170 | // .then(res => this._sendPageLifeCycleMessage('shareSuccess', res))
171 | // .catch(err => this._sendPageLifeCycleMessage('shareFailed', err));
172 | // this._sendPageLifeCycleMessage('shareAction', params);
173 | // },
174 |
175 | _reachBottom(params) {
176 | this.onReachBottom && this.onReachBottom(params);
177 | // customComponentLifeCycleExecutor(this, 'onReachBottom', params);
178 | this._sendPageLifeCycleMessage('onReachBottom', params);
179 | },
180 |
181 | _onPageScroll(params) {
182 | this.onPageScroll && this.onPageScroll(params);
183 | // customComponentLifeCycleExecutor(this, 'onPageScroll', params);
184 | this._sendPageLifeCycleMessage('onPageScroll', params);
185 | },
186 |
187 | /**
188 | * 向事件流中发送生命周期通知,以便于下游使用
189 | *
190 | * @param {string} [eventName] - 发生的事件名称
191 | * @param {Object} [e] - 发生事件的参数
192 | */
193 | _sendPageLifeCycleMessage(eventName, e = {}) {
194 | this._pageLifeCycleEventEmitter.fireMessage({
195 | type: 'PagelifeCycle',
196 | params: {
197 | eventName,
198 | slaveId: this.privateProperties.slaveId,
199 | accessUri: this.privateProperties.accessUri,
200 | e
201 | }
202 | });
203 | }
204 | };
205 |
206 | /**
207 | * Page中的生命周期
208 | * @param {Object} [pagePrototype] - Page的prototype
209 | * @param {Object} [swaninterface] - swaninterface
210 | * @param {Object} [pageLifeCycleEventEmitter] - 页面生命周期的事件流对象
211 | * @return merge后的Page的prototype
212 | */
213 | export const initLifeCycle = (mastermanager, pagePrototype, pageLifeCycleEventEmitter) => {
214 | const swaninterface = mastermanager.swaninterface;
215 | return Object.assign(pagePrototype, lifeCyclePrototype, {
216 | _pageLifeCycleEventEmitter: pageLifeCycleEventEmitter
217 | });
218 | };
219 | /* eslint-enable fecs-camelcase */
220 |
--------------------------------------------------------------------------------
/src/master/page/page-prototype.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file page类的原型对象
3 | * @author houyu(houyu01@baidu.com)
4 | */
5 | import {initLifeCycle} from './life-cycle';
6 | // import {builtInBehaviorsAction} from '../custom-component/inner-behaviors';
7 |
8 | const noop = () => {};
9 | const SETDATA_THROTTLE_TIME = 10;
10 |
11 | /**
12 | * 创建一个用户的page对象的原型单例
13 | * @param {Object} [masterManager] masterManager底层接口方法
14 | * @return {Object} page存放着所有page对象的原型方法的对象
15 | */
16 | export const createPagePrototype = (masterManager, globalSwan) => {
17 | return {
18 | getData(path) {
19 | return this.privateProperties.raw.get(path);
20 | },
21 | /**
22 | * 通用的,向slave传递的数据操作统一方法
23 | *
24 | * @param {Object} dataParams - 数据操作的参数
25 | * @param {string} dataParams.type - 数据操作的类型
26 | * @param {string} dataParams.path - 数据操作的路径值
27 | * @param {Object} dataParams.value - 数据操作的数据值
28 | * @param {Function} dataParams.cb - 数据操作后的回调
29 | * @param {Object} dataParams.options - 数据操作的额外选项
30 | */
31 | sendDataOperation({
32 | type,
33 | path,
34 | value,
35 | cb = noop,
36 | options
37 | }) {
38 | const {
39 | raw,
40 | slaveId
41 | } = this.privateProperties;
42 | const setObject = typeof path === 'object' ? path : {
43 | [path]: value
44 | };
45 | cb = typeof cb === 'function' ? cb : noop;
46 | const callback = typeof value === 'function' ? value : cb;
47 | const pageUpdateStart = Date.now() + '';
48 |
49 | // 暂时只优化自定义组件的数据设置,进行throttle
50 | if (type === 'setCustomComponent') {
51 | this.operationSet = this.operationSet || [];
52 | this.operationSet.push({
53 | setObject,
54 | options,
55 | pageUpdateStart
56 | });
57 | clearTimeout(this.operationTimmer);
58 | this.operationTimmer = setTimeout(() => {
59 | // 先set到本地,然后通知slave更新视图
60 | this.sendMessageToCurSlave({
61 | slaveId,
62 | type: `${type}Data`,
63 | operationSet: this.operationSet
64 | });
65 | this.operationSet = [];
66 | }, SETDATA_THROTTLE_TIME);
67 | }
68 | else {
69 | // 先set到本地,然后通知slave更新视图
70 | this.sendMessageToCurSlave({
71 | type: `${type}Data`,
72 | slaveId,
73 | setObject,
74 | pageUpdateStart,
75 | options
76 | });
77 | }
78 | // 更新data
79 | for (const path in setObject) {
80 | raw[type] && raw[type](path, setObject[path]);
81 | }
82 | this.nextTick(callback);
83 | },
84 |
85 | sendMessageToCurSlave(message) {
86 | masterManager.communicator.sendMessage(this.privateProperties.slaveId, message);
87 | },
88 |
89 | /**
90 | * 页面中挂载的setData操作方法,操作后,会传到slave,对视图进行更改
91 | *
92 | * @param {string|Object} [path] - setData的数据操作路径,或setData的对象{path: value}
93 | * @param {*} [value] - setData的操作值
94 | * @param {Function} [cb] - setData的回调函数
95 | */
96 | setData(path, value, cb) {
97 | this.sendDataOperation({
98 | type: 'set',
99 | path,
100 | value,
101 | cb
102 | });
103 | },
104 | // splice方法系列
105 | pushData(path, value, cb) {
106 | this.sendDataOperation({
107 | type: 'push',
108 | path,
109 | value,
110 | cb
111 | });
112 | },
113 | popData(path, cb) {
114 | this.sendDataOperation({
115 | type: 'pop',
116 | path,
117 | value: null,
118 | cb
119 | });
120 | },
121 | unshiftData(path, value, cb) {
122 | this.sendDataOperation({
123 | type: 'unshift',
124 | path,
125 | value,
126 | cb
127 | });
128 | },
129 | shiftData(path, cb) {
130 | this.sendDataOperation({
131 | type: 'shift',
132 | path,
133 | value: null,
134 | cb
135 | });
136 | },
137 | removeAtData(path, index, cb) {
138 | this.sendDataOperation({
139 | type: 'remove',
140 | path,
141 | value: index,
142 | cb
143 | });
144 | },
145 | spliceData(path, args, cb) {
146 | this.sendDataOperation({
147 | type: 'splice',
148 | path,
149 | value: args,
150 | cb
151 | });
152 | },
153 |
154 | createCanvasContext(...args) {
155 | return globalSwan.createCanvasContext.call(this, ...args);
156 | },
157 | nextTick(fn) {
158 | masterManager.communicator
159 | .onMessage(`nextTick:${this.privateProperties.slaveId}`, () => fn(), {
160 | once: true
161 | });
162 | },
163 |
164 | /**
165 | * 页面级选择某个(id、class)全部的自定义组件
166 | *
167 | * @param {string} selector - 待选择的自定义组件id
168 | * @return {Array} - 所选的全部自定义组件集合
169 | */
170 | selectAllComponents(selector) {
171 | return this.privateMethod
172 | .getComponentsFromList(this.privateProperties.customComponents, selector, '*');
173 | },
174 |
175 | /**
176 | * 页面级选择某个(id、class)第一个自定义组件
177 | *
178 | * @param {string} selector - 待选择的自定义组件id
179 | * @return {Object} - 自定义组件被拦截的export输出 | 所选的自定义组件实例
180 | */
181 | selectComponent(selector) {
182 | const selectRes = this.selectAllComponents(selector)[0];
183 | // 当自定义组件中包含内置behavior时, 进行拦截操作
184 | const exportRes = builtInBehaviorsAction('swanComponentExport', selectRes);
185 | return exportRes.isBuiltInBehavior ? exportRes.resData : selectRes;
186 | },
187 |
188 | // page实例中的私有方法合集
189 | privateMethod: {
190 |
191 | /**
192 | * 发送初始数据到当前Page对应的slave上
193 | *
194 | * @param {Object} [appConfig] - 发送初始化数据时携带的appConfig信息
195 | */
196 | sendInitData(appConfig) {
197 | masterManager.communicator.sendMessage(
198 | this.privateProperties.slaveId,
199 | {
200 | type: 'initData',
201 | path: 'initData',
202 | value: this.data,
203 | // extraMessage: {
204 | // componentsData: this.privateMethod.getCustomComponentsData
205 | // .call(this, this.usingComponents, masterManager.communicator)
206 | // },
207 | slaveId: this.privateProperties.slaveId,
208 | appConfig
209 | }
210 | );
211 | }
212 | }
213 | };
214 | };
215 |
216 | let pagePrototype = null;
217 | // 获取page的prototype的单例方法,节省初始化
218 | export const getPagePrototypeInstance = (masterManager, globalSwan, pageLifeCycleEventEmitter) => {
219 | if (!pagePrototype) {
220 | pagePrototype = createPagePrototype(masterManager, globalSwan);
221 | initLifeCycle(masterManager, pagePrototype, pageLifeCycleEventEmitter);
222 | }
223 | return pagePrototype;
224 | };
225 |
--------------------------------------------------------------------------------
/src/master/page/slave-events-router.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file 小程序slave中组件相关的事件的派发,到达master中
3 | * 需要转发给:开发者/私有对象/日志 进行对应处理
4 | * @author houyu(houyu01@baidu.com)
5 | */
6 | import swanEvents from '../../utils/swan-events';
7 | import Slave from '../navigator/slave';
8 | // import TabSlave from '../navigator/tab-slave';
9 |
10 | /**
11 | * slave的事件封装分发器,封装了大量的无逻辑公有方法
12 | *
13 | * @class
14 | */
15 | export default class SlaveEventsRouter {
16 | constructor(masterManager, pageLifeCycleEventEmitter) {
17 | this.masterManager = masterManager;
18 | this.history = masterManager.navigator.history;
19 | this.slaveCommunicator = masterManager.communicator;
20 | this.pageLifeCycleEventEmitter = pageLifeCycleEventEmitter;
21 | swanEvents('masterPreloadConstructSlaveEventsRouter');
22 | }
23 |
24 | /**
25 | * 初始化所有页面级相关事件的绑定
26 | */
27 | initbindingEvents() {
28 | // this.bindPrivateEvents();
29 | this.bindDeveloperEvents();
30 | // this.bindEnvironmentEvents();
31 | this.bindLifeCycleEvent(this.pageLifeCycleEventEmitter);
32 | swanEvents('masterPreloadInitBindingEvents');
33 | }
34 |
35 | /**
36 | * 绑定开发者绑定的events
37 | */
38 | bindDeveloperEvents() {
39 | this.slaveCommunicator.onMessage('event', event => {
40 | swanEvents('masterListenEvent', event);
41 |
42 | const eventOccurredPageObject = this.history.seek(event.slaveId).getUserPageInstance();
43 | // if (event.customEventParams) {
44 | // const nodeId = event.customEventParams.nodeId;
45 | // const reflectComponent = eventOccurredPageObject
46 | // .privateProperties.customComponents[nodeId];
47 | // if (reflectComponent[event.value.reflectMethod]) {
48 | // reflectComponent[event.value.reflectMethod]
49 | // .call(reflectComponent, event.value.e);
50 | // }
51 | // }
52 | // else
53 | if (eventOccurredPageObject[event.value.reflectMethod]) {
54 | eventOccurredPageObject[event.value.reflectMethod]
55 | .call(eventOccurredPageObject, event.value.e);
56 | }
57 | });
58 | }
59 |
60 | /**
61 | * 调用发生事件的页面的同名方法
62 | *
63 | * @param {string} slaveId 想要派发的页面的slave的id
64 | * @param {string} methodName 事件名称
65 | * @param {Object|null} options 派发事件的可配置项
66 | * @param {...*} args 透传的事件参数
67 | * @return {*} 调用私有方法后,私有方法的返回值
68 | */
69 | // callEventOccurredPageMethod(slaveId, methodName, options = {}, ...args) {
70 | // const occurredSlave = this.history.seek(slaveId);
71 | // if (occurredSlave) {
72 | // return occurredSlave.callPrivatePageMethod(methodName, options, ...args);
73 | // }
74 | // return null;
75 | // }
76 |
77 | /**
78 | * 向所有slave,派发事件
79 | *
80 | * @param {string} methodName 事件名称
81 | * @param {Object|null} options 发事件的可配置项
82 | * @param {...*} args 透传的事件参数
83 | */
84 | // dispatchAllSlaveEvent(methodName, options = {}, ...args) {
85 | // this.history.each(slave => {
86 | // slave.callPrivatePageMethod(methodName, options, ...args);
87 | // });
88 | // }
89 |
90 | /**
91 | * 框架使用的私有的事件的绑定
92 | */
93 | // bindPrivateEvents() {
94 | // this.slaveCommunicator.onMessage('abilityMessage', event => {
95 | // if (event.value.type === 'rendered') {
96 | // return;
97 | // }
98 | // try {
99 | // this.callEventOccurredPageMethod(event.slaveId, event.value.type, {}, event.value.params);
100 | // }
101 | // catch (e) {
102 | // console.error(e);
103 | // }
104 | // });
105 | // }
106 |
107 | /**
108 | * 保证onShow执行在onReady之前
109 | *
110 | * @param {string} currentSlaveId 需要触发的页面的slaveId
111 | */
112 | // emitPageRender(currentSlaveId) {
113 | // this.slaveCommunicator.onMessage('abilityMessage', events => {
114 | // if (Object.prototype.toString.call(events) !== '[object Array]') {
115 | // events = [events];
116 | // }
117 | // events.forEach(event => {
118 | // if (event.value.type === 'rendered'
119 | // && event.slaveId === currentSlaveId
120 | // ) {
121 | // this.callEventOccurredPageMethod(
122 | // event.slaveId,
123 | // event.value.type,
124 | // {},
125 | // event.value.params
126 | // );
127 | // }
128 | // });
129 | // }, {listenPreviousEvent: true});
130 | // }
131 |
132 | /**
133 | * 调用用户在page实例上挂载的方法
134 | *
135 | * @param {String} [slaveId] - 要调用的页面实例的slaveId
136 | * @param {String} [methodName] - 要调用的page实例上的方法名
137 | * @param {...*} [args] - 透传的事件参数
138 | * @return {*} 函数调用后的返回值
139 | */
140 | callPageMethod(slaveId, methodName, ...args) {
141 | const occurredSlave = this.history.seek(slaveId);
142 | if (occurredSlave) {
143 | const occurredSlavePageObject = occurredSlave.userPageInstance;
144 | if (typeof occurredSlavePageObject[methodName] === 'function') {
145 | try {
146 | return occurredSlavePageObject[methodName](...args);
147 | }
148 | catch (e) {
149 | console.error(e);
150 | }
151 | }
152 | }
153 | return null;
154 | }
155 |
156 | /**
157 | * 绑定开发者绑定的events
158 | */
159 | // bindDeveloperEvents() {
160 | // this.slaveCommunicator.onMessage('event', event => {
161 | // const eventOccurredPageObject = this.history.seek(event.slaveId).getUserPageInstance();
162 | // if (event.customEventParams) {
163 | // const nodeId = event.customEventParams.nodeId;
164 | // const reflectComponent = eventOccurredPageObject
165 | // .privateProperties.customComponents[nodeId];
166 | // if (reflectComponent[event.value.reflectMethod]) {
167 | // reflectComponent[event.value.reflectMethod]
168 | // .call(reflectComponent, event.value.e);
169 | // }
170 | // }
171 | // else if (eventOccurredPageObject[event.value.reflectMethod]) {
172 | // eventOccurredPageObject[event.value.reflectMethod]
173 | // .call(eventOccurredPageObject, event.value.e);
174 | // }
175 | // });
176 | // }
177 |
178 | /**
179 | * 客户端触发的协议事件,非前端派发
180 | * 用于接收协议事件
181 | *
182 | */
183 | // bindEnvironmentEvents() {
184 | // this.masterManager.swaninterface
185 | // .bind('sharebtn', event => {
186 | // this.callEventOccurredPageMethod(event.wvID, 'share', {}, event, 'menu');
187 | // })
188 | // .bind('accountChange', event => {
189 | // this.dispatchAllSlaveEvent('accountChange');
190 | // })
191 | // .bind('backtohome', ({url, from}) => {
192 | // let topSlave = this.history.getTopSlaves()[0];
193 | // let currentSlaveUrl = '';
194 | // if (topSlave instanceof Slave) {
195 | // currentSlaveUrl = topSlave.accessUri;
196 | // }
197 | // else if (topSlave instanceof TabSlave) {
198 | // currentSlaveUrl = topSlave.children[topSlave.currentIndex].accessUri;
199 | // }
200 | // if (from !== 'menu' && currentSlaveUrl === url) {
201 | // from === 'relaunch' && swanEvents('masterFePageShow');
202 | // return;
203 | // }
204 | //
205 | // if (from === 'relaunch') {
206 | // swanEvents('masterOnNewScheme');
207 | // }
208 | // this.masterManager.navigator.reLaunch({url: `/${url}`});
209 | // });
210 | // }
211 |
212 | bindLifeCycleEvent(pageLifeCycleEventEmitter) {
213 |
214 | // const pageEventsToLifeCycle = ['onHide', 'onTabItemTap'];
215 | // 需要稍后补执行的操作集合
216 | // let backPageEventToLifeCycles = {
217 | // onTabItemTap: Object.create(null)
218 | // };
219 | // 确保onShow在onLoad之后
220 | pageLifeCycleEventEmitter.onMessage('PagelifeCycle', events => {
221 | if (Object.prototype.toString.call(events) !== '[object Array]') {
222 | events = [events];
223 | }
224 | // 对于客户端事件做一次中转,因为前端有特殊逻辑处理(onShow必须在onLoad之后)
225 | const detectOnShow = event => {
226 | if (event.params.eventName === 'onLoad') {
227 |
228 | swanEvents('masterlifeCycleEventOnShow', event);
229 |
230 | this.masterManager.lifeCycleEventEmitter
231 | .onMessage('onShow' + event.params.slaveId, paramsQueue => {
232 |
233 | // 筛选出本次的onShow的对应参数
234 | paramsQueue = [].concat(paramsQueue);
235 |
236 | // onshow对应的slave
237 | const showedSlave = paramsQueue.filter(params => {
238 | return +params.event.wvID === +event.params.slaveId;
239 | }).slice(-1);
240 |
241 | // 获取对象中的event
242 | const e = showedSlave[0].event;
243 |
244 | // 执行对应slave的onShow
245 | this.callPageMethod(event.params.slaveId, '_onShow', {}, e);
246 |
247 | // 触发页面的onRender逻辑
248 | // this.emitPageRender(event.params.slaveId);
249 |
250 | }, {listenPreviousEvent: true});
251 | }
252 | };
253 | events.forEach(detectOnShow);
254 | }, {listenPreviousEvent: true});
255 |
256 | // pageLifeCycleEventEmitter.onMessage('onTabItemTap', ({event, from}) => {
257 | // let wvID = event.wvID;
258 | // // 客户端触发onTabItemTap先于switchtab
259 | // // 进入新页面pageInstance在onTabItemTap时没有还ready,等待switchtab后内部触发处理
260 | // if (from === 'switchTab') {
261 | // let {type, tabIndexPage} = event;
262 | // let targetEvent = backPageEventToLifeCycles[type];
263 | // if (targetEvent && targetEvent[tabIndexPage]) {
264 | // this.callPageMethod(wvID, '_' + type, targetEvent[tabIndexPage]);
265 | // backPageEventToLifeCycles[type] = Object.create(null);
266 | // }
267 | // return;
268 | // }
269 | // const targetSlave = this.history.seek(wvID);
270 | // if (targetSlave) {
271 | // // 原逻辑
272 | // this.callPageMethod(wvID, '_onTabItemTap', event);
273 | // }
274 | // else {
275 | // // 保存后续调用。 android对于新页面没有传wvID,所以根据index+pagePath进行统一保证
276 | // backPageEventToLifeCycles.onTabItemTap[event.index + event.pagePath] = event;
277 | // }
278 | // }, {listenPreviousEvent: true});
279 |
280 | this.masterManager.lifeCycleEventEmitter.onMessage('onHide', e => {
281 | let event = e.event;
282 | this.callPageMethod(event.wvID, '_onHide', {}, event);
283 | }, {listenPreviousEvent: true});
284 |
285 | // this.masterManager.swaninterface
286 | // .bind('onTabItemTap', event => {
287 | // pageLifeCycleEventEmitter.fireMessage({
288 | // type: 'onTabItemTap',
289 | // event
290 | // });
291 | // });
292 |
293 | // this.masterManager.swaninterface
294 | // .bind('onForceReLaunch', event => {
295 | // let {slaveId, homePath, pagePath} = event;
296 | // let finalReLaunchPath = homePath;
297 | // let currentSlave = this.history.seek(slaveId);
298 | // if (currentSlave) {
299 | // const currentSlavePageObject = currentSlave.userPageInstance;
300 | // if (typeof currentSlavePageObject['onForceReLaunch'] === 'function') {
301 | // try {
302 | // finalReLaunchPath = currentSlavePageObject['onForceReLaunch']({homePath, pagePath});
303 | // finalReLaunchPath = finalReLaunchPath.replace(/^\//g, '');
304 | // this.masterManager.navigator.redirectTo({url: `/${finalReLaunchPath}`});
305 | // return;
306 | // }
307 | // catch (e) {
308 | // finalReLaunchPath = homePath;
309 | // }
310 | // }
311 | // }
312 | // this.masterManager.navigator.reLaunch({
313 | // url: `/${finalReLaunchPath}`,
314 | // force: true
315 | // });
316 | // });
317 | }
318 | }
319 |
--------------------------------------------------------------------------------
/src/master/proccessors/api-proccessor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file 对于swan全局对象的处理
3 | * @author houyu(houyu01@baidu.com)
4 | */
5 |
6 | // 预留,节后上线
7 | // import nextTick from '../../utils/next-tick';
8 |
9 | /**
10 | * 防止在App与page的onShow中调用过多次数的login
11 | * @param {Object} originSwan 未decorate之前的swan
12 | * @param {Object} pageLifeCycleEventEmitter 页面的生命周期事件对象
13 | * @param {Object} appLifeCycleEventEmitter app的生命周期事件对象
14 | * @return {Function} login函数
15 | */
16 | const lockLogin = (originSwan, pageLifeCycleEventEmitter, appLifeCycleEventEmitter) => {
17 | let isShowing = false;
18 | let loginTimes = 0;
19 | const originLogin = originSwan['login'];
20 | const lockEvents = ['onShow', 'onPreShow'];
21 | pageLifeCycleEventEmitter.onMessage('PagelifeCycle', ({params}) => {
22 | isShowing = lockEvents.indexOf(params.eventName) === 1;
23 | });
24 | appLifeCycleEventEmitter.onMessage('ApplifeCycle', ({params}) => {
25 | isShowing = lockEvents.indexOf(params.eventName) === 1;
26 | });
27 | const locker = {
28 | timesCheck(times, delay) {
29 | if (this['repeat' + times] === undefined) {
30 | this['repeat' + times] = 0;
31 | }
32 | if (this['repeat' + times] >= times) {
33 | return false;
34 | }
35 | if (this['repeat' + times] < 1) {
36 | setTimeout(() => {
37 | this['repeat' + times] = 0;
38 | }, delay);
39 | }
40 | this['repeat' + times]++;
41 | return true;
42 | },
43 | enter(isShowing) {
44 | // 不在show中,可以任意发送
45 | // 如果在show中,则2秒内不得进入1次以上,30秒内不得进入2次以上
46 | return !isShowing || this.timesCheck(1, 2e3) && this.timesCheck(2, 3e4);
47 | }
48 | };
49 |
50 | return params => {
51 | if (!locker.enter(isShowing)) {
52 | params.fail && params.fail();
53 | return false;
54 | }
55 | return originLogin.call(originSwan, params);
56 | };
57 | };
58 |
59 | /**
60 | * 处理api的函数,对API进行swan-core本身的代理
61 | * @param {Object} originSwan 原始的api
62 | * @param {Object} context 装饰api使用的上下文
63 | * @param {Object} context.navigator 小程序的navigator对象
64 | * @param {Object} context.swanComponents 小程序的组件
65 | * @param {Object} context.pageLifeCycleEventEmitter 小程序page生命周期的事件流
66 | * @param {Object} context.appLifeCycleEventEmitter 小程序app生命周期的事件流
67 | * @param {Object} context.swanEventsCommunicator 小程序合并的统计事件流
68 | * @param {Object} context.hostShareParamsProccess 小程序自定义的宿主分享处理
69 | * @param {Object} context.communicator 小程序的slave-master通讯器
70 | * @param {Object} context.swaninterface 小程序的通用接口(包含swan与boxjs)
71 | * @return {Object} 处理后的api
72 | */
73 | export const apiProccess = (originSwan, {
74 | navigator,
75 | swanComponents,
76 | pageLifeCycleEventEmitter,
77 | appLifeCycleEventEmitter,
78 | swanEventsCommunicator,
79 | hostShareParamsProccess,
80 | communicator,
81 | swaninterface
82 | }) => {
83 | const getSlaveId = () => navigator.history.getTopSlaves()[0].getSlaveId();
84 | const operators = swanComponents.getContextOperators(swaninterface, communicator, getSlaveId);
85 | return Object.assign(originSwan, {
86 | navigateTo: navigator.navigateTo.bind(navigator),
87 | // navigateBack: navigator.navigateBack.bind(navigator),
88 | // redirectTo: navigator.redirectTo.bind(navigator),
89 | // switchTab: navigator.switchTab.bind(navigator),
90 | // reLaunch: navigator.reLaunch.bind(navigator),
91 |
92 | /**
93 | * 所有组件相关的操作API
94 | */
95 | ...operators,
96 |
97 | /**
98 | * 开发者的自定义数据上报
99 | * @param {string} reportName 上报的自定义事件名称
100 | * @param {Object} reportParams 用户上报的自定义事件的参数
101 | * @return {*} 发送日志后的返回值
102 | */
103 | reportAnalytics: (reportName, reportParams) => swanEventsCommunicator.fireMessage({
104 | type: 'SwanEvents',
105 | params: {
106 | eventName: 'reportAnalytics',
107 | e: {
108 | reportName,
109 | reportParams
110 | }
111 | }
112 | }),
113 | /**
114 | * 宿主自定义的分享,取代直接的api的分享
115 | * @param {Object} userParams 调用openShare的小程序开发者传递的param
116 | */
117 | openShare: (originShare => userParams => {
118 | // const appInfo = swaninterface.boxjs.data.get({name: 'swan-appInfoSync'});
119 | // let proccessedParams = hostShareParamsProccess(userParams, appInfo);
120 | // return originShare.call(originSwan, proccessedParams);
121 | return '';
122 | })(originSwan['openShare']),
123 |
124 | login: lockLogin(originSwan, pageLifeCycleEventEmitter, appLifeCycleEventEmitter),
125 |
126 | /**
127 | * 截图的调用回调
128 | * @param {Function} callback 截图后的回调
129 | */
130 | onUserCaptureScreen: callback => {
131 | swaninterface.bind('onUserCaptureScreen', () => {
132 | typeof callback === 'function' && callback();
133 | });
134 | }
135 | });
136 | };
137 |
--------------------------------------------------------------------------------
/src/slave/component-factory/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Communicator from '../../utils/communication';
3 | import swanEvents from '../../utils/swan-events';
4 |
5 | /**
6 | * san的组件工厂,生产组件类
7 | * @class
8 | */
9 | export default class SanFactory {
10 | constructor(componentDefaultProps, behaviors) {
11 | this.behaviors = behaviors;
12 | this.componentDefaultProps = componentDefaultProps;
13 | this.componentInfos = {};
14 |
15 | // 依赖池,所有的组件需要的依赖是工厂提供的
16 | const communicator = Communicator.getInstance(this.componentDefaultProps.swaninterface);
17 | this.dependenciesPool = {
18 | // san,
19 | communicator,
20 | ...this.componentDefaultProps
21 | };
22 | }
23 | /**
24 | * 创建组件的工厂方法
25 | *
26 | * @param {Object} componentName - 创建组件的名称
27 | * @param {Object} options - 创建组件用的属性
28 | */
29 | componentDefine(componentName, componentInfo = {}) {
30 | this.componentInfos[componentName] = {
31 | ...componentInfo
32 | };
33 | }
34 |
35 | /**
36 | * 获取所有的注册过的组件
37 | *
38 | * @return {Object} 获取的当前注册过的所有组件
39 | */
40 | getAllComponents() {
41 | return this.getComponents();
42 | }
43 | /**
44 | * 获取所有注册过的组件
45 | *
46 | * @param {string|Array} componentName - 需要获取的component的名称
47 | * @return {Object} 获取的所有的注册的组件
48 | */
49 | getComponents(componentName = this.getAllComponentsName()) {
50 |
51 | const componentNames = [].concat(componentName);
52 |
53 | const components = componentNames.reduce((componentSet, componentName) => {
54 | componentSet[componentName] = this.createComponents(componentName);
55 | return componentSet;
56 | }, {});
57 |
58 | return typeof componentName === 'string' ? components[componentName] : components;
59 | }
60 |
61 | /**
62 | * 获取所有组件的名称
63 | *
64 | * @return {Array} 所有组件的名称集合
65 | */
66 | getAllComponentsName() {
67 | return Object.keys(this.componentInfos);
68 | }
69 |
70 | /**
71 | * 关键类,百度小程序通过san创建san的组件,我改为使用vue来创建组件
72 | *
73 | * @param {string} componentName - 需要创建的组件的名称
74 | * @return {class} - 创建的组件
75 | */
76 | createComponents(componentName) {
77 |
78 | const componentInfo = this.componentInfos[componentName];
79 | // if (componentInfo && componentInfo.createdClass) {
80 | // return componentInfo.createdClass;
81 | // }
82 |
83 | // 获取超类名称
84 | const superComponentName = componentInfo.superComponent || '';
85 | // 获取到当前组件的超类
86 | const superComponent = this.componentInfos[superComponentName] || {};
87 |
88 | // 原始的组件的原型
89 | // const originComponentPrototype = componentInfo.componentPrototype;
90 |
91 | // 获取超类名称
92 | // const superComponentName = originComponentPrototype.superComponent || 'swan-component';
93 |
94 | // 继承
95 | const mergedComponentOptions = this.mergeComponentOptions(
96 | superComponent.options,
97 | componentInfo.options
98 | );
99 |
100 | const mergedDependencies = (superComponent.dependencies || [])
101 | .reduce((r, v, k) => {
102 | r.indexOf(v) < 0 && r.push(v);
103 | return r;
104 | }, (componentInfo.dependencies || []));
105 |
106 | // 获取dependencies
107 | // const mergedDependencies = componentInfo.dependencies || [];
108 | // mergedDependencies.push(superComponent.dependencies);
109 |
110 | // 用merge好的proto来定义san组件(类)并返回
111 | const vueComponent = this.defineVueComponent(mergedDependencies, mergedComponentOptions);
112 |
113 | return vueComponent;
114 |
115 | // 返回修饰过的类
116 | // return this.behaviors(componentPrototype.behaviors || [], componentPrototype);
117 | }
118 |
119 | /**
120 | * 将两个组件的proto给merge为一个
121 | *
122 | * @param {Object} targetProto - 被merge的组件proto
123 | * @param {Object} mergeProto - 待merge入的组件proto
124 | * @return {Object} merge结果
125 | */
126 | mergeComponentOptions = (targetOptions, mergeOptions) => {
127 | // merge传入的proto
128 | return Object.keys(mergeOptions)
129 | .reduce((mergedClassProto, propName) => {
130 | switch (propName) {
131 | case 'constructor':
132 | case 'detached':
133 | case 'created':
134 | mergedClassProto[propName] = function (options) {
135 | targetOptions[propName] && targetOptions[propName].call(this, options);
136 | mergeOptions[propName] && mergeOptions[propName].call(this, options);
137 | };
138 | break;
139 |
140 | case 'computed':
141 | mergedClassProto['computed'] = Object.assign(
142 | {},
143 | mergedClassProto[propName],
144 | mergeOptions[propName]
145 | );
146 | break;
147 |
148 | default:
149 | mergedClassProto[propName] = mergeOptions[propName];
150 | }
151 | return mergedClassProto;
152 | }, {...targetOptions});
153 | }
154 |
155 | /**
156 | * 传入proto,定义san组件(官网标准的定义san组件方法)
157 | *
158 | * @param {Object} proto 组件类的方法表
159 | * @return {Function} san组件类
160 | */
161 | defineVueComponent(dependencies, options) {
162 |
163 | // function Component(options) {
164 | // san.Component.call(this, options);
165 | // proto.constructor && proto.constructor.call(this, options);
166 | // }
167 | //
168 | // san.inherits(Component, san.Component);
169 | //
170 | // Object.keys(proto)
171 | // .forEach(propName => {
172 | // if (propName !== 'constructor') {
173 | // Component.prototype[propName] = proto[propName];
174 | // }
175 | // });
176 |
177 | console.log('defineVueComponent', options);
178 |
179 | const Component = Vue.extend(options);
180 |
181 | Component.getInitData = function(params){
182 | options = {
183 | data: {
184 |
185 | }
186 | };
187 | for (var k in params.value) {
188 | // this.data.set();
189 | // this.$set(this.$data, k, params.value[k]);
190 | options.data[k] = params.value[k];
191 | }
192 | return options;
193 | };
194 |
195 | Component.slaveLoaded = function () {
196 | console.log(`Slave 发送 slaveLoaded 事件,slaveId=${window.slaveId}`);
197 | window.testutils.clientActions.dispatchEvent('slaveLoaded', {
198 | value: {
199 | status: 'loaded'
200 | },
201 | slaveId: window.slaveId
202 | });
203 | };
204 |
205 | // const obj = {
206 | // component: Component,
207 | // getInitData: function(params){
208 | // options = {
209 | // data: {
210 | //
211 | // }
212 | // };
213 | // for (var k in params.value) {
214 | // // this.data.set();
215 | // // this.$set(this.$data, k, params.value[k]);
216 | // options.data[k] = params.value[k];
217 | // }
218 | // return options;
219 | // },
220 | // slaveLoaded: function () {
221 | // console.log(`Slave 发送 slaveLoaded 事件,slaveId=${window.slaveId}`);
222 | // window.testutils.clientActions.dispatchEvent('slaveLoaded', {
223 | // value: {
224 | // status: 'loaded'
225 | // },
226 | // slaveId: window.slaveId
227 | // });
228 | // }
229 | // };
230 |
231 | // 用组件依赖装饰组件原型,生成组件原型
232 | const componentPrototype = this.decorateComponentPrototype(dependencies);
233 | Object.keys(componentPrototype)
234 | .forEach(propName => {
235 | if (propName !== 'constructor') {
236 | Component[propName] = componentPrototype[propName];
237 | Component.prototype[`$${propName}`] = componentPrototype[propName];
238 | }
239 | });
240 |
241 | return Component;
242 | }
243 |
244 | /**
245 | * 使用装饰器装饰组件原型
246 | *
247 | * @param {Object} componentPrototype 组件原型
248 | * @param {Object} componentInfo 组件原始传入的构造器
249 | * @return {Object} 装饰后的组件原型
250 | */
251 | decorateComponentPrototype(dependencies) {
252 |
253 | // 所有的组件的依赖在依赖池寻找后的结果
254 | const newDependencies = dependencies.reduce((depends, depsName) => {
255 | depends[depsName] = this.dependenciesPool[depsName];
256 | return depends;
257 | }, {});
258 |
259 | // merge后的组件原型,可以用来注册san组件
260 | return newDependencies;
261 | }
262 |
263 | }
264 |
265 | /**
266 | * 获取component的生产工厂
267 | *
268 | * @param {Object} componentDefaultProps - 默认的组件的属性
269 | * @param {Object} componentProtos - 所有组件的原型
270 | * @param {Object} behaviors - 所有的组件装饰器
271 | * @return {Object} 初始化后的componentFactory
272 | */
273 | export const getComponentFactory = (componentDefaultProps, component, behaviors) => {
274 |
275 | swanEvents('slavePreloadGetComponentFactory');
276 |
277 | const sanFactory = new SanFactory(componentDefaultProps, behaviors);
278 |
279 | swanEvents('slavePreloadDefineComponentsStart');
280 |
281 | Object.keys(component)
282 | .forEach(protoName => sanFactory.componentDefine(protoName, component[protoName]));
283 |
284 | swanEvents('slavePreloadDefineComponentsEnd');
285 |
286 | return sanFactory;
287 | };
--------------------------------------------------------------------------------
/src/slave/index.js:
--------------------------------------------------------------------------------
1 | import {getComponentFactory} from './component-factory';
2 | import swanEvents from '../utils/swan-events';
3 | import {loader} from '../utils';
4 |
5 | export default class Slave {
6 | constructor(global, swaninterface, swanComponents) {
7 | swanEvents('slavePreloadStart');
8 | this.context = global;
9 | // this.context.require = require;
10 | // this.context.define = define;
11 | // this.context.san = san;
12 | // this.context.swan = swaninterface.swan;
13 | // this.context.swaninterface = swaninterface; // 远程调试用
14 | this.swaninterface = swaninterface;
15 | this.swanComponents = swanComponents;
16 | // this.openSourceDebugger();
17 | // this.extension = new Extension(global, swaninterface);
18 | this.registerComponents();
19 | this.listenPageReady(global);
20 | // this.extension.use(this);
21 | swanEvents('slavePreloadEnd');
22 | }
23 |
24 | registerComponents() {
25 | const swaninterface = this.swaninterface;
26 | const {versionCompare, boxVersion} = this.swaninterface.boxjs.platform;
27 | const componentProtos = this.swanComponents.getComponents({
28 | isIOS: false,
29 | versionCompare,
30 | boxVersion
31 | });
32 | swanEvents('slavePreloadGetComponents');
33 | const componentDefaultProps = {swaninterface};
34 | const componentFactory = getComponentFactory(componentDefaultProps,
35 | {...componentProtos},
36 | this.swanComponents.getBehaviorDecorators());
37 |
38 | global.componentFactory = componentFactory;
39 |
40 | global.pageRender = (pageTemplate, templateComponents, customComponents, filters, modules) => {
41 | console.log('salve pageRender run...');
42 |
43 | // 用于记录用户模板代码在执行pageRender之前的时间消耗,包括了pageContent以及自定义模板的代码还有filter在加载过程中的耗时
44 | // global.FeSlaveSwanJsParseEnd = Date.now();
45 | let filtersObj = {};
46 | // filters && filters.forEach(element => {
47 | // let func = element.func;
48 | // let module = element.module;
49 | // filtersObj[element.filterName] = (...args) => {
50 | // return modules[module][func](...args);
51 | // };
52 | // });
53 |
54 | global.isNewTemplate = true;
55 | swanEvents('slaveActivePageRender', pageTemplate);
56 |
57 | console.log('pageTemplate', pageTemplate);
58 | // 定义当前页面的组件
59 | componentFactory.componentDefine(
60 | 'page',
61 | {
62 | superComponent: 'super-page',
63 | options: {
64 | // template: `