@jianghe/sand-moco
Version:
sand移动端单页核心框架
1,827 lines (1,465 loc) • 44.9 kB
JavaScript
/*!
* sand-moco.js v2.1.3
* (c) 2019-2021 Jiang He
* Released under the MIT License.
*/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var reactRedux = require('react-redux');
var React = require('react');
var ReactDOM = require('react-dom');
var dvaCore = require('dva-core');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
// @ts-nocheck
/* eslint-disable */
/**
* 路由部分director
*/
let router;
const dloc = document.location;
function dlocHashEmpty() {
return dloc.hash === '' || dloc.hash === '#';
}
const listener = {
mode: 'modern',
hash: dloc.hash,
history: false,
check() {
const h = dloc.hash;
if (h != this.hash) {
this.hash = h;
this.onHashChanged();
}
},
fire() {
if (this.mode === 'modern') {
this.history === true ? window.onpopstate() : window.onhashchange();
} else {
this.onHashChanged();
}
},
init(fn, history) {
const self = this;
this.history = history;
if (!Router.listeners) {
Router.listeners = [];
}
function onchange(onChangeEvent) {
for (let i = 0, l = Router.listeners.length; i < l; i++) {
Router.listeners[i](onChangeEvent);
}
} // note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined || document.documentMode > 7)) {
// At least for now HTML5 history is available for 'modern' browsers only
if (this.history === true) {
// There is an old bug in Chrome that causes onpopstate to fire even
// upon initial page load. Since the handler is run manually in init(),
// this would cause Chrome to run it twise. Currently the only
// workaround seems to be to set the handler after the initial page load
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function () {
window.onpopstate = onchange;
}, 500);
} else {
window.onhashchange = onchange;
}
this.mode = 'modern';
} else {
//
// IE support, based on a concept by Erik Arvidson ...
//
const frame = document.createElement('iframe');
frame.id = 'state-frame';
frame.style.display = 'none';
document.body.appendChild(frame);
this.writeFrame('');
if ('onpropertychange' in document && 'attachEvent' in document) {
document.attachEvent('onpropertychange', function () {
if (event.propertyName === 'location') {
self.check();
}
});
}
window.setInterval(function () {
self.check();
}, 50);
this.onHashChanged = onchange;
this.mode = 'legacy';
}
Router.listeners.push(fn);
return this.mode;
},
destroy(fn) {
if (!Router || !Router.listeners) {
return;
}
const listeners = Router.listeners;
for (let i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
}
}
},
setHash(s) {
// Mozilla always adds an entry to the history
if (this.mode === 'legacy') {
this.writeFrame(s);
}
if (this.history === true) {
window.history.pushState({}, document.title, s); // Fire an onpopstate event manually since pushing does not obviously
// trigger the pop event.
this.fire();
} else {
dloc.hash = s[0] === '/' ? s : `/${s}`;
}
return this;
},
writeFrame(s) {
// IE support...
const f = document.getElementById('state-frame');
const d = f.contentDocument || f.contentWindow.document;
d.open();
d.write(`<script>_hash = '${s}'; onload = parent.listener.syncHash;<script>`);
d.close();
},
syncHash() {
// IE support...
const s = this._hash;
if (s != dloc.hash) {
dloc.hash = s;
}
return this;
},
onHashChanged() {}
};
router = function router(routes) {
// 执行方法也返回对象 var a = a(); var a = new a();
if (!(this instanceof Router)) return new Router(routes);
this.params = {};
this.routes = {};
this.methods = ['on', 'once', 'after', 'before'];
this.scope = [];
this._methods = {};
this._insert = this.insert;
this.insert = this.insertEx;
this.historySupport = (window.history != null ? window.history.pushState : null) != null;
this.configure();
this.mount(routes || {});
};
const Router = router;
Router.prototype.init = function (r) {
const self = this;
let routeTo;
this.handler = function (onChangeEvent) {
const newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
const url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url.charAt(0) === '/' ? url : `/${url}`);
};
listener.init(this.handler, this.history);
if (this.history === false) {
if (dlocHashEmpty() && r) {
dloc.hash = r;
} else if (!dlocHashEmpty()) {
self.dispatch('on', `/${dloc.hash.replace(/^(#\/|#|\/)/, '')}`);
}
} else {
if (this.convert_hash_in_init) {
// Use hash as route
routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
} else {
// Use canonical url
routeTo = this.getPath();
} // Router has been initialized, but due to the chrome bug it will not
// yet actually route HTML5 history state changes. Thus, decide if should route.
if (routeTo || this.run_in_init === true) {
this.handler();
}
}
return this;
};
Router.prototype.explode = function () {
let v = this.history === true ? this.getPath() : dloc.hash;
if (v.charAt(1) === '/') {
v = v.slice(1);
}
return v.slice(1, v.length).split('/');
};
Router.prototype.setRoute = function (i, v, val) {
let url = this.explode();
if (typeof i === 'number' && typeof v === 'string') {
url[i] = v;
} else if (typeof val === 'string') {
url.splice(i, v, s);
} else {
url = [i];
}
listener.setHash(url.join('/'));
return url;
};
Router.prototype.insertEx = function (method, path, route, parent) {
if (method === 'once') {
method = 'on';
route = function (route) {
let once = false;
return function () {
if (once) return;
once = true;
return route.apply(this, arguments);
};
}(route);
}
return this._insert(method, path, route, parent);
};
Router.prototype.getRoute = function (v) {
let ret = v;
if (typeof v === 'number') {
ret = this.explode()[v];
} else if (typeof v === 'string') {
const h = this.explode();
ret = h.indexOf(v);
} else {
ret = this.explode();
}
return ret;
};
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
};
Router.prototype.getPath = function () {
let path = window.location.pathname;
if (path.substr(0, 1) !== '/') {
path = `/${path}`;
}
return path;
};
function _every(arr, iterator) {
for (let i = 0; i < arr.length; i += 1) {
if (iterator(arr[i], i, arr) === false) {
return;
}
}
}
function _flatten(arr) {
let flat = [];
for (let i = 0, n = arr.length; i < n; i++) {
flat = flat.concat(arr[i]);
}
return flat;
}
function _asyncEverySeries(arr, iterator, callback) {
if (!arr.length) {
return callback();
}
let completed = 0;
(function iterate() {
iterator(arr[completed], function (err) {
if (err || err === false) {
callback(err);
callback = function callback() {};
} else {
completed += 1;
if (completed === arr.length) {
callback();
} else {
iterate();
}
}
});
})();
}
function paramifyString(str, params, mod) {
mod = str;
for (const param in params) {
if (params.hasOwnProperty(param)) {
mod = params[param](str);
if (mod !== str) {
break;
}
}
}
return mod === str ? '([._a-zA-Z0-9-%()]+)' : mod;
}
function regifyString(str, params) {
let matches;
let last = 0;
let out = '';
while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
last = matches.index + matches[0].length;
matches[0] = matches[0].replace(/^\*/, '([_.()!\\ %@&a-zA-Z0-9-]+)');
out += str.substr(0, matches.index) + matches[0];
}
str = out += str.substr(last);
const captures = str.match(/:([^\/]+)/gi);
let capture;
let length;
if (captures) {
length = captures.length;
for (let i = 0; i < length; i++) {
capture = captures[i];
if (capture.slice(0, 2) === '::') {
str = capture.slice(1);
} else {
str = str.replace(capture, paramifyString(capture, params));
}
}
}
return str;
}
function terminator(routes, delimiter, start, stop) {
let last = 0;
let left = 0;
let right = 0;
var start = (start || '(').toString();
var stop = (stop || ')').toString();
let i;
for (i = 0; i < routes.length; i++) {
const chunk = routes[i];
if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
left = chunk.indexOf(start, last);
right = chunk.indexOf(stop, last);
if (~left && !~right || !~left && ~right) {
const tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
routes = [tmp].concat(routes.slice((i || 1) + 1));
}
last = (right > left ? right : left) + 1;
i = 0;
} else {
last = 0;
}
}
return routes;
}
const QUERY_SEPARATOR = /\?.*/;
Router.prototype.configure = function (options) {
options = options || {};
for (let i = 0; i < this.methods.length; i++) {
this._methods[this.methods[i]] = true;
}
this.recurse = options.recurse || this.recurse || false;
this.async = options.async || false;
this.delimiter = options.delimiter || '/';
this.strict = typeof options.strict === 'undefined' ? true : options.strict;
this.notfound = options.notfound;
this.resource = options.resource;
this.history = options.html5history && this.historySupport || false;
this.run_in_init = this.history === true && options.run_handler_in_init !== false;
this.convert_hash_in_init = this.history === true && options.convert_hash_in_init !== false;
this.every = {
after: options.after || null,
before: options.before || null,
on: options.on || null
};
return this;
};
Router.prototype.param = function (token, matcher) {
if (token[0] !== ':') {
token = `:${token}`;
}
const compiled = new RegExp(token, 'g');
this.params[token] = function (str) {
return str.replace(compiled, matcher.source || matcher);
};
return this;
};
Router.prototype.on = Router.prototype.route = function (method, path, route) {
const self = this;
if (!route && typeof path === 'function') {
route = path;
path = method;
method = 'on';
}
if (Array.isArray(path)) {
return path.forEach(function (p) {
self.on(method, p, route);
});
}
if (path.source) {
path = path.source.replace(/\\\//gi, '/');
}
if (Array.isArray(method)) {
return method.forEach(function (m) {
self.on(m.toLowerCase(), path, route);
});
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.insert(method, this.scope.concat(path), route);
};
Router.prototype.path = function (path, routesFn) {
const length = this.scope.length;
if (path.source) {
path = path.source.replace(/\\\//gi, '/');
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.scope = this.scope.concat(path);
routesFn.call(this, this);
this.scope.splice(length, path.length);
};
Router.prototype.dispatch = function (method, path, callback) {
const self = this;
let fns = this.traverse(method, path.replace(QUERY_SEPARATOR, ''), this.routes, '');
const invoked = this._invoked;
let after;
this._invoked = true;
if (!fns || fns.length === 0) {
this.last = [];
if (typeof this.notfound === 'function') {
this.invoke([this.notfound], {
method,
path
}, callback);
}
return false;
}
if (this.recurse === 'forward') {
fns = fns.reverse();
}
function updateAndInvoke() {
self.last = fns.after;
self.invoke(self.runlist(fns), self, callback);
}
after = this.every && this.every.after ? [this.every.after].concat(this.last) : [this.last];
if (after && after.length > 0 && invoked) {
if (this.async) {
this.invoke(after, this, updateAndInvoke);
} else {
this.invoke(after, this);
updateAndInvoke();
}
return true;
}
updateAndInvoke();
return true;
};
Router.prototype.invoke = function (fns, thisArg, callback) {
const self = this;
let _apply2;
if (this.async) {
_apply2 = function apply(fn, next) {
if (Array.isArray(fn)) {
return _asyncEverySeries(fn, _apply2, next);
} else if (typeof fn === 'function') {
fn.apply(thisArg, (fns.captures || []).concat(next));
}
};
_asyncEverySeries(fns, _apply2, function () {
if (callback) {
callback.apply(thisArg, arguments);
}
});
} else {
_apply2 = function _apply(fn) {
if (Array.isArray(fn)) {
return _every(fn, _apply2);
} else if (typeof fn === 'function') {
return fn.apply(thisArg, fns.captures || []);
} else if (typeof fn === 'string' && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []);
}
};
_every(fns, _apply2);
}
};
Router.prototype.traverse = function (method, path, routes, regexp, filter) {
let fns = [];
let current;
let exact;
let match;
let next;
function filterRoutes(routes) {
if (!filter) {
return routes;
}
function deepCopy(source) {
const result = [];
for (let i = 0; i < source.length; i++) {
result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
}
return result;
}
function applyFilter(fns) {
for (let i = fns.length - 1; i >= 0; i--) {
if (Array.isArray(fns[i])) {
applyFilter(fns[i]);
if (fns[i].length === 0) {
fns.splice(i, 1);
}
} else if (!filter(fns[i])) {
fns.splice(i, 1);
}
}
}
const newRoutes = deepCopy(routes);
newRoutes.matched = routes.matched;
newRoutes.captures = routes.captures;
newRoutes.after = routes.after.filter(filter);
applyFilter(newRoutes);
return newRoutes;
}
if (path === this.delimiter && routes[method]) {
next = [[routes.before, routes[method]].filter(Boolean)];
next.after = [routes.after].filter(Boolean);
next.matched = true;
next.captures = [];
return filterRoutes(next);
}
for (const r in routes) {
if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === 'object' && !Array.isArray(routes[r]))) {
current = exact = regexp + this.delimiter + r;
if (!this.strict) {
exact += `[${this.delimiter}]?`;
}
match = path.match(new RegExp(`^${exact}`));
if (!match) {
continue;
}
if (match[0] && match[0] == path && routes[r][method]) {
next = [[routes[r].before, routes[r][method]].filter(Boolean)];
next.after = [routes[r].after].filter(Boolean);
next.matched = true;
next.captures = match.slice(1);
if (this.recurse && routes === this.routes) {
next.push([routes.before, routes.on].filter(Boolean));
next.after = next.after.concat([routes.after].filter(Boolean));
}
return filterRoutes(next);
}
next = this.traverse(method, path, routes[r], current);
if (next.matched) {
if (next.length > 0) {
fns = fns.concat(next);
}
if (this.recurse) {
fns.push([routes[r].before, routes[r].on].filter(Boolean));
next.after = next.after.concat([routes[r].after].filter(Boolean));
if (routes === this.routes) {
fns.push([routes.before, routes.on].filter(Boolean));
next.after = next.after.concat([routes.after].filter(Boolean));
}
}
fns.matched = true;
fns.captures = next.captures;
fns.after = next.after;
return filterRoutes(fns);
}
}
}
return false;
};
Router.prototype.insert = function (method, path, route, parent) {
let methodType;
let parentType;
let isArray;
let nested;
let part;
path = path.filter(function (p) {
return p && p.length > 0;
});
parent = parent || this.routes;
part = path.shift();
if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
part = regifyString(part, this.params);
}
if (path.length > 0) {
parent[part] = parent[part] || {};
return this.insert(method, path, route, parent[part]);
}
if (!part && !path.length && parent === this.routes) {
methodType = typeof parent[method];
switch (methodType) {
case 'function':
parent[method] = [parent[method], route];
return;
case 'object':
parent[method].push(route);
return;
case 'undefined':
parent[method] = route;
return;
}
return;
}
parentType = typeof parent[part];
isArray = Array.isArray(parent[part]);
if (parent[part] && !isArray && parentType == 'object') {
methodType = typeof parent[part][method];
switch (methodType) {
case 'function':
parent[part][method] = [parent[part][method], route];
return;
case 'object':
parent[part][method].push(route);
return;
case 'undefined':
parent[part][method] = route;
return;
}
} else if (parentType == 'undefined') {
nested = {};
nested[method] = route;
parent[part] = nested;
return;
}
throw new Error(`Invalid route context: ${parentType}`);
};
Router.prototype.extend = function (methods) {
const self = this;
const len = methods.length;
let i;
function extend(method) {
self._methods[method] = true;
self[method] = function () {
const extra = arguments.length === 1 ? [method, ''] : [method];
self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
};
}
for (i = 0; i < len; i++) {
extend(methods[i]);
}
};
Router.prototype.runlist = function (fns) {
const runlist = this.every && this.every.before ? [this.every.before].concat(_flatten(fns)) : _flatten(fns);
if (this.every && this.every.on) {
runlist.push(this.every.on);
}
runlist.captures = fns.captures;
runlist.source = fns.source;
return runlist;
};
Router.prototype.mount = function (routes, path) {
if (!routes || typeof routes !== 'object' || Array.isArray(routes)) {
return;
}
const self = this;
path = path || [];
if (!Array.isArray(path)) {
path = path.split(self.delimiter);
}
function insertOrMount(route, local) {
let rename = route;
const parts = route.split(self.delimiter);
const routeType = typeof routes[route];
const isRoute = parts[0] === '' || !self._methods[parts[0]];
const event = isRoute ? 'on' : rename;
if (isRoute) {
rename = rename.slice((rename.match(new RegExp(`^${self.delimiter}`)) || [''])[0].length);
parts.shift();
}
if (isRoute && routeType === 'object' && !Array.isArray(routes[route])) {
local = local.concat(parts);
self.mount(routes[route], local);
return;
}
if (isRoute) {
local = local.concat(rename.split(self.delimiter));
local = terminator(local, self.delimiter);
}
self.insert(event, local, routes[route]);
}
for (const route in routes) {
if (routes.hasOwnProperty(route)) {
insertOrMount(route, path.slice(0));
}
}
};
var router$1 = router;
// import { PAGE_ENUM } from '../common/config';
/**
* 页面栈类
*/
class Stacks {
constructor() {
// 页面集合
this.pageMap = {}; // 当前显示的页面队列
this.pageFlag = [];
this.pageMap = {};
this.pageFlag = [];
}
/**
* 页面入栈的方法
* @param page 页面实例
*/
push(page) {
this.pageFlag.push(page.pageId);
this.pageMap[page.pageId] = page;
}
/**
* 页面出栈的方法
*/
pop() {
return this.pageMap[this.pageFlag.pop()];
}
/**
* 移除栈中指定的pageId
*/
removeStacks(page) {
const flagIndex = this.pageExist(page);
if (flagIndex >= 0) {
this.pageFlag.splice(flagIndex, 1);
return true;
}
return false;
}
/**
* 查询栈中是否存在page对象
* @param page page对象
* @return this.pageFlag的下标(-1为没找到)
*/
pageExist(page) {
for (let i = 0; i < this.pageFlag.length; i++) {
if (this.pageFlag[i] === page.pageId) {
return i;
}
}
return -1;
} // /**
// * 执行goBack时根据goBack的入参返回待返回页的pageId,如果未找到符合条件的返回页返回false
// */
// findPageId(obj): string | boolean {
// const { pagePath, pageKey } = obj;
// if (pagePath) {
// /**
// * 传入了pagePath,返回距离栈顶最近的满足pagePath,pageKey的页面
// */
// for (let n = this.pageFlag.length - 2; n >= 0; n--) {
// const pg = this.getPage(n);
// if (
// pg &&
// pg.pagePath === pagePath &&
// (!pageKey || pg.pageKey === pageKey)
// ) {
// // 找到离栈顶最近的正常页
// return pg.pageId;
// }
// }
// } else {
// /**
// * 未传入pagePath
// */
// // 获取栈顶页
// const topPage = this.getPage(-1);
// if (topPage && topPage.pageType === PAGE_ENUM.NORMAL) {
// // 栈顶页是正常页返回
// for (let n = this.pageFlag.length - 2; n >= 0; n--) {
// const pg = this.getPage(n);
// if (pg && pg.pageType === PAGE_ENUM.NORMAL) {
// // 找到离栈顶最近的正常页
// return pg.pageId;
// }
// }
// } else if (topPage && topPage.pageType === 'temp') {
// // 栈顶页是临时页返回
// const tempPage = this.getPage(-2);
// if (tempPage) {
// return tempPage.pageId;
// }
// }
// }
// console.warn('返回页面不存在');
// return false;
// }
/**
* 获取栈中的page对象
* @param index 索引值如果为+是数组下标,如果为-是倒数
* @return {*}
*/
getPage(index) {
if (this.pageFlag.length === 0) {
return false;
}
const n = index % this.pageFlag.length;
if (n < 0) {
return this.pageMap[this.pageFlag[this.pageFlag.length + n]];
} else {
return this.pageMap[this.pageFlag[n]];
}
}
}
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* 获取8位随机字符串
*/
function getRandom() {
return Math.random() // 生成随机数字, eg: 0.123456
.toString(36) // 转化成36进制 : "0.4fzyo82mvyr"
.slice(-8);
}
/**
* 节流
* @param fn
* @param time
*/
function throttle(fn, time) {
let lastTime = 0;
return (...args) => {
const nowTime = +new Date();
if (!lastTime || nowTime - lastTime > time) {
fn(...args);
lastTime = nowTime;
}
};
}
/**
* obj转url参数
* @param data
*/
function obj2url(data) {
const result = []; // eslint-disable-next-line
for (const key in data) {
const value = data[key];
if (typeof value !== 'string') {
// value不是字符串,报错
console.error('参数value只支持字符串');
return '';
}
result.push(`${key}=${value}`);
}
return result.join('&');
}
/**
* 校验类型item的tostring是不是为type
* @param item
* @param type
* @return {boolean}
*/
function isType(item, type) {
return {}.toString.call(item) === type;
}
/**
* 页面类型枚举
* normal正常页
* temp临时页
*/
const PAGE_ENUM$2 = {
NORMAL: 'normal',
TEPM: 'temp'
};
/**
* 跳页类型
* init 初始化
* go 前进
* goBack 后退
*/
const JUMP_METHOD_ENUM$1 = {
INIT: 'init',
GO: 'go',
GO_BACK: 'goBack'
};
/**
* 根节点
*/
let ROOT_ELE = '#sand-root';
const setRootEle$1 = id => {
ROOT_ELE = id;
};
const getRootEle$1 = () => {
return ROOT_ELE;
};
/**
* 页面根节点类名
*/
const PAGE_ROOT_ELE$1 = 'sand-main-box';
/**
* 页面生命周期枚举
*/
const LIFE_CYCLE$1 = {
onCreate: 'onCreate',
onPause: 'onPause',
onResume: 'onResume',
onDestroy: 'onDestroy'
};
const CONSTANTS = {
PAGE_ENUM: PAGE_ENUM$2,
JUMP_METHOD_ENUM: JUMP_METHOD_ENUM$1,
LIFE_CYCLE: LIFE_CYCLE$1,
PAGE_ROOT_ELE: PAGE_ROOT_ELE$1,
setRootEle: setRootEle$1,
getRootEle: getRootEle$1
};
function styleInject(css, ref) {
if ( ref === void 0 ) ref = {};
var insertAt = ref.insertAt;
if (!css || typeof document === 'undefined') { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
if (insertAt === 'top') {
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
} else {
head.appendChild(style);
}
} else {
head.appendChild(style);
}
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
}
var css_248z = "body,\nhtml {\n margin: 0;\n padding: 0;\n}\n#sand-root {\n position: relative;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0);\n -webkit-transform: translate3d(0, 0, 0);\n -moz-transform: translate3d(0, 0, 0);\n -ms-transform: translate3d(0, 0, 0);\n -o-transform: translateZ(0);\n transform: translate3d(0, 0, 0);\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n -ms-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000;\n -moz-perspective: 1000;\n -ms-perspective: 1000;\n perspective: 1000;\n}\n.sand-main-box {\n width: 7.5rem;\n height: 100vh;\n position: absolute;\n top: 0;\n left: 0;\n}\n";
styleInject(css_248z);
const PAGE_ROOT_ELE = CONSTANTS.PAGE_ROOT_ELE;
/**
* 将页面组件渲染至sand-main-box,并返回真实dom
*/
function getPageDom(page) {
const Component = page.component,
moco = page.moco; // 生成页面根节点
const div = document.createElement('div'); // 设置页面类名
div.setAttribute('class', PAGE_ROOT_ELE); // 渲染界面
ReactDOM__default['default'].render( /*#__PURE__*/React__default['default'].createElement(reactRedux.Provider, {
store: moco.dvaApp.getStore()
}, /*#__PURE__*/React__default['default'].createElement(Component, {
mocoPage: page
})), div);
return div;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
const PAGE_ENUM$1 = CONSTANTS.PAGE_ENUM,
LIFE_CYCLE = CONSTANTS.LIFE_CYCLE;
/**
* 页面类
*/
class Page {
constructor(opts) {
// 页面id,保存到路由栈中的标志位
this.pageId = ''; // 页面路由
this.pagePath = ''; // 页面键值,和页面id的区别是页面id是系统自动生成的,而pagekey是使用者跳转页面是传入的标识页面的key
this.pageKey = ''; // 页面类型(normal正常页,temp临时页)
this.pageType = PAGE_ENUM$1.NORMAL; // 页面对应的React组件
this.component = null; // 页面对应的真实dom
this.dom = null; // Moco实例
this.moco = null; // dva状态模型
this.model = false; // 页面传入的监听器hook
this.monitorHook = {
// 页面创建
// eslint-disable-next-line
[LIFE_CYCLE.onCreate]: _page => {},
// 页面挂起
// eslint-disable-next-line
[LIFE_CYCLE.onPause]: _page => {},
// 页面重回
// eslint-disable-next-line
[LIFE_CYCLE.onResume]: _page => {},
// 页面销毁
// eslint-disable-next-line
[LIFE_CYCLE.onDestroy]: _page => {}
};
/**
* 监听器方法
*/
// eslint-disable-next-line @typescript-eslint/ban-types
this.monitor = (lifeCycleName, hook) => {
if (LIFE_CYCLE[lifeCycleName]) {
this.monitorHook[LIFE_CYCLE[lifeCycleName]] = hook;
} else {
console.error('生命周期事件名错误');
}
};
const pagePath = opts.pagePath,
component = opts.component,
_opts$model = opts.model,
model = _opts$model === void 0 ? false : _opts$model,
_opts$pageKey = opts.pageKey,
pageKey = _opts$pageKey === void 0 ? '' : _opts$pageKey,
_opts$pageType = opts.pageType,
pageType = _opts$pageType === void 0 ? PAGE_ENUM$1.NORMAL : _opts$pageType;
this.pagePath = pagePath;
this.pageKey = pageKey;
this.pageType = pageType;
this.component = component;
this.pageId = `${this.pagePath}${getRandom()}`; // 获取moco实例
this.moco = getMoco(); // 有dva实例和该页面有对应的dva模型,在页面创建时注册dva模型
if (this.moco.dvaApp && model) {
this.model = model;
this.moco.dvaApp.setModel(this.model);
} // 创建dom
this.createDom();
}
/**
* 页面创建的方法
*/
onCreate() {
// 有hook时执行hook
this.monitorHook[LIFE_CYCLE.onCreate] && this.monitorHook[LIFE_CYCLE.onCreate](this);
}
/**
* 页面暂停,挂起
*/
onPause() {
this.monitorHook[LIFE_CYCLE.onPause] && this.monitorHook[LIFE_CYCLE.onPause](this);
}
/**
* 重新回到页面的方法
*/
onResume() {
this.monitorHook[LIFE_CYCLE.onResume] && this.monitorHook[LIFE_CYCLE.onResume](this);
}
/**
* 销毁页面的方法
*/
onDestroy() {
try {
if (this.moco) {
// 移除栈
this.moco.stacks.removeStacks(this); // 移除堆
this.moco.stacks.pageMap[this.pageId] = '';
delete this.moco.stacks.pageMap[this.pageId];
}
} catch (e) {
console.error('移除堆区page对象失败');
}
this.monitorHook[LIFE_CYCLE.onDestroy] && this.monitorHook[LIFE_CYCLE.onDestroy](this);
}
/**
* 创建dom
*/
createDom() {
if (!this.dom) {
this.dom = getPageDom(this);
}
}
}
const getRootEle = CONSTANTS.getRootEle;
/**
* 首次页面显示的转场动画,直接显示
* @param showPage 需要显示的页面
*/
function firstShowTransition(showPage) {
const rootEle = getRootEle(); // 获取渲染根节点
const parentEle = window.document.querySelector(rootEle); // 添加到父节点中
parentEle && parentEle.appendChild(showPage.dom);
}
/**
* 基础进入转场动画
* @param hidePage 隐藏的页
* @param showPage 当前页
*/
function baseInTransition(hidePage, showPage, endCallBack) {
const rootEle = getRootEle(); // 要显示页
const showDom = showPage.dom; // 上一页
const upPageDom = hidePage.dom; // 获取根节点
const parentEle = window.document.querySelector(rootEle);
showDom.style.display = 'none';
showDom.style.left = '100%';
showDom.style.zIndex = '99';
showDom.style.display = 'block';
parentEle && parentEle.appendChild(showDom);
setTimeout(function () {
showDom.style.transition = 'left 0.4s ease';
showDom.style.left = '0';
}, 20);
setTimeout(function () {
showDom.style.transition = 'none';
showDom.style.zIndex = 'none';
upPageDom.style.display = 'none';
parentEle && parentEle.removeChild(upPageDom);
endCallBack();
}, 600);
}
/**
* 基础转场动画
* @param hidePage 隐藏的页
* @param showPage 当前页
*/
function baseOutTransition(hidePage, showPage, endCallBack) {
const rootEle = getRootEle(); // 要显示页
const showDom = showPage.dom; // 上一页
const upPageDom = hidePage.dom; // 获取根节点
const parentEle = window.document.querySelector(rootEle);
showDom.style.display = 'none';
showDom.style.left = '-100%';
showDom.style.zIndex = '99';
showDom.style.display = 'block';
parentEle && parentEle.appendChild(showDom);
setTimeout(function () {
showDom.style.transition = 'left 0.4s ease';
showDom.style.left = '0';
}, 20);
setTimeout(function () {
showDom.style.transition = 'none';
showDom.style.zIndex = 'none';
upPageDom.style.display = 'none';
parentEle && parentEle.removeChild(upPageDom);
endCallBack();
}, 600);
}
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/**
* 基于dva-core封装的类 挂载到moco实例上
*/
class DvaApp {
constructor() {
this.dva = null;
/**
* 获取dva实例
*/
this.getDvaApp = () => {
if (!this.dva) {
// 创建dva-core实例
this.dva = dvaCore.create({}, {});
if (!this.dva._store) {
this.dva.start.call(this.dva);
}
}
return this.dva;
};
/**
* 获取store
*/
this.getStore = () => {
return !this.dva ? this.getDvaApp()._store : this.dva._store;
};
/**
* 注册dva model 使用replaceModel方法如果存在直接替换
* @param {*} model
*/
this.setModel = model => {
return !this.dva ? this.getDvaApp().replaceModel(model) : this.dva.replaceModel(model);
};
/**
* 注销dva model
*/
this.unModel = namespace => {
if (!this.dva) {
this.getDvaApp().unmodel(namespace);
} else {
this.dva.unmodel(namespace);
}
};
this.dva = this.getDvaApp();
}
}
/* eslint-disable @typescript-eslint/no-explicit-any */
const JUMP_METHOD_ENUM = CONSTANTS.JUMP_METHOD_ENUM,
PAGE_ENUM = CONSTANTS.PAGE_ENUM;
/**
* 初始化的目标页信息
*/
const initTargetPageInfo = {
// 跳页方法go||goback
jumpMethod: JUMP_METHOD_ENUM.INIT,
// 页面路由
pagePath: '',
// 参数
args: {},
// key
pageKey: '',
// 页面类型
pageType: PAGE_ENUM.NORMAL,
// 转场动画
transition: baseInTransition
};
/**
* Moco类型
*/
class Moco {
constructor() {
/**
* 来源页面
*/
this.sourcePage = null;
/**
* 页面栈
*/
this.stacks = null;
/**
* 跳页信息
*/
this.targetPageInfo = null;
/**
* 在页面跳转中
*/
this.isInJump = false;
/**
* dva实例,用于页面之间转态管理
* https://github.com/dvajs/dva/blob/master/packages/dva-core/src/index.js
*/
this.dvaApp = null;
/**
* 命中路由时
*/
this.hitPath = opts => {
if (this.targetPageInfo.jumpMethod === JUMP_METHOD_ENUM.INIT) {
// 初始化命中路由
this.newPage(opts);
} else if (this.targetPageInfo.jumpMethod === JUMP_METHOD_ENUM.GO) {
// go跳页命中路由
this.goPage(opts);
} else {
// goBack跳页命中路由
this.backPage();
}
};
/**
* 转场动画结束回调
*/
this.transitionEnd = (hidePage, showPage, jumpMethod) => {
if (jumpMethod === JUMP_METHOD_ENUM.GO) {
// 前进转场动画结束
// 执行下一个页面创建生命周期和上一个页面的pause生命周期
hidePage.onPause();
showPage.onCreate(); // 重置targetPageInfo
this.targetPageInfo = initTargetPageInfo;
}
if (jumpMethod === JUMP_METHOD_ENUM.GO_BACK) {
// 返回转场动画结束
// 执行下一个页面创建生命周期和上一个页面的pause生命周期
hidePage.onDestroy();
showPage.onResume(); // 重置targetPageInfo
this.targetPageInfo = initTargetPageInfo;
}
this.isInJump = false;
};
/**
* 返回页面
* @param opts 需要显示的页面
*/
this.backPage = () => {
if (this.stacks.pageFlag.length > 1) {
// 取出栈顶页面
const hidePage = this.stacks.getPage(-1); // 取出跳转页面
const showPage = this.stacks.getPage(-2);
if (hidePage && showPage) {
// 在跳页流程中
this.isInJump = true; // 前一个页面
this.sourcePage = hidePage; // 转场动画结束回调
const endCallBack = () => {
this.transitionEnd( // 隐藏页
hidePage, // 显示页
showPage, // 跳转方式
this.targetPageInfo.jumpMethod);
}; // 执行转场动画
this.targetPageInfo.transition(hidePage, showPage, endCallBack);
} else {
console.error('无法取出栈顶页面');
}
} else {
console.error('没有可以返回的页面');
}
};
/**
* 显示页面
* @param opts 需要显示的页面
*/
this.goPage = opts => {
// 页面栈中有几个页
const len = this.stacks.pageFlag.length;
if (len > 0) {
const component = opts.component,
pagePath = opts.pagePath,
model = opts.model; // 实例化一个新页面
const showPage = new Page({
pagePath,
component,
model
}); // 取出栈顶页面
const hidePage = this.stacks.getPage(-1);
if (hidePage) {
// 在跳页流程中
this.isInJump = true; // 前一个页面
this.sourcePage = hidePage; // 不存在栈中 压栈
this.stacks.push(showPage); // 转场动画结束回调
const endCallBack = () => {
this.transitionEnd( // 隐藏页
hidePage, // 显示页
showPage, // 跳转方式
this.targetPageInfo.jumpMethod);
}; // 执行转场动画
this.targetPageInfo.transition(hidePage, showPage, endCallBack);
} else {
console.error('无法取出栈顶页面');
}
}
};
/**
* 创建新页(栈中无页面)
* @param page 页面对象
*/
this.newPage = opts => {
const component = opts.component,
pagePath = opts.pagePath,
model = opts.model; // 实例化一个新页面
const page = new Page({
pagePath,
component,
model
}); // 将页面入栈
this.stacks.push(page); // 执行默认显示动画
firstShowTransition(page); // 执行页面的生命周期方法
page.onCreate(); // 重置targetPageInfo
this.targetPageInfo = initTargetPageInfo;
};
/**
* 前进的跳转
* @param obj 跳转对象
*/
this.go = throttle(opts => {
// 在转场动画中 直接return
if (this.isInJump) return;
const pagePath = opts.pagePath,
_opts$args = opts.args,
args = _opts$args === void 0 ? {} : _opts$args,
_opts$pageKey = opts.pageKey,
pageKey = _opts$pageKey === void 0 ? '' : _opts$pageKey,
_opts$pageType = opts.pageType,
pageType = _opts$pageType === void 0 ? PAGE_ENUM.NORMAL : _opts$pageType,
_opts$transition = opts.transition,
transition = _opts$transition === void 0 ? baseInTransition : _opts$transition;
if (!pagePath) {
console.error('go方法缺少pagePath参数');
return;
}
if (!isType(args, '[object Object]')) {
console.error('go方法args参数不合法,只能是[object Object]');
return;
} // obj转url
const urlArgs = obj2url(args); // 记录目标页信息
this.targetPageInfo = {
jumpMethod: JUMP_METHOD_ENUM.GO,
pagePath,
args,
pageKey,
pageType,
transition
}; // 修改url
window.location.hash = `#${pagePath}${urlArgs ? `?${urlArgs}` : ''}`;
}, 300);
/**
* 返回的跳转
* @param obj 跳转对象
*/
this.goBack = throttle((opts = {}) => {
// 在转场动画中 直接return
if (this.isInJump) return;
const _opts$args2 = opts.args,
args = _opts$args2 === void 0 ? {} : _opts$args2,
_opts$pageKey2 = opts.pageKey,
pageKey = _opts$pageKey2 === void 0 ? '' : _opts$pageKey2,
_opts$pageType2 = opts.pageType,
pageType = _opts$pageType2 === void 0 ? PAGE_ENUM.NORMAL : _opts$pageType2,
_opts$transition2 = opts.transition,
transition = _opts$transition2 === void 0 ? baseOutTransition : _opts$transition2;
if (!isType(args, '[object Object]')) {
console.error('go方法args参数不合法,只能是[object Object]');
return;
} // obj转url
const urlArgs = obj2url(args); // 计算出要跳转的页面
if (this.stacks.pageFlag.length > 1) {
const backPage = this.stacks.getPage(-2);
if (backPage) {
// 记录目标页信息
this.targetPageInfo = {
jumpMethod: JUMP_METHOD_ENUM.GO_BACK,
pagePath: backPage.pagePath,
args,
pageKey,
pageType,
transition
}; // 修改url
window.location.hash = `#${backPage.pagePath}${urlArgs ? `?${urlArgs}` : ''}`;
}
} else {
console.error('没有可以返回的页面');
}
}, 300); // 创建页面栈
this.stacks = new Stacks(); // 初始化目标页面信息
this.targetPageInfo = initTargetPageInfo; // 创建dva实例
this.dvaApp = new DvaApp();
}
}
/**
* Moco的实例化方法,Moco是单例模式,每个应用只允许存在一个Moco实例
*/
let moco = null;
function getMoco() {
if (!moco) {
moco = new Moco();
}
return moco;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
const setRootEle = CONSTANTS.setRootEle;
/**
* 合并路由生成路由配置, 获取路由配置的方法
* @param webpackRouteConfig webpack读取的路由配置
*/
function getRoutes(webpackRouteConfig) {
// 子路由
const routes = []; // 遍历webpack读取的路由配置
webpackRouteConfig.keys().forEach(key => {
let config = webpackRouteConfig(key); // 取出拆分的路由配置
config = config.default || config; // 合并到主路由中
routes.push(config);
}); // 完整路由
return routes;
}
/**
* 获取路由配置,并根据路由配置,形成hash监听
*/
function addListen(opts) {
const webpackRouteConfig = opts.webpackRouteConfig,
defaultRoute = opts.defaultRoute,
_opts$rootEle = opts.rootEle,
rootEle = _opts$rootEle === void 0 ? '' : _opts$rootEle; // 根据外部入参设置根节点选择器
if (rootEle) {
setRootEle(rootEle);
} // 合并pages下的路由
const routes = getRoutes(webpackRouteConfig); // 实例化一个moco
const moco = getMoco(); // 根据路由配置生成路由集合
const routeMap = {};
for (let index = 0; index < routes.length; index++) {
const route = routes[index];
const component = route.component,
path = route.path,
_route$enable = route.enable,
enable = _route$enable === void 0 ? false : _route$enable,
_route$model = route.model,
model = _route$model === void 0 ? false : _route$model;
if (enable) {
routeMap[path] = {
// 当前路由hash值命中前
before: () => {},
// 当路由匹配成功时
on: () => {
// 匹配路径是调用hitPath
moco.hitPath({
pagePath: path,
component,
model
});
},
// 当离开当前注册路径时,需要执行的方法
after: () => {}
};
}
} // 根据路由集合,添加监听
router$1(routeMap).configure({
notfound: () => {
// 没发现路由时
console.log('没发现路由');
}
}).init(defaultRoute || '/');
}
Object.defineProperty(exports, 'connect', {
enumerable: true,
get: function () {
return reactRedux.connect;
}
});
exports.CONSTANTS = CONSTANTS;
exports.Moco = Moco;
exports.Page = Page;
exports.addListen = addListen;
exports.getMoco = getMoco;
//# sourceMappingURL=sand-moco.js.map