最近需要将项目中的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 扫码 ⤵️
