提交 17bbbf88 编写于 作者: fxy060608's avatar fxy060608

feat(runtime): add EventChannel

上级 6e2e7815
......@@ -636,6 +636,74 @@ Component = function (options = {}) {
return MPComponent(options)
};
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const PAGE_EVENT_HOOKS = [
'onPullDownRefresh',
'onReachBottom',
......@@ -1346,7 +1414,31 @@ function parseApp$1 (vm) {
return appOptions
}
const eventChannels = {};
const eventChannelStack = [];
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp$1(vm));
return vm
}
......@@ -1520,8 +1612,10 @@ function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query; // 兼容 mpvue
this.$vm.__call_hook('onLoad', query);
......
......@@ -8522,6 +8522,82 @@ var serviceContext = (function () {
}
}
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
let id$2 = 0;
function initEventChannel (events, cache = true) {
id$2++;
const eventChannel = new EventChannel(id$2, events);
return eventChannel
}
const pageFactory = Object.create(null);
function definePage (name, createPageVueComponent) {
......@@ -8613,7 +8689,8 @@ var serviceContext = (function () {
path,
query,
openType,
webview
webview,
eventChannel
}) {
if (preloadWebviews[url]) {
webview = preloadWebviews[url];
......@@ -8625,6 +8702,9 @@ var serviceContext = (function () {
}
webview = null;
} else {
if (eventChannel) {
webview.__page__.eventChannel = eventChannel;
}
pages.push(webview.__page__);
if (process.env.NODE_ENV !== 'production') {
console.log(`[uni-app] reuse preloadWebview(${path},${webview.id})`);
......@@ -8680,6 +8760,7 @@ var serviceContext = (function () {
// 导致 webview.getStyle 等逻辑出错(旧的 webview 内部 plus 被释放)
return plus.webview.getWebviewById(webview.id)
},
eventChannel,
$page: {
id: parseInt(webview.id),
meta: routeOptions.meta,
......@@ -8745,10 +8826,11 @@ var serviceContext = (function () {
return webview
}
function _navigateTo ({
function _navigateTo({
url,
path,
query,
events,
animationType,
animationDuration
}, callbackId) {
......@@ -8757,26 +8839,30 @@ var serviceContext = (function () {
path
});
const eventChannel = initEventChannel(events, false);
showWebview(
registerPage({
url,
path,
query,
openType: 'navigate'
openType: 'navigate',
eventChannel
}),
animationType,
animationDuration,
() => {
invoke$1(callbackId, {
errMsg: 'navigateTo:ok'
errMsg: 'navigateTo:ok',
eventChannel
});
}
);
setStatusBarStyle();
}
function navigateTo$1 ({
function navigateTo$1({
url,
events,
openType,
animationType,
animationDuration
......@@ -8792,11 +8878,12 @@ var serviceContext = (function () {
animationDuration = routeStyles.animationDuration || globalStyle.animationDuration || ANI_DURATION;
}
const query = parseQuery(urls[1] || '');
navigate(path, function () {
navigate(path, function() {
_navigateTo({
url,
path,
query,
events,
animationType,
animationDuration
}, callbackId);
......@@ -14304,7 +14391,7 @@ var serviceContext = (function () {
}
var vuePlugin = {
install (Vue, options) {
install(Vue, options) {
initVue(Vue);
initData(Vue);
......@@ -14312,14 +14399,18 @@ var serviceContext = (function () {
initPolyfill(Vue);
Vue.prototype.getOpenerEventChannel = function() {
return this.$root.$scope.eventChannel || new EventChannel()
};
Object.defineProperty(Vue.prototype, '$page', {
get () {
get() {
return this.$root.$scope.$page
}
});
// 兼容旧版本
Object.defineProperty(Vue.prototype, '$mp', {
get () {
get() {
return {
page: this.$root.$scope
}
......@@ -14327,9 +14418,9 @@ var serviceContext = (function () {
});
const oldMount = Vue.prototype.$mount;
Vue.prototype.$mount = function mount (el, hydrating) {
Vue.prototype.$mount = function mount(el, hydrating) {
if (this.mpType === 'app') {
this.$options.render = function () {};
this.$options.render = function() {};
if (weex.config.preload) { // preload
if (process.env.NODE_ENV !== 'production') {
console.log('[uni-app] preload app-service.js');
......@@ -14338,7 +14429,7 @@ var serviceContext = (function () {
globalEvent.addEventListener('launchApp', () => {
if (process.env.NODE_ENV !== 'production') {
console.log('[uni-app] launchApp');
}
}
plus.updateConfigInfo && plus.updateConfigInfo();
registerApp(this);
oldMount.call(this, el, hydrating);
......@@ -14350,7 +14441,7 @@ var serviceContext = (function () {
return oldMount.call(this, el, hydrating)
};
Vue.prototype.$nextTick = function nextTick (cb) {
Vue.prototype.$nextTick = function nextTick(cb) {
const renderWatcher = this._watcher;
const callback = typeof cb === 'function';
const result = new Promise((resolve) => {
......
......@@ -356,6 +356,109 @@ var baseApi = /*#__PURE__*/Object.freeze({
interceptors: interceptors
});
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const eventChannels = {};
const eventChannelStack = [];
let id = 0;
function initEventChannel (events) {
id++;
const eventChannel = new EventChannel(id, events);
eventChannels[id] = eventChannel;
eventChannelStack.push(eventChannel);
return eventChannel
}
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
var navigateTo = {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id;
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id;
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel();
}
};
function findExistsPageIndex (url) {
const pages = getCurrentPages();
let len = pages.length;
......@@ -466,6 +569,7 @@ function _handleSystemInfo (result) {
}
const protocols = { // 需要做转换的 API 列表
navigateTo,
redirectTo,
returnValue (methodName, res = {}) { // 通用 returnValue 解析
if (res.error || res.errorMessage) {
......@@ -2081,6 +2185,17 @@ function parseApp (vm) {
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp(vm));
return vm
}
......@@ -2163,8 +2278,11 @@ function parsePage (vuePageOptions) {
// 触发首次 setData
this.$vm.$mount();
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + this.route + stringifyQuery(copyQuery)
};
this.options = query;
......
......@@ -356,6 +356,109 @@ var baseApi = /*#__PURE__*/Object.freeze({
interceptors: interceptors
});
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const eventChannels = {};
const eventChannelStack = [];
let id = 0;
function initEventChannel (events) {
id++;
const eventChannel = new EventChannel(id, events);
eventChannels[id] = eventChannel;
eventChannelStack.push(eventChannel);
return eventChannel
}
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
var navigateTo = {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id;
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id;
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel();
}
};
function findExistsPageIndex (url) {
const pages = getCurrentPages();
let len = pages.length;
......@@ -479,6 +582,9 @@ function _handleEnvInfo (result) {
// 需要做转换的 API 列表
const protocols = {
returnValue (methodName, res = {}) { // 通用 returnValue 解析,部分 API 的 res 为 undefined,比如 navigateTo
return res
},
request: {
args (fromArgs) {
// TODO
......@@ -499,7 +605,8 @@ const protocols = {
args: {
method: false
}
},
},
navigateTo,
redirectTo,
previewImage,
getRecorderManager: {
......@@ -1518,6 +1625,17 @@ function parseApp (vm) {
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp(vm));
return vm
}
......@@ -1675,8 +1793,14 @@ function parseComponent (vueOptions) {
// 百度 当组件作为页面时 pageinstancce 不是原来组件的 instance
this.pageinstance.$vm = this.$vm;
if (hasOwn(this.pageinstance, '_$args')) {
this.$vm.$mp.query = this.pageinstance._$args;
this.$vm.__call_hook('onLoad', this.pageinstance._$args);
const query = this.pageinstance._$args;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.pageinstance.$page = this.$page = {
fullPath: '/' + this.pageinstance.route + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query;
this.$vm.__call_hook('onLoad', query);
this.$vm.__call_hook('onShow');
delete this.pageinstance._$args;
}
......@@ -1720,8 +1844,10 @@ function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query; // 兼容 mpvue
this.$vm.__call_hook('onLoad', query);
......@@ -1751,20 +1877,25 @@ function parsePage (vuePageOptions) {
});
// 纠正百度小程序生命周期methods:onShow在methods:onLoad之前触发的问题
pageOptions.methods.onShow = function onShow () {
if (this.$vm && this.$vm.$mp.query) {
this.$vm.__call_hook('onShow');
}
pageOptions.methods.onShow = function onShow () {
if (this.$vm && this.$vm.$mp.query) {
this.$vm.__call_hook('onShow');
}
};
pageOptions.methods.onLoad = function onLoad (args) {
pageOptions.methods.onLoad = function onLoad (query) {
// 百度 onLoad 在 attached 之前触发,先存储 args, 在 attached 里边触发 onLoad
if (this.$vm) {
this.$vm.$mp.query = args;
this.$vm.__call_hook('onLoad', args);
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.pageinstance.$page = this.$page = {
fullPath: '/' + this.pageinstance.route + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query;
this.$vm.__call_hook('onLoad', query);
this.$vm.__call_hook('onShow');
} else {
this.pageinstance._$args = args;
this.pageinstance._$args = query;
}
};
......
......@@ -356,6 +356,109 @@ var baseApi = /*#__PURE__*/Object.freeze({
interceptors: interceptors
});
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const eventChannels = {};
const eventChannelStack = [];
let id = 0;
function initEventChannel (events) {
id++;
const eventChannel = new EventChannel(id, events);
eventChannels[id] = eventChannel;
eventChannelStack.push(eventChannel);
return eventChannel
}
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
var navigateTo = {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id;
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id;
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel();
}
};
function findExistsPageIndex (url) {
const pages = getCurrentPages();
let len = pages.length;
......@@ -423,6 +526,7 @@ var previewImage = {
};
const protocols = {
navigateTo,
redirectTo,
previewImage
};
......@@ -1435,6 +1539,17 @@ function parseApp$1 (vm) {
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp$1(vm));
return vm
}
......@@ -1610,8 +1725,10 @@ function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query; // 兼容 mpvue
this.$vm.__call_hook('onLoad', query);
......
......@@ -356,6 +356,109 @@ var baseApi = /*#__PURE__*/Object.freeze({
interceptors: interceptors
});
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const eventChannels = {};
const eventChannelStack = [];
let id = 0;
function initEventChannel (events) {
id++;
const eventChannel = new EventChannel(id, events);
eventChannels[id] = eventChannel;
eventChannelStack.push(eventChannel);
return eventChannel
}
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
var navigateTo = {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id;
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id;
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel();
}
};
function findExistsPageIndex (url) {
const pages = getCurrentPages();
let len = pages.length;
......@@ -521,6 +624,7 @@ const protocols = {
sizeType: false
}
},
navigateTo,
redirectTo,
previewImage,
connectSocket: {
......@@ -1607,6 +1711,17 @@ function parseApp (vm) {
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp(vm));
return vm
}
......@@ -1802,8 +1917,10 @@ function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query; // 兼容 mpvue
this.$vm.__call_hook('onLoad', query);
......
......@@ -356,6 +356,109 @@ var baseApi = /*#__PURE__*/Object.freeze({
interceptors: interceptors
});
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const eventChannels = {};
const eventChannelStack = [];
let id = 0;
function initEventChannel (events) {
id++;
const eventChannel = new EventChannel(id, events);
eventChannels[id] = eventChannel;
eventChannelStack.push(eventChannel);
return eventChannel
}
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
var navigateTo = {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id;
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id;
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel();
}
};
function findExistsPageIndex (url) {
const pages = getCurrentPages();
let len = pages.length;
......@@ -434,7 +537,8 @@ function addSafeAreaInsets (result) {
}
}
const protocols = {
redirectTo,
redirectTo,
navigateTo,
previewImage,
getSystemInfo: {
returnValue: addSafeAreaInsets
......@@ -1388,6 +1492,17 @@ function parseApp (vm) {
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp(vm));
return vm
}
......@@ -1559,8 +1674,10 @@ function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query; // 兼容 mpvue
this.$vm.__call_hook('onLoad', query);
......
......@@ -356,6 +356,109 @@ var baseApi = /*#__PURE__*/Object.freeze({
interceptors: interceptors
});
class EventChannel {
constructor (id, events) {
this.id = id;
this.listener = {};
this.emitCache = {};
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name]);
});
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName];
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args);
});
this.listener[eventName] = fns.filter(opt => opt.type !== 'once');
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn);
this._clearCache(eventName);
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn);
this._clearCache(eventName);
}
off (eventName, fn) {
const fns = this.listener[eventName];
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1);
i--;
}
i++;
}
} else {
delete this.listener[eventName];
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName];
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()));
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
});
}
}
const eventChannels = {};
const eventChannelStack = [];
let id = 0;
function initEventChannel (events) {
id++;
const eventChannel = new EventChannel(id, events);
eventChannels[id] = eventChannel;
eventChannelStack.push(eventChannel);
return eventChannel
}
function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id];
delete eventChannels[id];
return eventChannel
}
return eventChannelStack.shift()
}
var navigateTo = {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id;
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id;
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel();
}
};
function findExistsPageIndex (url) {
const pages = getCurrentPages();
let len = pages.length;
......@@ -423,6 +526,7 @@ var previewImage = {
};
const protocols = {
navigateTo,
redirectTo,
previewImage
};
......@@ -1438,6 +1542,17 @@ function parseApp (vm) {
}
function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
return this.__eventChannel__ || new EventChannel()
};
const callHook = Vue.prototype.__call_hook;
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__);
delete args.__id__;
}
return callHook.call(this, hook, args)
};
App(parseApp(vm));
return vm
}
......@@ -1633,8 +1748,10 @@ function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query;
const copyQuery = Object.assign({}, query);
delete copyQuery.__id__;
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
};
this.$vm.$mp.query = query; // 兼容 mpvue
this.$vm.__call_hook('onLoad', query);
......
export default class EventChannel {
constructor (id, events) {
this.id = id
this.listener = {}
this.emitCache = {}
if (events) {
Object.keys(events).forEach(name => {
this.on(name, events[name])
})
}
}
emit (eventName, ...args) {
const fns = this.listener[eventName]
if (!fns) {
return (this.emitCache[eventName] || (this.emitCache[eventName] = [])).push(args)
}
fns.forEach(opt => {
opt.fn.apply(opt.fn, args)
})
this.listener[eventName] = fns.filter(opt => opt.type !== 'once')
}
on (eventName, fn) {
this._addListener(eventName, 'on', fn)
this._clearCache(eventName)
}
once (eventName, fn) {
this._addListener(eventName, 'once', fn)
this._clearCache(eventName)
}
off (eventName, fn) {
const fns = this.listener[eventName]
if (!fns) {
return
}
if (fn) {
for (let i = 0; i < fns.length;) {
if (fns[i].fn === fn) {
fns.splice(i, 1)
i--
}
i++
}
} else {
delete this.listener[eventName]
}
}
_clearCache (eventName) {
const cacheArgs = this.emitCache[eventName]
if (cacheArgs) {
for (; cacheArgs.length > 0;) {
this.emit.apply(this, [eventName].concat(cacheArgs.shift()))
}
}
}
_addListener (eventName, type, fn) {
(this.listener[eventName] || (this.listener[eventName] = [])).push({
fn,
type
})
}
}
import EventChannel from './EventChannel'
const eventChannels = {}
const eventChannelStack = []
let id = 0
export function initEventChannel (events, cache = true) {
id++
const eventChannel = new EventChannel(id, events)
if (cache) {
eventChannels[id] = eventChannel
eventChannelStack.push(eventChannel)
}
return eventChannel
}
export function getEventChannel (id) {
if (id) {
const eventChannel = eventChannels[id]
delete eventChannels[id]
return eventChannel
}
return eventChannelStack.shift()
}
export default {
args (fromArgs, toArgs) {
const id = initEventChannel(fromArgs.events).id
if (fromArgs.url) {
fromArgs.url = fromArgs.url + (fromArgs.url.indexOf('?') === -1 ? '?' : '&') + '__id__=' + id
}
},
returnValue (fromRes, toRes) {
fromRes.eventChannel = getEventChannel()
}
}
import 'uni-platform/runtime/index'
import Vue from 'vue'
import 'uni-platform/runtime/index'
import EventChannel from 'uni-helpers/EventChannel'
import parseApp from 'uni-platform/runtime/wrapper/app-parser'
import {
getEventChannel
} from 'uni-helpers/navigate-to'
export default function createApp (vm) {
Vue.prototype.getOpenerEventChannel = function () {
if (!this.__eventChannel__) {
this.__eventChannel__ = new EventChannel()
}
return this.__eventChannel__
}
const callHook = Vue.prototype.__call_hook
Vue.prototype.__call_hook = function (hook, args) {
if (hook === 'onLoad' && args && args.__id__) {
this.__eventChannel__ = getEventChannel(args.__id__)
delete args.__id__
}
return callHook.call(this, hook, args)
}
App(parseApp(vm))
return vm
}
......@@ -2,6 +2,8 @@ import {
hasOwn
} from 'uni-shared'
import EventChannel from 'uni-helpers/EventChannel'
export default function createPage (pageVm, options) {
const $route = pageVm.$route
pageVm.route = $route.meta.pagePath
......@@ -18,6 +20,10 @@ export default function createPage (pageVm, options) {
options: options,
meta: Object.assign({}, $route.meta)
}
const eventChannel = pageVm.$router.$eventChannel || new EventChannel()
pageVm.getOpenerEventChannel = function () {
return eventChannel
}
// 兼容 mpvue
pageVm.$vm = pageVm
pageVm.$root = pageVm
......
import {
parseQuery
} from 'uni-shared'
import {
initEventChannel
} from 'uni-helpers/navigate-to'
import {
ANI_SHOW,
......@@ -28,6 +31,7 @@ function _navigateTo ({
url,
path,
query,
events,
animationType,
animationDuration
}, callbackId) {
......@@ -36,18 +40,21 @@ function _navigateTo ({
path
})
const eventChannel = initEventChannel(events, false)
showWebview(
registerPage({
url,
path,
query,
openType: 'navigate'
openType: 'navigate',
eventChannel
}),
animationType,
animationDuration,
() => {
invoke(callbackId, {
errMsg: 'navigateTo:ok'
errMsg: 'navigateTo:ok',
eventChannel
})
}
)
......@@ -56,6 +63,7 @@ function _navigateTo ({
export function navigateTo ({
url,
events,
openType,
animationType,
animationDuration
......@@ -76,8 +84,9 @@ export function navigateTo ({
url,
path,
query,
events,
animationType,
animationDuration
}, callbackId)
}, openType === 'appLaunch')
}
}
......@@ -80,7 +80,8 @@ export function registerPage ({
path,
query,
openType,
webview
webview,
eventChannel
}) {
if (preloadWebviews[url]) {
webview = preloadWebviews[url]
......@@ -92,6 +93,9 @@ export function registerPage ({
}
webview = null
} else {
if (eventChannel) {
webview.__page__.eventChannel = eventChannel
}
pages.push(webview.__page__)
if (process.env.NODE_ENV !== 'production') {
console.log(`[uni-app] reuse preloadWebview(${path},${webview.id})`)
......@@ -147,6 +151,7 @@ export function registerPage ({
// 导致 webview.getStyle 等逻辑出错(旧的 webview 内部 plus 被释放)
return plus.webview.getWebviewById(webview.id)
},
eventChannel,
$page: {
id: parseInt(webview.id),
meta: routeOptions.meta,
......
......@@ -4,6 +4,8 @@ import {
initPolyfill
} from 'uni-core/service/plugins/polyfill'
import EventChannel from 'uni-helpers/EventChannel'
import {
registerApp
} from '../app'
......@@ -29,6 +31,13 @@ export default {
initPolyfill(Vue)
Vue.prototype.getOpenerEventChannel = function () {
if (!this.$root.$scope.eventChannel) {
this.$root.$scope.eventChannel = new EventChannel()
}
return this.$root.$scope.eventChannel
}
Object.defineProperty(Vue.prototype, '$page', {
get () {
return this.$root.$scope.$page
......@@ -55,7 +64,7 @@ export default {
globalEvent.addEventListener('launchApp', () => {
if (process.env.NODE_ENV !== 'production') {
console.log('[uni-app] launchApp')
}
}
plus.updateConfigInfo && plus.updateConfigInfo()
registerApp(this)
oldMount.call(this, el, hydrating)
......
import {
hasLifecycleHook,
hasLifecycleHook,
findExistsPageIndex
} from 'uni-helpers/index'
import {
initEventChannel
} from 'uni-helpers/navigate-to'
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
......@@ -10,6 +14,7 @@ const {
function onAppRoute (type, {
url,
delta,
events,
exists,
animationType,
animationDuration,
......@@ -17,6 +22,7 @@ function onAppRoute (type, {
detail
} = {}) {
const router = getApp().$router
delete router.$eventChannel
switch (type) {
case 'redirectTo':
if (exists === 'back') {
......@@ -36,13 +42,17 @@ function onAppRoute (type, {
})
break
case 'navigateTo':
router.$eventChannel = initEventChannel(events)
router.push({
type,
path: url,
animationType,
animationDuration
})
break
return {
errMsg: type + ':ok',
eventChannel: router.$eventChannel
}
case 'navigateBack':
{
let canBack = true
......
import {
isPlainObject
} from 'uni-shared'
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
// 不支持的 API 列表
const todos = [
......@@ -80,6 +81,7 @@ function _handleSystemInfo (result) {
}
const protocols = { // 需要做转换的 API 列表
navigateTo,
redirectTo,
returnValue (methodName, res = {}) { // 通用 returnValue 解析
if (res.error || res.errorMessage) {
......
......@@ -56,8 +56,11 @@ export default function parsePage (vuePageOptions) {
// 触发首次 setData
this.$vm.$mount()
const copyQuery = Object.assign({}, query)
delete copyQuery.__id__
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + this.route + stringifyQuery(copyQuery)
}
this.options = query
......
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import previewImage from '../../../mp-weixin/helpers/normalize-preview-image'
// 不支持的 API 列表
const todos = [
......@@ -57,6 +58,9 @@ function _handleEnvInfo (result) {
// 需要做转换的 API 列表
const protocols = {
returnValue (methodName, res = {}) { // 通用 returnValue 解析,部分 API 的 res 为 undefined,比如 navigateTo
return res
},
request: {
args (fromArgs) {
// TODO
......@@ -77,7 +81,8 @@ const protocols = {
args: {
method: false
}
},
},
navigateTo,
redirectTo,
previewImage,
getRecorderManager: {
......@@ -120,4 +125,4 @@ export {
protocols,
todos,
canIUses
}
}
......@@ -2,6 +2,10 @@ import {
hasOwn
} from 'uni-shared'
import {
stringifyQuery
} from 'uni-shared/query'
import {
isPage,
initRelation
......@@ -27,8 +31,14 @@ export default function parseComponent (vueOptions) {
// 百度 当组件作为页面时 pageinstancce 不是原来组件的 instance
this.pageinstance.$vm = this.$vm
if (hasOwn(this.pageinstance, '_$args')) {
this.$vm.$mp.query = this.pageinstance._$args
this.$vm.__call_hook('onLoad', this.pageinstance._$args)
const query = this.pageinstance._$args
const copyQuery = Object.assign({}, query)
delete copyQuery.__id__
this.pageinstance.$page = this.$page = {
fullPath: '/' + this.pageinstance.route + stringifyQuery(copyQuery)
}
this.$vm.$mp.query = query
this.$vm.__call_hook('onLoad', query)
this.$vm.__call_hook('onShow')
delete this.pageinstance._$args
}
......@@ -52,4 +62,4 @@ export default function parseComponent (vueOptions) {
delete componentOptions.methods.__l
return componentOptions
}
}
import {
stringifyQuery
} from 'uni-shared/query'
import {
isPage,
initRelation
......@@ -26,20 +30,25 @@ export default function parsePage (vuePageOptions) {
})
// 纠正百度小程序生命周期methods:onShow在methods:onLoad之前触发的问题
pageOptions.methods.onShow = function onShow () {
if (this.$vm && this.$vm.$mp.query) {
this.$vm.__call_hook('onShow')
}
pageOptions.methods.onShow = function onShow () {
if (this.$vm && this.$vm.$mp.query) {
this.$vm.__call_hook('onShow')
}
}
pageOptions.methods.onLoad = function onLoad (args) {
pageOptions.methods.onLoad = function onLoad (query) {
// 百度 onLoad 在 attached 之前触发,先存储 args, 在 attached 里边触发 onLoad
if (this.$vm) {
this.$vm.$mp.query = args
this.$vm.__call_hook('onLoad', args)
const copyQuery = Object.assign({}, query)
delete copyQuery.__id__
this.pageinstance.$page = this.$page = {
fullPath: '/' + this.pageinstance.route + stringifyQuery(copyQuery)
}
this.$vm.$mp.query = query
this.$vm.__call_hook('onLoad', query)
this.$vm.__call_hook('onShow')
} else {
this.pageinstance._$args = args
this.pageinstance._$args = query
}
}
......@@ -49,4 +58,4 @@ export default function parsePage (vuePageOptions) {
}
return pageOptions
}
}
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import previewImage from '../../helpers/normalize-preview-image'
export const protocols = {
navigateTo,
redirectTo,
previewImage
}
......
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import previewImage from '../../../mp-weixin/helpers/normalize-preview-image'
export const protocols = {
navigateTo,
redirectTo,
previewImage
}
......
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import previewImage from '../../../mp-weixin/helpers/normalize-preview-image'
......@@ -100,6 +101,7 @@ const protocols = {
sizeType: false
}
},
navigateTo,
redirectTo,
previewImage,
connectSocket: {
......
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../helpers/redirect-to'
import previewImage from '../../helpers/normalize-preview-image'
......@@ -13,7 +14,8 @@ function addSafeAreaInsets (result) {
}
}
export const protocols = {
redirectTo,
redirectTo,
navigateTo,
previewImage,
getSystemInfo: {
returnValue: addSafeAreaInsets
......
import {
stringifyQuery
} from 'uni-shared/query'
} from 'uni-shared/query'
import {
initHooks,
......@@ -30,8 +30,10 @@ export default function parseBasePage (vuePageOptions, {
pageOptions.methods.onLoad = function (query) {
this.options = query
const copyQuery = Object.assign({}, query)
delete copyQuery.__id__
this.$page = {
fullPath: '/' + this.route + stringifyQuery(query)
fullPath: '/' + (this.route || this.is) + stringifyQuery(copyQuery)
}
this.$vm.$mp.query = query // 兼容 mpvue
this.$vm.__call_hook('onLoad', query)
......
import navigateTo from 'uni-helpers/navigate-to'
import redirectTo from '../../../mp-weixin/helpers/redirect-to'
import previewImage from '../../../mp-weixin/helpers/normalize-preview-image'
export const protocols = {
navigateTo,
redirectTo,
previewImage
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册