ReactRouter의 구현 방법
17686 단어 ReactRouter실현
React Router의 구현
ReactRouter
은React
의 핵심 구성 요소로 주로 React
의 루트 관리자로서 UI
과URL
의 동기화를 유지하고 간단한API
과 강력한 기능을 가진다. 예를 들어 코드 버퍼 불러오기, 동적 루트 일치, 정확한 위치 과도 처리 등이다.묘사
React Router
는 history
대상 위에 세워진 것이다. 간단히 말하면 history
대상은 브라우저 주소 표시줄의 변화를 감청하는 방법을 알고 이것URL
을 location
대상으로 전환시킨 다음에 router
을 사용하여 루트에 일치하게 하고 마지막으로 대응하는 구성 요소를 정확하게 렌더링한다. 자주 사용하는 history
은 세 가지 형식이 있다. Browser History
, Hash History
, Memory History
.Browser History
Browser History
은React Router
의 응용 프로그램 추천history
을 사용하고 브라우저의 History
대상pushState
,replaceState
등API
및popstate
사건 등을 처리하여 URL
처럼 진실한https://www.example.com/path
을 만들 수 있습니다. 마찬가지로 페이지가 이동할 때 페이지를 다시 불러올 필요가 없습니다. 물론 서버에 대한 요청도 하지 않습니다.물론 URL
모델에 대해서는 백엔드의 설정 지원이 필요합니다. 비첫 페이지의 요청과 리셋 시 백엔드가 되돌아오는 자원을 지원하기 위해서입니다. 응용 프로그램은 단일 페이지 클라이언트 응용 프로그램이기 때문에 백엔드에 정확한 설정이 없으면 사용자가 브라우저에서 직접 방문history
하면 되돌아오기 때문에 서버에 모든 상황을 덮어쓰는 후보 자원을 추가해야 합니다.URL
정적 자원이 일치하지 않으면 404
응용 프로그램 의존 페이지를 되돌려야 합니다. 예를 들어 URL
아래에 있는 설정입니다.
location / {
try_files $uri $uri/ /index.html;
}
Hash History
index.html
기호 즉 Nginx
의 원래 목적은 Hash
에서 웹 페이지의 위치를 표시하는 데 사용됩니다. 예를 들어 #
은 URL
의 https://www.example.com/index.html#print
위치를 나타내고, 브라우저가 이것example
을 읽으면 index.html
위치를 자동으로 가시 영역으로 스크롤합니다. 보통 print
라벨의 URL
속성이나 print
라벨의 <a>
속성을 사용하여 닻점을 지정합니다.name
속성을 통해 닻 위치를 읽을 수 있으며, <div>
의 변경에 id
감청 이벤트를 추가할 수 있으며, 매번 변경window.location.hash
할 때마다 브라우저 접근 역사에 기록을 추가합니다. 또한 Hash
hashchange
에는 나타나지만 Hash
요청에 포함되지 않습니다. 즉, Hash
및 그 후의 문자는 서버에 전송되어 자원이나 데이터의 요청을 하지 않습니다.브라우저 동작을 지도하는 데 사용되며 서버에 효과가 없기 때문에 변경 URL
은 페이지를 다시 불러오지 않습니다.HTTP
의 역할은 변경#
을 통해 페이지를 다시 요청하지 않는 상황에서 페이지 보기를 업데이트하여 동적 불러오기와 구성 요소를 없애는 것이다. 쉽게 말하면 주소 표시줄의 주소가 바뀌었지만 새로운 페이지가 아니라 이전의 페이지 일부분을 수정한 것이다. 이것도 Hash
단일 페이지 응용의 특징이다. 그 모든 활동은 하나ReactRouter
페이지에 국한된다.게으르지 않은 페이지는 이 URL
페이지가 초기화될 때만 해당하는 SPA
, Web
, Web
파일을 불러옵니다. 페이지 불러오기가 완료되면 HTML
페이지를 다시 불러오거나 이동하지 않고 JavaScript
동적 변환CSS
을 사용합니다. 기본SPA
모드는 닻을 통해 루트를 실현하고 구성 요소의 표시와 숨김을 제어하여 페이지 이동과 유사한 상호작용을 합니다.Memory History
JavaScript
주소 표시줄에서 조작되거나 읽히지 않기 때문에 서버 렌더링을 어떻게 실현하는지 설명할 수 있을 뿐만 아니라 테스트와 다른 렌더링 환경(예를 들어 HTML
과 다른 두 가지Hash
의 차이점은 우리가 그것을 만들어야 한다는 것이다. 이런 방식은 테스트에 편리하다.
const history = createMemoryHistory(location);
실현
우리는 매우 간단한
Memory History
모드와 React Native
모드의 실현을 실현한다. History
의Browser History
방법은 로컬 파일 프로토콜Hash History
에서 실행할 수 없기 때문에 실행하기 위해서는 H5
환경을 구축해야 한다. 사용pushState
,file://
,http://
등은 모두 가능하고 webpack
모드로 돌아가야 한다.Nginx
루트 점프를 실현하고 페이지를 갱신하지 않는 것은 Apache
가 제공한 Browser History
, history
등 방법과 H5
등 사건 덕분이다. 이런 방법은 루트 경로를 바꿀 수도 있지만 페이지 점프를 하지 않는다. 물론 백엔드가 설정되지 않은 상황에서 루트 개편 후 페이지를 갱신하면 pushState()
모델을 제시할 수 있다. replaceState()
모델에 대해 우리의 실현 방향은 비슷하다.주로 popstate
등404
을 사용하지 않은Hash History
, 그리고 감청 사건과 달리 pushState
사건의 변화를 감청한 다음에 해당H5
을 받아 대응하는 보기를 업데이트하는 데 있다.
<!-- Browser History -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Router</title>
</head>
<body>
<ul>
<li><a href="/home" rel="external nofollow" >home</a></li>
<li><a href="/about" rel="external nofollow" >about</a></li>
<div id="routeView"></div>
</ul>
</body>
<script>
function Router() {
this.routeView = null; //
this.routes = Object.create(null); //
}
//
Router.prototype.route = function (path, callback) {
this.routes[path] = () => this.routeView.innerHTML = callback() || "";
};
//
Router.prototype.init = function(root, rootView) {
this.routeView = rootView; //
this.refresh(); //
root.addEventListener("click", (e) => { // root
if (e.target.nodeName === "A") {
e.preventDefault();
history.pushState(null, "", e.target.getAttribute("href"));
this.refresh(); //
}
})
//
// pushState replaceState popstate
window.addEventListener("popstate", this.refresh.bind(this), false);
};
//
Router.prototype.refresh = function () {
let path = location.pathname;
console.log("refresh", path);
if(this.routes[path]) this.routes[path]();
else this.routeView.innerHTML = "";
};
window.Router = new Router();
Router.route("/home", function() {
return "home";
});
Router.route("/about", function () {
return "about";
});
Router.init(document, document.getElementById("routeView"));
</script>
</html>
<!-- Hash History -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Router</title>
</head>
<body>
<ul>
<li><a href="#/home" rel="external nofollow" >home</a></li>
<li><a href="#/about" rel="external nofollow" >about</a></li>
<div id="routeView"></div>
</ul>
</body>
<script>
function Router() {
this.routeView = null; //
this.routes = Object.create(null); //
}
//
Router.prototype.route = function (path, callback) {
this.routes[path] = () => this.routeView.innerHTML = callback() || "";
};
//
Router.prototype.init = function(root, rootView) {
this.routeView = rootView; //
this.refresh(); //
// hashchange
window.addEventListener("hashchange", this.refresh.bind(this), false);
};
//
Router.prototype.refresh = function () {
let hash = location.hash;
console.log("refresh", hash);
if(this.routes[hash]) this.routes[hash]();
else this.routeView.innerHTML = "";
};
window.Router = new Router();
Router.route("#/home", function() {
return "home";
});
Router.route("#/about", function () {
return "about";
});
Router.init(document, document.getElementById("routeView"));
</script>
</html>
분석
API
은hashchange
, location.hash
은ReactRouter
이다. 그 전에 commit id
라이브러리, eef79d5
라이브러리, TAG
에 의존하는 대4.4.0
강화판의 history
라이브러리를 알아야 한다. 그 중에서 주로 history
대상이 현재의 ReactRouter
라이브러리와 window.history
일치하는 결과를 나타낸다.history
대상은 match
라이브러리 기반URL
의 파생이다.path
는 루트를 몇 개의 패키지로 분리했다. location
는 일반적인 루트 논리를 맡고 history
는 브라우저의 루트 관리를 맡으며 window.location
는 ReactRouter
의 루트 관리를 맡는다.react-router
구성 요소를 예로 들면, react-router-dom
는 react-router-native
에서 고급 구성 요소로 내부에 전체적인 react-native
대상을 만들고, 전체 루트의 변화를 감청하고, BrowserRouter
을 BrowserRouter
구성 요소로 전달할 수 있으며, react-router-dom
구성 요소는 이 history
의 속성을 history
하위 구성 요소로 다시 전달할 수 있다.
// packages\react-router-dom\modules\HashRouter.js line 10
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />;
}
}
다음으로 우리는 props
구성 요소에 가서 react-router
구성 요소는 Router
환경을 만들었고 Router
을 빌려 history
에 context
를 전달했다. 이것은 왜 Router
모든 Router
바깥에 있어야 하는지를 설명한다.React Context
의context
에 Route
를 추가하면 루트의 변화를 감청하고 리셋 이벤트를 실행할 수 있으며 여기서 촉발된다context
.Router
시 즉 매번 루트가 변할 때Route
촉발 정상층Router
의 리셋 이벤트componentWillMount
history.listen
setState
아래로 전달setState
이때->
에 최신Router
->
아래Router
를 포함하여 새로운setState
을 획득하여 렌더링 여부를 판단한다.
// line packages\react-router\modules\Router.js line 10
class Router extends React.Component {
static computeRootMatch(pathname) {
return { path: "/", url: "/", params: {}, isExact: pathname === "/" };
}
constructor(props) {
super(props);
this.state = {
location: props.history.location
};
// This is a bit of a hack. We have to start listening for location
// changes here in the constructor in case there are any <Redirect>s
// on the initial render. If there are, they will replace/push when
// they mount and since cDM fires in children before parents, we may
// get a new location before the <Router> is mounted.
this._isMounted = false;
this._pendingLocation = null;
if (!props.staticContext) {
this.unlisten = props.history.listen(location => {
if (this._isMounted) {
this.setState({ location });
} else {
this._pendingLocation = location;
}
});
}
}
componentDidMount() {
this._isMounted = true;
if (this._pendingLocation) {
this.setState({ location: this._pendingLocation });
}
}
componentWillUnmount() {
if (this.unlisten) this.unlisten();
}
render() {
return (
<RouterContext.Provider
children={this.props.children || null}
value={{
history: this.props.history,
location: this.state.location,
match: Router.computeRootMatch(this.state.location.pathname),
staticContext: this.props.staticContext
}}
/>
);
}
}
우리는 사용할 때 ->
를 끼워 넣는 것을 사용했기 때문에 이때 nextContext
구성 요소, context
의 역할은 일치하는 루트를 사용하고 렌더링할 구성 요소location
, ->
상층부Route
전송된 nextContext
, Router
중의 Route
를 받아들여 전체 페이지의 루트 변화를 감청하고 페이지가 전환될 때 감청Route
이벤트를 촉발한다.Route
아래로 전달props
하면 Route
의Router
와context
를 업데이트하여 현재Router
의history
일치 여부history
를 판단하고, 일치하면 렌더링하고, 그렇지 않으면 렌더링하지 않으며, 일치 여부의 근거는 Router
이 함수입니다. 다음은 분석할 것입니다. 여기에서 일치 실패는nextContext
의Route
만 알면 됩니다.일치에 성공하면 props
결과를 context
의 일부로 Route
에서 렌더링할 구성 요소에 전달합니다.path
세 가지 유형location
, computeMatch
, match
, null
을 받아들여야 한다. 이때 주의해야 할 것은 들어오는 match
이 내연 함수라면 매번props
이 새로 만들어지기 때문에 render
새로운 구성 요소가 들어왔다고 생각하기 때문에 낡은 구성 요소Route
를 다시 render props
받아들인다.이때 <Route component>
를 사용해야 한다. 패키지의 <Route render>
요소가 한 겹 없어지고, <Route children>
전개된 원소의 유형이 매번 같으면 component
발생하지 않고, 그 밖에 props.component
발생하지 않는다React
.
// \packages\react-router\modules\Route.js line 17
class Route extends React.Component {
render() {
return (
<RouterContext.Consumer>
{context => {
invariant(context, "You should not use <Route> outside a <Router>");
const location = this.props.location || context.location;
const match = this.props.computedMatch
? this.props.computedMatch // <Switch> already computed the match for us
: this.props.path
? matchPath(location.pathname, this.props)
: context.match;
const props = { ...context, location, match };
let { children, component, render } = this.props;
// Preact uses an empty array as children by
// default, so use null if that's the case.
if (Array.isArray(children) && children.length === 0) {
children = null;
}
if (typeof children === "function") {
children = children(props);
// ...
}
return (
<RouterContext.Provider value={props}>
{children && !isEmptyChildren(children)
? children
: props.match
? component
? React.createElement(component, props)
: render
? render(props)
: null
: null}
</RouterContext.Provider>
);
}}
</RouterContext.Consumer>
);
}
}
우리가 실제로 가장 많이 쓸 수 있는 것은 바로 diff
라벨이다. 그래서 unmount
구성 요소를 다시 한 번 보면 re-mount
결국 render
라벨을 만들어서 돌릴 요소를 감싸는 것을 볼 수 있다. 이 component
라벨의 render
클릭 사건에서 re-mount
기본 돌림을 금지하기 때문에 실제로 이곳children
은 실제적인 작용이 없다.그러나 이동할 페이지의 re-mount
를 표시할 수 있고 더 좋은 Link
의미가 있습니다.<Link>
중 Link
, 마우스 왼쪽 단추로 눌리지 않은, 비a
점프하지 않은, 다른 기능키를 누르지 않은 클릭a
, 그리고handleClick
preventDefault
에 들어간 것도 앞에서 말한 루트의 변화가 페이지의 점프와 서로 관련이 없는 것이고, href
URL
라이브러리html
를 통해 handleClick
호출된 것이다.그러나 이것은 루트만 변화시킬 뿐 다른 것은 아무것도 변하지 않는다.preventDefault
중의 _blank
루트의 변화를 감청하고 preventDefault
업데이트 push
와 history
하위 ReactRouter
를 통해 다시 일치하게 하여 렌더링 부분의 업데이트를 완성합니다.
// packages\react-router-dom\modules\Link.js line 14
class Link extends React.Component {
handleClick(event, history) {
if (this.props.onClick) this.props.onClick(event);
if (
!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
(!this.props.target || this.props.target === "_self") && // let browser handle "target=_blank" etc.
!isModifiedEvent(event) // ignore clicks with modifier keys
) {
event.preventDefault();
const method = this.props.replace ? history.replace : history.push;
method(this.props.to);
}
}
render() {
const { innerRef, replace, to, ...rest } = this.props; // eslint-disable-line no-unused-vars
return (
<RouterContext.Consumer>
{context => {
invariant(context, "You should not use <Link> outside a <Router>");
const location =
typeof to === "string"
? createLocation(to, null, null, context.location)
: to;
const href = location ? context.history.createHref(location) : "";
return (
<a
{...rest}
onClick={event => this.handleClick(event, context.history)}
href={href}
ref={innerRef}
/>
);
}}
</RouterContext.Consumer>
);
}
}
매일 한 문제https://github.com/WindrunnerMax/EveryDay
참고
https://zhuanlan.zhihu.com/p/44548552 https://github.com/fi3ework/blog/issues/21 https://juejin.cn/post/6844903661672333326 https://juejin.cn/post/6844904094772002823 https://juejin.cn/post/6844903878568181768 https://segmentfault.com/a/1190000014294604 https://github.com/youngwind/blog/issues/109 http://react-guide.github.io/react-router-cn/docs/guides/basics/Histories.html
이 React Router의 실현 방법에 관한 글은 여기 소개되어 있습니다. 더 많은 React Router의 실현 내용은 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Asp와 XML로 상호작용하는 실례 원본XML은 표준 확장 언어로 미래 웹 프로그래밍의 표준이다. asp는 현재 널리 전해지는 웹 프로그래밍 언어 중의 하나이다. 그들 두 사람이 연합하여 역할을 발휘할 수 있을까?두부는 여기서 여러분에게 아주 간단한 As...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.