精华内容
下载资源
问答
  • 目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改部分 DOM,来减少原先因为多页应用页面跳转带来巨量性能损耗。它们都有自己典型路由解决方案,@...

    4de0bc624e930baddec93c1ce07932fd.gif  猛戳关注 前端发动机,在前端的路上一起成长

    c68986f1ec5e4fb6c9178e40b8798efd.png

    目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改的部分 DOM,来减少原先因为多页应用的页面跳转带来的巨量性能损耗。它们都有自己的典型路由解决方案,@angular/router、react-router、vue-router。

    一般来说,这些路由插件总是提供两种不同方式的路由方式: Hash 和 History,有时也会提供非浏览器环境下的路由方式 Abstract,在 vue-router 中是使用了外观模式将几种不同的路由方式提供了一个一致的高层接口,让我们可以更解耦的在不同路由方式中切换。

    值得一提的是,Hash 和 History 除了外观上的不同之外,还一个区别是:Hash 方式的状态保存需要另行传递,而 HTML5 History 原生提供了自定义状态传递的能力,我们可以直接利用其来传递信息。

    下面我们具体看看这两种方式都有哪些特点,并提供简单的实现,更复杂的功能比如懒加载、动态路径匹配、嵌套路由、路由别名等等,可以关注一下后面的 vue-router 源码解读方面的博客。

    1. Hash

    1.1 相关 Api

    Hash 方法是在路由中带有一个 #,主要原理是通过监听 # 后的 URL 路径标识符的更改而触发的浏览器 hashchange 事件,然后通过获取 location.hash 得到当前的路径标识符,再进行一些路由跳转的操作,参见 MDN

    1. location.href:返回完整的 URL

    2. location.hash:返回 URL 的锚部分

    3. location.pathname:返回 URL 路径名

    4. hashchange 事件:当 location.hash 发生改变时,将触发这个事件

    比如访问一个路径 http://sherlocked93.club/base/#/page1,那么上面几个值分别为:

    # http://sherlocked93.club/base/#/page1

    {

     "href": "http://sherlocked93.club/base/#/page1",

     "pathname": "/base/",

     "hash": "#/page1"

    }

    注意: 因为 Hash 方法是利用了相当于页面锚点的功能,所以与原来的通过锚点定位来进行页面滚动定位的方式冲突,导致定位到错误的路由路径,所以需要采用别的办法,之前在写 progress-catalog 这个插件碰到了这个情况。

    1.2 实例

    这里简单做一个实现,原理是把目标路由和对应的回调记录下来,点击跳转触发 hashchange 的时候获取当前路径并执行对应回调,效果:

    9735d185565189038ceaef22851dcdc7.gif

    class RouterClass {

     constructor() {

       this.routes = {} // 记录路径标识符对应的cb

       this.currentUrl = '' // 记录hash只为方便执行cb

       window.addEventListener('load', () => this.render())

       window.addEventListener('hashchange', () => this.render())

     }

     /* 初始化 */

     static init() {

       window.Router = new RouterClass()

     }

     /* 注册路由和回调 */

     route(path, cb) {

       this.routes[path] = cb || function() {}

     }

     /* 记录当前hash,执行cb */

     render() {

       this.currentUrl = location.hash.slice(1) || '/'

       this.routes[this.currentUrl]()

     }

    }

    具体实现参照 CodePen

    如果希望使用脚本来控制 Hash 路由的后退,可以将经历的路由记录下来,路由后退跳转的实现是对 location.hash 进行赋值。但是这样会引发重新引发 hashchange 事件,第二次进入 render 。所以我们需要增加一个标志位,来标明进入 render 方法是因为回退进入的还是用户跳转

    e82c228171ba6be48d35b4adca277c4b.gif

    class RouterClass {

     constructor() {

       this.isBack = false

       this.routes = {} // 记录路径标识符对应的cb

       this.currentUrl = '' // 记录hash只为方便执行cb

       this.historyStack = [] // hash栈

       window.addEventListener('load', () => this.render())

       window.addEventListener('hashchange', () => this.render())

     }

     /* 初始化 */

     static init() {

       window.Router = new RouterClass()

     }

     /* 记录path对应cb */

     route(path, cb) {

       this.routes[path] = cb || function() {}

     }

     /* 入栈当前hash,执行cb */

     render() {

       if (this.isBack) { // 如果是由backoff进入,则置false之后return

         this.isBack = false // 其他操作在backoff方法中已经做了

         return

       }

       this.currentUrl = location.hash.slice(1) || '/'

       this.historyStack.push(this.currentUrl)

       this.routes[this.currentUrl]()

     }

     /* 路由后退 */

     back() {

       this.isBack = true

       this.historyStack.pop() // 移除当前hash,回退到上一个

       const { length } = this.historyStack

       if (!length) return

       let prev = this.historyStack[length - 1] // 拿到要回退到的目标hash

       location.hash = `#${ prev }`

       this.currentUrl = prev

       this.routes[prev]() // 执行对应cb

     }

    }

    代码实现参考 CodePen

    2. HTML5 History Api

    2.1 相关 Api

    HTML5 提供了一些路由操作的 Api,关于使用可以参看 这篇 MDN 上的文章,这里就列举一下常用 Api 和他们的作用,具体参数什么的就不介绍了,MDN 上都有

    1. history.go(n):路由跳转,比如n为 2 是往前移动2个页面,n为 -2 是向后移动2个页面,n为0是刷新页面

    2. history.back():路由后退,相当于 history.go(-1)

    3. history.forward():路由前进,相当于 history.go(1)

    4. history.pushState():添加一条路由历史记录,如果设置跨域网址则报错

    5. history.replaceState():替换当前页在路由历史记录的信息

    6. popstate 事件:当活动的历史记录发生变化,就会触发 popstate 事件,在点击浏览器的前进后退按钮或者调用上面前三个方法的时候也会触发,参见 MDN

    2.2 实例

    将之前的例子改造一下,在需要路由跳转的地方使用 history.pushState 来入栈并记录 cb,前进后退的时候监听 popstate 事件拿到之前传给 pushState 的参数并执行对应 cb,因为借用了浏览器自己的 Api,因此代码看起来整洁不少

    d4973c49110138b7ceb0543ff2aae64a.gif

    class RouterClass {

     constructor(path) {

       this.routes = {} // 记录路径标识符对应的cb

       history.replaceState({ path }, null, path)    // 进入状态

       this.routes[path] && this.routes[path]()

       window.addEventListener('popstate', e => {

         const path = e.state && e.state.path

         this.routes[path] && this.routes[path]()

       })

     }

     /* 初始化 */

     static init() {

       window.Router = new RouterClass(location.pathname)

     }

     /* 注册路由和回调 */

     route(path, cb) {

       this.routes[path] = cb || function() {}

     }

     /* 跳转路由,并触发路由对应回调 */

     go(path) {

       history.pushState({ path }, null, path)

       this.routes[path] && this.routes[path]()

     }

    }

    Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,比如跳转 index.html 什么的,一般来说是你的 app 依赖的页面,事实上 vue-router 等库也是这么推介的,还提供了常见的服务器配置。

    代码实现参考 CodePen


    网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

    参考:

    1. history \| MDN

    2. hashchange \| MDN

    3. Manipulating the browser history \| MDN

    4. 前端路由的基本原理 - 大史不说话

    5. History 对象 -- JavaScript 标准参考教程

    41cf6192402dd195a58e9350d4d86446.gif

    展开全文
  • 目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改部分 DOM,来减少原先因为多页应用页面跳转带来巨量性能损耗。它们都有自己典型路由解决方案,@...

    fb1b3e9fb6687a0e5c7d7f0d36835355.gif  猛戳关注 前端发动机,在前端的路上一起成长

    68f1e82010f5e9b1ea818d212624b30e.png

    目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改的部分 DOM,来减少原先因为多页应用的页面跳转带来的巨量性能损耗。它们都有自己的典型路由解决方案,@angular/router、react-router、vue-router。

    一般来说,这些路由插件总是提供两种不同方式的路由方式: Hash 和 History,有时也会提供非浏览器环境下的路由方式 Abstract,在 vue-router 中是使用了外观模式将几种不同的路由方式提供了一个一致的高层接口,让我们可以更解耦的在不同路由方式中切换。

    值得一提的是,Hash 和 History 除了外观上的不同之外,还一个区别是:Hash 方式的状态保存需要另行传递,而 HTML5 History 原生提供了自定义状态传递的能力,我们可以直接利用其来传递信息。

    下面我们具体看看这两种方式都有哪些特点,并提供简单的实现,更复杂的功能比如懒加载、动态路径匹配、嵌套路由、路由别名等等,可以关注一下后面的 vue-router 源码解读方面的博客。

    1. Hash

    1.1 相关 Api

    Hash 方法是在路由中带有一个 #,主要原理是通过监听 # 后的 URL 路径标识符的更改而触发的浏览器 hashchange 事件,然后通过获取 location.hash 得到当前的路径标识符,再进行一些路由跳转的操作,参见 MDN

    1. location.href:返回完整的 URL

    2. location.hash:返回 URL 的锚部分

    3. location.pathname:返回 URL 路径名

    4. hashchange 事件:当 location.hash 发生改变时,将触发这个事件

    比如访问一个路径 http://sherlocked93.club/base/#/page1,那么上面几个值分别为:

    # http://sherlocked93.club/base/#/page1

    {

     "href": "http://sherlocked93.club/base/#/page1",

     "pathname": "/base/",

     "hash": "#/page1"

    }

    注意: 因为 Hash 方法是利用了相当于页面锚点的功能,所以与原来的通过锚点定位来进行页面滚动定位的方式冲突,导致定位到错误的路由路径,所以需要采用别的办法,之前在写 progress-catalog 这个插件碰到了这个情况。

    1.2 实例

    这里简单做一个实现,原理是把目标路由和对应的回调记录下来,点击跳转触发 hashchange 的时候获取当前路径并执行对应回调,效果:

    0f4d142dfcbf731bc4735fcb82c0eb4c.gif

    class RouterClass {

     constructor() {

       this.routes = {} // 记录路径标识符对应的cb

       this.currentUrl = '' // 记录hash只为方便执行cb

       window.addEventListener('load', () => this.render())

       window.addEventListener('hashchange', () => this.render())

     }

     /* 初始化 */

     static init() {

       window.Router = new RouterClass()

     }

     /* 注册路由和回调 */

     route(path, cb) {

       this.routes[path] = cb || function() {}

     }

     /* 记录当前hash,执行cb */

     render() {

       this.currentUrl = location.hash.slice(1) || '/'

       this.routes[this.currentUrl]()

     }

    }

    具体实现参照 CodePen

    如果希望使用脚本来控制 Hash 路由的后退,可以将经历的路由记录下来,路由后退跳转的实现是对 location.hash 进行赋值。但是这样会引发重新引发 hashchange 事件,第二次进入 render 。所以我们需要增加一个标志位,来标明进入 render 方法是因为回退进入的还是用户跳转

    b368256338e0eae22cea1eaca5732aab.gif

    class RouterClass {

     constructor() {

       this.isBack = false

       this.routes = {} // 记录路径标识符对应的cb

       this.currentUrl = '' // 记录hash只为方便执行cb

       this.historyStack = [] // hash栈

       window.addEventListener('load', () => this.render())

       window.addEventListener('hashchange', () => this.render())

     }

     /* 初始化 */

     static init() {

       window.Router = new RouterClass()

     }

     /* 记录path对应cb */

     route(path, cb) {

       this.routes[path] = cb || function() {}

     }

     /* 入栈当前hash,执行cb */

     render() {

       if (this.isBack) { // 如果是由backoff进入,则置false之后return

         this.isBack = false // 其他操作在backoff方法中已经做了

         return

       }

       this.currentUrl = location.hash.slice(1) || '/'

       this.historyStack.push(this.currentUrl)

       this.routes[this.currentUrl]()

     }

     /* 路由后退 */

     back() {

       this.isBack = true

       this.historyStack.pop() // 移除当前hash,回退到上一个

       const { length } = this.historyStack

       if (!length) return

       let prev = this.historyStack[length - 1] // 拿到要回退到的目标hash

       location.hash = `#${ prev }`

       this.currentUrl = prev

       this.routes[prev]() // 执行对应cb

     }

    }

    代码实现参考 CodePen

    2. HTML5 History Api

    2.1 相关 Api

    HTML5 提供了一些路由操作的 Api,关于使用可以参看 这篇 MDN 上的文章,这里就列举一下常用 Api 和他们的作用,具体参数什么的就不介绍了,MDN 上都有

    1. history.go(n):路由跳转,比如n为 2 是往前移动2个页面,n为 -2 是向后移动2个页面,n为0是刷新页面

    2. history.back():路由后退,相当于 history.go(-1)

    3. history.forward():路由前进,相当于 history.go(1)

    4. history.pushState():添加一条路由历史记录,如果设置跨域网址则报错

    5. history.replaceState():替换当前页在路由历史记录的信息

    6. popstate 事件:当活动的历史记录发生变化,就会触发 popstate 事件,在点击浏览器的前进后退按钮或者调用上面前三个方法的时候也会触发,参见 MDN

    2.2 实例

    将之前的例子改造一下,在需要路由跳转的地方使用 history.pushState 来入栈并记录 cb,前进后退的时候监听 popstate 事件拿到之前传给 pushState 的参数并执行对应 cb,因为借用了浏览器自己的 Api,因此代码看起来整洁不少

    e47920527a990fbc08656c4febbda4d8.gif

    class RouterClass {

     constructor(path) {

       this.routes = {} // 记录路径标识符对应的cb

       history.replaceState({ path }, null, path)    // 进入状态

       this.routes[path] && this.routes[path]()

       window.addEventListener('popstate', e => {

         const path = e.state && e.state.path

         this.routes[path] && this.routes[path]()

       })

     }

     /* 初始化 */

     static init() {

       window.Router = new RouterClass(location.pathname)

     }

     /* 注册路由和回调 */

     route(path, cb) {

       this.routes[path] = cb || function() {}

     }

     /* 跳转路由,并触发路由对应回调 */

     go(path) {

       history.pushState({ path }, null, path)

       this.routes[path] && this.routes[path]()

     }

    }

    Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,比如跳转 index.html 什么的,一般来说是你的 app 依赖的页面,事实上 vue-router 等库也是这么推介的,还提供了常见的服务器配置。

    代码实现参考 CodePen


    网上的帖子大多深浅不一,甚至有些前后矛盾,在下的文章都是学习过程中的总结,如果发现错误,欢迎留言指出~

    参考:

    1. history \| MDN

    2. hashchange \| MDN

    3. Manipulating the browser history \| MDN

    4. 前端路由的基本原理 - 大史不说话

    5. History 对象 -- JavaScript 标准参考教程

    cf7c220b5a78517df5662afbed0a397e.gif

    展开全文
  • 路由的原理是通过改变网址,来实现页面局部刷新,相比a标签跳转不同之处在于,路由跳转不需要刷新整个页面。 一、路由跳转 1、安装路由vue-router: npm install vue-router   2、vue项目...

    转载:https://blog.csdn.net/Mr_JavaScript/article/details/80744268

    路由的原理是通过改变网址,来实现页面的局部刷新,相比a标签跳转的不同之处在于,路由跳转不需要刷新整个页面。

    一、路由跳转

    1、安装路由vue-router:

    npm install vue-router

     

    2、vue项目引入vue-ruoter:

    3、配置路由(页面跳转):

    可以建一个专门用于路由的js文件,里面配置路径。

    1)router.js路由配置文件

    2)main.js里引入router.js路由文件

    4、组件里调用

    1)使用router-view标签给vue组件的跳转提供一个容器

    2)使用router-link标签实现跳转(它类似于a标签,区别在于router-link跳转不需要刷新页面)

    方法一:to里填写的是跳转的路径,即定义路由时的path路径

    方法二:to里使用路由名称name跳转

    跳转至table组件:

    3、实现效果

    项目的首页:

    点击table后跳转:

    (完成)

     

    二、路由嵌套

    1、配置嵌套路由

    1. {
    2. path:'/about', //一级路由
    3. component:About,
    4. children:[
    5. { //二级路由
    6. path:'/', //二级的默认路由(此path指向上一级,即path:'/about'
    7. name:'expressLink',
    8. component:Express
    9. },
    10. {
    11. path:'/about/guide',
    12. name:'guideLink',
    13. component:Guide
    14. },
    15. {
    16. path:'/about/contact',
    17. name:'contactLink',
    18. component:Contact,
    19. children:[
    20. { //三级路由
    21. path:'/about/contact/personName',
    22. name:'personNameLink',
    23. component:PersonName,
    24. },
    25. {
    26. path:'/about/contact/phone',
    27. name:'phoneLink',
    28. component:Phone
    29. },
    30. ]
    31. }
    32. ]
    33. },

    2、组件中嵌套使用<router-view></router-view>

    1)一级路由

    2)二级路由

    3)三级路由

    3、实现效果

    (完)

    				<script>
    					(function(){
    						function setArticleH(btnReadmore,posi){
    							var winH = $(window).height();
    							var articleBox = $("div.article_content");
    							var artH = articleBox.height();
    							if(artH > winH*posi){
    								articleBox.css({
    									'height':winH*posi+'px',
    									'overflow':'hidden'
    								})
    								btnReadmore.click(function(){
    									articleBox.removeAttr("style");
    									$(this).parent().remove();
    								})
    							}else{
    								btnReadmore.parent().remove();
    							}
    						}
    						var btnReadmore = $("#btn-readmore");
    						if(btnReadmore.length>0){
    							if(currentUserName){
    								setArticleH(btnReadmore,3);
    							}else{
    								setArticleH(btnReadmore,1.2);
    							}
    						}
    					})()
    				</script>
    				</article>
    
    展开全文
  • 前端路由是直接找到与地址匹配一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求有两种方式:1. 在地址中加入#以欺骗浏览器,地址改变是由于正在进行页内导航2. 使用H5window.history功能,...

    前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求有两种方式:

    1. 在地址中加入#以欺骗浏览器,地址的改变是由于正在进行页内导航

    2. 使用H5的window.history功能,使用URL的Hash来模拟一个完整的URL。

    当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

    目录结构

    先来看看整体的目录结构

    和流程相关的主要需要关注点的就是 components、history 目录以及 create-matcher.js、create-route-map.js、index.js、install.js。下面就从 basic 应用入口开始来分析 vue-router 的整个流程。

    import Vue from 'vue'

    import VueRouter from 'vue-router'

    // 1. 插件

    // 安装 and 组件

    // 且给当前应用下所有的组件都注入 $router and $route 对象

    Vue.use(VueRouter)

    // 2. 定义各个路由下使用的组件,简称路由组件

    const Home = { template: '

    home
    ' }

    const Foo = { template: '

    foo
    ' }

    const Bar = { template: '

    bar
    ' }

    // 3. 创建 VueRouter 实例 router

    const router = new VueRouter({

    mode: 'history',

    base: __dirname,

    routes: [

    { path: '/', component: Home },

    { path: '/foo', component: Foo },

    { path: '/bar', component: Bar }

    ]

    })

    // 4. 创建 启动应用

    // 一定要确认注入了 router

    // 在 中将会渲染路由组件

    new Vue({

    router,

    template: `

    Basic

    • /
    • /foo
    • /bar

    /bar

    `

    }).$mount('#app')123456789101112131415161718192021222324252627282930313233343536373839404142

    作为插件

    上边代码中关键的第 1 步,利用 Vue.js 提供的插件机制 .use(plugin) 来安装 VueRouter,而这个插件机制则会调用该 plugin 对象的 install 方法(当然如果该 plugin 没有该方法的话会把 plugin 自身作为函数来调用);下边来看下 vue-router 这个插件具体的实现部分。

    VueRouter 对象是在 src/index.js 中暴露出来的,这个对象有一个静态的 install 方法:

    /* @flow */

    // 导入 install 模块

    import { install } from './install'// ...import { inBrowser, supportsHistory } from './util/dom'// ...export default class VueRouter {

    // ...}

    // 赋值 install

    VueRouter.install = install

    // 自动使用插件if (inBrowser && window.Vue) {

    window.Vue.use(VueRouter)

    }123456789101112131415161718

    可以看到这是一个 Vue.js 插件的经典写法,给插件对象增加 install 方法用来安装插件具体逻辑,同时在最后判断下如果是在浏览器环境且存在 window.Vue 的话就会自动使用插件。

    install 在这里是一个单独的模块,继续来看同级下的 src/install.js 的主要逻辑:

    // router-view router-link 组件import View from './components/view'import Link from './components/link'// export 一个 Vue 引用export let _Vue// 安装函数export function install (Vue) {

    if (install.installed) return

    install.installed = true

    // 赋值私有 Vue 引用

    _Vue = Vue // 注入 $router $route

    Object.defineProperty(Vue.prototype, '$router', {

    get () { return this.$root._router }

    }) Object.defineProperty(Vue.prototype, '$route', {

    get () { return this.$root._route }

    }) // beforeCreate mixin

    Vue.mixin({

    beforeCreate () { // 判断是否有 router

    if (this.$options.router) { // 赋值 _router

    this._router = this.$options.router // 初始化 init

    this._router.init(this) // 定义响应式的 _route 对象

    Vue.util.defineReactive(this, '_route', this._router.history.current)

    }

    }

    }) // 注册组件

    Vue.component('router-view', View)

    Vue.component('router-link', Link)// ...}12345678910111213141516171819202122232425262728293031323334353637383940414243

    这里就会有一些疑问了?

    · 为啥要 export 一个 Vue 引用?

    插件在打包的时候是肯定不希望把 vue 作为一个依赖包打进去的,但是呢又希望使用 Vue 对象本身的一些方法,此时就可以采用上边类似的做法,在 install 的时候把这个变量赋值 Vue ,这样就可以在其他地方使用 Vue 的一些方法而不必引入 vue 依赖包(前提是保证 install 后才会使用)。

    · 通过给 Vue.prototype 定义 $router、$route 属性就可以把他们注入到所有组件中吗?

    在 Vue.js 中所有的组件都是被扩展的 Vue 实例,也就意味着所有的组件都可以访问到这个实例原型上定义的属性。

    beforeCreate mixin 这个在后边创建 Vue 实例的时候再细说。

    实例化 VueRouter

    在入口文件中,首先要实例化一个 VueRouter ,然后将其传入 Vue 实例的 options 中。现在继续来看在 src/index.js 中暴露出来的 VueRouter 类:

    // ...import { createMatcher } from './create-matcher'// ...export default class VueRouter {

    // ...

    constructor (options: RouterOptions = {}) {

    this.app = null

    this.options = options

    this.beforeHooks = []

    this.afterHooks = []

    // 创建 match 匹配函数

    this.match = createMatcher(options.routes || [])

    // 根据 mode 实例化具体的 History

    let mode = options.mode || 'hash'

    this.fallback = mode === 'history' && !supportsHistory if (this.fallback) {

    mode = 'hash'

    } if (!inBrowser) {

    mode = 'abstract'

    }

    this.mode = mode switch (mode) {

    case 'history':

    this.history = new HTML5History(this, options.base) break

    case 'hash':

    this.history = new HashHistory(this, options.base, this.fallback) break

    case 'abstract':

    this.history = new AbstractHistory(this) break

    default:

    assert(false, `invalid mode: ${mode}`)

    }

    }

    // ...}123456789101112131415161718192021222324252627282930313233343536373839

    里边包含了重要的一步:创建 match 匹配函数。

    match 匹配函数

    匹配函数是由 src/create-matcher.js 中的 createMatcher 创建的:

    /* @flow */

    import Regexp from 'path-to-regexp'// ...import { createRouteMap } from './create-route-map'// ...export function createMatcher (routes: Array): Matcher {

    // 创建路由 map

    const { pathMap, nameMap } = createRouteMap(routes)

    // 匹配函数 function match (

    raw: RawLocation,

    currentRoute?: Route,

    redirectedFrom?: Location

    ): Route {

    // ...

    } function redirect (

    record: RouteRecord,

    location: Location

    ): Route {

    // ...

    } function alias (

    record: RouteRecord,

    location: Location,

    matchAs: string

    ): Route {

    // ...

    } function _createRoute (

    record: ?RouteRecord,

    location: Location,

    redirectedFrom?: Location

    ): Route { if (record && record.redirect) { return redirect(record, redirectedFrom || location)

    } if (record && record.matchAs) { return alias(record, location, record.matchAs)

    } return createRoute(record, location, redirectedFrom)

    }

    // 返回 return match

    }

    // ...123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051

    具体逻辑后续再具体分析,现在只需要理解为根据传入的 routes 配置生成对应的路由 map,然后直接返回了 match 匹配函数。

    继续来看 src/create-route-map.js 中的 createRouteMap 函数:

    /* @flow */import { assert, warn } from './util/warn'import { cleanPath } from './util/path'// 创建路由 mapexport function createRouteMap (routes: Array): {

    pathMap: Dictionary,

    nameMap: Dictionary

    } { // path 路由 map

    const pathMap: Dictionary = Object.create(null) // name 路由 map

    const nameMap: Dictionary = Object.create(null) // 遍历路由配置对象 增加 路由记录

    routes.forEach(route => {

    addRouteRecord(pathMap, nameMap, route)

    }) return {

    pathMap,

    nameMap

    }

    }// 增加 路由记录 函数function addRouteRecord (

    pathMap: Dictionary,

    nameMap: Dictionary,

    route: RouteConfig,

    parent?: RouteRecord,

    matchAs?: string

    ) {

    // 获取 path 、name

    const { path, name } = route

    assert(path != null, `"path" is required in a route configuration.`) // 路由记录 对象

    const record: RouteRecord = {

    path: normalizePath(path, parent),

    components: route.components || { default: route.component },

    instances: {},

    name, parent,

    matchAs,

    redirect: route.redirect,

    beforeEnter: route.beforeEnter,

    meta: route.meta || {}

    } // 嵌套子路由 则递归增加 记录

    if (route.children) {// ...

    route.children.forEach(child => {

    addRouteRecord(pathMap, nameMap, child, record)

    })

    } // 处理别名 alias 逻辑 增加对应的 记录

    if (route.alias !== undefined) { if (Array.isArray(route.alias)) {

    route.alias.forEach(alias => {

    addRouteRecord(pathMap, nameMap, { path: alias }, parent, record.path)

    })

    } else {

    addRouteRecord(pathMap, nameMap, { path: route.alias }, parent, record.path)

    }

    } // 更新 path map

    pathMap[record.path] = record // 更新 name map

    if (name) { if (!nameMap[name]) {

    nameMap[name] = record

    } else {

    warn(false, `Duplicate named routes definition: { name: "${name}", path: "${record.path}" }`)

    }

    }

    }function normalizePath (path: string, parent?: RouteRecord): string {

    path = path.replace(/\/$/, '') if (path[0] === '/') return path if (parent == null) return path return cleanPath(`${parent.path}/${path}`)

    }1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283

    可以看出主要做的事情就是根据用户路由配置对象生成普通的根据 path 来对应的路由记录以及根据 name 来对应的路由记录的 map,方便后续匹配对应。

    实例化 History

    这也是很重要的一步,所有的 History 类都是在 src/history/ 目录下,现在呢不需要关心具体的每种 History 的具体实现上差异,只需要知道他们都是继承自 src/history/base.js 中的 History 类的:

    /* @flow */// ...import { inBrowser } from '../util/dom'import { runQueue } from '../util/async'import { START, isSameRoute } from '../util/route'// 这里从之前分析过的 install.js 中 export _Vueimport { _Vue } from '../install'export class History {// ...

    constructor (router: VueRouter, base: ?string) { this.router = router this.base = normalizeBase(base) // start with a route object that stands for "nowhere"

    this.current = START this.pending = null

    }// ...}// 得到 base 值function normalizeBase (base: ?string): string { if (!base) { if (inBrowser) { // respect tag

    const baseEl = document.querySelector('base') base = baseEl ? baseEl.getAttribute('href') : '/'

    } else { base = '/'

    }

    } // make sure there's the starting slash

    if (base.charAt(0) !== '/') { base = '/' + base

    展开全文
  • vue实现路由跳转和嵌套(快速入门)

    万次阅读 多人点赞 2018-06-20 12:39:26
    vue-router路由的原理是通过改变网址,来实现页面局部刷新,相比a标签跳转不同之处在于,路由跳转不需要刷新整个页面。 一、路由跳转 1、安装路由vue-router: npm install vue-router 2、vue项目引入vue...
  • Vue路由跳转原理(哈希模式)

    千次阅读 2019-10-12 05:56:55
    概述 哈希模式(利用`hashchange` 事件监听 urlhash 改变...核心是锚点值改变,我们监听到锚点值改变了就去局部改变页面数据,不做跳转。跟传统开发模式url改变后 立刻发起请求,响应整个页面,渲染整个页面...
  • vue-router路由的原理是通过改变网址,来实现页面局部刷新,相比a标签跳转不同之处在于,路由跳转不需要刷新整个页面。一、路由跳转1、安装路由vue-router:npm install vue-router2、vue项目引入vue-ruoter:3、...
  • 单页面更是把这种方式用到了极致,不仅仅是在页面交互是无刷新,连页面跳转都是无刷新,本文就从源码来分析下 Vue路由vue-router实现原理。 1.导入插件 import Router from 'vue-router' export d...
  • 来自公众号:前端下午茶链接:目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改的部分 DOM,...一般来说,这些路由插件总是提供两种不同方式的路由方式:Has...
  • 前后端分离带来问题基于前端分离带来问题在路由级,模块之间切换、跳转需要前端进行独立维护在接口级,前后端数据交互由接口进行连接(异步)这是重点:前端需要根据用户登录态或角色身份进行权限控制拦截,...
  • vue-router 跳转原理

    2019-07-18 15:07:00
    vue-router路由的原理是通过改变网址,来实现页面局部刷新,相比a标签跳转不同之处在于,路由跳转不需要刷新整个页面。 大概流程可以看成:  1. 浏览器发出请求  2.服务器监听到端口有请求过来,并解析url路径 ...
  • Vue学习笔记十一:路由的跳转原理(哈希原理) Vue版本:2.5.21 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-...
  • 路由的跳转原理(哈希模式) 单页应用的路由模式有两种 哈希模式(利用hashchange 事件监听 url的hash 的改变) history模式(使用此模式需要后台配合把接口都打到我们打包后的index.html上) 哈希模式...
  • 浅析Web开发中前端路由实现几种方式 故事从名叫Oliver绿箭虾`说起,这位大虾酷爱社交网站,一天他打开了Twitter,从发过tweets选项卡一路切到followers选项卡,Oliver发现页面内容变化了,URL也变化了,...
  • 目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改的部分 DOM,来减少原先因为多页...一般来说,这些路由插件总是提供两种不同方式的路由方式:Hash 和 Histo...
  • 1.在vue项目中说起路由跳转,我们最先想到就是router-link标签以及this.$router.push函数。 router-link和this.\$router.push实现原理是一样,在点击router-link时,内部调用就是this.$router.push。 2.this...
  • hash和history区别 vue路由跳转

    千次阅读 2019-03-20 10:32:56
    1. 原理:监听onhashchange事件 2. 当url发生变化时,浏览器会记录下来,因此前进后退按钮都可以使用。需要注意是hash模式下修改是#后面内容,使用urlhash来模拟一个完整url,所以当url改变时,页面不会...
  • vue-router路由实现原理

    2020-12-03 23:53:50
    通常我们使用Vue来开发单页面应用(SPA)时,通常都会使用vue-router来实现页面路由跳转。单页面应用采用前端路由系统,通过改变URL,在不重新请求页面情况下,更新页面视图。 Vue-router提供了以下几种路由跳转...
  • 1.$router和$route区别router为VueRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子...route相当于当前正在跳转的路由对象,可以从里面获取name,path,params,query等。2.vue路由实现原理通过改变 UR...
  • 目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改部分 DOM,来减少原先因为多页应用...
  • 问题出现原因:重复路由跳转,比如说当前路由是商品详情页面/goodsDetail,但是点击按钮进行this.$router.push操作,要跳转还是商品详情页面/goodsDetail。 二、问题解决 方式一:升级vue-router版本为3.0即可...
  • 前端路由是直接找到与地址匹配一个组件或对象并将其渲染出来。改变浏览器地址而不向服务器发出请求有两种方式: 1. 在地址中加入#以欺骗浏览器,地址改变是由于正在进行页内导航 2. 使用H5window.history功能...
  • 今天碰到一个问题 vue路由跳转到新页面时会直接显示页面最底部 正常情况下是显示最顶部 而且好多路由中不是全部都是这种情况 折腾好长时间也没解决 最后在网上找到了解决办法 其实原理很简单 就是在页面加载...
  • 转前端路由跳转基本原理 目前前端三杰 Angular、React、Vue 都推介单页面应用 SPA 开发模式,在路由切换时替换 DOM Tree 中最小修改部分 DOM,来减少原先因为多页应用页面跳转带来巨量性能损耗。它们都有自己...
  • 默认模式,通过路径中hash值来控制路由跳转,不存在兼容问题 hash模式实现原理 在正常路径后跟一个 # 号,匹配 # 后边路径为前端路由,通过window.onhashchange方法来操控路由改变时候切换内容 2.history模式 ...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 196
精华内容 78
关键字:

vue的路由跳转原理

vue 订阅