导航

[[深刻01] 执行上下文](https://juejin.im/post/684490...
[[深刻02] 原型链](https://juejin.im/post/684490...
[[深刻03] 继承](https://juejin.im/post/684490...
[[深刻04] 事件循环](https://juejin.im/post/684490...
[[深刻05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...
[[深刻06] 隐式转换 和 运算符](https://juejin.im/post/684490...
[[深刻07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...
[[深刻08] 前端平安](https://juejin.im/post/684490...
[[深刻09] 深浅拷贝](https://juejin.im/post/684490...
[[深刻10] Debounce Throttle](https://juejin.im/post/684490...
[[深刻11] 前端路由](https://juejin.im/post/684490...
[[深刻12] 前端模块化](https://juejin.im/post/684490...
[[深刻13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490...
[[深刻14] canvas](https://juejin.im/post/684490...
[[深刻15] webSocket](https://juejin.im/post/684490...
[[深刻16] webpack](https://juejin.im/post/684490...
[[深刻17] http 和 https](https://juejin.im/post/684490...
[[深刻18] CSS-interview](https://juejin.im/post/684490...
[[深刻19] 手写Promise](https://juejin.im/post/684490...
[[深刻20] 手写函数](https://juejin.im/post/684490...

[[react] Hooks](https://juejin.im/post/684490...

[[部署01] Nginx](https://juejin.im/post/684490...
[[部署02] Docker 部署vue我的项目](https://juejin.im/post/684490...
[[部署03] gitlab-CI](https://juejin.im/post/684490...

[[源码-webpack01-前置常识] AST形象语法树](https://juejin.im/post/684490...
[[源码-webpack02-前置常识] Tapable](https://juejin.im/post/684490...
[[源码-webpack03] 手写webpack - compiler简略编译流程](https://juejin.im/post/684490...
[[源码] Redux React-Redux01](https://juejin.im/post/684490...
[[源码] axios ](https://juejin.im/post/684490...
[[源码] vuex ](https://juejin.im/post/684490...
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...
[[源码-vue02] computed 响应式 - 初始化,拜访,更新过程 ](https://juejin.im/post/684490...

前置常识

URL 和 URI

  • <font color=red>URI:对立资源标识符 ( I => Identifier:标识符Id)</font>
    (Universal Resource Identifier
  • <font color=red>URL:对立资源定位符 ( L => Locator:定位器)</font>
    Uniform Resource Locator
  • 区别:

    • URL:强调的是地址,即( <font color=red>定位</font> )这个惟一的资源
    • URI:强调的是( <font color=red>标识</font> )资源,资源具备的( <font color=red>唯一性</font> )
    • 别离标识惟一资源和标识惟一地址很麻烦,所以用URL也充当RUI的角色,即标记惟一资源还标记该资源的惟一地址

URL的组成

http://www.baidu.com:80/stu/index.html?name=xxx&age=25#teacher

  • Protocol:协定 http://https://
  • Domain:域名 www.baicu.com
  • Port:端口 :80

    • http协定的默认地址是 :80
    • https自已的默认地址是:443
  • Path:文件门路, => /开始 ?之前的局部, 本例中是:/stu/index.html
  • Query:查问字符串 => ?结尾到结尾,或者?结尾到#之前,本例是:?name=xxx&age=25
  • Hash:哈希值 => #结尾到结尾,本例是:teacher
  • <font color=red>protocol,domain,port,path,query,hash</font>

DOMContentLoaded 事件,load事件

  • window.onload window.addEventListener('load', ....)
  • DOMContentLoaded
  • 区别:

    • <font color=red>DOMContentLoaded:DOM加载实现时触发</font>
    • <font color=red>load:须要DOM,款式,脚本,图片,视频等所有资源都加载实现时才会触发,即页面加载实现才触发</font>
    DOM残缺解析过程:
  • 解析html
  • 解析css - 包含当html中的款式和内部引入的款式
  • 解析并运行脚本 - 报错本html中的脚本和内部引入的脚本
  • DOM构建实现 ---------------------------------------------------- DOM加载实现,触发 DOMContentLoaded
  • 加载图片,视频等其余资源
  • 页面加载结束 --------------------------------------------------- 页面加载实现,触发 load


    // 2021/07/25更新 - 下面的过程不欠缺,这里再简略记录下
    // - 详见https://juejin.cn/post/698357...

  • parse-html
  • parse-styleSheet
  • evaluate-script
  • layout
  • paint
  • composite

字符串 slice 办法特例

  • slice可用于 ( 数组 ) 和 ( 字符串 )
  • 有返回值,不扭转原字符串
  • <font color=red>String.prototype.slice(开始地位,完结地位) 截取字符串,不包含完结地位</font>

    String.prototype.slice()特例:''.slice(1) ----------- 返回 '' 空字符串案例:window.location.hash // 因为:当地址栏的url中的hash不存在时,window.location.hash返回的是空字符串,这种状况如下// 所以:window.location.hash.slice(1) => 返回空字符串

window.location 对象

window.location 对象属性:pathname: 返回url的path局部,/开始 ?之前 或者 /开始到后果,如果没有query和hashorigin:protocal + hostname + port 三者之和,相当于协定,域名,端口protocal:协定 http:// https://hostnme: 主机名port:端口号host:主机 (hostname + port)search:查问字符串 (?结尾到#之前,或者?结尾到结尾)hash:片段字符串 (哈希值,#结尾到结尾)返回值:- window.location返回的是一个只读的Location对象,你依然能够赋给它一个 DOMString- 这意味着您能够在大多数状况下解决 location,就像它是一个字符串一样- window.location = 'http://www.example.com',是 window.location.href = 'http://www.example.com'的同义词问题:- window.location.assign() 和 window.location.href 的区别?- location.assign(url)是函数式的形式- window.location.href=url 比 location.assign(url) 更快

hash路由

  • url中的hash以#号结尾,本来用来作为锚点,从而定位到页面的特定区域
  • 当 hash 产生扭转时,页面不会刷新,浏览器也不会向服务器发送申请
  • 留神:<font color=red>hash扭转时,能够触发 hashchange 事件,在监听函数中能够申请数据,实现页面的更新操作</font>

作为锚点,定位页面特点区域

<a href="#anchor1">锚点1</a><a href="#anchor2">锚点2</a><div id="anchor1">锚点1的地位</div><div id="anchor2">锚点2的地位</div>阐明:- 点击a2,页面会跳转到div2的地位- 并且页面的hash局部也会扭转,即 url 中以 #结尾的字符串会扭转- anchor:是锚的意思- 留神:a标签的name属性曾经废除,用id代替 (因为有的教程应用name属性实现的)

hashchange事件

  • <font color=red>如果监听了hashchange事件,hash扭转,地址栏的url中的hash局部就会扭转,同时hashchange也会触发</font>
  • 然而页面不会刷新,即浏览器的刷新按钮的圈圈不会转动
  • 然而能够利用hashchange的回调函数更新页面的内容,留神不是页面刷新

    <body><a href="#anchor1">锚点1</a><a href="#anchor2">锚点2</a><script>  window.addEventListener('hashchange', function() {    console.log('111111111')  }, false)</script></body>
  • 点击a标签,url中的hash扭转,hash扭转,hashchange事件触发,则监听函数就会执行,输入111111

手动实现一个 hash-router

hash-router原理:(1) hash扭转,地址栏url的hash字符串扭转,触发hashchange事件(2) 在hashchange事件的回调函数中更新视图代码:<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <meta http-equiv="X-UA-Compatible" content="ie=edge">  <title>Document</title></head><body><a href="#/home">home</a><a href="#/other">other</a><div id="content">内容局部</div><script>  const routes = [{    path: '/home',    component: '<h1>home页面</h1>'  }, {    path: '/other',    component: '<h1>other页面</h1>'  }]  class Router {    constructor(routes) { // 构造函数      this.route = {} // 路由映射      this.createRouteMap(routes) // 创立路由映射      this.init() // 初始化    }    createRouteMap = (routes) => {      routes.forEach(item => {        this.route[item.path] = () => {          document.getElementById('content').innerHTML = item.component          // 函数体的作用:将id是content的div中的内容换成 componet        }        // 循环配置        // key是path        // value包装成一个更新html内容的函数,在 load 和 hahschange 中调用      })    }    init = () => {      window.addEventListener('load', this.updateView, false) // 页面加载实现时触发,留神辨别DOMContentLoaded      window.addEventListener('hashchange', this.updateView, false)    }    updateView = () => {      const hash = window.location.hash.slice(1) || '/home'; // 首次加载home页面      // load事件触发时,window.location.hash => 返回 '' 空字符串      // ''.slice(1) => 返回''      if (this.route[hash]) this.route[hash]()      // 存在,则执行函数    }  }  new Router(routes)</script></body></html>

2020/12/24温习 - hash路由

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Document</title></head><body>  <a href="#/home">home页面</a>  <a href="#/other">other页面</a>  <div id="content">内容局部,即路由替换的局部</div>  <div id="current-hash"></div>  <script>    const routes = [{      path: '/home',      component: 'home页面的内容'    }, {      path: '/other',      component: 'other页面的内容'    }]    class HashRouter {      constructor(routes) {        this.route = {} // 路由映射        this.createRouteMap(routes) // 创立路由映射,为 ( this.route ) 创立 ( map ) 映射;key=path;value=()=>{更新页面}        this.init() // 初始化      }      createRouteMap = (routes) => {        if (routes.length) {          routes.forEach(({ path, component }) => {            this.route[path] = () => {              document.getElementById('content').innerHTML = component // 替换内容            }          })        }      }      createRouteMap = (routes) => {        if (routes.length) {          routes.forEach(({ path, component }) => {            this.route[path] = () => {              document.getElementById('content').innerHTML = component // 替换内容            }          })        }      }      init = () => {        window.addEventListener('load', this.updateView, false)        window.addEventListener('hashchange', this.updateView, false)      }      // 更新视图      updateView = () => {        // (1)        // 这里 ( load事件 ) 和 ( hashchange事件 ) 都会触发 ( updateView办法 )        // (2)        // load事件: ( 页面加载实现时触发 ),包含 ( DOM,款式,图片,视频等所有资源都加载实现 )        // DOMContentLoaded事件: 是在 ( DOM加载实现时触发 )         // (3)        // 当load事件触发时,hash并没有扭转,即 window.location.hash = '' => ''.slice(1) => ''        const hash = this.getCurrentHash() // 获取hash        // if (Object.keys(this.route).includes(hash)) { // 还有更简略的办法        //   this.route[hash]()        // }        if (this.route[hash]) this.route[hash]() // 如果this.route对象中的key对应得值存在,就执行该函数      }      // 获取以后地址栏的 hash      getCurrentHash = () => {        const hash = window.location.hash.slice(1)        this.printHahToHtml(hash) // 该函数是用来在html中显示以后hash的        return hash ? hash : '/home'        // load事件触发时,hash就不存在,hash='',这种状况下即默认状况下返回 '/home' 路由        // load事件触发时,window.location.hash => 返回 '' 空字符串        // ''.slice(1) => 返回''      }      printHahToHtml = (hash) => {        const DOM = document.getElementById('current-hash')        DOM.innerHTML = `以后页面的hash是:=> #${hash}`        DOM.style.setProperty('background', 'yellow')        DOM.style.setProperty('padding', '10px')      }    }    new HashRouter(routes)  </script></body></html>

2021/07/25优化 - hash路由

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <title>Document</title>  </head>  <body>    <a href="#/home">home页面</a>    <a href="#/other">other页面</a>    <div id="content">内容局部,即路由替换的局部</div>    <div id="current-hash"></div>    <script>      const routes = [        {          path: "/home",          component: "home页面的内容",        },        {          path: "/other",          component: "other页面的内容",        },      ];      class HashRouter {        constructor(routes) {          this.route = {};          this.createRouteMap(routes);          this.init();        }        init = () => {          window.addEventListener("load", this.updateView, false);          window.addEventListener("hashchange", this.updateView, false);          this.DOM_CURRENT_HASH = document.getElementById("current-hash");          this.DOM_CONTENT = document.getElementById("content");        };        // createRouteMap = (routes) => {        //   if (routes.length) {        //     routes.forEach(({ path, component }) => {        //       this.route[path] = () => {        //         document.getElementById("content").innerHTML = component; // 替换内容        //       };        //     });        //   }        // };        createRouteMap = (routes = []) => {          routes.forEach(            ({ path, component }) =>              (this.route[path] = () =>                (this.DOM_CONTENT.innerHTML = component))          );        };        // updateView = () => {        //   const hash = this.getCurrentHash(); // 获取hash        //   if (this.route[hash]) this.route[hash](); // 如果this.route对象中的key对应得值存在,就执行该函数        // };        updateView = () => {          const path = this.getCurrentHash();          this.route?.[path]?.();        };        // getCurrentHash = () => {        //   const hash = window.location.hash.slice(1);        //   this.printHahToHtml(hash); // 该函数是用来在html中显示以后hash的        //   return hash ? hash : "/home";        // };        getCurrentHash = () => {          const path = window.location.hash.slice(1) || "/home";          this.printHahToHtml(path); // 该函数是用来在html中显示以后hash的          return path;        };        // printHahToHtml = (hash) => {        //   const DOM = document.getElementById("current-hash");        //   DOM.innerHTML = `以后页面的hash是:=> #${hash}`;        //   DOM.style.setProperty("background", "yellow");        //   DOM.style.setProperty("padding", "10px");        // };        printHahToHtml = (hash) => {          const DOM = this.DOM_CURRENT_HASH;          DOM.innerHTML = `以后页面的hash是:=> #${hash}`;          DOM.style.setProperty("padding", "10px");          DOM.style.setProperty("background", "yellow");        };      }      new HashRouter(routes);    </script>  </body></html>

history路由

window.history 对象

  • window.history对象的办法:back()forward()go()pushState()replaceState()
  • pushState()
  • replaceState()
  • <font color=red>pushState() 和 replaceState()</font>

    • <font color=red>不会触发页面刷新,只能导致History对象发生变化,地址栏的url会变动</font>
    • <font color=red>会扭转url,不会触发 popstate 事件,地址栏的url有所变动</font>

window.history.pushState(state, title, url)

  • window.history.pushState(state, title, url)
  • state:一个与增加的记录相关联的对象
  • title:新页面的题目,当初所有浏览器都疏忽该参数,能够传入空字符串
  • url:新的url地址,必须与以后页面同一个域,浏览器的地址栏显示这个网址
  • window.history.pushState({}, null, url)
  • 留神:pushState不会刷新页面,只会扭转History对象,地址栏url会变动

    • <font color=red>能够通过 History.state 读取状态对象</font>

popstate

  • <font color=red>popstate触发的条件</font>

    • <font color=red>浏览器的后退后退按钮</font>
    • <font color=red>history.go(), history.back(), history.forward()</font>
  • <font color=red>留神:window.history.pushState() 和 window.history.replaceState()不会触发 popstate 事件</font>
  • 留神:pushState()和replaceState()能够扭转url,且实现不向服务器发送申请,不存在#号,比hash路由更好看,然而 History 路由须要服务器的反对,并且需将所有的路由重定向到根页面

手动实现一个 history-router

  • 原理剖析

    • 第一步:给每个a标签都绑定一个click事件,click事件触发时,再去触发pushState()事件,将url的path局部扭转为a的data-href自定义属性的值即路由的path,传入window.history.pushState({}, null, path),这样地址栏的url就扭转了
    • 第二步:扭转地址栏的url后,通过window.location.pathname获取更新的url的path局部
    • 第三步:用 path 和 route对象中的key去匹配,匹配上就去执行更新视图的函数
    • 第四步:

      • 这只是一条线:1-3步即点击a标签的状况
      • 还有一条线:浏览器的后退后退,和函数式导航go() back() forward() 则有 popstate 事件来解决,过程差不多

        history-router原理:(1) 封装一个办法,在pushState()和replaceState()扭转url后调用,在该办法中获取最新的window.location.path,更置信页面(2) 通过 go() back() forward() 浏览器后退后退等触发 popstate 事件代码:<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>Document</title></head><body><a href="javascript:void(0)" data-href="/home">home</a> // html中的自定义属性<a href="javascript:void(0)" data-href="/other">other</a><div id="content">content的内容</div><script>const routes = [{path: '/home',component: '<h1>home页面</h1>'}, {path: '/other',component: '<h1>other页面</h1>'}]class Router {constructor(routes) {  this.route = {} // key-value键值对,key是path,value是更新视图的函数  this.createRouteMap(routes) // 创立路由映射  this.bindEvent() // 绑定a标签的点击事件  this.init() // 绑定load和popstate事件}createRouteMap = (routes) => {  routes.forEach(item => {    this.route[item.path] = () => {      document.getElementById('content').innerHTML = item.component    }  })}bindEvent = () => {  const a = document.getElementsByTagName('a')  Array.prototype.forEach.call(a, item => { // 第二个参数,是forEach须要传入的回调函数    item.addEventListener('click', () => {      const path = item.getAttribute('data-href') // 获取data-herf属性      this.pushStateFn(path)       // 执行History.pushState()办法      // 这里因为是箭头函数,this指向父级所在的上下文环境,即 bindEvent 所在的上下文环境,即Router     }, false)  })}pushStateFn = (url) => {  window.history.pushState({}, null, url) // 扭转url后,调用更新视图的函数updateView  this.updateView() // 更新视图}init = () => {  window.addEventListener('load', this.updateView, false) // 页面加载实现时触发  window.addEventListener('popstate', this.updateView, false) // 浏览器后退后退,History.go() back() forward()时触发}updateView = () => {  const path = window.location.pathname || '/'; // 获取url的path局部  if(this.route[path]) this.route[path]() // path在route中存在,就执行对象的函数,key-value键值对}}new Router(routes)</script></body></html>留神:该html须要用 Live Server 启动,vscode插件,来提供server

2020/12/24温习 - history路由

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Document</title></head><body>  <a href="javascript:void(0)" data-href="/home">点击去home页面</a>  <a href="javascript:void(0)" data-href="/other">点击去other页面</a>  <div id="content">内容局部,即路由要替换的内容</div>  <script>    const routes = [{      path: '/home',      component: '<h1>home页面</h1>'    }, {      path: '/other',      component: '<h1>other页面</h1>'    }]    class HistoryRouter {      constructor(routes) {        this.route = {} // 路由映射 key=path value=()=>{更新视图}        this.createRouteMap(routes) // 创立路由映射        this.bindEvent() // 绑定事件        this.init() // 初始化      }      createRouteMap = (routes) => {        if (routes.length) {          routes.forEach(({ path, component }) => {            this.route[path] = () => {              document.getElementById('content').innerHTML = component            }          })        }      }      bindEvent = () => {        const a = document.getElementsByTagName('a')        Array.prototype.forEach.call(a, aDom => {          aDom.addEventListener('click', () => {            const path = aDom.getAttribute('data-href')            this.triggerPushState(path) // 触发pushState事件          }, false)        })      }      triggerPushState = (path) => {        window.history.pushState({}, null, path)        // pushState() 能够扭转地址栏的url,然而不会触发页面更新,所以要执行上面的更新函数        // (1) 状况1:这只是 ( 点击a标签 ) 的状况,应用的是 pushState() 函数        // (2) 状况2:还有就是 ( 点击浏览器的后退后退按钮 ) 和 ( 函数式k导航 window.history.go() back() forward() 的状况 )        // (3) 状况3:就是初始化时,在 ( load ) 事件触发是的状况,默认path='/'        this.updateView()      }      updateView = () => {        // 因为:在执行该办法之前,曾经触发了 pushState() || popstate事件 || load事件        // 所以:能够用window.location.pathname 获取最新的 url中的 path 局部        const currentPath = window.location.pathname          ? window.location.pathname          : '/'        if (this.route[currentPath]) this.route[currentPath]()      }      init = () => {        window.addEventListener('load', this.updateView, false) // 页面加载实现时的状况        window.addEventListener('popstate', this.updateView, false) // popstate触发的状况,浏览器后退后退和函数式导航      }    }    new HistoryRouter(routes)  </script></body></html>留神:该html须要用 Live Server 启动,vscode插件,来提供server

2021/07/25优化 - history路由

<!DOCTYPE html><html lang="en">  <head>    <meta charset="UTF-8" />    <meta http-equiv="X-UA-Compatible" content="IE=edge" />    <meta name="viewport" content="width=device-width, initial-scale=1.0" />    <title>Document</title>  </head>  <body>    <a href="javascript:void(0)" data-href="/home">home</a>    <a href="javascript:void(0)" data-href="/other">other</a>    <div class="root">content</div>    <script>      // history路由      // 原理:      //  - 1. window.history.pushState(stateObj, title, url) 和 window.history.replaceState(stateObj, title, url) 不会触发popstate,不会刷新浏览器,然而会扭转history对象,并且url地址栏会扭转      //  - 2. popstate事件,触发条件      //       - window.history.go()/back()/forward()      //       - 浏览器的后退,后退按钮      // history路由实现      // - 1. 给每个a标签增加 ( data-href自定义属性 ),并且给每个a标签 ( 绑定click事件 ),点击后获取data-href,而后触发 ( window.history.pushState({}, null, data-href))      // - 2. 通过1后,触发了window.history.pushState()后,地址栏的url会扭转,而后通过 ( window.location.pathname ) 获取扭转后的 path      // - 3. 而后通过path去匹配routes数组中的path,匹配上就去更新 ui      const routes = [        {          path: "/home",          component: "<h1>home页面</h1>",        },        {          path: "/other",          component: "<h1>other页面</h1>",        },      ];      class HistoryRouter {        constructor(routes) {          this.route = {};          this.init();          this.createRouteMap(routes); // ( path ) 和 ( ui更新函数 ) 之间的 ( 映射 )          this.bindEvent(); // a标签绑定click事件        }        createRouteMap = (routes = []) => {          routes.forEach(            ({ path, component }) =>              (this.route[path] = () =>                (this.DOM_CONTENT.innerHTML = component))          );        };        init = () => {          window.addEventListener("load", this.updateView, false);          window.addEventListener("popstate", this.updateView, false);          this.DOM_CONTENT = document.getElementsByClassName("root")[0];        };        bindEvent = () => {          const aList = document.getElementsByTagName("a");          [...aList].forEach((a) => {            const path = a.getAttribute("data-href");            a.addEventListener(              "click",              (e) => {                window.history.pushState({}, null, path); // window.history.pushState(state, title, url),会扭转地址栏url                this.updateView(); // 更新视图              },              false            );          });        };        updateView = () => {          const path = window.location.pathname; // 获取最新path          this.route?.[path]?.();        };      }      new HistoryRouter(routes);    </script>  </body></html>留神:该html须要用 Live Server 启动,vscode插件,来提供server

手动实现一个vue-router(hash版)

  • 原理

    • 大体的原理和hash路由的html版本一样,渺小区别
    • 都是利用 ( a标签 ) 的 ( href ) 中的 ( '#/xxx' ) 这样的hash串
    • 只有点击 a标签 => 地址栏的url的hash局部就会扭转 => 同时触发 hashchange 事件 => 通过window.location.hash获取最新的hash
    • 获取到 hash 在和 routeMap 中的path匹配,匹配后就扭转视图
  • 区别

    • 区别就是 Vue 本人封装了 <router-link><router-view> 组件
    • 能够通过 Vue.component() 办法注册下面两个组件
    • 通过 vm.name 能够拜访到 new Vue({data: {name: xx}})中的name
    手动实现一个vue-router(hash版)
  • <router-link to="#/home">home</router-link> // 点击会跳转到 '#/home' 地址
  • <router-view></router-view> // 路由将显示的DOM地位

    // 定义一个名为 button-counter 的新组件
    Vue.component('button-counter', {
    data: function () {
    return {

    count: 0

    }
    },
    template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
    })

  • 因为组件是可复用的 Vue 实例,所以它们与 new Vue 接管雷同的选项
  • 例如 data、computed、watch、methods 以及生命周期钩子等。


    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <!-- 引入 Vue 通过CDN引入 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
    <div id="app">
    <!-- 留神 router-link组件具备 to 属性 -->
    <router-link to="#/home">home</router-link>
    <router-link to="#/other">other</router-link>
    <router-view></router-view>
    </div>

    <script>
    // 创立两个vue组件
    const Home = {template: '<h1>home页面</h1>'}
    const Other = {template: '<h1>other页面</h1>'}

    // 创立vue路由数组
    const routes = [{

    path: '/home',component: Home

    }, {

    path: '/other',component: Other

    }]

    class VueRouter {

    constructor(Vue, option) {  // 参数:  // Vue:Vue构造函数,通过cdn引入的  // option: 配置对象,蕴含routes路由数组属性  this.$options = option  this.routeMap = {} // 路由映射,是这样的构造 { path: component }  this.createRouteMap(this.$options) // 创立路由映射  this.app = new Vue({    data: {      currentHash: '#/'    }  })  // this.app.currentHash => 能够拜访到currentHash的值 '#/'  // 举例  // var data = {name: 'woow_wu7'}  // var vm = new Vue({  //  data: data  // })  // vm.name === data.name => true  this.init() // 初始化监听函数  this.initComponent(Vue) // 初始化Vue种的各种组件}createRouteMap = (option) => {  // 留神:option 是传入VueRoute的第二个参数,即 {routes: routes}  // 所以:options是一个对象  option.routes.forEach(item => {    this.routeMap[item.path] = item.component    // this.routeMap是这样一个对象:{path: component}  })}init = () => {  window.addEventListener('load', this.onHashChange, false)  // 页面加载实现触发,留神区别 DOMContentLoaded  // load:页面加载实现时触发,包含 DOM加载实现,图片,视频等所有资源加载实现  // DOMContentLoaded:DOM加载实现时触发  window.addEventListener('hashchange', this.onHashChange, false)  // 监听 hashchange 事件  // 触发hashchange的条件:hash扭转时候}onHashChange = () => {  this.app.currentHash = window.location.hash.slice(1) || '/'  // (1)  // 当 hahs没有扭转时,load事件触发时  // window.location.hash = '' =>  window.location.hash.slice(1) = ''  // 所以:此种状况:this.app.currentHash =  '/'  // (2)  // hash扭转时,window.location.hash有值,是 '#/...' 这样的字符串}initComponent = (Vue) => {  // router-link组件  // props to属性  // template 实质上会被解决成a标签,href属性是传入的 to 属性,内容是 slot 插入的内容  Vue.component('router-link', {    props: {      to: {        type: String,        value: ''      }    },    template: '<a :href="to"><slot/></a>'  })  Vue.component('router-view', {    render: (h) => {      const component = this.routeMap[this.app.currentHash] // 拿到最新hash对应的组件      return h(component)      // h(component) 相当于 createElement(component)      // render: function(createElement) { return createElement(App); }    }  })}

    }
    new VueRouter(Vue, {

    routes

    })
    new Vue({

    el: '#app'

    })
    </script>
    </body>
    </html>

材料

URI和URL:https://www.luyuqiang.com/uri...
URI和URL的区别举例(很形象)https://juejin.im/post/684490...
URL的组成(优良)https://www.jianshu.com/p/406...
DOMContentLoaded和load的区别:https://www.jianshu.com/p/1a8...
window.location对象:https://wangdoc.com/javascrip...
vue-router模仿实现 https://juejin.im/post/684490...
hash history 路由 模仿实现 https://juejin.im/post/684490...
vue-router源码记录 https://juejin.im/post/684490...
VueRouter源码剖析 https://juejin.im/post/684490...