@esmx/router 3.0.0-rc.12 → 3.0.0-rc.122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +158 -0
  3. package/README.zh-CN.md +158 -0
  4. package/dist/error.d.ts +23 -0
  5. package/dist/error.mjs +64 -0
  6. package/dist/increment-id.d.ts +7 -0
  7. package/dist/increment-id.mjs +16 -0
  8. package/dist/index.d.ts +14 -3
  9. package/dist/index.mjs +13 -3
  10. package/dist/location.d.ts +22 -0
  11. package/dist/location.mjs +64 -0
  12. package/dist/matcher.d.ts +4 -0
  13. package/dist/matcher.mjs +49 -0
  14. package/dist/micro-app.d.ts +20 -0
  15. package/dist/micro-app.mjs +132 -0
  16. package/dist/navigation.d.ts +45 -0
  17. package/dist/navigation.mjs +153 -0
  18. package/dist/options.d.ts +4 -0
  19. package/dist/options.mjs +95 -0
  20. package/dist/route-task.d.ts +40 -0
  21. package/dist/route-task.mjs +77 -0
  22. package/dist/route-transition.d.ts +54 -0
  23. package/dist/route-transition.mjs +362 -0
  24. package/dist/route.d.ts +77 -0
  25. package/dist/route.mjs +223 -0
  26. package/dist/router-link.d.ts +10 -0
  27. package/dist/router-link.mjs +142 -0
  28. package/dist/router.d.ts +113 -102
  29. package/dist/router.mjs +321 -354
  30. package/dist/scroll.d.ts +33 -0
  31. package/dist/scroll.mjs +50 -0
  32. package/dist/types.d.ts +312 -0
  33. package/dist/types.mjs +18 -0
  34. package/dist/util.d.ts +32 -0
  35. package/dist/util.mjs +100 -0
  36. package/package.json +89 -62
  37. package/src/error.ts +84 -0
  38. package/src/increment-id.ts +12 -0
  39. package/src/index.ts +67 -3
  40. package/src/location.ts +124 -0
  41. package/src/matcher.ts +71 -0
  42. package/src/micro-app.ts +153 -0
  43. package/src/navigation.ts +202 -0
  44. package/src/options.ts +136 -0
  45. package/src/route-task.ts +102 -0
  46. package/src/route-transition.ts +480 -0
  47. package/src/route.ts +335 -0
  48. package/src/router-link.ts +241 -0
  49. package/src/router.ts +351 -467
  50. package/src/scroll.ts +106 -0
  51. package/src/types.ts +415 -0
  52. package/src/util.ts +184 -0
  53. package/dist/history/abstract.d.ts +0 -29
  54. package/dist/history/abstract.mjs +0 -107
  55. package/dist/history/base.d.ts +0 -79
  56. package/dist/history/base.mjs +0 -275
  57. package/dist/history/html.d.ts +0 -22
  58. package/dist/history/html.mjs +0 -181
  59. package/dist/history/index.d.ts +0 -7
  60. package/dist/history/index.mjs +0 -16
  61. package/dist/matcher/create-matcher.d.ts +0 -5
  62. package/dist/matcher/create-matcher.mjs +0 -218
  63. package/dist/matcher/create-matcher.spec.d.ts +0 -1
  64. package/dist/matcher/create-matcher.spec.mjs +0 -0
  65. package/dist/matcher/index.d.ts +0 -1
  66. package/dist/matcher/index.mjs +0 -1
  67. package/dist/task-pipe/index.d.ts +0 -1
  68. package/dist/task-pipe/index.mjs +0 -1
  69. package/dist/task-pipe/task.d.ts +0 -30
  70. package/dist/task-pipe/task.mjs +0 -66
  71. package/dist/utils/bom.d.ts +0 -5
  72. package/dist/utils/bom.mjs +0 -10
  73. package/dist/utils/encoding.d.ts +0 -48
  74. package/dist/utils/encoding.mjs +0 -44
  75. package/dist/utils/guards.d.ts +0 -9
  76. package/dist/utils/guards.mjs +0 -12
  77. package/dist/utils/index.d.ts +0 -7
  78. package/dist/utils/index.mjs +0 -27
  79. package/dist/utils/path.d.ts +0 -60
  80. package/dist/utils/path.mjs +0 -264
  81. package/dist/utils/path.spec.d.ts +0 -1
  82. package/dist/utils/path.spec.mjs +0 -30
  83. package/dist/utils/scroll.d.ts +0 -25
  84. package/dist/utils/scroll.mjs +0 -59
  85. package/dist/utils/utils.d.ts +0 -16
  86. package/dist/utils/utils.mjs +0 -11
  87. package/dist/utils/warn.d.ts +0 -2
  88. package/dist/utils/warn.mjs +0 -12
  89. package/src/history/abstract.ts +0 -149
  90. package/src/history/base.ts +0 -408
  91. package/src/history/html.ts +0 -231
  92. package/src/history/index.ts +0 -20
  93. package/src/matcher/create-matcher.spec.ts +0 -3
  94. package/src/matcher/create-matcher.ts +0 -293
  95. package/src/matcher/index.ts +0 -1
  96. package/src/task-pipe/index.ts +0 -1
  97. package/src/task-pipe/task.ts +0 -97
  98. package/src/utils/bom.ts +0 -14
  99. package/src/utils/encoding.ts +0 -153
  100. package/src/utils/guards.ts +0 -25
  101. package/src/utils/index.ts +0 -27
  102. package/src/utils/path.spec.ts +0 -44
  103. package/src/utils/path.ts +0 -397
  104. package/src/utils/scroll.ts +0 -120
  105. package/src/utils/utils.ts +0 -30
  106. package/src/utils/warn.ts +0 -13
