1.React 中的 路由拦截策略与 vue 对比
1.App.js 里面根据 路径 和 是否有 token 等信息进行判断去哪个路由 2.router 文件夹下面的 index.js 进行鉴权判断去哪个路由和动态标题的设置 App.js > main.js > index.js > routeConfigLazy.js 下面是文件的包含利用关系:
对应的 vue-router 拦截器配置:把两部分内容合到一起的
if (to.path !== "/404" && to.path !== "/checkLogin") {
/*
/#\/main\/ReportNav\/ReportManage:匹配哈希部分中包含#/main/ReportNav/ReportManage的URL。
#\/main\/supervise:匹配哈希部分中包含#/main/supervise的URL。
#\/main\/FeedbackDetails:匹配哈希部分中包含#/main/FeedbackDetails的URL。
*/
if (
search.nw &&
/#\/main\/ReportNav\/ReportManage|#\/main\/supervise|#\/main\/FeedbackDetails/.test(
hash
)
) {
if (token) {
next(); //直接向下走,这三个路径不用鉴权,登陆过了就行
} else {
if (GetQueryString().ssoToken) {
//如果路径后面拼接了 ssoToken 的话:https://test-agent.myyixue.com/#/ssologin?ssoToken=1n6LO9wsn88tuP50E9bgnTn9Qoz1Fewy8E9ajnnRK27NxXssPN6auw0iE98p49j09ic1Zy8E96
localStorage.setItem("needBackSso", 1);
next({ path: "/ssologin" });
} else {
next({ path: "/login" });
}
}
} else if (
to.path === "/login" ||
to.path === "/pt-login" ||
to.path === "/agent-login" ||
to.path === "/ssologin" ||
to.fullPath.indexOf("/ImproveScoreCase/Preview") > -1
) {
if (token) {
//必须是 cookie 中没有 token了才可以去登录页面
//如果已经处于登录状态要进行拦截,转到首页(本身如果直接改是进不去登录的,如果在前面加个#还是可以进入登录,所以一定要拦截一下)
//跳转到之前点击的那个侧边栏 url
next({ path: this.$store.state.selectSideUrl });
} else {
next(); //没登录的话,可以去登录
}
} else {
if (token) {
//登陆了才有必要鉴权,否则必须先登录!
let isEmpty = false;
let CrmNavUrls = JSON.parse(
localStorage.getItem("CRM_NAVFUNC_URLS") || "[]"
);
// 鉴权(防止直接通过路径跳转来访问没有权限的页面)
isEmpty =
CrmNavUrls.indexOf(to.path) >= 0 || to.path == "/main" //去 main 页面的话不应该拦截(前提是在已登录的情况下)
? true
: false;
console.log(CrmNavUrls.indexOf(to.path) >= 0);
console.log(to.path == "/main");
if (!isEmpty) {
//没有权限
next({ path: "/main/noMenu" });
} else {
//有权限
next(); //大多数正常的走这里
}
} else {
//如果不是去登录的(任何 login 都不是)还没有 token
next({ path: "/login" });
}
}
} else {
next(); //404或者checkLogin走这里
}
区别的理解:react 将逻辑分摊到组件里面,看起来比较直观,逻辑性强,适合进行非常复杂的配置(可以拆分复杂度); 而 vue 把逻辑统一到配置文件,方便配置和修改。
2.ComputedMatch
computedMatch 是使用 Switch 包裹的子组件才有的值,Switch 的作用是从上到下开始渲染,只要匹配到一个,其他的就不匹配。 在 React Router 的 Switch 组件内的每个 Route 组件都会有一个 computedMatch 属性,无论其是否匹配当前 URL。 ComputedMatch 是由 Route 组件内部根据路由匹配逻辑计算得出的一个属性。它包含有关路由匹配的信息,如 path、url 和 isExact 等。 注意:ComputedMatch 属性只有在组件被成功匹配到当前 URL 时才会有值,否则为 null。 所以可以把 computedMatch.url 理解为就是当前的组件对应的路径。——> 可以说等价于 match. Path(但是很多时候我们不能直接获得到 history 对象,比如 Route 外面包裹了高阶组件 PrivateRoute,而 PrivateRoute 不能获取到 history 对象,所以只能用 computedMatch) 例子:使用 computedMatch.url 来做路由鉴权
let flag =
CrmNavUrls.indexOf(rest.computedMatch.url) >= 0 ||
rest.type === "notMenu" ||
rest.computedMatch.url.indexOf("/main/SchoolArchives") >= 0;
Route 的路由包装器 ——> PrivateRoute 高阶组件
PrivateRoute 是一个高阶组件,它封装了 Route 组件并添加了一些额外的逻辑和处理。当你希望在渲染 Route 组件之前进行一些特定的检查或操作时,你可以使用 PrivateRoute 作为中间组件。 这样要传递给 Route 组件的内容都会经过 PrivateRoute 这个高阶组件 例子:
return (
<PrivateRoute
type={item.type || false}
title={item.title}
path={item.path}
key={index}
meta={item.meta}
component={item.component}
/>
);
function PrivateRoute({ component: Component, ...rest }) {
//rest里面有computedMatch属性
isEmpty =
CrmNavUrls.indexOf(rest.computedMatch.url) >= 0 ||
rest.type === "notMenu" ||
rest.computedMatch.url.indexOf("/main/SchoolArchives") >= 0;
}
Location. Pathname 和 match. Path 对比:获取当前路由的路径信息
Location. Pathname 和 match. Path 是用于路由匹配的 React Router 属性,用于获取当前路由的路径信息。它们之间有一些区别:
- Location. Pathname:实际的 url 属于 location 对象的属性。 返回当前 URL 的路径部分。 不考虑路由的匹配规则,仅返回 URL 中的路径部分。 例如,对于 URL /users/123/profile,location. Pathname 将返回 /users/123/profile。
- Match. Path:定义的 url(包含占位符) 属于 match 对象的属性。 返回当前路由的模式(path),即路由定义时指定的路径。 考虑路由的匹配规则,与当前 URL 的路径部分进行比较。 用于在嵌套路由或动态路径中,获取当前路由定义的路径模式。 例如,对于路由定义 ,如果当前 URL 为 /users/123/profile,match. Path 将返回 /users/: id/profile。 综上所述,location. Pathname 返回当前 URL 的路径部分,而 match. Path 返回当前路由的路径模式(path)。它们用途不同,根据需要选择使用。一般来说,当需要获取当前 URL 的路径时,可以使用 location. Pathname;当需要获取当前路由定义的路径模式时,可以使用 match. Path。
Route 组件对于 history 对象(以及 location、match 对象)属性的获取:
在 React Router 中,Route 组件渲染的组件会自动获得 history 属性,不论该组件是否位于 Switch 组件中。 如果你在 Route 的 render 属性中返回的组件需要访问 history 对象,你可以将 history 对象作为参数传递给回调函数,并将其传递给渲染的组件。(否则可能不会拥有 history 对象)
<Route
path="/my-route"
render={(routeProps) => (
<MyComponent {...routeProps} history={routeProps.history} />
)}
/>
3.hash 模式的特点
在 React Router 的 hash 模式下,当路径发生变化时,路由的 path
属性会反映当前的 URL 路径。 在 hash 模式下,URL 中的路径会以 #
符号为前缀,例如 http://example.com/#/users
。当路径发生变化时,React Router 会解析 URL 中的 hash 部分,并将其与定义的路由路径进行匹配。 hash 模式只会匹配#之后的作为路径(并且?参数之前的) 所以说这样写的话: 注意:rest 本身就有一个被传进来的属性叫做 path
会导致下面的访问路径不能被匹配:
从而会走向这里:
4.注意:不同的网页之间本地存储和 cookie 不互通!
准确的说是,不同的服务(ip 端口号不同),那么就会有不同的本地存储和 cookie
5.类式封装的 axios
这种封装方式适合于 react 中:
import axios from "axios";
import { getBaseUrl, getPhpUrl } from "./config";
import { CRM_TOKEN, PT_SCHOOL_LOGIN } from "./keys";
import { getCookie, delCookie } from "./cookie";
import { message } from "antd";
import qs from "qs";
import { clearLocalStorage } from "./util";
axios.withCredentials = true;
axios.defaults.baseURL = getBaseUrl();
axios.defaults.headers.post["Content-Type"] = "application/json; charset=UTF-8";
axios.interceptors.request.use(
//拦截器
function (config) {
let token = getCookie(CRM_TOKEN);
console.log(window.location.href.indexOf("/ssologin"));
console.log(window.location.href);
if (
config.url.indexOf("/user/login") === -1 &&
config.url.indexOf("user/resetPassword") === -1 &&
window.location.href.indexOf("login") === -1 &&
!token
) {
loginOut();
}
if (
(config.url.indexOf("/user/login") === -1 &&
config.url.indexOf("user/resetPassword") === -1 &&
window.location.href.indexOf("/ssologin") === -1) ||
config.url.indexOf("/user/loginOut")
) {
console.log(token);
config.headers.common["Authorization"] = `${token}`;
}
return Promise.resolve(config);
},
function (error) {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
// 获取token接口不校验,直接返回
if (response.config.url.indexOf("token") !== -1) {
return Promise.resolve(response.data);
}
// 登录失效
if (
response.data &&
(response.data.code === -1 || response.data.code === 1000)
) {
loginOut();
} else if (response.data.code === -2) {
// ssologin
goBackSso();
}
let retData;
if (response.data.code === -1) {
retData = {
...response.data,
message: "当前登录状态失效 请重新登录",
};
} else if (response.data.code === 500) {
retData = {
...response.data,
message: "服务器开小差了,请稍后重试!",
};
} else {
retData = {
...response.data,
};
}
return Promise.resolve(retData);
},
function (error) {
// 获取token接口不校验,直接返回
if (
error.response &&
error.response.data &&
(error.response.data.code === -1 ||
error.response.data.code === 1000)
) {
loginOut();
}
message.warning("服务器开小差了,请稍后重试!");
return Promise.reject(error);
}
);
//真正地封装
class CRMAxios {
constructor(options = {}) {
this.options = options;
}
request(options) {
return axios.request(options);
}
get(url, options = {}) {
return this.request({
url: url,
params: {
...options,
},
});
}
phpGet(url, options = {}) {
return this.request({
url: getPhpUrl() + url,
params: {
...options,
},
});
}
post(url, data, options = {}) {
if (data instanceof Object) {
data = JSON.stringify(data);
}
return this.request({
method: "post",
url,
data,
...options,
});
}
phpPost(url, data, options = {}) {
if (data instanceof Object) {
data = JSON.stringify(data);
}
return this.request({
method: "post",
url: getPhpUrl() + url,
data,
...options,
});
}
}
const instanceAxios = new CRMAxios();
export const request = instanceAxios.request.bind(instanceAxios);
export const get = instanceAxios.get.bind(instanceAxios); //重新绑定给instanceAxios实例
export const post = instanceAxios.post.bind(instanceAxios);
export const phpPost = instanceAxios.phpPost.bind(instanceAxios);
export const phpGet = instanceAxios.phpGet.bind(instanceAxios);