原生html实现一个mini-react-router

简介: 原生html实现一个mini-react-router前言实现一个简单版本的react-router, 揭秘路由的神秘面纱思考• 前端路由本质上是什么

原生html实现一个mini-react-router

前言

实现一个简单版本的react-router, 揭秘路由的神秘面纱

思考

前端路由本质上是什么

前端路由里的一些坑和注意点

hash路由和history路由的区别

Router组件和Route组件分别做了什么

路由的本质

浏览器端的路由不是真实的网页跳转,和服务器没有任何交互,本质上就是对url进行监听,让某个dom节点显示对应的视图路由的区别

路由的区别

一般来说,前端路由分为两种 1、hash 路由, 特征是url后面会有 # 号, 如 baidu.com/#foo/bar/baz 2、history 路由, url和普通路由没有差异。 如 baidu.com/foo/bar/baz

实际上只要搞清楚两种路由分别是如何改变,并且组件是如何完成视图的展示的

hash

通过location.hash = 'foo' 这样的语法来改变, 路径, 路径就会由baidu.com变成baidu.com/#foo通过window.addEventListener('hashchange')这个事件监听到hash值的变化

-history

通过window.history.pushState(data, title, targetURL)

  • @状态对象:传给目标路由的信息,可为空
  • @页面标题:目前所有浏览器都不支持,填空字符串即可
  • @可选url:目标url,不会检查url是否存在,且不能跨域。如不传该项,即给当前url添加data

通过history.pushState({}, '', 'foo'),可以让 baidu.com 变化为 baidu.com/foo

!坑

history路由的监听,浏览器虽然提供了window.addEventListener('popstate事件'),但是只能监听浏览器回退和前进产生的路由变化,对于主动的pushState却监听不到

基于history版本从零到1实现 react-mini-router

实现一个history

1、history.push2、history.listen

利用观察者模式封装简单的listen API。让用户监听到history.push 产生的路径变化

原生html实现一个mini-react-router
前言
实现一个简单版本的react-router, 揭秘路由的神秘面纱
思考
• 前端路由本质上是什么
• 前端路由里的一些坑和注意点
• hash路由和history路由的区别
• Router组件和Route组件分别做了什么
路由的本质
浏览器端的路由不是真实的网页跳转,和服务器没有任何交互,本质上就是对url进行监听,让某个dom节点显示对应的视图
路由的区别
路由的区别
一般来说,前端路由分为两种
1、hash 路由, 特征是url后面会有 # 号, 如 baidu.com/#foo/bar/baz
2、history 路由, url和普通路由没有差异。 如 baidu.com/foo/bar/baz
实际上只要搞清楚两种路由分别是如何改变,并且组件是如何完成视图的展示的
hash
通过location.hash = 'foo' 这样的语法来改变, 路径, 路径就会由baidu.com变成baidu.com/#foo
通过window.addEventListener('hashchange')这个事件监听到hash值的变化
- history
通过window.history.pushState(data, title, targetURL)
@状态对象:传给目标路由的信息,可为空@页面标题:目前所有浏览器都不支持,填空字符串即可@可选url:目标url,不会检查url是否存在,且不能跨域。如不传该项,即给当前url添加data
通过
history.pushState({}, '', 'foo'),可以让 baidu.com 变化为 baidu.com/foo
!坑
history路由的监听,浏览器虽然提供了window.addEventListener('popstate事件'),但是只能监听浏览器回退和前进产生的路由变化,对于主动的pushState却监听不到
基于history版本从零到1实现 react-mini-router
实现一个history
1、history.push
2、history.listen
利用观察者模式封装简单的listen API。让用户监听到history.push 产生的路径变化
作者:重阳微噪
链接:https://juejin.cn/post/6875262564209524749
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

简单实现

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Router History</title>
  </head>
  <body>
    <div id="app"><div>
  </body>
  <script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script>
    // 参考 https://github.com/sl1673495/react-mini-router
    let listeners = [];
    function listen(fn){
      listeners.push(fn)
    }
    function push(to, state) {
      // 解析用户传入的 url
      // 调用原生 history 的方法改变路由
      window.history.pushState(state, '', to);
      // 执行用户传入的监听函数
      listeners.forEach(fn => fn(location));
    }
    // 用于处理浏览器前进后退操作
    window.addEventListener('popstate', () => {
      listeners.forEach(fn => fn(location));
    });
    history.listen = listen
    history.push = push
    history.location = location
  </script>
  <script type="text/babel">
    class App extends React.Component{
      constructor(props){
        super(props)
        this.state={
          showFoo: false
        }
      }
      componentDidMount(){
        history.listen(location => {
          console.log(location,'location');
          const {pathname} = location;
          if(pathname == '/Router/foo'){
            this.setState({
              showFoo: true
            })
          }else{
            this.setState({
              showFoo: false
            })
          }
        });
      }
      changeRouter(to){
          history.push(to, {});
      }
      render(){
        const {showFoo} = this.state;
        return(
          <div>
             <button onClick={()=>{this.changeRouter('foo')}}>展示foo组件</button>
             <button onClick={()=>{this.changeRouter('index')}}>回到首页</button>
             {!showFoo && <div>首页</div>}
             {showFoo && <div>我是Foo</div>}
          </div>
        )
      }
    }
    ReactDOM.render(<App/>,document.getElementById('app'));
  </script>
</html>
实现 Router
Router的核心原理就是通过Provider把location和history等路由关键信息传递给子组件,并切在路由发生变化的时候让子组件可以感知
复制代码 const RouterContext = React.createContext(null)
 class Router extends React.Component{
    constructor(props){
      super(props)
      this.state={
        location: location
      }
    }
    componentDidMount(){
        history.listen(location => {
          this.setState({
            location
          })
      });
    }
    render(){
      const { location } = this.state
      return(
        <div>
          <RouterContext.Provider value={{ history, location }}>
            {this.props.children}
          </RouterContext.Provider>
        </div>
      )
    }
  }

