Commit 9c0177da by 肖康

module

parent 674e0222
Showing with 8 additions and 3936 deletions
'use strict';
const strictUriEncode = require('strict-uri-encode');
const decodeComponent = require('decode-uri-component');
const splitOnFirst = require('split-on-first');
const filterObject = require('filter-obj');
const isNullOrUndefined = value => value === null || value === undefined;
function encoderForArrayFormat(options) {
switch (options.arrayFormat) {
case 'index':
return key => (result, value) => {
const index = result.length;
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}
if (value === null) {
return [...result, [encode(key, options), '[', index, ']'].join('')];
}
return [
...result,
[encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join('')
];
};
case 'bracket':
return key => (result, value) => {
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}
if (value === null) {
return [...result, [encode(key, options), '[]'].join('')];
}
return [...result, [encode(key, options), '[]=', encode(value, options)].join('')];
};
case 'comma':
case 'separator':
return key => (result, value) => {
if (value === null || value === undefined || value.length === 0) {
return result;
}
if (result.length === 0) {
return [[encode(key, options), '=', encode(value, options)].join('')];
}
return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
};
default:
return key => (result, value) => {
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}
if (value === null) {
return [...result, encode(key, options)];
}
return [...result, [encode(key, options), '=', encode(value, options)].join('')];
};
}
}
function parserForArrayFormat(options) {
let result;
switch (options.arrayFormat) {
case 'index':
return (key, value, accumulator) => {
result = /\[(\d*)\]$/.exec(key);
key = key.replace(/\[\d*\]$/, '');
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === undefined) {
accumulator[key] = {};
}
accumulator[key][result[1]] = value;
};
case 'bracket':
return (key, value, accumulator) => {
result = /(\[\])$/.exec(key);
key = key.replace(/\[\]$/, '');
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === undefined) {
accumulator[key] = [value];
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
case 'comma':
case 'separator':
return (key, value, accumulator) => {
const isArray = typeof value === 'string' && value.includes(options.arrayFormatSeparator);
const isEncodedArray = (typeof value === 'string' && !isArray && decode(value, options).includes(options.arrayFormatSeparator));
value = isEncodedArray ? decode(value, options) : value;
const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options);
accumulator[key] = newValue;
};
default:
return (key, value, accumulator) => {
if (accumulator[key] === undefined) {
accumulator[key] = value;
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
}
}
function validateArrayFormatSeparator(value) {
if (typeof value !== 'string' || value.length !== 1) {
throw new TypeError('arrayFormatSeparator must be single character string');
}
}
function encode(value, options) {
if (options.encode) {
return options.strict ? strictUriEncode(value) : encodeURIComponent(value);
}
return value;
}
function decode(value, options) {
if (options.decode) {
return decodeComponent(value);
}
return value;
}
function keysSorter(input) {
if (Array.isArray(input)) {
return input.sort();
}
if (typeof input === 'object') {
return keysSorter(Object.keys(input))
.sort((a, b) => Number(a) - Number(b))
.map(key => input[key]);
}
return input;
}
function removeHash(input) {
const hashStart = input.indexOf('#');
if (hashStart !== -1) {
input = input.slice(0, hashStart);
}
return input;
}
function getHash(url) {
let hash = '';
const hashStart = url.indexOf('#');
if (hashStart !== -1) {
hash = url.slice(hashStart);
}
return hash;
}
function extract(input) {
input = removeHash(input);
const queryStart = input.indexOf('?');
if (queryStart === -1) {
return '';
}
return input.slice(queryStart + 1);
}
function parseValue(value, options) {
if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) {
value = Number(value);
} else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
value = value.toLowerCase() === 'true';
}
return value;
}
function parse(query, options) {
options = Object.assign({
decode: true,
sort: true,
arrayFormat: 'none',
arrayFormatSeparator: ',',
parseNumbers: false,
parseBooleans: false
}, options);
validateArrayFormatSeparator(options.arrayFormatSeparator);
const formatter = parserForArrayFormat(options);
// Create an object with no prototype
const ret = Object.create(null);
if (typeof query !== 'string') {
return ret;
}
query = query.trim().replace(/^[?#&]/, '');
if (!query) {
return ret;
}
for (const param of query.split('&')) {
if (param === '') {
continue;
}
let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '=');
// Missing `=` should be `null`:
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
value = value === undefined ? null : ['comma', 'separator'].includes(options.arrayFormat) ? value : decode(value, options);
formatter(decode(key, options), value, ret);
}
for (const key of Object.keys(ret)) {
const value = ret[key];
if (typeof value === 'object' && value !== null) {
for (const k of Object.keys(value)) {
value[k] = parseValue(value[k], options);
}
} else {
ret[key] = parseValue(value, options);
}
}
if (options.sort === false) {
return ret;
}
return (options.sort === true ? Object.keys(ret).sort() : Object.keys(ret).sort(options.sort)).reduce((result, key) => {
const value = ret[key];
if (Boolean(value) && typeof value === 'object' && !Array.isArray(value)) {
// Sort object keys, not values
result[key] = keysSorter(value);
} else {
result[key] = value;
}
return result;
}, Object.create(null));
}
exports.extract = extract;
exports.parse = parse;
exports.stringify = (object, options) => {
if (!object) {
return '';
}
options = Object.assign({
encode: true,
strict: true,
arrayFormat: 'none',
arrayFormatSeparator: ','
}, options);
validateArrayFormatSeparator(options.arrayFormatSeparator);
const shouldFilter = key => (
(options.skipNull && isNullOrUndefined(object[key])) ||
(options.skipEmptyString && object[key] === '')
);
const formatter = encoderForArrayFormat(options);
const objectCopy = {};
for (const key of Object.keys(object)) {
if (!shouldFilter(key)) {
objectCopy[key] = object[key];
}
}
const keys = Object.keys(objectCopy);
if (options.sort !== false) {
keys.sort(options.sort);
}
return keys.map(key => {
const value = object[key];
if (value === undefined) {
return '';
}
if (value === null) {
return encode(key, options);
}
if (Array.isArray(value)) {
return value
.reduce(formatter(key), [])
.join('&');
}
return encode(key, options) + '=' + encode(value, options);
}).filter(x => x.length > 0).join('&');
};
exports.parseUrl = (url, options) => {
options = Object.assign({
decode: true
}, options);
const [url_, hash] = splitOnFirst(url, '#');
return Object.assign(
{
url: url_.split('?')[0] || '',
query: parse(extract(url), options)
},
options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {}
);
};
exports.stringifyUrl = (object, options) => {
options = Object.assign({
encode: true,
strict: true
}, options);
const url = removeHash(object.url).split('?')[0] || '';
const queryFromUrl = exports.extract(object.url);
const parsedQueryFromUrl = exports.parse(queryFromUrl, {sort: false});
const query = Object.assign(parsedQueryFromUrl, object.query);
let queryString = exports.stringify(query, options);
if (queryString) {
queryString = `?${queryString}`;
}
let hash = getHash(object.url);
if (object.fragmentIdentifier) {
hash = `#${encode(object.fragmentIdentifier, options)}`;
}
return `${url}${queryString}${hash}`;
};
exports.pick = (input, filter, options) => {
options = Object.assign({
parseFragmentIdentifier: true
}, options);
const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
return exports.stringifyUrl({
url,
query: filterObject(query, filter),
fragmentIdentifier
}, options);
};
exports.exclude = (input, filter, options) => {
const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
return exports.pick(input, exclusionFilter, options);
};
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (http://sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
var token = '%[a-f0-9]{2}';
var singleMatcher = new RegExp('(' + token + ')|([^%]+?)', 'gi');
var multiMatcher = new RegExp('(' + token + ')+', 'gi');
function decodeComponents(components, split) {
try {
// Try to decode the entire string first
return [decodeURIComponent(components.join(''))];
} catch (err) {
// Do nothing
}
if (components.length === 1) {
return components;
}
split = split || 1;
// Split the array in 2 parts
var left = components.slice(0, split);
var right = components.slice(split);
return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right));
}
function decode(input) {
try {
return decodeURIComponent(input);
} catch (err) {
var tokens = input.match(singleMatcher) || [];
for (var i = 1; i < tokens.length; i++) {
input = decodeComponents(tokens, i).join('');
tokens = input.match(singleMatcher) || [];
}
return input;
}
}
function customDecodeURIComponent(input) {
// Keep track of all the replacements and prefill the map with the `BOM`
var replaceMap = {
'%FE%FF': '\uFFFD\uFFFD',
'%FF%FE': '\uFFFD\uFFFD'
};
var match = multiMatcher.exec(input);
while (match) {
try {
// Decode as big chunks as possible
replaceMap[match[0]] = decodeURIComponent(match[0]);
} catch (err) {
var result = decode(match[0]);
if (result !== match[0]) {
replaceMap[match[0]] = result;
}
}
match = multiMatcher.exec(input);
}
// Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else
replaceMap['%C2'] = '\uFFFD';
var entries = Object.keys(replaceMap);
for (var i = 0; i < entries.length; i++) {
// Replace all decoded components
var key = entries[i];
input = input.replace(new RegExp(key, 'g'), replaceMap[key]);
}
return input;
}
module.exports = function (encodedURI) {
if (typeof encodedURI !== 'string') {
throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`');
}
try {
encodedURI = encodedURI.replace(/\+/g, ' ');
// Try the built in decoder first
return decodeURIComponent(encodedURI);
} catch (err) {
// Fallback to a more advanced decoder
return customDecodeURIComponent(encodedURI);
}
};
The MIT License (MIT)
Copyright (c) 2017, Sam Verschueren <sam.verschueren@gmail.com> (github.com/SamVerschueren)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{
"_from": "decode-uri-component@0.2.2",
"_id": "decode-uri-component@0.2.2",
"_inBundle": false,
"_integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"_location": "/query-string/decode-uri-component",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "decode-uri-component@0.2.2",
"name": "decode-uri-component",
"escapedName": "decode-uri-component",
"rawSpec": "0.2.2",
"saveSpec": null,
"fetchSpec": "0.2.2"
},
"_requiredBy": [
"/query-string"
],
"_resolved": "https://repo.huaweicloud.com/repository/npm/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"_shasum": "e69dbe25d37941171dd540e024c444cd5188e1e9",
"_spec": "decode-uri-component@0.2.2",
"_where": "E:\\H5_2.0\\node_modules\\query-string",
"author": {
"name": "Sam Verschueren",
"email": "sam.verschueren@gmail.com",
"url": "github.com/SamVerschueren"
},
"bugs": {
"url": "https://github.com/SamVerschueren/decode-uri-component/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "A better decodeURIComponent",
"devDependencies": {
"ava": "^0.17.0",
"coveralls": "^2.13.1",
"nyc": "^10.3.2",
"xo": "^0.16.0"
},
"engines": {
"node": ">=0.10"
},
"files": [
"index.js"
],
"homepage": "https://github.com/SamVerschueren/decode-uri-component#readme",
"keywords": [
"decode",
"uri",
"component",
"decodeuricomponent",
"components",
"decoder",
"url"
],
"license": "MIT",
"name": "decode-uri-component",
"repository": {
"type": "git",
"url": "git+https://github.com/SamVerschueren/decode-uri-component.git"
},
"scripts": {
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"test": "xo && nyc ava"
},
"version": "0.2.2"
}
# decode-uri-component
![CI](https://github.com/SamVerschueren/decode-uri-component/workflows/CI/badge.svg) [![Coverage Status](https://coveralls.io/repos/SamVerschueren/decode-uri-component/badge.svg?branch=master&service=github)](https://coveralls.io/github/SamVerschueren/decode-uri-component?branch=master)
> A better [decodeURIComponent](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent)
## Why?
- Decodes `+` to a space.
- Converts the [BOM](https://en.wikipedia.org/wiki/Byte_order_mark) to a [replacement character](https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character) `�`.
- Does not throw with invalid encoded input.
- Decodes as much of the string as possible.
## Install
```
$ npm install --save decode-uri-component
```
## Usage
```js
const decodeUriComponent = require('decode-uri-component');
decodeUriComponent('%25');
//=> '%'
decodeUriComponent('%');
//=> '%'
decodeUriComponent('st%C3%A5le');
//=> 'ståle'
decodeUriComponent('%st%C3%A5le%');
//=> '%ståle%'
decodeUriComponent('%%7Bst%C3%A5le%7D%');
//=> '%{ståle}%'
decodeUriComponent('%7B%ab%%7C%de%%7D');
//=> '{%ab%|%de%}'
decodeUriComponent('%FE%FF');
//=> '\uFFFD\uFFFD'
decodeUriComponent('%C2');
//=> '\uFFFD'
decodeUriComponent('%C2%B5');
//=> 'µ'
```
## API
### decodeUriComponent(encodedURI)
#### encodedURI
Type: `string`
An encoded component of a Uniform Resource Identifier.
## License
MIT © [Sam Verschueren](https://github.com/SamVerschueren)
---
<div align="center">
<b>
<a href="https://tidelift.com/subscription/pkg/npm-decode-uri-component?utm_source=npm-decode-uri-component&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
</b>
<br>
<sub>
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
</sub>
</div>
'use strict';
module.exports = function (obj, predicate) {
var ret = {};
var keys = Object.keys(obj);
var isArr = Array.isArray(predicate);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var val = obj[key];
if (isArr ? predicate.indexOf(key) !== -1 : predicate(key, val, obj)) {
ret[key] = val;
}
}
return ret;
};
The MIT License (MIT)
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
{
"_from": "filter-obj@1.1.0",
"_id": "filter-obj@1.1.0",
"_inBundle": false,
"_integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"_location": "/query-string/filter-obj",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "filter-obj@1.1.0",
"name": "filter-obj",
"escapedName": "filter-obj",
"rawSpec": "1.1.0",
"saveSpec": null,
"fetchSpec": "1.1.0"
},
"_requiredBy": [
"/query-string"
],
"_resolved": "https://repo.huaweicloud.com/repository/npm/filter-obj/-/filter-obj-1.1.0.tgz",
"_shasum": "9b311112bc6c6127a16e016c6c5d7f19e0805c5b",
"_spec": "filter-obj@1.1.0",
"_where": "E:\\H5_2.0\\node_modules\\query-string",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/filter-obj/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Filter object keys and values into a new object",
"devDependencies": {
"ava": "0.0.4",
"xo": "*"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"index.js"
],
"homepage": "https://github.com/sindresorhus/filter-obj#readme",
"keywords": [
"filter",
"obj",
"object",
"key",
"keys",
"value",
"values",
"val",
"iterate",
"iterator"
],
"license": "MIT",
"name": "filter-obj",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/filter-obj.git"
},
"scripts": {
"test": "xo && node test.js"
},
"version": "1.1.0"
}
# filter-obj [![Build Status](https://travis-ci.org/sindresorhus/filter-obj.svg?branch=master)](https://travis-ci.org/sindresorhus/filter-obj)
> Filter object keys and values into a new object
## Install
```
$ npm install --save filter-obj
```
## Usage
```js
var filterObj = require('filter-obj');
var obj = {
foo: true,
bar: false
};
var newObject = filterObj(obj, function (key, value, object) {
return value === true;
});
//=> {foo: true}
var newObject2 = filterObj(obj, ['bar']);
//=> {bar: true}
```
## Related
- [map-obj](https://github.com/sindresorhus/map-obj) - Map object keys and values into a new object
- [object-assign](https://github.com/sindresorhus/object-assign) - Copy enumerable own properties from one or more source objects to a target object
## License
MIT © [Sindre Sorhus](http://sindresorhus.com)
/**
Split a string on the first occurrence of a given separator.
@param string - The string to split.
@param separator - The separator to split on.
@example
```
import splitOnFirst = require('split-on-first');
splitOnFirst('a-b-c', '-');
//=> ['a', 'b-c']
splitOnFirst('key:value:value2', ':');
//=> ['key', 'value:value2']
splitOnFirst('a---b---c', '---');
//=> ['a', 'b---c']
splitOnFirst('a-b-c', '+');
//=> ['a-b-c']
```
*/
declare function splitOnFirst(
string: string,
separator: string
): [string, string?];
export = splitOnFirst;
'use strict';
module.exports = (string, separator) => {
if (!(typeof string === 'string' && typeof separator === 'string')) {
throw new TypeError('Expected the arguments to be of type `string`');
}
if (separator === '') {
return [string];
}
const separatorIndex = string.indexOf(separator);
if (separatorIndex === -1) {
return [string];
}
return [
string.slice(0, separatorIndex),
string.slice(separatorIndex + separator.length)
];
};
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{
"_from": "split-on-first@1.1.0",
"_id": "split-on-first@1.1.0",
"_inBundle": false,
"_integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"_location": "/query-string/split-on-first",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "split-on-first@1.1.0",
"name": "split-on-first",
"escapedName": "split-on-first",
"rawSpec": "1.1.0",
"saveSpec": null,
"fetchSpec": "1.1.0"
},
"_requiredBy": [
"/query-string"
],
"_resolved": "https://repo.huaweicloud.com/repository/npm/split-on-first/-/split-on-first-1.1.0.tgz",
"_shasum": "f610afeee3b12bce1d0c30425e76398b78249a5f",
"_spec": "split-on-first@1.1.0",
"_where": "E:\\H5_2.0\\node_modules\\query-string",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/split-on-first/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Split a string on the first occurance of a given separator",
"devDependencies": {
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
},
"engines": {
"node": ">=6"
},
"files": [
"index.js",
"index.d.ts"
],
"homepage": "https://github.com/sindresorhus/split-on-first#readme",
"keywords": [
"split",
"string",
"first",
"occurrence",
"separator",
"delimiter",
"text"
],
"license": "MIT",
"name": "split-on-first",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/split-on-first.git"
},
"scripts": {
"test": "xo && ava && tsd"
},
"version": "1.1.0"
}
# split-on-first [![Build Status](https://travis-ci.com/sindresorhus/split-on-first.svg?branch=master)](https://travis-ci.com/sindresorhus/split-on-first)
> Split a string on the first occurrence of a given separator
This is similar to [`String#split()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split), but that one splits on all the occurrences, not just the first one.
## Install
```
$ npm install split-on-first
```
## Usage
```js
const splitOnFirst = require('split-on-first');
splitOnFirst('a-b-c', '-');
//=> ['a', 'b-c']
splitOnFirst('key:value:value2', ':');
//=> ['key', 'value:value2']
splitOnFirst('a---b---c', '---');
//=> ['a', 'b---c']
splitOnFirst('a-b-c', '+');
//=> ['a-b-c']
```
## API
### splitOnFirst(string, separator)
#### string
Type: `string`
The string to split.
#### separator
Type: `string`
The separator to split on.
## Related
- [split-at](https://github.com/sindresorhus/split-at) - Split a string at one or more indices
## License
MIT © [Sindre Sorhus](https://sindresorhus.com)
'use strict';
module.exports = str => encodeURIComponent(str).replace(/[!'()*]/g, x => `%${x.charCodeAt(0).toString(16).toUpperCase()}`);
The MIT License (MIT)
Copyright (c) Kevin Martensson <kevinmartensson@gmail.com> (github.com/kevva)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
{
"_from": "strict-uri-encode@2.0.0",
"_id": "strict-uri-encode@2.0.0",
"_inBundle": false,
"_integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==",
"_location": "/query-string/strict-uri-encode",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "strict-uri-encode@2.0.0",
"name": "strict-uri-encode",
"escapedName": "strict-uri-encode",
"rawSpec": "2.0.0",
"saveSpec": null,
"fetchSpec": "2.0.0"
},
"_requiredBy": [
"/query-string"
],
"_resolved": "https://repo.huaweicloud.com/repository/npm/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"_shasum": "b9c7330c7042862f6b142dc274bbcc5866ce3546",
"_spec": "strict-uri-encode@2.0.0",
"_where": "E:\\H5_2.0\\node_modules\\query-string",
"author": {
"name": "Kevin Mårtensson",
"email": "kevinmartensson@gmail.com",
"url": "github.com/kevva"
},
"bugs": {
"url": "https://github.com/kevva/strict-uri-encode/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "A stricter URI encode adhering to RFC 3986",
"devDependencies": {
"ava": "*",
"xo": "*"
},
"engines": {
"node": ">=4"
},
"files": [
"index.js"
],
"homepage": "https://github.com/kevva/strict-uri-encode#readme",
"keywords": [
"component",
"encode",
"RFC3986",
"uri"
],
"license": "MIT",
"name": "strict-uri-encode",
"repository": {
"type": "git",
"url": "git+https://github.com/kevva/strict-uri-encode.git"
},
"scripts": {
"test": "xo && ava"
},
"version": "2.0.0"
}
# strict-uri-encode [![Build Status](https://travis-ci.org/kevva/strict-uri-encode.svg?branch=master)](https://travis-ci.org/kevva/strict-uri-encode)
> A stricter URI encode adhering to [RFC 3986](http://tools.ietf.org/html/rfc3986)
## Install
```
$ npm install --save strict-uri-encode
```
## Usage
```js
const strictUriEncode = require('strict-uri-encode');
strictUriEncode('unicorn!foobar');
//=> 'unicorn%21foobar'
strictUriEncode('unicorn*foobar');
//=> 'unicorn%2Afoobar'
```
## API
### strictUriEncode(string)
#### string
Type: `string`, `number`
String to URI encode.
## License
MIT © [Kevin Mårtensson](http://github.com/kevva)
{
"name": "query-string",
"version": "6.14.1",
"description": "Parse and stringify URL query strings",
"license": "MIT",
"repository": "sindresorhus/query-string",
"funding": "https://github.com/sponsors/sindresorhus",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"engines": {
"node": ">=6"
},
"scripts": {
"benchmark": "node benchmark.js",
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"browser",
"querystring",
"query",
"string",
"qs",
"param",
"parameter",
"url",
"parse",
"stringify",
"encode",
"decode",
"searchparams",
"filter"
],
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"devDependencies": {
"ava": "^1.4.1",
"benchmark": "^2.1.4",
"deep-equal": "^1.0.1",
"fast-check": "^1.5.0",
"tsd": "^0.7.3",
"xo": "^0.24.0"
},
"__npminstall_done": true,
"_from": "query-string@6.14.1",
"_resolved": "https://registry.npmmirror.com/query-string/-/query-string-6.14.1.tgz"
}
\ No newline at end of file
# uni-simple-router
> 一个更为简洁的[Vue-router](https://router.vuejs.org/zh/),专为 [uni-app](https://uniapp.dcloud.io/) 量身打造
## 介绍
`uni-simple-router` 是专为 [uni-app](https://uniapp.dcloud.io/) 打造的路由器。它与 [uni-app](https://uniapp.dcloud.io/) 核心深度集成,使使用 [uni-app](https://uniapp.dcloud.io/) 轻松构建单页应用程序变得轻而易举。功能包括:
* `H5端` 能完全使用 `vue-router` 进行开发。
* 模块化,基于组件的路由器配置。
* 路由参数,查询,通配符。
* `H5端` 查看由 `uni-simple-router` 过渡系统提供动力的过渡效果。
* 更细粒度的导航控制。
* `H端`自动控制活动的CSS类链接。
* 通配小程序端、APP端、H5端。
开始使用 [查看文档](http://hhyang.cn),或 [使用示例](https://github.com/SilurianYang/uni-simple-router/tree/master/examples)(请参见下面的示例)。
## 问题
在提交问题的之前,请确保阅读 [“问题报告清单”](https://github.com/SilurianYang/uni-simple-router/issues/new?assignees=&labels=&template=bug_report.md&title=) 。不符合准则的问题可能会立即被解决。
## 贡献
提出拉取请求之前,请务必先阅读 [查看文档](http://hhyang.cn)(请参见下面的示例)。。
## 变更日志
[发行说明](https://github.com/SilurianYang/uni-simple-router/releases) 中记录了每个发行版的详细信息更改。
## 特别感谢
特别感谢 [markrgba](https://github.com/markrgba) 一直以来对文档和相关测试的维护。
## 技术交流
<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=0f4d7f38e5d15dd49bf7c3032c80ed3f54ecfa3dd800053d6ae145c869f9eb47"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="uni-app 插件" title="uni-app 插件"></a>
import {
proxyLaunchHook, beforeBackHooks, beforeTabHooks, backApiCallHook,
} from './hooks';
import { Global, uniAppHook } from '../helpers/config';
import { assertCanBack } from './util';
import { warn } from '../helpers/warn';
/**
* 重写掉uni-app的 uni.getLocation 和 uni.chooseLocation APi
* @param {Object} Router 当前路由对象
*/
export const rewriteUniFun = function (Router) {
const oldSwitchTab = uni.switchTab; // 缓存 跳转到 tabBar 页面
uni.switchTab = function ({ url, ...args }, normal = false) {
if (normal === true || uniAppHook.pageReady === false) { // 调用原始的uni-app api
oldSwitchTab({
url,
...args,
});
} else {
if (uniAppHook.pageReady) { // 只有在路由守卫等 处理完所有操作后才能触发
const { path } = Router.$Route; // 获取当前路径
if (path == url) { // 路径相同不执行
return warn(`当前跳转路径:${url} 已在本页面无须跳转`);
}
beforeTabHooks.call(Router, url.substring(1)); // 不要 /
} else {
warn('路由守卫正在忙碌中 不允许执行 ‘uni.switchTab’');
}
}
};
};
/**
* 对当前app做一个动画页面 用来过渡首次next 等待时间过长的尴尬
* @param {Object} Router 当前路由对象
*/
export const registerLoddingPage = function (Router) {
const { loddingPageHook, loddingPageStyle } = Router.CONFIG.APP; // 获取app所有配置
const view = new plus.nativeObj.View('router-loadding', {
top: '0px',
left: '0px',
height: '100%',
width: '100%',
...loddingPageStyle.call(Router),
});
loddingPageHook.call(Router, view); // 触发等待页面生命周期
};
/**
* 移除当前 页面上 非router 声明的 onBackPress 事件
* @param {Object} page 当前 vue 组件对象
* @param {Object} options 当前page对象的 $options
* 修复 https://github.com/SilurianYang/uni-simple-router/issues/106
*/
export const removeBackPressEvent = function (page, options) {
const isBack = assertCanBack(page);
if (isBack) { // 可返回
options.onBackPress = [options.onBackPress[0]]; // 路由混入的都干掉
}
};
/**
* 判断当前页面是否需要拦截返回
*
* @param {Object} page 当前 vue 组件对象
* @param {Object} options 当前 vue 组件对象下的$options对象
* @param {Array} args 当前页面是点击头部返回还是底部返回
* 修复 https://github.com/SilurianYang/uni-simple-router/issues/66
*
* this 为当前 Router 对象
*/
export const pageIsHeadBack = function (page, options, args) {
if (args[0].from == 'navigateBack') { // 调用api返回
if (Global.LockStatus) { // 正在跳转的时候 返回按键按的太快啦
warn('当前页面正在处于跳转状态,请稍后再进行跳转....');
return true;
}
Global.LockStatus = true; // 设置为锁住状态
backApiCallHook.call(this, options, args);
return true;
}
const isBack = assertCanBack(page);
if (isBack) { // 可返回
if (Global.LockStatus) { // 正在跳转的时候 返回按键按的太快啦
warn('当前页面正在处于跳转状态,请稍后再进行跳转....');
return true;
}
Global.LockStatus = true; // 设置为锁住状态
beforeBackHooks.call(this, options, args);
return true;
}
return false;
};
/**
* 开始初始化app端路由配置
*
* @param {Object} Router
*
* this 为当前 page 对象
*/
export const appInit = function (Router) {
proxyLaunchHook.call(this);
const { holdTabbar } = Router.CONFIG.APP;
if (holdTabbar) { // 开启tab拦截时
rewriteUniFun(Router);
}
registerLoddingPage(Router);
};
import { methods, baseConfig, Global } from '../helpers/config';
import { noop, formatURLQuery } from '../helpers/util';
let stop = null;
/**
* @param {Object} finalRoute 格式化后的路由跳转规则
* @param {Object} NAVTYPE 需要调用的跳转方法
*/
const uniPushTo = function (finalRoute, NAVTYPE) {
return new Promise((resolve) => {
const query = formatURLQuery(`?${finalRoute.uniRoute.query}`);
const { APP } = baseConfig;
const { url } = finalRoute.uniRoute;
stop = setTimeout(() => {
resolve(url);
resolve = noop; // 执行完了就没了 确保不会被下一次执行
Global.LockStatus = false; // 跳转完成解锁状态
}, APP.switchPageOutTime);
uni[methods[NAVTYPE]]({
url: url + query,
...finalRoute.route.animation,
complete: () => {
clearTimeout(stop);
resolve(url);
resolve = noop; // 执行完了就没了 确保不会被下一次执行
Global.LockStatus = false; // 跳转完成解锁状态
},
}, true); // 这里传递true 主要是兼容重写 uni.switchTab
});
};
export default uniPushTo;
import { err } from '../helpers/warn';
import { copyObject, parseQuery } from '../helpers/util';
import { Global, route as mergeRoute } from '../helpers/config';
/**
* 触发指定生命钩子
* @param {Array} funList //需要执行的方法列表
* @param {Object} args //触发生命钩子传递的参数
*/
export const callAppHook = function (funList = [], args) {
for (let i = 0; i < funList.length; i += 1) {
funList[i].call(this, args);
}
};
/**
* @param {Number} index //需要获取的页面下标 -2:表示获取最后一个即当前页面 -1:表示全部 -3:当前页面的前一个页面
* @param {Boolean} all //是否获取全部的页面
*/
export const getPages = function (index = -1, all) {
const pages = getCurrentPages(all);
if (index === -1) {
return pages;
}
if (index === -2) {
return pages[pages.length - 1];
}
if (index === -3) {
return pages[pages.length - 2];
}
return pages[index];
};
/**
* 验证当前页面是否为nvue页面
* @param {Object} page 当前页面对象
*/
export const isNvuePage = function (page) {
const cstr = page.constructor.name;
const pageType = {
s: true,
z: false,
};
return pageType[cstr];
};
/**
* @param {Object} page //当前顶级页面对象
* @param {Object} vim:? //是否获取 $vm 对象还是 $mp 对象
*/
export const getPageVmOrMp = function (page, vim = true) {
if (vim) {
return page.$vm;
}
if (page.$vm.$mp) {
return page.$vm.$mp;
}
if (isNvuePage(page)) { // nvue 页面
return {
page,
query: page.__displayReporter.query,
};
}
};
/**
* 获取 to 的配置参数
* @param {Object} rule 当前跳转的规则
*/
export const formatTo = function (finalRoute) {
const route = copyObject(finalRoute.route);
const { rule } = finalRoute;
route.query = rule.query || rule.params || {};
return route;
};
/**
* 通过一个未知的路径或者名称 在路由表中查找指定路由表 并返回
* @param {string} type //path 或者 name
* @param {Object} routes //当前对象的所有路由表
*/
export const pathOrNameToRoute = function (type, routes = Global.Router.CONFIG.routes) {
const routesKeys = Object.keys(routes);
for (let i = 0; i < routesKeys.length; i += 1) {
const key = routesKeys[i];
const item = routes[key];
if (item.path === `/${type}`) {
return mergeRoute(item); // 合并一下对象,主要是合并 query:{} 及 params:{}
}
if (item.path === type) {
return mergeRoute(item); // 合并一下对象,主要是合并 query:{} 及 params:{}
}
if (item.name == type) {
return mergeRoute(item); // 合并一下对象,主要是合并 query:{} 及 params:{}
}
}
err(`当前 '${type}' 在路由表中没有找到匹配的 name 或者 path`);
};
/**
* 统一格式话 路由传递的参数 看看是编码还是非编码 做相应的对策
*
* @param {Object} query 当前的路由参数
* @param {Boolean} getter 是从页面获取 route 对象下的参数 还是编码后传输
*/
export const getFormatQuery = function (query = {}) {
if (Global.Router.CONFIG.encodeURI) {
try {
query = JSON.parse(decodeURIComponent(query.query || encodeURIComponent('{}')));
} catch (e) {
query = JSON.parse(query.query);
}
}
return query;
};
/**
* 获取 from 的配置参数 from 页面永远都是站在当前页面忘其它地方走 所以都是最后一个页面
*
* @param {Object} routes //当前对象的所有路由表
*/
export const formatFrom = function (routes) {
const topPage = getPages(-2);
const { page, query } = getPageVmOrMp(topPage, false);
const route = pathOrNameToRoute(page.route, routes); // 获取到当前路由表下的 route
route.query = getFormatQuery(query); // 不管是编码传输还是非编码 最后都得在 to/from 中换成json对象
return route;
};
/**
*
* 把用户的跳转路由规则格式化成uni-app可用的路由跳转规则
*
* @param {Object} rule //当前用户跳转的路由规则
* @param {Object} routes //当前simple-router 下的路由表
*/
export const ruleToUniNavInfo = function (rule, routes) {
if (rule == null) {
return err('当前跳转规则为空,请检查跳转代码');
}
// eslint-disable-next-line
let [navType, route, query, animation] = ['path', null, {}, {}];
if (rule.constructor === String) { // 是字符串类型 那当前就是路径啦
route = pathOrNameToRoute(rule, routes); // 直接把 rule 当 path 传递 完事
} else if (rule.constructor === Object) { // 对象类型 可以是 path 或者 name
route = pathOrNameToRoute(rule.path || (navType = 'name', rule.name), routes); // 两则必有其一 报错自己处理
query = rule.query || rule.params || {};
animation = rule.animation || {};
} else {
return err('传的什么乱七八糟的类型?路由跳转规则只认字符串 \'path\' , 对象 \'path\' , 对象 \'name\' ');
}
animation = { ...Global.Router.CONFIG.APP.animation, ...route.animation || {}, ...animation }; // 合并多种方式声明的动画效果
route.animation = animation; // 这才是最终的页面切换效果
// 路径处理完后 开始格式化参数
const uniRoute = parseQuery(route.path, query); // uni-app 需要的跳转规则
return {
rule,
route,
uniRoute,
};
};
/**
* 获取当前页面下的 Route 信息
*
* @param {Object} pages 获取页面对象集合
* @param {Object} Vim 用户传递的当前页面对象
*/
export const APPGetPageRoute = function (pages, Vim) {
let [query, path] = [{}, ''];
const page = pages[pages.length - 1]; // 获取到当前页面
if (pages.length > 0) {
query = getFormatQuery(page.options, true);
path = page.route;
} else if (Vim != null) {
query = getFormatQuery(Vim.$mp.page.options, true);
path = page.route;
}
const route = pathOrNameToRoute(path);
route.query = query;
return route;
};
/**
* 获取当前页面下的 onBeforeBack 生命周期并执行
*
* @param {Object} args 当前返回页面时uni-app传递的参数
*/
export const getPageOnBeforeBack = function (args) {
return new Promise(async (resolve) => {
const currPage = getPages(-2); // 获取到当前页面
const { onBeforeBack } = currPage.$vm.$options;
if (onBeforeBack != null && onBeforeBack.constructor === Function) {
const isNext = await onBeforeBack.call(currPage.$vm, args);
if (isNext === true) {
return resolve(false);
}
}
return resolve(true);
});
};
/**
* 断言当前页面是否可返回上一级
* @param {Object} page 当前页面webview对象
*/
export const assertCanBack = function (page) {
const pageStyle = page.$getAppWebview().getStyle();
if (pageStyle.titleNView != null && pageStyle.titleNView.autoBackButton) { // 只有处理有带返回按钮的页面
return true;
}
// 两种情况 1.真的是顶级页面时 2.自定义头部
const { $page } = page;
if ($page && $page.meta.isQuit === false) { // 自定义头部 不是顶级页面
return true;
}
return false; // 不可返回 真的是顶级页面时 返回就直接退出app了
};
import { methods, Global } from '../helpers/config';
import { formatURLQuery } from '../helpers/util';
/**
* @param {Object} finalRoute 格式化后的路由跳转规则
* @param {Object} NAVTYPE 需要调用的跳转方法
*/
const appletsUniPushTo = function (finalRoute, NAVTYPE) {
return new Promise((resolve) => {
const query = formatURLQuery(`?${finalRoute.uniRoute.query}`);
const { url } = finalRoute.uniRoute;
uni[methods[NAVTYPE]]({
url: url + query,
complete: () => {
resolve(url);
Global.LockStatus = false; // 跳转完成解锁状态
},
});
});
};
export default appletsUniPushTo;
import { proxyLaunchHook } from './hooks';
/**
* 开始初始化app端路由配置
*
* @param {Object} Router 当前Router对象
*
* this 为当前 page 对象
*/
const appletsInit = function () {
proxyLaunchHook.call(this);
};
export default appletsInit;
import { Global, route as mergeRoute } from '../helpers/config';
import { copyObject, parseQuery } from '../helpers/util';
import { err } from '../helpers/warn';
import { baiduApple, touTiao } from '../helpers/compile';
/**
* 触发指定生命钩子
* @param {Array} funList //需要执行的方法列表
* @param {Object} args //触发生命钩子传递的参数
*/
export const callAppHook = function (funList, args) {
for (let i = 0; i < funList.length; i += 1) {
funList[i].call(this, args);
}
};
/**
* @param {Object} page //当前顶级页面对象
* @param {Object} vim:? //是否获取 $vm 对象还是 $mp 对象
*/
export const getPageVmOrMp = function (page, vim = true) {
if (vim) {
return page.$vm;
}
const { $mp } = page.$vm;
baiduApple(() => { // 百度小程序新增一个route属性
$mp.page.route = $mp.page.is;
});
touTiao(() => { // 头条小程序新增一个route属性
$mp.page.route = $mp.page.is;
});
return $mp;
};
/**
* 统一格式话 路由传递的参数 看看是编码还是非编码 做相应的对策
*
* @param {Object} query 当前的路由参数
* @param {Boolean} getter 是从页面获取 route 对象下的参数 还是编码后传输
*/
export const getFormatQuery = function (query = {}, getter = false) {
if (Global.Router.CONFIG.encodeURI) {
if (getter) {
try { // 除去微信小程序都不需要 decodeURIComponent
query = JSON.parse(decodeURIComponent(query.query) || '{}');
} catch (e) { // 其他小程序
query = JSON.parse(query.query || '{}');
}
} else {
try {
query = JSON.parse(decodeURIComponent(query.query || encodeURIComponent('{}')));
} catch (e) {
query = JSON.parse(query.query);
}
}
}
return query;
};
/**
* @param {Number} index //需要获取的页面下标 -2:表示获取最后一个即当前页面 -1:表示全部 -3:当前页面的前一个页面
* @param {Boolean} all //是否获取全部的页面
*/
export const getPages = function (index = -1, all) {
const pages = getCurrentPages(all);
if (index === -1) {
return pages;
}
if (index === -2) {
return pages[pages.length - 1];
}
if (index === -3) {
return pages[pages.length - 2];
}
return pages[index];
};
/**
* 通过一个未知的路径或者名称 在路由表中查找指定路由表 并返回
* @param {string} type //path 或者 name
* @param {Object} routes //当前对象的所有路由表
*/
export const pathOrNameToRoute = function (type, routes = Global.Router.CONFIG.routes) {
const routesKeys = Object.keys(routes);
for (let i = 0; i < routesKeys.length; i += 1) {
const key = routesKeys[i];
const item = routes[key];
if (item.path === `/${type}`) {
return mergeRoute(item); // 合并一下对象,主要是合并 query:{} 及 params:{}
}
if (item.path === type) {
return mergeRoute(item); // 合并一下对象,主要是合并 query:{} 及 params:{}
}
if (item.name == type) {
return mergeRoute(item); // 合并一下对象,主要是合并 query:{} 及 params:{}
}
}
err(`当前 '${type}' 在路由表中没有找到匹配的 name 或者 path`);
};
/**
* 获取 to 的配置参数
* @param {Object} rule 当前跳转的规则
*/
export const formatTo = function (finalRoute) {
const route = copyObject(finalRoute.route);
const { rule } = finalRoute;
route.query = rule.query || rule.params || {};
return route;
};
/**
* 获取 from 的配置参数 from 页面永远都是站在当前页面忘其它地方走 所以都是最后一个页面
*
* @param {Object} routes //当前对象的所有路由表
*/
export const formatFrom = function (routes) {
const topPage = getPages(-2);
const { page, query } = getPageVmOrMp(topPage, false);
const route = pathOrNameToRoute(page.route, routes); // 获取到当前路由表下的 route
route.query = getFormatQuery(query); // 不管是编码传输还是非编码 最后都得在 to/from 中换成json对象
return route;
};
/**
*
* 把用户的跳转路由规则格式化成uni-app可用的路由跳转规则
*
* @param {Object} rule //当前用户跳转的路由规则
* @param {Object} routes //当前simple-router 下的路由表
*/
export const ruleToUniNavInfo = function (rule, routes) {
if (rule == null) {
return err('当前跳转规则为空,请检查跳转代码');
}
// eslint-disable-next-line
let [navType, route, query] = ['path', null, {}];
if (rule.constructor === String) { // 是字符串类型 那当前就是路径啦
route = pathOrNameToRoute(rule, routes); // 直接把 rule 当 path 传递 完事
} else if (rule.constructor === Object) { // 对象类型 可以是 path 或者 name
route = pathOrNameToRoute(rule.path || (navType = 'name', rule.name), routes); // 两则必有其一 报错自己处理
query = rule.query || rule.params || {};
} else {
return err('传的什么乱七八糟的类型?路由跳转规则只认字符串 \'path\' , 对象 \'path\' , 对象 \'name\' ');
}
// 路径处理完后 开始格式化参数
const uniRoute = parseQuery(route.path, query); // uni-app 需要的跳转规则
return {
rule,
route,
uniRoute,
};
};
/**
* 获取当前页面下的 Route 信息
*
* @param {Object} pages 获取页面对象集合
* @param {Object} Vim 用户传递的当前页面对象
*/
export const AppletsPageRoute = function (pages, Vim) {
let [query, path] = [{}, ''];
const page = pages[pages.length - 1]; // 获取到当前页面
if (pages.length > 0) {
const uniQuery = getPageVmOrMp(page, false).query;
query = getFormatQuery(uniQuery, true);
path = page.route;
} else if (Vim != null) {
query = getFormatQuery(Vim.$mp.page.options, true);
path = page.route;
}
const route = pathOrNameToRoute(path);
route.query = query;
return route;
};
const render = function (node) {
if (typeof node == 'string') { // 是一个文本节点
return document.createTextNode(node);
}
if (node instanceof HTMLElement) {
return node;
}
// eslint-disable-next-line
return createElement(node);
};
/**
* 根据标签及属性创建一个dom
*/
const createElement = function ({
tag,
attrs,
children,
} = {}) {
const $el = document.createElement(tag);
// eslint-disable-next-line
for (const [k, v] of Object.entries(attrs)) {
$el.setAttribute(k, v);
}
// eslint-disable-next-line
for (const item of children) {
$el.appendChild(render(item));
}
return $el;
};
const html = createElement({
tag: 'div',
attrs: {
id: 'router-loadding',
},
children: [
createElement({
tag: 'div',
attrs: {
class: 'loadding',
},
children: [],
}),
],
});
/* eslint-disable */
const style = createElement({
tag: 'style',
attrs: {
id: 'HHYANG_style',
},
children: [
`
body{padding:0;margin:0}#router-loadding{position:fixed;width:100%;height:3px;transition:all .05s;top:0;z-index:9999999999999999;}#router-loadding .loadding{position:fixed;top:0;height:3px;background-color:#47b14b;width:0;box-shadow:0 0 15px #4CAF50;transition:all .8s;border-top-right-radius:3px;border-bottom-right-radius:3px}
`,
],
});
const script = createElement({
tag: 'script',
attrs: {
id: 'HHYANG_script',
},
children: [
`
var HHYANG_El=document.querySelector("#router-loadding .loadding"),HHYANG_Pel=document.querySelector("#router-loadding"),w=0,stop=null,WH=window.innerWidth,loop=function(){w=w>=WH-35?w+parseInt(5*Math.random()):w+parseInt(35*Math.random());HHYANG_El.style.cssText="width:"+w+"px";w>=WH&&clearInterval(stop)};window.startLodding=function(a){a=void 0===a?500:a;HHYANG_Pel.style.cssText="display: block;";HHYANG_El.style.cssText="transition: all 0.8s;";w=0;clearInterval(stop);stop=setInterval(function(){loop()},a)};window.stopLodding=function(a){a=void 0===a?200:a;clearInterval(stop);HHYANG_El.style.cssText="width:"+WH+"px;transition: all "+a/1E3+"s;";HHYANG_Pel.style.cssText="opacity: 0;transition: all "+a/1E3+"s;";setTimeout(function(){HHYANG_Pel.style.cssText="display: none;";HHYANG_El.style.cssText="width:0px";w=0},a)};
`,
],
});
export const DOM = {
style,
html,
script,
};
/* eslint-enable */
<template>
<view @click="gotoPage()"><slot></slot></view>
</template>
<script>
const navType = {
push: 'push',
replace: 'replace',
replaceAll: 'replaceAll',
pushTab: 'pushTab'
};
export default {
props: {
to: {
type: [String, Object],
},
stopNavto: {
type: Boolean,
default: false
},
navType: {
type: String,
default: 'push'
},
level: {
type: Number,
default: 1
},
append: {
type: Boolean,
default: false
}
},
methods: {
formatNav(text) {
if (text != null && text.constructor === String) {
text = text.replace(/\'/g, '');
text = text.replace(/(\w+)(?=:)/g, function(val) {
return `"${val}"`;
});
text = text.replace(/:\s*([^,{}\s"]+)/g, function(val) {
const arr = val.split(':');
return `:"${arr[1].trim()}"`;
});
try {
text = JSON.parse(text);
} catch (e) {}
}
if (this.append) {
let pathArr = this.$Route.path.split('/');
pathArr.splice(pathArr.length - this.level, this.level);
pathArr = pathArr.join('/');
if (text.constructor === Object) {
if (text.path) {
text.path = pathArr + text.path;
}
} else {
text = pathArr + text;
}
}
return text;
},
gotoPage() {
if (this.stopNavto) {
return true;
}
const type = navType[this.navType];
if (type == null) {
return console.error(` "navType" unknown type \n\n value:${Object.values(navType).join('、')}`);
}
const navInfo = this.formatNav(this.to);
this.$Router[type](navInfo);
}
}
};
</script>
<style></style>
export const H5 = function (fn) {
// #ifdef H5
fn();
// #endif
};
export const APP = function (fn) {
// #ifdef APP-PLUS
fn();
// #endif
};
export const applets = function (fn) {
// #ifdef MP
fn();
// #endif
};
export const notH5 = function (fn) {
// #ifndef H5
fn();
// #endif
};
export const baiduApple = function (fn) {
// #ifdef MP-BAIDU
fn();
// #endif
};
export const touTiao = function (fn) {
// #ifdef MP-TOUTIAO
fn();
// #endif
};
export const mp = function (fn) {
// #ifdef MP
fn();
// #endif
};
export const baseConfig = {
h5: {
rewriteFun: true, // 是否对uni-app reLaunch/navigateBack 两个方法重写 处理uni刷新直接返回到首页和触发路由守卫
paramsToQuery: false, // h5端上通过params传参时规则是vue-router 刷新会丢失 开启此开关将变成?连接的方式
loading: true, // 是否显示加载动画
hinderTab: false, // 是否拦截uni-app自带底部菜单 TODO
vueRouterDev: false, // 完全使用采用vue-router的开发模式
useUniConfig: true, // 是否采用在pages.json下的所有页面配置信息,false时需开发者自行设置页面
keepUniIntercept: false, // 保留uni-app使用vue-router的拦截器
vueNext: false, // 在next管道函数中是否获取vueRouter next的原本参数
replaceStyle: false, // 是否对resetStyle函数中返回的style节点进行全部替换,否则为追加
resetStyle: () => JSON.parse('{}'), // 自定义加载样式函数 可返回一个包涵 html、style、script 的对象来重置Router内置的加载动画
mode: 'hash',
base: '/',
linkActiveClass: 'router-link-active',
linkExactActiveClass: 'router-link-exact-active',
scrollBehavior: (to, from, savedPostion) => savedPostion,
fallback: true,
},
APP: {
holdTabbar: true, // 是否开启底部菜单拦截
loddingPageStyle: () => JSON.parse('{"backgroundColor":"#FFF"}'), // 当前等待页面的样式 必须返回一个json
loddingPageHook: (view) => { plus.navigator.closeSplashscreen(); view.show(); }, // 刚刚打开页面处于等待状态,会触发此事件
animation: { animationType: 'pop-in', animationDuration: 300 }, // 页面切换动画
switchPageOutTime: 1000, // 最高能忍耐的页面切换时间 达到此时间 不管切换有没有完成都会显示页面出来 这对启动页帮助很大
},
debugger: false, // 是否处于开发阶段 设置为true则打印日志
encodeURI: true, // 是否对url传递的参数进行编码
routerBeforeEach: () => {}, // router 前置路由函数 每次触发跳转前先会触发此函数
routerAfterEach: () => {}, // router 后置路由函数 每次触发跳转后会触发此函数
routes: [],
};
export const methods = {
push: 'navigateTo',
replace: 'redirectTo',
replaceAll: 'reLaunch',
pushTab: 'switchTab',
back: 'navigateBack',
};
export const H5FnTypeToggle = {
push: 'push',
replace: 'replace',
replaceAll: 'replace',
pushTab: 'replace',
};
export const lifeCycle = {
beforeHooks: [],
afterHooks: [],
routerHooks: [],
routerbeforeHooks: [], // 内部跳转前生命周期
routerAfterHooks: [], // 内部跳转后生命周期
};
export const Global = { // 缓存一些必要的对象,作为全局可以访问的参数
$parseQuery: null, // url query 帮助类实例
Router: {},
vueRouter: {},
addedRoutes: [], // 用于缓存用户动态添加的路由
RouterReadyPromise: () => {},
H5RouterReady: null, // 当前router是否就绪
backLayerC: 1, // 返回api调用时开发者传递的 delta
LockStatus: false, // 当前是否正在进行跳转 正在跳转调用api是不给跳转的
};
export const uniAppHook = {
indexVue: {}, // 首页 组件对象
toutiaoIndexThis: {}, // 头条小程序Index this对象
appVue: {}, // 同getApp()获取到的对象一毛一样的 其实就是app.vue组件
onLaunch: { fun: [], args: {}, isHijack: false }, // 这两个是app.vue
onShow: { fun: [], args: {}, isHijack: false },
variationFuns: ['onReady', 'onUnload'], // 一些uni-app的变异方法 需要处理下
waitHooks: {}, // 首页等待中的生命钩子 一些需要等待的Hooks,就是在页面没有进来之前一些提前触发的生命钩子 主要是用户已经声明好的
indexCallHooks: ['onLoad', 'onReady', 'created', 'onShow'], // 在首页首次启动时需要触发的生命周期
needHooks: ['onLoad', 'onReady', 'onShow', 'created', 'onHide', 'onUnload', 'onResize'], // 首页需要拦截的生命钩子
pageReady: false,
onLaunched: false, // 否触发onLaunch事件
};
export const appletsConfig = { // 小程序端的一些路由所需配置
onLaunchEd: false, // 当前小程序端是否触发onLaunch事件
};
export const route = function (object = {}) {
return {
...object,
params: {},
query: {},
};
};
import { uniAppHook } from './config';
import H5init from '../vueRouter/init';
import { appInit, removeBackPressEvent, pageIsHeadBack } from '../appRouter/init';
import appletsInit from '../appletsRouter/init';
import { appPlatform } from './util';
import { proxyIndexHook } from '../appRouter/hooks';
import { appletsProxyIndexHook } from '../appletsRouter/hooks';
/**
* 获取一些需要在各个平台混入的事件
* @param {Object} Router 当前原始路由对象
*/
const getMixins = function (Router) {
return {
H5: {
beforeCreate() {
if (this.$options.router) {
H5init(Router.$root, this.$options.router, this);
}
},
},
APP: {
onLaunch() {
uniAppHook.onLaunched = true; // 标志已经触发了 onLaunch 事件
appInit.call(this, Router.$root);
},
onLoad() {
// 第一个页面 拦截所有生命周期
if (uniAppHook.onLaunched && !uniAppHook.pageReady) {
uniAppHook.onLaunched = false;
proxyIndexHook.call(this, Router.$root);
}
removeBackPressEvent(this.$mp.page, this.$options); // 移除页面的onBackPress事件
},
onBackPress(...args) {
return pageIsHeadBack.call(Router.$root, this.$mp.page, this.$options, args);
},
},
APPLETS: {
onLaunch() {
uniAppHook.onLaunched = true; // 标志已经触发了 onLaunch 事件
appletsInit.call(this, Router.$root);
},
onLoad() {
if (uniAppHook.onLaunched && !uniAppHook.pageReady) { // 必须是第一个页面
uniAppHook.onLaunched = false;
appletsProxyIndexHook.call(this, Router.$root);
}
},
},
};
};
const initMixins = function (Vue, Router) {
Vue.mixin({
...getMixins(Router)[appPlatform(true)],
});
};
export default initMixins;
import { appPlatform } from './util';
import { methods, H5FnTypeToggle, Global } from './config';
import { transitionTo } from '../appRouter/hooks';
import { appletsTransitionTo, backCallHook } from '../appletsRouter/hooks';
import uniPushTo from '../appRouter/uniNav';
import appletsUniPushTo from '../appletsRouter/appletsNav';
import { err, warn } from './warn';
import H5PushTo from '../vueRouter/routerNav';
import * as compile from './compile';
/**
* 返回api 触发的公共函数
* @param {Object/String} rule 当前跳转规则
* @param {String} fnType 跳转页面的类型方法
*
* this 为当前 Router 实例
*/
const isBcakNav = function ({
backLayer,
delta,
H5PATCH,
}) {
compile.H5(() => {
H5PATCH.on('historyBack', {
backLayer,
delta,
});
});
compile.APP(() => {
Global.backLayerC = backLayer; // 告诉路由需要返回几层
uni.navigateBack({
delta: backLayer,
complete: () => {
Global.LockStatus = false; // 跳转完成解锁状态
},
});
});
compile.mp(() => {
backCallHook.call(this, backLayer, () => {
uni.navigateBack({
delta: backLayer,
});
});
});
};
/**
* 非 返回api 触发的公共函数
* @param {Object/String} rule 当前跳转规则
* @param {String} fnType 跳转页面的类型方法
*
* this 为当前 Router 实例
*/
const notBackNav = function (rule, fnType) {
if (rule == null) {
return err('跳转规则为空,不允许这样操作');
}
if (rule.constructor === String) { // 单独 path 的情况 允许?拼接参数
const ruleArray = rule.split('?');
if (ruleArray.length > 1) {
rule = {
path: ruleArray[0],
query: Global.$parseQuery.parse(ruleArray[1]),
};
}
}
switch (appPlatform(true)) {
case 'H5':
return H5PushTo.call(this, H5FnTypeToggle[fnType], rule, methods[fnType]);
case 'APP':
Global.LockStatus = true; // 设置为锁住状态
return transitionTo.call(this, rule, fnType, uniPushTo);
case 'APPLETS':
Global.LockStatus = true; // 设置为锁住状态
return appletsTransitionTo.call(this, rule, fnType, appletsUniPushTo);
default:
err('糟糕!!!还有其他的执行环境???没听说过啊。一脸懵逼???加QQ群问问:769241495');
break;
}
};
/**
* 处理正在跳转的公共api
* @param {Object/String} rule 当前跳转规则
* @param {String} fnType 跳转页面的类型方法
* @param {Boolean} isBack 是否通过 back() api 调用的 默认为false
* @param {Boolean} enforce 是否强制越过跳转加锁检查 默认false 目前只有back() api 传递了
*
* this 为当前 Router 实例
*/
const navjump = function (rule, fnType, isBack = false, enforce = false) {
if (Global.LockStatus && !enforce) { // 正在跳转的状态下 给出提示正在跳转
return warn('当前页面正在处于跳转状态,请稍后再进行跳转....');
}
if (isBack) { // 是返回api触发的
return isBcakNav.call(this, rule, fnType);
}
return notBackNav.call(this, rule, fnType);
};
export default navjump;
import { Global } from './config';
import { warn, err } from './warn';
const nodeURL = require('query-string');
class ParseQuery {
get queryName() {
return nodeURL;
}
/**
* 判断当前这个对象是否为深度对象
* @param {Object} obj
*/
isDepthObject(obj) {
const str = JSON.stringify(obj);
return str.match(/}/g).length > 1;
}
/**
* 从URL中提取查询字符串
* @param {String} url
*/
extract(url) {
return nodeURL.extract(url);
}
/**
* 把一个 key=value&key1=value 的字符串转成对象
* @param {string} strQuery key=value&key1=value 类型的字符串
*/
parse(strQuery) {
return nodeURL.parse(strQuery);
}
/**
* 把一个对象转成 key=value&key1=value 类型的字符串
* @param {Object} ObjQuery 符合js标注的对象
* @param {Boolean} intact 是否在转成的字符串前添加?号
*/
stringify(ObjQuery, intact = true) {
const strQuery = nodeURL.stringify(ObjQuery);
if (intact) {
return `?${strQuery}`;
}
return strQuery;
}
/**
* 把一个对象或者 key=value&key1=value 类型的数据加密成 query=encodeURIComponent(value)
* @param {Object|String} query 符合js标注的对象 或者 key=value&key1=value 字符串
* @param {Boolean} intact 是否在转成的字符串前添加?号
*/
encode(query, intact = true) {
let [strQuery, formatQuery] = ['', ''];
if (query == null) {
warn('加密参数没有传递,你知道?', true);
return '';
}
if (query.constructor === String) { // 字符串 尝试 转成 对象
strQuery = JSON.stringify(this.parse(query));
} else if (query.constructor === Object) { // 直接转成字符串对象即可
if (Object.keys(query).length === 0) {
warn('当前参数不满足加密规范!');
return '';
}
strQuery = JSON.stringify(query);
}
if (intact) {
formatQuery = '?';
}
formatQuery += `query=${encodeURIComponent(strQuery)}`;
return formatQuery;
}
/**
* 把一个已经加密好的字符串 query=encodeURIComponent(value) 解密成 对象
* @param {string} strQuery 已经加密好的字符串 query=encodeURIComponent(value)
*/
decode(strQuery) {
if (strQuery == null) {
warn('解密参数没有传递,你知道?', true);
return {};
}
let jsonQuery = strQuery;
if (strQuery.constructor === Object) { // 如果是对象 看能不能满足要求
jsonQuery = strQuery.query;
if (jsonQuery == null) {
warn('当前解密参数不满足编码规则');
return {};
}
jsonQuery = `query=${jsonQuery}`;
}
let decode = {};
// query 长这个样 query=encodeURIComponent(value)
const decodeStr = decodeURIComponent(jsonQuery);
const { query } = this.parse(decodeStr); // 转成 json 获取到正真的json字符串
if (query == null) {
warn('当前解密参数不满足编码规则');
} else {
try {
decode = JSON.parse(query);
} catch (error) {
warn('当前解密参数不满足编码规则');
}
}
return decode;
}
queryGet(query) {
const { encodeURI } = Global.Router.CONFIG; // 获取到路由配置
let [decode, historyObj, strQuery] = [query, query, ''];
switch (encodeURI) {
case true: { // 加密模式
decode = this.decode(query);
strQuery = this.encode(decode);
historyObj = {
query: encodeURIComponent(JSON.stringify(decode)),
};
break;
}
case false: { // 不加密模式
strQuery = this.stringify(query);
break;
}
default: {
err('未知参数模式,请检查 \'encodeURI\'', true);
}
}
return { strQuery, historyObj, decode };
}
/**
* 对需要传递的参数进行加密解密
* @param {Object|String} query get为false 必须为 Object 类型
* @param {String} get 是取值 还是通过api传值
*/
transfer(query = {}) {
const { encodeURI } = Global.Router.CONFIG; // 获取到路由配置
switch (encodeURI) {
case true: {
// 加密模式
return this.encode(query, false);
}
case false: {
// 不加密模式
return this.stringify(query);
}
default: {
err('未知参数模式,请检查 \'encodeURI\' ', true);
}
}
}
}
export default ParseQuery;
import { route, baseConfig, Global } from './config';
import { builtIn } from '../vueRouter/base';
import { err, log, warn } from './warn';
/**
* 当前是不是H5运行环境
*/
export const isH5 = function () {
return typeof window !== 'undefined' && typeof document !== 'undefined';
};
/**
* 判断当前变量是否为Object
* @param {Object} strObj
*/
export const isObject = function (strObj) {
return strObj.toString() === '[object Object]' && strObj.constructor === Object;
};
/**
* 获取当前运行平台
* @param {Boolean} applets 默认false true时所有小程序平台统一返回 APPLETS
*/
export const appPlatform = function (applets = false) {
let platform = '';
// #ifdef APP-PLUS-NVUE
platform = 'APPNVUE';
// #endif
// #ifdef APP-PLUS
platform = 'APP';
// #endif
// #ifdef H5
platform = 'H5';
// #endif
// #ifdef MP-ALIPAY
platform = 'ALIPAY';
// #endif
// #ifdef MP-BAIDU
platform = 'BAIDU';
// #endif
// #ifdef MP-QQ
platform = 'QQ';
// #endif
// #ifdef MP-WEIXIN
platform = 'WEIXIN';
// #endif
// #ifdef MP-TOUTIAO
platform = 'TOUTIAO';
// #endif
if (applets) {
// #ifdef MP
platform = 'APPLETS';
// #endif
}
return platform;
};
/**
* 定义一个空方法 如果最后一个参数为true则打印所有参数
* @param {...any} args
*/
export const noop = function (...args) {
if (args[args.length - 1] === true) {
log(args);
}
};
/**
* 格式化基础配置信息 通过new Router传递过来的参数
*/
export const formatConfig = function (userConfig) {
if (!userConfig.routes || userConfig.routes.constructor !== Array) {
return err(`路由参数 'routes' 必须传递 \r\n\r\n${JSON.stringify(userConfig)}`);
}
if (userConfig.h5 != null && userConfig.h5.constructor !== Object) {
return err(`h5参数传递错误,应该是一个 'Object' 类型 示例:\r\n\r\n${JSON.stringify(baseConfig.h5)}`);
}
const config = Object.create(null);
const baseConfigKeys = Object.keys(baseConfig);
for (let i = 0; i < baseConfigKeys.length; i += 1) {
const key = baseConfigKeys[i];
if (userConfig[key] != null) {
if (userConfig[key].constructor === Object) {
config[key] = {
...baseConfig[key],
...userConfig[key],
};
} else if (key == 'routes') { // 需要加入已知的白名单
config[key] = [...baseConfig[key], ...userConfig[key], ...builtIn];
} else {
config[key] = userConfig[key];
}
} else {
config[key] = baseConfig[key];
}
}
return config;
};
export const filter = function (str) {
str += '';
str = str.replace(/%/g, '%25');
str = str.replace(/\+/g, '%2B');
str = str.replace(/ /g, '%20');
str = str.replace(/\//g, '%2F');
str = str.replace(/\?/g, '%3F');
str = str.replace(/&/g, '%26');
str = str.replace(/=/g, '%3D');
str = str.replace(/#/g, '%23');
return str;
};
/**
* 使用encodeURI:true的情况 需要进行编码后再传递,解码等等 可以传递深度对象并会在路径后面加入一个query=
*
* @param {String} routerName //路径名称
* @param {JSON} query //需要格式化参数
* @param {Boolean} Encode //是获取还是编码后传递
*/
export const parseQueryN = function (routerName, query, Encode) {
if (Encode) {
return {
url: routerName,
query: JSON.parse(decodeURIComponent(query.replace(/^query=/, ''))),
};
}
return {
url: routerName,
query: `query=${encodeURIComponent(JSON.stringify(query))}`,
};
};
/**
* 使用encodeURI:false的情况 直接格式化为普通的queryURl参数形式传递即可 扁平深度对象
*
* @param {String} routerName //路径名称
* @param {JSON} query //需要格式化参数
* @param {Boolean} Encode //是获取还是编码后传递
*/
export const parseQueryD = function (routerName, query, Encode) {
if (Encode) {
const obj = {};
const reg = /([^=&\s]+)[=\s]*([^&\s]*)/g;
while (reg.exec(query)) {
obj[RegExp.$1] = RegExp.$2;
}
return {
url: routerName,
query: obj,
};
}
const encodeArr = [];
const queryKeys = Object.keys(query);
for (let i = 0; i < queryKeys.length; i += 1) {
const attr = queryKeys[i];
let encodeStr = '';
if (query[attr].constructor == Object) {
encodeStr = parseQueryD(routerName, query[attr], Encode).query;
encodeArr.push(encodeStr);
} else {
encodeStr = filter(query[attr]);
encodeArr.push(`${attr}=${encodeStr}`);
}
}
return {
url: routerName,
query: encodeArr.join('&'),
};
};
/**
* @param {String} routerName //路径名称
* @param {JSON} query //需要格式化参数
* @param {Boolean} Encode //是获取还是编码后传递
*/
export const parseQuery = function (routerName, query, Encode = false) {
if (Global.Router.CONFIG.encodeURI) {
return parseQueryN(routerName, query, Encode);
}
return parseQueryD(routerName, query, Encode);
};
export const exactRule = function (cloneRule, routes, ruleKey, getRule = false) {
const params = {};
let i = 0;
// eslint-disable-next-line
while (true) {
const item = routes[i];
if (item == null) {
if (!getRule) {
err(`路由表中未查找到 '${ruleKey}' 为 '${cloneRule[ruleKey]}'`);
}
return {
path: '',
name: '',
};
}
if (item[ruleKey] != null && item[ruleKey] === cloneRule[ruleKey]) {
if (!getRule) {
params.url = item.path;
params.rule = item;
if (isH5()) { // 如果是h5 则使用优先使用自定义路径名称
params.url = item.aliasPath || item.path;
}
return params;
}
return item;
}
i += 1;
}
};
export const resolveRule = function (router, rule, query = {}, ruleKey = 'path') {
const ruleInfo = route(
exactRule({
...rule,
},
router.CONFIG.routes,
ruleKey,
router),
);
return {
...ruleInfo,
query,
};
};
/**
* 把一些不必要的参数进行格式化掉,完成url的美观
* @param {String} URLQuery URL中传递的参数
*/
export const formatURLQuery = function (URLQuery) {
switch (URLQuery.trim()) {
case 'query=%7B%7D':
case '%7B%7D':
case '?query=%7B%7D':
case '?':
case '?[object Object]':
case '?query={}':
URLQuery = '';
break;
default:
warn('url已经很完美啦,不需要格式化!');
break;
}
return URLQuery;
};
/**
* 拷贝对象
* @param {Object} object
*/
export const copyObject = function (object) {
return JSON.parse(JSON.stringify(object));
};
import { Global } from './config';
const isLog = function (type, errText, enforce) {
if (!enforce) {
const dev = Global.Router.CONFIG.debugger;
const isObject = dev.toString() === '[object Object]';
if (dev === false) {
return false;
} if (dev === false) {
return false;
} if (isObject) {
if (dev[type] === false) {
return false;
}
}
}
/* eslint no-console:"off" */
console[type](errText);
};
export const err = function (errInfo, enforce = false) {
isLog('error', errInfo, enforce);
};
export const warn = function (errInfo, enforce = false) {
isLog('warn', errInfo, enforce);
};
export const log = function (errInfo, enforce = false) {
isLog('log', errInfo, enforce);
};
export const warnLock = function (errInfo) {
console.warn(errInfo);
};
import { isH5, formatConfig, appPlatform } from './helpers/util';
import navjump from './helpers/navJump';
import { H5GetPageRoute } from './vueRouter/util';
import { APPGetPageRoute } from './appRouter/util';
import { AppletsPageRoute } from './appletsRouter/util';
import { lifeCycle, Global } from './helpers/config';
import { warn, err } from './helpers/warn';
import { registerRouterHooks, registerHook } from './lifeCycle/hooks';
import { vueMount } from './vueRouter/base';
import appletsMount from './patch/applets-patch';
import appMount from './patch/app-patch';
import initMixins from './helpers/mixins';
import ParseQuery from './helpers/urlQuery';
// #ifdef H5
import H5 from './patch/h5-patch';
// #endif
let H5PATCH = null;
// #ifdef H5
H5PATCH = new H5(isH5());
// #endif
const parseQuery = new ParseQuery();
Global.H5RouterReady = new Promise((resolve) => Global.RouterReadyPromise = resolve);
class Router {
constructor(arg) {
Router.$root = this;
Global.Router = this; // 全局缓存一个对象,不必使用时都传递
Global.$parseQuery = parseQuery;
this.CONFIG = formatConfig(arg);
this.lifeCycle = lifeCycle;
registerRouterHooks.call(this); // 注册全局Router生命钩子
if (appPlatform() === 'H5') {
H5PATCH.setLoadingStatus(this.CONFIG.h5);
}
}
get $Route() {
return this.getPageRoute();
}
/**
* 获取 url 参数帮助类实例
*/
get $parseQuery() {
return Global.$parseQuery;
}
/**
* 获取当前是否处于正在跳转的状态
* H5 状态下始终为false
*/
get $lockStatus() {
return Global.LockStatus;
}
/**
* 动态设置拦截状态
*/
set $lockStatus(status) {
warn('你确定要这么做?你知道后果?', true);
Global.LockStatus = status;
}
/** 动态的导航到一个新 URL 保留浏览历史
* navigateTo
* @param {Object} rule
*/
push(rule) {
navjump.call(this, rule, 'push');
}
/** 动态的导航到一个新 URL 关闭当前页面,跳转到的某个页面。
* redirectTo
* @param {Object} rule
*/
replace(rule) {
navjump.call(this, rule, 'replace');
}
/** 动态的导航到一个新 URL 关闭所有页面,打开到应用内的某个页面
* reLaunch
* @param {Object} rule
*/
replaceAll(rule) {
navjump.call(this, rule, 'replaceAll');
}
/** 动态的导航到一个新 url 关闭所有页面,打开到应用内的某个tab
* @param {Object} rule
*/
pushTab(rule) {
navjump.call(this, rule, 'pushTab');
}
/**
* 返回到指定层级页面上
* @param {Number} backLayer 需要返回的页面层级 默认 1
* @param {Object} delta 暂时无用
* @param {enforce} 是否强制越过跳转加锁检查 默认 false
*/
back(backLayer = 1, delta, enforce = false) {
if (backLayer.constructor != Number) {
return err(
`返回层级参数必须是一个Number类型且必须大于1:${backLayer}`,
);
}
navjump.call(this, {
backLayer, delta, H5PATCH,
}, 'back', true, enforce);
}
/**
* 获取当前页面下的 Route 信息
*
* @param {Object} Vim 当前开发者可以传递一个 vue 组件对象 来获取当前下的 Route 信息
*/
getPageRoute(Vim) {
const pages = getCurrentPages();
switch (appPlatform(true)) {
case 'H5':
return H5GetPageRoute.call(this, pages, Vim);
case 'APP':
return APPGetPageRoute(pages, Vim);
case 'APPLETS':
return AppletsPageRoute(pages, Vim);
default:
break;
}
}
beforeEach(fn) {
return registerHook(this.lifeCycle.beforeHooks, fn);
}
afterEach(fn) {
return registerHook(this.lifeCycle.afterHooks, fn);
}
}
Router.install = function (Vue) {
initMixins(Vue, Router);
Object.defineProperty(Vue.prototype, '$Router', {
get() {
return Router.$root;
},
});
Object.defineProperty(Vue.prototype, '$Route', {
get() {
return Router.$root.getPageRoute(this);
},
});
};
export default Router;
/**
*
* @param {VueComponent } Vim vue实例对象
* @param {dom} el dom节点选择器
*/
export const RouterMount = function (Vim, el) {
switch (appPlatform(true)) {
case 'APP':
appMount(Vim, el);
break;
case 'APPLETS':
appletsMount(Vim, el);
break;
case 'H5':
vueMount.push({ Vim, el });
break;
default:
warn('糟糕!!!还有其他的执行环境???没听说过啊。一脸懵逼???加QQ群问问:769241495');
break;
}
};
import { appPlatform, isH5 } from '../helpers/util';
// #ifdef H5
import H5 from '../patch/h5-patch';
const H5PATCH = new H5(isH5());
// #endif
export const registerHook = function (list, fn) {
list.push(fn);
return () => {
const i = list.indexOf(fn);
if (i > -1) list.splice(i, 1);
};
};
/**
* 注册全局Router生命钩子
*/
export const registerRouterHooks = function () {
registerHook(this.lifeCycle.routerbeforeHooks, function () {
return new Promise(async (resolve) => {
this.CONFIG.routerBeforeEach(); // 触发暴露给开发者的生命钩子
if (appPlatform(true) === 'H5') {
H5PATCH.on('toogle', 'startLodding');
}
return resolve(true);
});
});
registerHook(this.lifeCycle.routerAfterHooks, function (res = {}) {
if (res.H5Intercept !== true) {
this.CONFIG.routerAfterEach(); // 触发暴露给开发者的生命钩子
}
if (appPlatform(true) === 'H5') {
H5PATCH.on('toogle', 'stopLodding');
}
return true;
});
};
'use strict';
const strictUriEncode = require('strict-uri-encode');
const decodeComponent = require('decode-uri-component');
const splitOnFirst = require('split-on-first');
const filterObject = require('filter-obj');
const isNullOrUndefined = value => value === null || value === undefined;
function encoderForArrayFormat(options) {
switch (options.arrayFormat) {
case 'index':
return key => (result, value) => {
const index = result.length;
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}
if (value === null) {
return [...result, [encode(key, options), '[', index, ']'].join('')];
}
return [
...result,
[encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join('')
];
};
case 'bracket':
return key => (result, value) => {
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}
if (value === null) {
return [...result, [encode(key, options), '[]'].join('')];
}
return [...result, [encode(key, options), '[]=', encode(value, options)].join('')];
};
case 'comma':
case 'separator':
return key => (result, value) => {
if (value === null || value === undefined || value.length === 0) {
return result;
}
if (result.length === 0) {
return [[encode(key, options), '=', encode(value, options)].join('')];
}
return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
};
default:
return key => (result, value) => {
if (
value === undefined ||
(options.skipNull && value === null) ||
(options.skipEmptyString && value === '')
) {
return result;
}
if (value === null) {
return [...result, encode(key, options)];
}
return [...result, [encode(key, options), '=', encode(value, options)].join('')];
};
}
}
function parserForArrayFormat(options) {
let result;
switch (options.arrayFormat) {
case 'index':
return (key, value, accumulator) => {
result = /\[(\d*)\]$/.exec(key);
key = key.replace(/\[\d*\]$/, '');
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === undefined) {
accumulator[key] = {};
}
accumulator[key][result[1]] = value;
};
case 'bracket':
return (key, value, accumulator) => {
result = /(\[\])$/.exec(key);
key = key.replace(/\[\]$/, '');
if (!result) {
accumulator[key] = value;
return;
}
if (accumulator[key] === undefined) {
accumulator[key] = [value];
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
case 'comma':
case 'separator':
return (key, value, accumulator) => {
const isArray = typeof value === 'string' && value.includes(options.arrayFormatSeparator);
const isEncodedArray = (typeof value === 'string' && !isArray && decode(value, options).includes(options.arrayFormatSeparator));
value = isEncodedArray ? decode(value, options) : value;
const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options);
accumulator[key] = newValue;
};
default:
return (key, value, accumulator) => {
if (accumulator[key] === undefined) {
accumulator[key] = value;
return;
}
accumulator[key] = [].concat(accumulator[key], value);
};
}
}
function validateArrayFormatSeparator(value) {
if (typeof value !== 'string' || value.length !== 1) {
throw new TypeError('arrayFormatSeparator must be single character string');
}
}
function encode(value, options) {
if (options.encode) {
return options.strict ? strictUriEncode(value) : encodeURIComponent(value);
}
return value;
}
function decode(value, options) {
if (options.decode) {
return decodeComponent(value);
}
return value;
}
function keysSorter(input) {
if (Array.isArray(input)) {
return input.sort();
}
if (typeof input === 'object') {
return keysSorter(Object.keys(input))
.sort((a, b) => Number(a) - Number(b))
.map(key => input[key]);
}
return input;
}
function removeHash(input) {
const hashStart = input.indexOf('#');
if (hashStart !== -1) {
input = input.slice(0, hashStart);
}
return input;
}
function getHash(url) {
let hash = '';
const hashStart = url.indexOf('#');
if (hashStart !== -1) {
hash = url.slice(hashStart);
}
return hash;
}
function extract(input) {
input = removeHash(input);
const queryStart = input.indexOf('?');
if (queryStart === -1) {
return '';
}
return input.slice(queryStart + 1);
}
function parseValue(value, options) {
if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) {
value = Number(value);
} else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
value = value.toLowerCase() === 'true';
}
return value;
}
function parse(query, options) {
options = Object.assign({
decode: true,
sort: true,
arrayFormat: 'none',
arrayFormatSeparator: ',',
parseNumbers: false,
parseBooleans: false
}, options);
validateArrayFormatSeparator(options.arrayFormatSeparator);
const formatter = parserForArrayFormat(options);
// Create an object with no prototype
const ret = Object.create(null);
if (typeof query !== 'string') {
return ret;
}
query = query.trim().replace(/^[?#&]/, '');
if (!query) {
return ret;
}
for (const param of query.split('&')) {
if (param === '') {
continue;
}
let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '=');
// Missing `=` should be `null`:
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
value = value === undefined ? null : ['comma', 'separator'].includes(options.arrayFormat) ? value : decode(value, options);
formatter(decode(key, options), value, ret);
}
for (const key of Object.keys(ret)) {
const value = ret[key];
if (typeof value === 'object' && value !== null) {
for (const k of Object.keys(value)) {
value[k] = parseValue(value[k], options);
}
} else {
ret[key] = parseValue(value, options);
}
}
if (options.sort === false) {
return ret;
}
return (options.sort === true ? Object.keys(ret).sort() : Object.keys(ret).sort(options.sort)).reduce((result, key) => {
const value = ret[key];
if (Boolean(value) && typeof value === 'object' && !Array.isArray(value)) {
// Sort object keys, not values
result[key] = keysSorter(value);
} else {
result[key] = value;
}
return result;
}, Object.create(null));
}
exports.extract = extract;
exports.parse = parse;
exports.stringify = (object, options) => {
if (!object) {
return '';
}
options = Object.assign({
encode: true,
strict: true,
arrayFormat: 'none',
arrayFormatSeparator: ','
}, options);
validateArrayFormatSeparator(options.arrayFormatSeparator);
const shouldFilter = key => (
(options.skipNull && isNullOrUndefined(object[key])) ||
(options.skipEmptyString && object[key] === '')
);
const formatter = encoderForArrayFormat(options);
const objectCopy = {};
for (const key of Object.keys(object)) {
if (!shouldFilter(key)) {
objectCopy[key] = object[key];
}
}
const keys = Object.keys(objectCopy);
if (options.sort !== false) {
keys.sort(options.sort);
}
return keys.map(key => {
const value = object[key];
if (value === undefined) {
return '';
}
if (value === null) {
return encode(key, options);
}
if (Array.isArray(value)) {
return value
.reduce(formatter(key), [])
.join('&');
}
return encode(key, options) + '=' + encode(value, options);
}).filter(x => x.length > 0).join('&');
};
exports.parseUrl = (url, options) => {
options = Object.assign({
decode: true
}, options);
const [url_, hash] = splitOnFirst(url, '#');
return Object.assign(
{
url: url_.split('?')[0] || '',
query: parse(extract(url), options)
},
options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {}
);
};
exports.stringifyUrl = (object, options) => {
options = Object.assign({
encode: true,
strict: true
}, options);
const url = removeHash(object.url).split('?')[0] || '';
const queryFromUrl = exports.extract(object.url);
const parsedQueryFromUrl = exports.parse(queryFromUrl, {sort: false});
const query = Object.assign(parsedQueryFromUrl, object.query);
let queryString = exports.stringify(query, options);
if (queryString) {
queryString = `?${queryString}`;
}
let hash = getHash(object.url);
if (object.fragmentIdentifier) {
hash = `#${encode(object.fragmentIdentifier, options)}`;
}
return `${url}${queryString}${hash}`;
};
exports.pick = (input, filter, options) => {
options = Object.assign({
parseFragmentIdentifier: true
}, options);
const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
return exports.stringifyUrl({
url,
query: filterObject(query, filter),
fragmentIdentifier
}, options);
};
exports.exclude = (input, filter, options) => {
const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
return exports.pick(input, exclusionFilter, options);
};
MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (http://sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{
"_from": "query-string@6.14.1",
"_id": "query-string@6.14.1",
"_inBundle": false,
"_integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
"_location": "/uni-simple-router/query-string",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "query-string@6.14.1",
"name": "query-string",
"escapedName": "query-string",
"rawSpec": "6.14.1",
"saveSpec": null,
"fetchSpec": "6.14.1"
},
"_requiredBy": [
"/uni-simple-router"
],
"_resolved": "https://repo.huaweicloud.com/repository/npm/query-string/-/query-string-6.14.1.tgz",
"_shasum": "7ac2dca46da7f309449ba0f86b1fd28255b0c86a",
"_spec": "query-string@6.14.1",
"_where": "E:\\H5_2.0\\node_modules\\uni-simple-router",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "https://sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/query-string/issues"
},
"bundleDependencies": false,
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"deprecated": false,
"description": "Parse and stringify URL query strings",
"devDependencies": {
"ava": "^1.4.1",
"benchmark": "^2.1.4",
"deep-equal": "^1.0.1",
"fast-check": "^1.5.0",
"tsd": "^0.7.3",
"xo": "^0.24.0"
},
"engines": {
"node": ">=6"
},
"files": [
"index.js",
"index.d.ts"
],
"funding": "https://github.com/sponsors/sindresorhus",
"homepage": "https://github.com/sindresorhus/query-string#readme",
"keywords": [
"browser",
"querystring",
"query",
"string",
"qs",
"param",
"parameter",
"url",
"parse",
"stringify",
"encode",
"decode",
"searchparams",
"filter"
],
"license": "MIT",
"name": "query-string",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/query-string.git"
},
"scripts": {
"benchmark": "node benchmark.js",
"test": "xo && ava && tsd"
},
"version": "6.14.1"
}
{
"name": "uni-simple-router",
"version": "1.5.5",
"description": "> 一个更为简洁的[Vue-router](https://router.vuejs.org/zh/),专为 [uni-app](https://uniapp.dcloud.io/) 量身打造",
"main": "index.js",
"directories": {
"example": "examples"
},
"scripts": {
"postinstall": "node -e \"console.log('\\x1B[32m \\x1B[1m','\\n 欢迎下载 uni-simple-router ↓↓↓↓ \\n\\n 注意事项:\\n 1: 编译为app端如果开启V3,请关闭fast启动模式 \\n 2: 多看文档,特别是‘快速上手’栏目。文档地址:http://hhyang.cn/ \\n 3: 不要再问路由表是否可以不配置两遍。问就是文档有!\\n 4: 有啥问题解决不了的加群:769241495 \\n 5: 任何时候你都可以在github上提出你的想法及问题,相信我很快会得到回应 \\n\\n showTime:\\n 1:开源不易,需要鼓励。去给 uni-simple-router 项目 点个 star 吧 \\n')\""
},
"dependencies": {
"query-string": "^6.12.1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/SilurianYang/uni-simple-router.git"
},
"keywords": [
"router",
"uni-app-router",
"interceptor",
"uni-app",
"uniapp"
],
"author": "hhyang",
"license": "MIT",
"bugs": {
"url": "https://github.com/SilurianYang/uni-simple-router/issues"
},
"homepage": "https://github.com/SilurianYang/uni-simple-router#readme",
"types": "./types/index.d.ts",
"__npminstall_done": true,
"_from": "uni-simple-router@1.5.5",
"_resolved": "https://registry.npmmirror.com/uni-simple-router/-/uni-simple-router-1.5.5.tgz"
}
\ No newline at end of file
/**
* 截止 1.3.5 版本 不做任何操作
* @param {element} el dom节点
*/
const appMount = function (Vim) {
Vim.$mount();
};
export default appMount;
/**
* 截止 1.3.5 版本 不做任何操作
* @param {element} el dom节点
*/
const appletsMount = function (Vim) {
Vim.$mount();
};
export default appletsMount;
// #ifdef H5
import { DOM } from '../component/h5-dom';
import init from '../vueRouter/init';
// #endif
import { warn } from '../helpers/warn';
class Patch {
constructor(H5) {
this.H5 = H5;
this.isLoading = true;
this.loadingCount = 0; // 在APP.vue中进行跳转时,DOMContentLoaded过慢。使用索引来判断
}
on(fun, args, callback) {
if (this.H5) {
return this[fun](args);
}
if (callback) {
callback();
}
}
/**
*把vueRouter的生命周期代理过来
* @param {Object} Router
* @param {Object} vueRouter
* @param {VueComponent} vueVim
*/
// eslint-disable-next-line
registerHook(Router, vueRouter, vueVim) {
init(Router, vueRouter, vueVim);
}
/**
* H5 专属 history.back API
* @param {Number} backLayer 需要返回的层级必须是正整数
* 2020年1月14日14:39:38 修复 https://github.com/SilurianYang/uni-simple-router/issues/73
*/
// eslint-disable-next-line
historyBack({ backLayer, delta = { from: 'navigateBack' } } = {}) {
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const { onBackPress } = page.$options;
if (onBackPress != null && onBackPress.constructor === Array) {
const callFun = onBackPress[onBackPress.length - 1];
const isNext = callFun.call(page, delta);
if (isNext) {
return true;
}
}
// eslint-disable-next-line
history.go(-backLayer);
}
/**
* 把加载动画添加到dom下面,为什么一定要先添加,后移除。保证动画的连续性
*/
appendHTML({
style,
html,
script,
}) {
window.addEventListener('DOMContentLoaded', () => {
const body = document.querySelector('body');
body.appendChild(style);
body.appendChild(html);
body.appendChild(script);
this.toogle('startLodding', true);
});
}
/**
* 页面是否加载完毕触发对应事件
*/
toogle(toogle, DOMContentLoaded = false) {
if (DOMContentLoaded && this.loadingCount !== 0) {
this.loadingCount += 1;
return false;
}
try {
this.loadingCount += 1;
if (this.isLoading) {
window[toogle]();
}
} catch (error) {
warn('你使用了 addRoutes API 提前进行了生命周期 并触发了startLodding');
}
}
async setLoadingStatus({
loading,
replaceStyle,
resetStyle,
}) {
this.isLoading = loading;
if (loading) { // 确认需要加载样式 开始插入节点
const userStyle = resetStyle();
const userStyleKeys = Object.keys(userStyle);
for (let i = 0; i < userStyleKeys.length; i += 1) {
const key = userStyleKeys[i];
let html = userStyle[key];
if (key === 'style' && !replaceStyle) { // 开发者设置为追加style
html = DOM[key].innerHTML + html;
}
DOM[key].innerHTML = html;
}
this.appendHTML(DOM);
}
}
}
export default Patch;
import {Component,PluginFunction,Location,Route,Animation,H5,APP,RouteConfig,RouterOptions} from './options'
/**
* 路由挂载点
* @param {VueComponent } Vim vue实例对象
* @param {dom} el dom节点选择器
*/
declare const RouterMount: (Vim: any, el: string | Element) => void;
declare class Router {
constructor(options:RouterOptions)
/**
* 当前的 Route
*/
get $Route():Route;
/**
* 获取 url 参数帮助类实例
*/
get $parseQuery():object;
/**
* 获取当前是否处于正在跳转的状态
* H5 状态下始终为false
*/
get $lockStatus():boolean;
/**
* 动态设置拦截状态
*/
set $lockStatus(status:boolean);
/**动态的导航到一个新 URL 保留浏览历史
* navigateTo
* @param {Object} rule
*/
push(rule: Location | string): void;
/**动态的导航到一个新 URL 关闭当前页面,跳转到的某个页面。
* redirectTo
* @param {Object} rule
*/
replace(rule:Location | string):void;
/**动态的导航到一个新 URL 关闭所有页面,打开到应用内的某个页面
* reLaunch
* @param {Object} rule
*/
replaceAll(rule:Location | string):void;
/**动态的导航到一个新 url 关闭所有页面,打开到应用内的某个tab
* @param {Object} rule
*/
pushTab(rule:Location | string) :void;
/**
* 返回到指定层级页面上
*/
back(backLayer?:number,delta?:Object):void
/**
* 获取当前页面下的 Route 信息
* @param {Object} Vim 当前开发者可以传递一个 vue 组件对象 来获取当前下的 Route 信息
*/
getPageRoute(Vim?:Component) : Route
/**
* 注册的全局前置生命周期
* @param hooks 注册的全局前置生命周期函数
*/
beforeEach(hooks:Function) : Function
/**
* 注册的全局后置生命周期
* @param hooks 注册的全局后置生命周期函数
*/
afterEach(hooks:Function) : Function
static install:PluginFunction<never>
}
declare module "vue/types/vue" {
interface Vue {
$Router: Router;
$Route: Route;
}
}
export default Router;
export {
RouterMount,
Location,
Route,
Animation,
H5,
APP,
RouteConfig,
RouterOptions
}
\ No newline at end of file
import Vue, { ComponentOptions,PluginFunction, AsyncComponent } from "vue";
type Component = ComponentOptions<Vue> | typeof Vue | AsyncComponent;
type Dictionary<T> = { [key: string]: T };
type Position = { x: number; y: number };
type PositionResult = Position | { selector: string; offset?: Position } | void;
type NAVTYPE= 'push' | 'replace' | 'replaceAll' | 'pushTab'
interface Location {
name?: string;
path?: string;
query?: Dictionary<string | (string | null)[] | null | undefined>;
params?: Dictionary<string>;
NAVTYPE?: NAVTYPE;
}
interface Route {
path: string;
name?: string;
params?: any;
query?: any;
beforeEnter?:(to:Route, from:Route, next:Function) => void;
meta?: any; //其他格外参数
}
interface Animation{
animationType?:string;
animationDuration?:number;
}
interface H5{
rewriteFun?:boolean; //是否对uni-app reLaunch/navigateBack 两个方法重写 处理uni刷新直接返回到首页和触发路由守卫
paramsToQuery?: boolean; //h5端上通过params传参时规则是vue-router 刷新会丢失 开启此开关将变成?连接的方式
loading?: boolean; //是否显示加载动画
hinderTab?: boolean; //是否拦截uni-app自带底部菜单 TODO
vueRouterDev?: boolean; //完全使用采用vue-router的开发模式
useUniConfig?: boolean; //是否采用在pages.json下的所有页面配置信息,false时需开发者自行设置页面
keepUniIntercept?: boolean; //保留uni-app使用vue-router的拦截器
vueNext?: boolean; //在next管道函数中是否获取vueRouter next的原本参数
replaceStyle?: boolean; //是否对resetStyle函数中返回的style节点进行全部替换,否则为追加
resetStyle?: () => Object; //自定义加载样式函数 可返回一个包涵 html、style、script 的对象来重置Router内置的加载动画
mode?: string;
base?: string;
linkActiveClass?: string;
linkExactActiveClass?: string;
scrollBehavior?: (to:Route, from:Route, savedPostion:Position|void) => PositionResult | Promise<PositionResult>,
fallback?: boolean,
}
interface APP{
holdTabbar?:boolean; //是否开启底部菜单拦截
rewriteFun?:boolean; //是否对uni-app 下的chooseLocation/openLocation 两个方法重写 目的是隐藏和显示拦截tabbar
loddingPageStyle?:()=>Object; //当前等待页面的样式 必须返回一个json
loddingPageHook?:()=>void; //刚刚打开页面处于等待状态,会触发此事件
holdTabbarStyle?:()=>Object;
animation?:Animation; //页面切换动画
switchPageOutTime?:number, //最高能忍耐的页面切换时间 达到此时间 不管切换有没有完成都会显示页面出来 这对启动页帮助很大
}
interface RouteConfig {
path: string; //pages.json中的path 必须加上 '/' 开头
component?: Component; //H5端可用
name?: string; // 命名路由
components?: { [name: string]: Component }; // 命名视图组件,H5端可用
redirect?: string | Location | Function; //H5端可用
props?: boolean | Object | Function; //H5端可用
aliasPath?:string; //h5端 设置一个别名路径来替换 uni-app的默认路径
alias?: string | Array<string>; //H5端可用
children?: Array<RouteConfig>; // 嵌套路由,H5端可用
beforeEnter?: (to: Route, from: Route, next: Function) => void; //路由元守卫
meta?: any; //其他格外参数
}
interface RouterOptions{
h5?:H5;
APP?:APP;
debugger?: boolean; //是否处于开发阶段 设置为true则打印日志
encodeURI?: boolean; //是否对url传递的参数进行编码
routerBeforeEach?: () => Object; //router 前置路由函数 每次触发跳转前先会触发此函数
routerAfterEach?: () => Object; //router 后置路由函数 每次触发跳转后会触发此函数
routes?: RouteConfig[];
}
export {
PluginFunction,
Component,
Location,
Route,
Animation,
H5,
APP,
RouteConfig,
RouterOptions
}
\ No newline at end of file
export const builtIn = [{
path: '/preview-image',
name: 'previewImage',
component: {
render: () => {},
},
}, {
path: '/choose-location',
name: 'chooseLocation',
component: {
render: () => {},
},
}, {
path: '/open-location',
name: 'openLocation',
component: {
render: () => {},
},
}]; // uni-app内置路由
export const vuelifeHooks = { // vueRouter的原始生命周期
beforeHooks: [],
afterHooks: [],
};
export const vueMount = [];// 使用内部对象保留实例化下的appVue,并使用Router进行挂载触发第一次路由钩子
/**
* 实现一个继承的 数组类 代理掉 vue-router 生命钩子的数据
*/
class MyArray extends Array {
constructor(Router, vueOldHooks, hookFun) {
super();
this.Router = Router;
this.vueOldHooks = vueOldHooks;
this.hookFun = hookFun;
}
push(v) {
this.vueOldHooks.splice(0, 1, v);// 把vue-router路由生命钩子保存起来
this[this.length] = (to, from, next) => {
this.hookFun(to, from, next, this.Router);
};
}
}
export default MyArray;
import { afterHooks, beforeHooks, registerRouter } from './concat';
import { fromatRoutes } from './util';
import { err, warn } from '../helpers/warn';
import { proxyEachHooks } from './proxy/proxy';
/**
* 重写掉H5端 uni-app原始存在的bug
*
* @param {Object} Router
*/
const rewriteUniFun = function (Router) {
if (Router.CONFIG.h5.rewriteFun === false) { // 不需要重写
return false;
}
uni.reLaunch = function ({
url,
}) {
if (url === '/') {
warn('H5端 uni.reLaunch(\'/\')时 默认被重写了! 你可以使用 this.$Router.replaceAll() 或者 uni.reLaunch(\'/\'?xxx)');
// eslint-disable-next-line
if (history.length > 1) { // 只有在有历史记录的时候才返回 不然直接返回首页
return Router.back();
}
return Router.replaceAll('/');
}
const path = url.match(/^[^?]+|(\/)/)[0];
try {
const query = {};
url.replace(/([^?&]+)=([^?&]+)/g, (s, v, k) => {
query[v] = decodeURIComponent(k);
return `${k}=${v}`;
});
Router.replaceAll({
path,
query,
});
} catch (e) {
err(`${url}解析失败了.... 试试 this.$Router.replaceAll() 吧`);
}
};
uni.navigateBack = function (delta) {
let backLayer = delta;
if (delta.constructor === Object) { // 这种可能就只是uni-app自带的返回按钮,还有种可能就是开发者另类传递的
backLayer = 1;
}
Router.back(backLayer, delta);
};
};
/**
* 拦截并注册vueRouter中的生命钩子,路由表解析
* @param {Object} Router
* @param {vueRouter} vueRouter
*/
const init = function (Router, vueRouter) {
const CONFIG = Router.CONFIG.h5;
vueRouter.afterHooks = proxyEachHooks(Router, 'afterHooks', afterHooks);
vueRouter.beforeHooks = proxyEachHooks(Router, 'beforeHooks', beforeHooks);
const objVueRoutes = fromatRoutes(vueRouter.options.routes, false, {}); // 返回一个格式化好的routes 键值对的形式
const objSelfRoutes = fromatRoutes(Router.CONFIG.routes, true, CONFIG);
Router.vueRoutes = objVueRoutes; // 挂载vue-routes到当前的路由下
Router.selfRoutes = {
...Router.selfRoutes || {},
...objSelfRoutes,
}; // 挂载self-routes到当前路由下
Router.$route = vueRouter; // 挂载vue-router到$route
rewriteUniFun(Router); // 重新掉uniapp上的一些有异常的方法
registerRouter(Router, vueRouter, CONFIG.vueRouterDev);
};
export default init;
import { beforeEnterHooks } from '../concat';
import { vuelifeHooks } from '../base';
import MyArray from '../extends/myArray';
/**
* 通过 Object.defineProperty 代理一个对象主要是拦截beforeEnter 生命钩子
* @param {Router} Router 路由实例对象
* @param {Object} BeProxy 需要代理的路由表
*/
export const proxyBeforeEnter = function (Router, BeProxy) {
const proxyDc = Object.create(null);// 创建没有继承的属性
const BeProxyKeys = Object.keys(BeProxy);
for (let i = 0; i < BeProxyKeys.length; i += 1) {
const key = BeProxyKeys[i];
Object.defineProperty(proxyDc, key, {
enumerable: true,
configurable: true,
get() {
const value = BeProxy[key];
if (key == 'beforeEnter' && value !== undefined) {
return (to, from, next) => {
beforeEnterHooks(to, from, next, value, Router);
};
}
return value;
},
set(v) {
BeProxy[key] = v;
},
});
}
return proxyDc;
};
/**
* 在uni-app没有注入生命周期时先直接代理相关生命周期数组
* @param {Object} Router
* @param {Object} key
* @param {Funtion} hookFun
*/
export const proxyEachHooks = function (Router, key, hookFun) {
const vueOldHooks = vuelifeHooks[key];
return new MyArray(Router, vueOldHooks, hookFun);
};
import { err } from '../helpers/warn';
import { formatUserRule, strPathToObjPath } from './util';
/**
* @param {Object} replace vue-router的跳转方式
* @param {Object} rule 需要跳转到的路由匹配规则
* @param {Object} type 对应的官方跳转模式
*
* this 为当前 Router 实例
*/
const H5PushTo = function (replace, rule, type) {
if (this.$route == null) {
return err('h5端路由为就绪,请检查调用代码');
}
rule = formatUserRule(rule, this.selfRoutes, this.CONFIG);
const objPath = strPathToObjPath(rule);
objPath.type = type;
this.$route[replace](objPath);
};
export default H5PushTo;
{
"id": "ay-operate",
"name": "操作组件:左滑删除,自定义任意内容",
// "id": "ay-operate",
// "name": "操作组件:左滑删除,自定义任意内容",
// "version": "1.0.1",
// "description": "操作组件:左滑删除,自定义任意内容",
// "keywords": [
// "左滑删除"
// ],
"name":"h5_2.0",
"version": "1.0.1",
"description": "操作组件:左滑删除,自定义任意内容",
"keywords": [
"左滑删除"
],
"dependencies": {
"lodash": "^4.17.21",
"query-string": "^6.14.1",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment