最近需要將專案中的browserHistory
路由改成memoryHistory
路由,(不懂的看這裡:github.com/ReactTraini…),但是專案中有很多根據window.location
來判斷當前路由的程式碼,不好一一修改,所以決定自己實現一個location
,目的就是對接到history上,根據業務實現的並不完整,但也差不多,直接看程式碼:
import resolvePathname from 'resolve-pathname';
import history from 'path/to/history'; // createMemoryHistory
export enum ILocationProtocol {
HTTP = 'http:',
HTTPS = 'https:',
}
export interface IHistoryLocationProps {
pathname: string;
search: string;
hash: string;
}
export interface ILocationProps {
protocol?: ILocationProtocol;
hostname?: string;
port?: string;
reload?: (props?: IHistoryLocationProps) => void;
}
const defaultProps = {
protocol: ILocationProtocol.HTTPS,
hostname: 'bytedance.feishu.cn',
port: '',
};
export default class Location {
private _protocol: ILocationProtocol;
private _hostname: string;
private _port: string;
private _host: string;
private _origin: string;
private _reload?: (props?: IHistoryLocationProps) => void;
constructor(props: ILocationProps = {}) {
this._protocol = props.protocol || defaultProps.protocol;
this._hostname = props.hostname || defaultProps.hostname;
this._port = props.port || defaultProps.port;
this._host = this._port === '80' || this._port === ''
? this.hostname
: `${this._hostname}:${this._port}`;
this._origin = `${this._protocol}//${this._host}`;
this._reload = props.reload;
}
get protocol(): ILocationProtocol {
return this._protocol;
}
set protocol(value: ILocationProtocol) {
throw new Error('Set protocol is not allowed');
}
get hostname() {
return this._hostname;
}
set hostname(value: string) {
throw new Error('Set hostname is not allowed');
}
get port() {
return this._port;
}
set port(value: string) {
throw new Error('Set port is not allowed');
}
get host() {
return this._host;
}
set host(value: string) {
throw new Error('Set host is not allowed');
}
get origin(): string {
return this._origin;
}
set origin(value: string) {
throw new Error('Set origin is not allowed');
}
get pathname() {
return history.location.pathname;
}
set pathname(value: string) {
const pathname = value;
this.assign(pathname);
}
get search() {
return history.location.search;
}
set search(value: string) {
let search = value;
if (!search.startsWith('?')) search = `?${search}`;
this.assign(search);
}
get hash(): string {
return history.location.hash;
}
set hash(value: string) {
let hash = value;
if (!hash.startsWith('#')) hash = `#${hash}`;
this.assign(hash);
}
get href() {
return `${this._origin}${this.pathname}${this.search}${this.hash}`;
}
set href(value: string) {
const href = value;
this.assign(href);
}
public assign(url: string) {
this.replace(url);
}
public reload() {
const props = {
pathname: this.pathname,
search: this.search,
hash: this.hash,
};
this._reload && this._reload(props);
}
public replace(url: string) {
let props: IHistoryLocationProps;
const _url = encodeURI(url);
if (_url.startsWith('#')) {
history.push(_url);
return;
}
if (_url.startsWith('?')) {
props = {
pathname: this.pathname,
search: _url,
hash: '',
};
} else if (/^https?:\/\//.test(_url)) {
const [ , , pathname = '/', search = '', hash = '' ] =
_url.match(/^[a-z]+:\/\/[^\/:]+(:\d+)?(\/[^\?]+)?(\?[^#]+)?(#.+)?$/) as any[];
props = { pathname, search, hash };
} else {
props = {
pathname: resolvePathname(_url, this.pathname),
search: '',
hash: '',
};
}
this._reload && this._reload(props);
}
public toString(): string {
return this.href;
}
public valueOf(): Location {
return this;
}
}
複製程式碼
具體就不細說了,只允許修改pathname
/search
/hash
以及呼叫介面,所以說實現不完整;protocol
/hostname
/port
欄位和reload
介面由外部定義;
可是還有一個問題,就是程式碼中一般是以location
或window.location
來呼叫,如何讓這些指向我實現的location
呢?這裡用了一個黑科技,就是webpack
的DefinePlugin
(不懂的看這裡:webpack.js.org/plugins/def…),如下:
module.exports = {
// ...
plugins: [
// ...
new webpack.DefinePlugin({
'location': '(window.__MY__WINDOW__ || window).location',
'window.location': '(window.__MY__WINDOW__ || window).location',
})
],
// ...
}
複製程式碼
也不細說,大家自行感受。
插播一條廣告
位元組跳動招聘 - EE內推專場,廣州、深圳、上海,歡迎狂砸簡歷:
or 掃碼 ⤵️