В процессе разработки одностраничных приложений полным ходом все меньше внимания уделялосьВнешняя маршрутизация.
И ххх интервьюеры часто спрашивают, что такое front-end роутинг, каков его принцип, как он реализован, а страница при переходе не обновляется...
Много чего, спроси у тебя голова большая, в это время я достану заветные картинки, зрители, пять сигарет. 🤣
предисловие
Сегодня главный разговор:
- Нативный js реализует hashRouter
- Нативный js реализует historyRouter
- BrowserRouter для реактивного роутера
- HistoryRouter для реактивного роутера
Принцип реализации четырех видов маршрутов.
Экологические проблемы
Потому что я буду использовать новые h5 позже.pushState()метод, потому что эта игра (диао) значит (мао) слишком лицемерна, не поддерживает местныеФайловый протоколзапустить, иначе будет сообщено о следующей ошибке
только вhttp(s) протоколБегая, эта ямная сволочь тоже давно наступила, наступив на сомнения относительно своего пола.
с момента использованияфайловый протоколНе только использовать егоwebpackУстановите грубую окружающую среду, вы также можете использовать Apache, Tomcat ... ах собака ах кошка и тому подобное.
Этот шлак используетсяwebpackОкружающая среда, также удобно ждать объясненияreact-router-domПринцип двух маршрутов. Конфигурацию окружения я просто выложу, и рассказывать здесь не буду. если ты правwebpackЕсли вам интересно, можете взглянуть на эту мразьэта статья, Хоть и не очень хорошо написано, но для начала достаточно.
npm i webpack webpack-cli babel-loader @babel-core @babel/preset-env html-webpack-plugin webpack-dev-server -D
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry:path.resolve(__dirname,'./index.js'),
output:{
filename:'[name].[hash:6].js',
path:path.resolve(__dirname,'../dist')
},
module:{
rules:[
{
test:/\.js$/,
exclude:/node_module/,
use:[
{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
}
}
]
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:path.resolve(__dirname,'./public/index.html'),
filename:'index.html'
})
]
}
Добавьте команду в скрипт package.json
"dev":"webpack-dev-server --config ./src/webpack.config.js --open"
каталог проекта
бегать
npm run dev
Теперь, когда все готово, можно переходить к теме.
Нативный js реализует hashRouter
html
<ul>
<li><a href='#/home'>home</a></li>
<li><a href='#/about'>about</a></li>
<div id="routeView"></div>
</ul>
js
window.addEventListener('DOMContentLoaded', onLoad)
window.addEventListener('hashchange', changeView)
let routeView = ''
function onLoad() {
routeView = document.getElementById('routeView')
changeView()
}
function changeView() {
switch (location.hash) {
case '#/home':
routeView.innerHTML = 'home'
break;
case '#/about':
routeView.innerHTML = 'about'
break;
}
}
Собственная js-реализация hashRouter в основном прослушивает изменения в своем событии hashchange, а затем получает соответствующий location.hash для обновления соответствующего представления.
Нативный js реализует historyRouter
html
<ul>
<li><a href='/home'>home</a></li>
<li><a href='/about'>about</a></li>
<div id="routeView"></div>
</ul>
historyRoute
window.addEventListener('DOMContentLoaded', onLoad)
window.addEventListener('popstate', changeView)
let routeView = ''
function onLoad() {
routeView = document.getElementById('routeView')
changeView()
let event = document.getElementsByTagName('ul')[0]
event.addEventListener('click', (e) => {
if(e.target.nodeName === 'A'){
e.preventDefault()
history.pushState(null, "", e.target.getAttribute('href'))
changeView()
}
})
}
function changeView() {
switch (location.pathname) {
case '/home':
routeView.innerHTML = 'home'
break;
case '/about':
routeView.innerHTML = 'about'
break;
}
}
Реализовать переход по маршруту истории можно без обновления страницы.В этом есть преимущества от pushState(), replaceState() и других методов, предоставляемых H5.Эти методы также могут изменять состояние маршрута (путь), но не делают переходов по страницам. , Мы можем передать location.pathname для отображения соответствующего представления.
react-router-dom
react-router-dom — это маршрут реакции, он помогает нам реализовывать одностраничные приложения в проекте, он предоставляет нам два маршрута, один из которых основан на хеш-сегментеHashRouter, реализация на основе H5ApiBrowserRouter.
Давайте использовать его кратко.
Если вы используете окружение, предоставленное вам этим шлаком, вам необходимо его настроить.Следующие 👇 эти вещи, если нет, пожалуйста, игнорируйте.
npm i react react-dom react-router-dom @babel/preset-react -D
webpack.config.js, добавьте предустановку в параметры конфигурации js
Будуindex.jsИзмените это на это.
import React from 'react'
import ReactDom from 'react-dom'
import { BrowserRouter, Route, Link } from 'react-router-dom'
function App() {
return (
<BrowserRouter>
<Link to='/home'>home</Link>
<Link to='/about'>about</Link>
<Route path='/home' render={()=><div>home</div>}></Route>
<Route path='/about' render={()=><div>about</div>}></Route>
</BrowserRouter>
)
}
ReactDom.render(<App></App>,document.getElementById('root'))
public/index.html
<div id="root"></div>
Обычно я умею только ими пользоваться, но редко задумываюсь, как это делается, поэтому когда нас спросят, мы будем в замешательстве; если вы сегодня прочтете эту статью, эта мразь обещает вам, что вы больше не только будете использовать react-router, он больше не зависит от вас, но вы можете делать с ним все, что хотите.
BrowserRouter реализация react-router-dom
Во-первых, мы создаем новый файл BrowserRouter.js в index.js и реализуем наш собственный BrowserRouter. Поскольку должен быть реализован BrowserRouter, этот файл должен состоять из трех компонентов: BrowserRouter, Route и Link.
Хорошо, теперь у нас все на месте, давайте их по одному 😂
Компоненты BrowserRouter
Компонент Browberter в основном делает текущий путь к текущему пути и прослушивает событие Popstate, поэтому мы должны общаться с потребителем, поперечными поставщиками. Если вы не понимаете, вы можете посмотреть на шлакВ этой статье этот шлак иллюстрирует все методы связи реакции.
const { Consumer, Provider } = React.createContext()
export class BrowserRouter extends React.Component {
constructor(props) {
super(props)
this.state = {
currentPath: this.getParams.bind(this)(window.location.pathname)
}
}
onChangeView() {
const currentPath = this.getParams.bind(this)(window.location.pathname)
this.setState({ currentPath });
};
getParams(url) {
return url
}
componentDidMount() {
window.addEventListener("popstate", this.onChangeView.bind(this));
}
componentWillUnmount() {
window.removeEventListener("popstate", this.onChangeView.bind(this));
}
render() {
return (
<Provider value={{ currentPath: this.state.currentPath, onChangeView: this.onChangeView.bind(this) }}>
<div>
{
React.Children.map(this.props.children, function (child) {
return child
})
}
</div>
</Provider>
);
}
}
Реализация компонента маршрутизатора
Что в основном делает компонент Router, так это сравнивает текущее значение, переданное BrowserRouter, с путем, переданным Route через реквизиты, а затем решает, выполнять ли функцию рендеринга, переданную реквизитами.
export class Route extends React.Component {
constructor(props) {
super(props)
}
render() {
let { path, render } = this.props
return (
<Consumer>
{({ currentPath }) => currentPath === path && render()}
</Consumer>
)
}
}
Реализация компонента Link
Что в основном делает компонент Link, так это получает реквизит, передает его, изменяет состояние маршрутизации с помощью PushState(), а затем вручную обновляет представление с помощью onChangeView, переданного из BrowserRouter.
export class Link extends React.Component {
constructor(props){
super(props)
}
render() {
let { to, ...props } = this.props
return (
<Consumer>
{({ onChangeView }) => (
<a
{...props}
onClick={e => {
e.preventDefault();
window.history.pushState(null, "", to);
onChangeView();
}}
/>
)}
</Consumer>
)
}
}
полный код
import React from 'react'
const { Consumer, Provider } = React.createContext()
export class BrowserRouter extends React.Component {
constructor(props) {
super(props)
this.state = {
currentPath: this.getParams.bind(this)(window.location.pathname)
}
}
onChangeView() {
const currentPath = this.getParams.bind(this)(window.location.pathname)
this.setState({ currentPath });
};
getParams(url) {
return url
}
componentDidMount() {
window.addEventListener("popstate", this.onChangeView.bind(this));
}
componentWillUnmount() {
window.removeEventListener("popstate", this.onChangeView.bind(this));
}
render() {
return (
<Provider value={{ currentPath: this.state.currentPath, onChangeView: this.onChangeView.bind(this) }}>
<div>
{
React.Children.map(this.props.children, function (child) {
return child
})
}
</div>
</Provider>
);
}
}
export class Route extends React.Component {
constructor(props) {
super(props)
}
render() {
let { path, render } = this.props
return (
<Consumer>
{({ currentPath }) => currentPath === path && render()}
</Consumer>
)
}
}
export class Link extends React.Component {
constructor(props){
super(props)
}
render() {
let { to, ...props } = this.props
return (
<Consumer>
{({ onChangeView }) => (
<a
{...props}
onClick={e => {
e.preventDefault();
window.history.pushState(null, "", to);
onChangeView();
}}
/>
)}
</Consumer>
)
}
}
использовать
把刚才在index.js使用的react-router-dom换成这个文件路径就OK。
Реализация hashRouter из react-router-dom
hashRouter не говорит о компонентах один за другим, он похож на BrowserRouter, просто вставьте полный код напрямую
import React from 'react'
let { Provider, Consumer } = React.createContext()
export class HashRouter extends React.Component {
constructor(props) {
super(props)
this.state = {
currentPath: this.getCurrentPath.bind(this)(window.location.href)
}
}
componentDidMount() {
window.addEventListener('hashchange', this.onChangeView.bind(this))
}
componentWillUnmount() {
window.removeEventListener('hashchange')
}
onChangeView(e) {
let currentPath = this.getCurrentPath.bind(this)(window.location.href)
this.setState({ currentPath })
}
getCurrentPath(url) {
let hashRoute = url.split('#')
return hashRoute[1]
}
render() {
return (
<Provider value={{ currentPath: this.state.currentPath }}>
<div>
{
React.Children.map(this.props.children, function (child) {
return child
})
}
</div>
</Provider>
)
}
}
export class Route extends React.Component {
constructor(props) {
super(props)
}
render() {
let { path, render } = this.props
return (
<Consumer>
{
(value) => {
console.log(value)
return (
value.currentPath === path && render()
)
}
}
</Consumer>
)
}
}
export class Link extends React.Component {
constructor(props) {
super(props)
}
render() {
let { to, ...props } = this.props
return <a href={'#' + to} {...props} />
}