@@ -1,264 +0,0 @@
1
- import URLParse from "url-parse";
2
- import {
3
- decode,
4
- decodeQuery,
5
- encodeHash,
6
- encodeQueryKey,
7
- encodeQueryValue
8
- } from "./encoding.mjs";
9
- import { isValidValue } from "./utils.mjs";
10
- import { warn } from "./warn.mjs";
11
- export const regexDomain = /^(?:https?:\/\/|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9](\/.*)?/i;
12
- export const regexScheme = /^(?:[a-z][a-z\d+.-]*:.+)/i;
13
- export const regexHttpScheme = /^(http(s)?:\/\/)/;
14
- function removeDuplicateSlashes(url) {
15
- if (url.includes("://")) {
16
- const [base, path] = url.split("://");
17
- const result2 = path.replace(/\/{2,}/g, "/");
18
- return `${base}://${result2}`;
19
- }
20
- const result = url.replace(/\/{2,}/g, "/");
21
- return result;
22
- }
23
- export function normalizePath(path, parentPath) {
24
- let normalizedPath = parentPath ? `${parentPath}/${path}` : `${path}`;
25
- if (!regexHttpScheme.test(normalizedPath) && !normalizedPath.startsWith("/")) {
26
- normalizedPath = `/${normalizedPath}`;
27
- }
28
- return (
29
- // 只有存在父级路由才进行路由拼接
30
- removeDuplicateSlashes(normalizedPath).replace(/\/$/, "") || // 移除结尾的斜杠 /
31
- "/"
32
- );
33
- }
34
- export function parsePath(path = "") {
35
- path = normalizePath(path);
36
- const { pathname, query, hash } = new URLParse(path || "/");
37
- const queryObj = {};
38
- const queryArray = {};
39
- if (query.length > 0) {
40
- query.slice(1).split("&").forEach((item) => {
41
- let [key = "", value = ""] = item.split("=");
42
- key = decode(key);
43
- value = decodeQuery(value);
44
- if (key) {
45
- queryObj[key] = value;
46
- queryArray[key] = (queryArray[key] || []).concat(value);
47
- }
48
- });
49
- }
50
- return {
51
- pathname,
52
- query: queryObj,
53
- queryArray,
54
- hash: decode(hash)
55
- };
56
- }
57
- export function stringifyPath({
58
- pathname = "",
59
- query = {},
60
- queryArray = {},
61
- hash = ""
62
- } = {
63
- pathname: "",
64
- query: {},
65
- queryArray: {},
66
- hash: ""
67
- }) {
68
- const queryString = Object.entries(
69
- Object.assign({}, query, queryArray)
70
- ).reduce((acc, [key, value]) => {
71
- let query2 = "";
72
- const encodedKey = encodeQueryKey(key);
73
- if (Array.isArray(value)) {
74
- query2 = value.reduce((all, item) => {
75
- if (!isValidValue(item)) return all;
76
- const encodedValue = encodeQueryValue(item);
77
- if (encodedValue) {
78
- all = all ? `${all}&${encodedKey}=${encodedValue}` : `${encodedKey}=${encodedValue}`;
79
- }
80
- return all;
81
- }, "");
82
- } else {
83
- const encodedValue = encodeQueryValue(value);
84
- if (isValidValue(value)) {
85
- query2 = `${encodedKey}=${encodedValue}`;
86
- }
87
- }
88
- if (query2) {
89
- acc = acc ? `${acc}&${query2}` : `?${query2}`;
90
- }
91
- return acc;
92
- }, "");
93
- const hashContent = hash.startsWith("#") ? hash.replace(/^#/, "") : hash;
94
- const hashString = hashContent ? `#${encodeHash(hashContent)}` : "";
95
- return `${pathname}${queryString}${hashString}`;
96
- }
97
- export function normalizeLocation(rawLocation, base = "") {
98
- let pathname = "";
99
- let query = {};
100
- let queryArray = {};
101
- let hash = "";
102
- let params;
103
- let state = {};
104
- if (typeof rawLocation === "object") {
105
- const parsedOption = parsePath(rawLocation.path);
106
- pathname = parsedOption.pathname;
107
- if (rawLocation.query || rawLocation.queryArray) {
108
- queryArray = rawLocation.queryArray || {};
109
- query = rawLocation.query || {};
110
- } else {
111
- queryArray = parsedOption.queryArray;
112
- query = parsedOption.query;
113
- }
114
- hash = rawLocation.hash || parsedOption.hash;
115
- params = rawLocation.params;
116
- state = rawLocation.state || {};
117
- } else {
118
- const parsedOption = parsePath(rawLocation);
119
- pathname = parsedOption.pathname;
120
- query = parsedOption.query;
121
- queryArray = parsedOption.queryArray;
122
- hash = parsedOption.hash;
123
- }
124
- const fullPath = stringifyPath({
125
- pathname,
126
- query,
127
- queryArray,
128
- hash
129
- });
130
- const baseString = normalizePath(
131
- typeof base === "function" ? base({
132
- fullPath,
133
- query,
134
- queryArray,
135
- hash
136
- }) : base
137
- );
138
- let path = pathname;
139
- if (regexDomain.test(baseString)) {
140
- const { pathname: pathname2 } = new URLParse(baseString);
141
- path = normalizePath(path.replace(new RegExp(`^(${pathname2})`), ""));
142
- }
143
- path = normalizePath(path.replace(new RegExp(`^(${baseString})`), ""));
144
- const { query: realQuery, queryArray: realQueryArray } = parsePath(fullPath);
145
- const res = {
146
- base: baseString,
147
- path,
148
- query: realQuery,
149
- queryArray: realQueryArray,
150
- hash,
151
- state
152
- };
153
- if (params) res.params = params;
154
- return res;
155
- }
156
- export function isPathWithProtocolOrDomain(location) {
157
- let url = "";
158
- let state = {};
159
- if (typeof location === "string") {
160
- url = location;
161
- } else {
162
- state = location.state || {};
163
- const {
164
- path,
165
- query: query2 = {},
166
- queryArray: queryArray2,
167
- hash: hash2 = "",
168
- ...nLocation
169
- } = normalizeLocation(location);
170
- url = stringifyPath({
171
- ...nLocation,
172
- query: query2,
173
- queryArray: queryArray2,
174
- pathname: path,
175
- hash: hash2
176
- });
177
- }
178
- if (regexScheme.test(url) && !regexHttpScheme.test(url)) {
179
- try {
180
- const {
181
- hash: hash2,
182
- host: host2,
183
- hostname: hostname2,
184
- href: href2,
185
- origin: origin2,
186
- pathname: pathname2,
187
- port: port2,
188
- protocol: protocol2,
189
- search: search2
190
- } = new URL(url);
191
- const route2 = {
192
- hash: hash2,
193
- host: host2,
194
- hostname: hostname2,
195
- href: href2,
196
- origin: origin2,
197
- pathname: pathname2,
198
- port: port2,
199
- protocol: protocol2,
200
- search: search2,
201
- params: {},
202
- query: {},
203
- queryArray: {},
204
- state,
205
- meta: {},
206
- path: pathname2,
207
- fullPath: url,
208
- base: "",
209
- matched: []
210
- };
211
- return {
212
- flag: true,
213
- route: route2
214
- };
215
- } catch (error) {
216
- warn(error);
217
- }
218
- }
219
- if (!/^https?:\/\//i.test(url)) {
220
- url = `http://${url}`;
221
- }
222
- const {
223
- hash,
224
- host,
225
- hostname,
226
- href,
227
- origin,
228
- pathname,
229
- port,
230
- protocol,
231
- query: search
232
- } = new URLParse(url);
233
- const { query = {}, queryArray } = normalizeLocation(url);
234
- const route = {
235
- href,
236
- origin,
237
- host,
238
- protocol,
239
- hostname,
240
- port,
241
- pathname,
242
- search,
243
- hash,
244
- query,
245
- queryArray,
246
- params: {},
247
- state,
248
- meta: {},
249
- path: pathname,
250
- fullPath: `${pathname}${search}${hash}`,
251
- base: "",
252
- matched: []
253
- };
254
- if (regexDomain.test(url)) {
255
- return {
256
- flag: true,
257
- route
258
- };
259
- }
260
- return {
261
- flag: false,
262
- route
263
- };
264
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,30 +0,0 @@
1
- import { describe, it } from "vitest";
2
- import {
3
- normalizeLocation,
4
- normalizePath
5
- } from "./path.mjs";
6
- describe("testing normalizeLocation", () => {
7
- it("testing normal domain", ({ expect }) => {
8
- expect(
9
- normalizeLocation(
10
- "http://localhost:5173/en/en/en/en/en",
11
- "http://localhost:5173/en/"
12
- ).path
13
- ).toBe("/en/en/en/en");
14
- });
15
- it("testing path", ({ expect }) => {
16
- expect(
17
- normalizeLocation("/test1/test2?t=https://www-six.betafollowme.com").path
18
- ).toBe("/test1/test2");
19
- });
20
- });
21
- describe("testing normalizePath", () => {
22
- it("testing normal domain", ({ expect }) => {
23
- expect(normalizePath("test2", "test1")).toBe("/test1/test2");
24
- });
25
- it("testing path", ({ expect }) => {
26
- expect(
27
- normalizeLocation("/test1/test2?t=https://www-six.betafollowme.com").path
28
- ).toBe("/test1/test2");
29
- });
30
- });
@@ -1,25 +0,0 @@
1
- import type { RouterRawLocation, ScrollPosition, _ScrollPositionNormalized } from '../types';
2
- /**
3
- * 获取当前滚动位置
4
- */
5
- export declare const computeScrollPosition: () => _ScrollPositionNormalized;
6
- /**
7
- * 滚动到指定位置
8
- */
9
- export declare function scrollToPosition(position: ScrollPosition): void;
10
- /**
11
- * 存储的滚动位置
12
- */
13
- export declare const scrollPositions: Map<string, _ScrollPositionNormalized>;
14
- /**
15
- * 保存滚动位置
16
- */
17
- export declare function saveScrollPosition(key: string, scrollPosition: _ScrollPositionNormalized): void;
18
- /**
19
- * 获取存储的滚动位置
20
- */
21
- export declare function getSavedScrollPosition(key: string): _ScrollPositionNormalized | null;
22
- /**
23
- * 获取跳转配置的保持滚动位置参数
24
- */
25
- export declare function getKeepScrollPosition(location: RouterRawLocation): boolean;
@@ -1,59 +0,0 @@
1
- import { warn } from "./warn.mjs";
2
- export const computeScrollPosition = () => ({
3
- left: window.scrollX,
4
- top: window.scrollY
5
- });
6
- function getElementPosition(el, offset) {
7
- const docRect = document.documentElement.getBoundingClientRect();
8
- const elRect = el.getBoundingClientRect();
9
- return {
10
- behavior: offset.behavior,
11
- left: elRect.left - docRect.left - (offset.left || 0),
12
- top: elRect.top - docRect.top - (offset.top || 0)
13
- };
14
- }
15
- export function scrollToPosition(position) {
16
- let scrollToOptions;
17
- if ("el" in position) {
18
- const positionEl = position.el;
19
- const isIdSelector = typeof positionEl === "string" && positionEl.startsWith("#");
20
- const el = typeof positionEl === "string" ? isIdSelector ? document.getElementById(positionEl.slice(1)) : document.querySelector(positionEl) : positionEl;
21
- if (!el) {
22
- return;
23
- }
24
- scrollToOptions = getElementPosition(el, position);
25
- } else {
26
- scrollToOptions = position;
27
- }
28
- if ("scrollBehavior" in document.documentElement.style) {
29
- window.scrollTo(scrollToOptions);
30
- } else {
31
- window.scrollTo(
32
- scrollToOptions.left != null ? scrollToOptions.left : window.scrollX,
33
- scrollToOptions.top != null ? scrollToOptions.top : window.scrollY
34
- );
35
- }
36
- }
37
- export const scrollPositions = /* @__PURE__ */ new Map();
38
- const POSITION_KEY = "__scroll_position_key";
39
- export function saveScrollPosition(key, scrollPosition) {
40
- scrollPositions.set(key, scrollPosition);
41
- const stateCopy = Object.assign({}, window.history.state);
42
- stateCopy[POSITION_KEY] = scrollPosition;
43
- try {
44
- const protocolAndPath = window.location.protocol + "//" + window.location.host;
45
- const absolutePath = window.location.href.replace(protocolAndPath, "");
46
- window.history.replaceState(stateCopy, "", absolutePath);
47
- } catch (error) {
48
- warn(`Failed to save scroll position.`, error);
49
- }
50
- }
51
- export function getSavedScrollPosition(key) {
52
- const scroll = scrollPositions.get(key) || history.state[POSITION_KEY];
53
- scrollPositions.delete(key);
54
- return scroll || null;
55
- }
56
- export function getKeepScrollPosition(location) {
57
- if (typeof location === "string") return false;
58
- return location.keepScrollPosition || false;
59
- }
@@ -1,16 +0,0 @@
1
- /**
2
- * 是否在浏览器环境
3
- */
4
- export declare const inBrowser: boolean;
5
- /**
6
- * Symbol 是否可用
7
- */
8
- export declare const isSymbolAble: boolean;
9
- /**
10
- * 判断是否是 es module 对象
11
- */
12
- export declare function isESModule(obj: any): boolean;
13
- /**
14
- * 判断是否是合法的值
15
- */
16
- export declare function isValidValue(value: any): boolean;
@@ -1,11 +0,0 @@
1
- export const inBrowser = typeof window !== "undefined";
2
- export const isSymbolAble = typeof Symbol === "function" && typeof Symbol.toStringTag === "symbol";
3
- export function isESModule(obj) {
4
- return Boolean(obj.__esModule) || isSymbolAble && obj[Symbol.toStringTag] === "Module";
5
- }
6
- export function isValidValue(value) {
7
- if (value === null) return false;
8
- if (value === void 0) return false;
9
- if (typeof value === "number" && isNaN(value)) return false;
10
- return true;
11
- }
@@ -1,2 +0,0 @@
1
- export declare function assert(condition: boolean, message: string): void;
2
- export declare function warn(...args: any[]): void;
@@ -1,12 +0,0 @@
1
- export function assert(condition, message) {
2
- if (!condition) {
3
- throw new Error(`[@esmx/router] ${message}`);
4
- }
5
- }
6
- export function warn(...args) {
7
- console.log(
8
- "%c ROUTER WARNING:",
9
- "color: rgb(214, 77, 77); font-weight: bold;",
10
- ...args
11
- );
12
- }
@@ -1,149 +0,0 @@
1
- import type {
2
- Route,
3
- RouteRecord,
4
- RouterInstance,
5
- RouterRawLocation
6
- } from '../types';
7
- import { isPathWithProtocolOrDomain, normalizeLocation } from '../utils';
8
- import { BaseRouterHistory } from './base';
9
-
10
- export class AbstractHistory extends BaseRouterHistory {
11
- index: number;
12
- stack: RouteRecord[];
13
-
14
- constructor(router: RouterInstance) {
15
- super(router);
16
- this.index = -1;
17
- this.stack = [];
18
- this.init();
19
- }
20
-
21
- async init({ replace }: { replace?: boolean } = { replace: true }) {
22
- const { initUrl } = this.router.options;
23
- if (initUrl !== undefined) {
24
- // 存在 initUrl 则用 initUrl 进行初始化
25
- if (replace) {
26
- await this.replace(initUrl);
27
- } else {
28
- await this.push(initUrl);
29
- }
30
- }
31
- }
32
-
33
- destroy() {}
34
-
35
- // 设置监听函数
36
- setupListeners() {}
37
-
38
- // 服务端判断是否是相同域名
39
- isSameHost(location: RouterRawLocation, route: Route) {
40
- const rawLocation =
41
- typeof location === 'string' ? { path: location } : location;
42
- if (rawLocation.path === undefined) {
43
- rawLocation.path = this.current.fullPath;
44
- }
45
-
46
- // 服务端 base 需要包含域名,判断 base 是否包含传入路径的域名即可
47
- const { base } = normalizeLocation(rawLocation, this.router.base);
48
-
49
- return base.includes(route.hostname);
50
- }
51
-
52
- // 处理外站跳转逻辑
53
- handleOutside(
54
- location: RouterRawLocation,
55
- replace = false,
56
- isTriggerWithWindow = false
57
- ) {
58
- const { flag, route } = isPathWithProtocolOrDomain(location);
59
- if (!flag) {
60
- // 如果不以域名开头则跳出
61
- return false;
62
- }
63
-
64
- const router = this.router;
65
- const { validateOutside, handleOutside } = router.options;
66
-
67
- // 如果域名相同 和 非外站(存在就算同域也会被视为外站的情况) 则跳出
68
- const isSameHost = this.isSameHost(location, route);
69
- if (isSameHost && !validateOutside?.({ router, location, route })) {
70
- return false;
71
- }
72
-
73
- // 如果有配置跳转外站函数,则执行配置函数
74
- handleOutside?.({
75
- router,
76
- route,
77
- replace,
78
- isTriggerWithWindow,
79
- isSameHost
80
- });
81
-
82
- return true;
83
- }
84
-
85
- // 新增路由记录跳转
86
- async push(location: RouterRawLocation) {
87
- await this.jump(location, false);
88
- }
89
-
90
- /**
91
- * 新开浏览器窗口的方法,在服务端会调用 push 作为替代
92
- */
93
- async pushWindow(location: RouterRawLocation) {
94
- await this._jump(location, false, true);
95
- }
96
-
97
- // 替换当前路由记录跳转
98
- async replace(location: RouterRawLocation) {
99
- await this.jump(location, true);
100
- }
101
-
102
- /**
103
- * 替换当前浏览器窗口的方法,在服务端会调用 replace 作为替代
104
- */
105
- async replaceWindow(location: RouterRawLocation) {
106
- await this._jump(location, true, true);
107
- }
108
-
109
- // 跳转方法
110
- async jump(location: RouterRawLocation, replace = false) {
111
- await this._jump(location, replace);
112
- }
113
-
114
- private async _jump(
115
- location: RouterRawLocation,
116
- replace = false,
117
- isTriggerWithWindow = false
118
- ) {
119
- if (this.handleOutside(location, replace, isTriggerWithWindow)) {
120
- return;
121
- }
122
-
123
- await this.transitionTo(location, (route) => {
124
- const index = replace ? this.index : this.index + 1;
125
- this.stack = this.stack.slice(0, index).concat(route);
126
- });
127
- }
128
-
129
- go(delta: number): void {
130
- const targetIndex = this.index + delta;
131
- // 浏览器在跳转到不存在的历史记录时不会进行跳转
132
- if (targetIndex < 0 || targetIndex >= this.stack.length) {
133
- return;
134
- }
135
- const route = this.stack[targetIndex];
136
- this.index = targetIndex;
137
- this.updateRoute(route);
138
- }
139
-
140
- /* 路由历史记录前进方法 */
141
- forward() {
142
- this.go(1);
143
- }
144
-
145
- /* 路由历史记录后退方法 */
146
- back() {
147
- this.go(-1);
148
- }
149
- }