实现 Route

Route 组件接受 pathchildren两个 prop ,本质上就决定了在某个路径下需要渲染什么组件,我们又可以通过 Router 的 Provider 传递下来的 location 信息拿到当前路径,所以这个组件需要做的就是判断当前的路径是否匹配,渲染对应组件

const Route = ({ path, children }) => {
   let {history,location} =  React.useContext(RouterContext);
   let { pathname } = location
   console.log(pathname,path,'iii');
   if(pathname === path){
     return children
   }
   return null
};

实现 Link

Link 组件接受to和name两个参数,通过 Router 的 Provider 传递下来的 history 拿到当前的push方法,点击的时候去触发

 const Link = ({name,to})=>{
    let {history} =  React.useContext(RouterContext);
    function changeRouter(to){
      history.push(to, {});
    }
    return (
      <div>
        <button onClick={()=>{changeRouter(to)}}>{name}</button>
      </div>
    )
  }

完整代码

 <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>React Router History</title>
</head>
<body>
    <div id="app"><div>
</body>
<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="./history.js"></script>
<script type="text/babel">
  const RouterContext = React.createContext(null)
  class Router extends React.Component{
    constructor(props){
      super(props)
      this.state={
        location: location
      }
    }
    componentDidMount(){
        history.listen(location => {
          this.setState({
            location
          })
      });
    }
    render(){
      const { location } = this.state
      return(
        <div>
          <RouterContext.Provider value={{ history, location }}>
            {this.props.children}
          </RouterContext.Provider>
        </div>
      )
    }
  }
  const Route = ({ path, children }) => {
       let {history,location} =  React.useContext(RouterContext);
       let { pathname } = location
       console.log(pathname,path,'iii');
       if(pathname === path){
         return children
       }
       return null
  };
  const Link = ({name,to})=>{
    let {history} =  React.useContext(RouterContext);
    function changeRouter(to){
      history.push(to, {});
    }
    return (
      <div>
        <button onClick={()=>{changeRouter(to)}}>{name}</button>
      </div>
    )
  }
  class App extends React.Component{
    constructor(props){
      super(props)
    }
    render(){
      return(
      <div> 
          <Router>
              <Link to="/foo" name="展示foo组件"/>
              <Link to="/index" name="回到首页"/>
              <Route path="/index">
                  <Index/>  
              </Route>
              <Route path="/foo">
                  <Foo/>
              </Route>
          </Router>
        </div>
      )
  }}
  class Foo extends React.Component{
    render(){
      return <div>我是foo</div>
    }
  }
  class Index extends React.Component{
    render(){
      return <div>我是index1</div>
    }
  }
  ReactDOM.render(<App/>,document.getElementById('app'));
</script>
</html>

参考

hooks+ts实现的版本

最后

大家可以关注我的公众号,回复vue源码,可以得到完整的代码,也可以不懂的地方在下面留言!

相关文章
|
2月前
|
XML 前端开发 JavaScript
前端开发进阶:从HTML到React.js
【10月更文挑战第9天】前端开发进阶:从HTML到React.js
|
3月前
|
JavaScript 前端开发
react字符串转为dom标签,类似于Vue中的v-html
本文介绍了在React中将字符串转换为DOM标签的方法,类似于Vue中的`v-html`指令,通过使用`dangerouslySetInnerHTML`属性实现。
101 0
react字符串转为dom标签,类似于Vue中的v-html
|
4月前
|
移动开发 前端开发 API
React路由和HTML5 History API有什么区别
【8月更文挑战第11天】React路由和HTML5 History API有什么区别
46 1
|
4月前
|
前端开发 JavaScript 开发者
React Server Component 使用问题之为什么选择使用 React 官方的 renderToString 来渲染 HTML,如何解决
React Server Component 使用问题之为什么选择使用 React 官方的 renderToString 来渲染 HTML,如何解决
|
7月前
|
前端开发 JavaScript 开发者
【专栏:HTML与CSS前端技术趋势篇】前端框架(React/Vue/Angular)与HTML/CSS的结合使用
【4月更文挑战第30天】前端框架React、Vue和Angular助力UI开发,通过组件化、状态管理和虚拟DOM提升效率。这些框架与HTML/CSS结合,使用模板语法、样式管理及组件化思想。未来趋势包括框架简化、Web组件标准采用和CSS在框架中角色的演变。开发者需紧跟技术发展,掌握新工具,提升开发效能。
108 11
|
7月前
|
前端开发 JavaScript 搜索推荐
react-app框架——使用monaco editor实现online编辑html代码编辑器
react-app框架——使用monaco editor实现online编辑html代码编辑器
306 3
|
7月前
|
JavaScript 前端开发 BI
原生html—摆脱ps、excel 在线绘制财务表格加水印(html绘制表格js加水印)
原生html—摆脱ps、excel 在线绘制财务表格加水印(html绘制表格js加水印)
110 1
|
7月前
|
前端开发 JavaScript
React中渲染html结构---dangerouslySetInnerHTML
React中渲染html结构---dangerouslySetInnerHTML
61 0
|
7月前
|
前端开发 JavaScript API
react搭建在线编辑html的站点——引入grapes实现在线拖拉拽编辑html
react搭建在线编辑html的站点——引入grapes实现在线拖拉拽编辑html
62 0
|
7月前
|
前端开发 JavaScript 安全
如何在React项目中动态插入HTML内容
如何在React项目中动态插入HTML内容
247 0