浅析vue-router原理

近期被问到一个问题,在你们项目中使用的是Vue的SPA(单页面)还是Vue的多页面设计?

这篇文章主要围绕Vue的SPA单页面设计展开。 关于如何展开Vue多页面设计请点击查看。

vue-router是什么?

首先我们需要知道vue-router是什么,它是干什么的?

这里指的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。 换句话说,vue-router就是WebApp的链接路径管理系统。

vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。

那与传统的页面跳转有什么区别呢?

1.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。

2.传统的页面应用,是用一些超链接来实现页面切换和跳转的。

在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。

至于为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,所以你写的标签是不起作用的,必须使用vue-router来进行管理。

vue-router实现原理

SPA(single page application):单一页面应用程序,有且只有一个完整的页面;当它在加载页面的时候,不会加载整个页面的内容,而只更新某个指定的容器中内容。

单页面应用(SPA)的核心之一是:

1.更新视图而不重新请求页面;

2.vue-router在实现单页面前端路由时,提供了三种方式:Hash模式、History模式、abstract模式,根据mode参数来决定采用哪一种方式。

路由模式

vue-router 提供了三种运行模式:

● hash: 使用 URL hash 值来作路由。默认模式。

● history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。

● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。

Hash模式

vue-router 默认模式是 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会去重新加载。

hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分(/#/..),浏览器只会加载相应位置的内容,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置; 所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据 。

History模式

HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;

由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: 'history'",这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

//main.js文件中
const router = new VueRouter({
 mode: 'history',
 routes: [...]
})

当使用 history 模式时,URL 就像正常的 url,例如 yoursite.com/user/id,比较好… 不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

export const routes = [
 {path: "/", name: "homeLink", component:Home}
 {path: "/register", name: "registerLink", component: Register},
 {path: "/login", name: "loginLink", component: Login},
 {path: "*", redirect: "/"}] 

此处就设置如果URL输入错误或者是URL 匹配不到任何静态资源,就自动跳到到Home页面。

abstract模式

abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。

根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (当然,你也可以明确指定在所有情况下都使用 abstract 模式)

vue-router使用方式

1:下载npm i vue-router -S

**2:在main.js中引入 ** import VueRouter from 'vue-router';

3:安装插件Vue.use(VueRouter);

4:创建路由对象并配置路由规则

let router = new VueRouter({routes:[{path:'/home',component:Home}]});

5:将其路由对象传递给Vue的实例,options中加入 router:router

6:在app.vue中留坑

<router-view></router-view>

具体实现请看如下代码:

//main.js文件中引入
import Vue from 'vue';
import VueRouter from 'vue-router';
//主体
import App from './components/app.vue';
import index from './components/index.vue'
//安装插件
Vue.use(VueRouter); //挂载属性
//创建路由对象并配置路由规则
let router = new VueRouter({
  routes: [
    //一个个对象
    { path: '/index', component: index }
  ]
});
//new Vue 启动
new Vue({
  el: '#app',
  //让vue知道我们的路由规则
  router: router, //可以简写router
  render: c => c(App),
})

最后记得在在app.vue中“留坑”

//app.vue中
<template>
  <div>
    <!-- 留坑,非常重要 -->
    <router-view></router-view>
  </div>
</template>
<script>
  export default {
    data(){
      return {}
    }
  }
</script>

vue-router源码分析

我们先来看看vue的实现路径。

在入口文件中需要实例化一个 VueRouter 的实例对象 ,然后将其传入 Vue 实例的 options 中。

export default class VueRouter {
 static install: () => void;
 static version: string;
 app: any;
 apps: Array<any>;
 ready: boolean;
 readyCbs: Array<Function>;
 options: RouterOptions;
 mode: string;
 history: HashHistory | HTML5History | AbstractHistory;
 matcher: Matcher;
 fallback: boolean;
 beforeHooks: Array<?NavigationGuard>;
 resolveHooks: Array<?NavigationGuard>;
 afterHooks: Array<?AfterNavigationHook>;
 constructor (options: RouterOptions = {}) {
  this.app = null
  this.apps = []
  this.options = options
  this.beforeHooks = []
  this.resolveHooks = []
  this.afterHooks = []
  // 创建 matcher 匹配函数
  this.matcher = createMatcher(options.routes || [], this)
  // 根据 mode 实例化具体的 History,默认为'hash'模式
  let mode = options.mode || 'hash'
  // 通过 supportsPushState 判断浏览器是否支持'history'模式
  // 如果设置的是'history'但是如果浏览器不支持的话,'history'模式会退回到'hash'模式
  // fallback 是当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。
  this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
  if (this.fallback) {
   mode = 'hash'
  }
  // 不在浏览器内部的话,就会变成'abstract'模式
  if (!inBrowser) {
   mode = 'abstract'
  }
  this.mode = mode
   // 根据不同模式选择实例化对应的 History 类
  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, options.base)
    break
   default:
    if (process.env.NODE_ENV !== 'production') {
     assert(false, `invalid mode: ${mode}`)
    }
  }
 }
 match (
  raw: RawLocation,
  current?: Route,
  redirectedFrom?: Location
 ): Route {
  return this.matcher.match(raw, current, redirectedFrom)
 }
 get currentRoute (): ?Route {
  return this.history && this.history.current
 }
 init (app: any /* Vue component instance */) {
  process.env.NODE_ENV !== 'production' && assert(
   install.installed,
   `not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
   `before creating root instance.`
  )
  this.apps.push(app)
  // main app already initialized.
  if (this.app) {
   return
  }
  this.app = app
  const history = this.history
  // 根据history的类别执行相应的初始化操作和监听
  if (history instanceof HTML5History) {
   history.transitionTo(history.getCurrentLocation())
  } else if (history instanceof HashHistory) {
   const setupHashListener = () => {
    history.setupListeners()
   }
   history.transitionTo(
    history.getCurrentLocation(),
    setupHashListener,
    setupHashListener
   )
  }
  history.listen(route => {
   this.apps.forEach((app) => {
    app._route = route
   })
  })
 }
 // 路由跳转之前
 beforeEach (fn: Function): Function {
  return registerHook(this.beforeHooks, fn)
 }
 // 路由导航被确认之间前
 beforeResolve (fn: Function): Function {
  return registerHook(this.resolveHooks, fn)
 }
 // 路由跳转之后
 afterEach (fn: Function): Function {
  return registerHook(this.afterHooks, fn)
 }
 // 第一次路由跳转完成时被调用的回调函数
 onReady (cb: Function, errorCb?: Function) {
  this.history.onReady(cb, errorCb)
 }
 // 路由报错
 onError (errorCb: Function) {
  this.history.onError(errorCb)
 }
 // 路由添加,这个方法会向history栈添加一个记录,点击后退会返回到上一个页面。
 push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.history.push(location, onComplete, onAbort)
 }
 // 这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。
 replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.history.replace(location, onComplete, onAbort)
 }
 // 相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)。n可为正数可为负数。正数返回上一个页面
 go (n: number) {
  this.history.go(n)
 }
 // 后退到上一个页面
 back () {
  this.go(-1)
 }
 // 前进到下一个页面
 forward () {
  this.go(1)
 }
 getMatchedComponents (to?: RawLocation | Route): Array<any> {
  const route: any = to
   ? to.matched
    ? to
    : this.resolve(to).route
   : this.currentRoute
  if (!route) {
   return []
  }
  return [].concat.apply([], route.matched.map(m => {
   return Object.keys(m.components).map(key => {
    return m.components[key]
   })
  }))
 }
 resolve (
  to: RawLocation,
  current?: Route,
  append?: boolean
 ): {
  location: Location,
  route: Route,
  href: string,
  // for backwards compat
  normalizedTo: Location,
  resolved: Route
 } {
  const location = normalizeLocation(
   to,
   current || this.history.current,
   append,
   this
  )
  const route = this.match(location, current)
  const fullPath = route.redirectedFrom || route.fullPath
  const base = this.history.base
  const href = createHref(base, fullPath, this.mode)
  return {
   location,
   route,
   href,
   // for backwards compat
   normalizedTo: location,
   resolved: route
  }
 }
 addRoutes (routes: Array<RouteConfig>) {
  this.matcher.addRoutes(routes)
  if (this.history.current !== START) {
   this.history.transitionTo(this.history.getCurrentLocation())
  }
 }
}

HashHistory

• hash虽然出现在url中,但不会被包括在http请求中,它是用来指导浏览器动作的,对服务器端没影响,因此,改变hash不会重新加载页面。

• 可以为hash的改变添加监听事件:

window.addEventListener("hashchange",funcRef,false)

• 每一次改变hash(window.location.hash),都会在浏览器访问历史中增加一个记录。

export class HashHistory extends History {
 constructor (router: Router, base: ?string, fallback: boolean) {
  super(router, base)
  // check history fallback deeplinking
  // 如果是从history模式降级来的,需要做降级检查
  if (fallback && checkFallback(this.base)) {
  // 如果降级且做了降级处理,则返回
   return
  }
  ensureSlash()
 }
 .......

function checkFallback (base) {
 const location = getLocation(base)
 // 得到除去base的真正的 location 值
 if (!/^\/#/.test(location)) {
 // 如果此时地址不是以 /# 开头的
 // 需要做一次降级处理,降为 hash 模式下应有的 /# 开头
  window.location.replace(
   cleanPath(base + '/#' + location)
  )
  return true
 }
}
function ensureSlash (): boolean {
// 得到 hash 值
 const path = getHash()
 if (path.charAt(0) === '/') {
  // 如果是以 / 开头的,直接返回即可
  return true
 }
 // 不是的话,需要手动保证一次 替换 hash 值
 replaceHash('/' + path)
 return false
}
export function getHash (): string {
 // We can't use window.location.hash here because it's not
 // consistent across browsers - Firefox will pre-decode it!
 // 因为兼容性的问题,这里没有直接使用 window.location.hash
 // 因为 Firefox decode hash 值
 const href = window.location.href
 const index = href.indexOf('#')
 return index === -1 ? '' : decodeURI(href.slice(index + 1))
}
// 得到hash之前的url地址
function getUrl (path) {
 const href = window.location.href
 const i = href.indexOf('#')
 const base = i >= 0 ? href.slice(0, i) : href
 return `${base}#${path}`
}
// 添加一个hash
function pushHash (path) {
 if (supportsPushState) {
  pushState(getUrl(path))
 } else {
  window.location.hash = path
 }
}
// 替代hash
function replaceHash (path) {
 if (supportsPushState) {
  replaceState(getUrl(path))
 } else {
  window.location.replace(getUrl(path))
 }
}

hash的改变会自动添加到浏览器的访问历史记录中。 那么视图的更新是怎么实现的呢,看下 transitionTo()方法:

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const route = this.router.match(location, this.current) //找到匹配路由
  this.confirmTransition(route, () => { //确认是否转化
   this.updateRoute(route) //更新route
   onComplete && onComplete(route)
   this.ensureURL()

   // fire ready cbs once
   if (!this.ready) {
    this.ready = true
    this.readyCbs.forEach(cb => { cb(route) })
   }
  }, err => {
   if (onAbort) {
    onAbort(err)
   }
   if (err && !this.ready) {
    this.ready = true
    this.readyErrorCbs.forEach(cb => { cb(err) })
   }
  })
 }

//更新路由
updateRoute (route: Route) {
  const prev = this.current // 跳转前路由
  this.current = route // 装备跳转路由
  this.cb && this.cb(route) // 回调函数,这一步很重要,这个回调函数在index文件中注册,会更新被劫持的数据 _router
  this.router.afterHooks.forEach(hook => {
   hook && hook(route, prev)
  })
 }
}

pushState
export function pushState (url?: string, replace?: boolean) {
 saveScrollPosition()
 // try...catch the pushState call to get around Safari
 // DOM Exception 18 where it limits to 100 pushState calls
 // 加了 try...catch 是因为 Safari 有调用 pushState 100 次限制
 // 一旦达到就会抛出 DOM Exception 18 错误
 const history = window.history
 try {
  if (replace) {
  // replace 的话 key 还是当前的 key 没必要生成新的
   history.replaceState({ key: _key }, '', url)
  } else {
  // 重新生成 key
   _key = genKey()
    // 带入新的 key 值
   history.pushState({ key: _key }, '', url)
  }
 } catch (e) {
 // 达到限制了 则重新指定新的地址
  window.location[replace ? 'replace' : 'assign'](url)
 }
}

replaceState
// 直接调用 pushState 传入 replace 为 true
export function replaceState (url?: string) {
 pushState(url, true)
}

pushState和replaceState两种方法的共同特点:当调用他们修改浏览器历史栈后,虽然当前url改变了,但浏览器不会立即发送请求该url,这就为单页应用前端路由,更新视图但不重新请求页面提供了基础。

supportsPushState
export const supportsPushState = inBrowser && (function () {
 const ua = window.navigator.userAgent
 if (
  (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
  ua.indexOf('Mobile Safari') !== -1 &&
  ua.indexOf('Chrome') === -1 &&
  ua.indexOf('Windows Phone') === -1
 ) {
  return false
 }
 return window.history && 'pushState' in window.history
})()

其实所谓响应式属性,即当_route值改变时,会自动调用Vue实例的render()方法,更新视图。 $router.push()-->HashHistory.push()-->History.transitionTo()-->History.updateRoute()-->{app._route=route}-->vm.render()

监听地址栏

在浏览器中,用户可以直接在浏览器地址栏中输入改变路由,因此还需要监听浏览器地址栏中路由的变化 ,并具有与通过代码调用相同的响应行为,在HashHistory中这一功能通过setupListeners监听hashchange实现:

setupListeners () {
  window.addEventListener('hashchange', () => {
    if (!ensureSlash()) {
      return
    }
    this.transitionTo(getHash(), route => {
      replaceHash(route.fullPath)
    })
  })
}

HTML5History

History interface是浏览器历史记录栈提供的接口,通过back(),forward(),go()等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。

export class HTML5History extends History {
 constructor (router: Router, base: ?string) {
  super(router, base)
  const expectScroll = router.options.scrollBehavior //指回滚方式
  const supportsScroll = supportsPushState && expectScroll
  if (supportsScroll) {
   setupScroll()
  }
  const initLocation = getLocation(this.base)
  //监控popstate事件
  window.addEventListener('popstate', e => {
   const current = this.current
   // Avoiding first `popstate` event dispatched in some browsers but first
   // history route not updated since async guard at the same time.
   // 避免在某些浏览器中首次发出“popstate”事件
   // 由于同一时间异步监听,history路由没有同时更新。
   const location = getLocation(this.base)
   if (this.current === START && location === initLocation) {
    return
   }
   this.transitionTo(location, route => {
    if (supportsScroll) {
     handleScroll(router, route, current, true)
    }
   })
  })
 }

hash模式仅改变hash部分的内容,而hash部分是不会包含在http请求中的(hash带#):

oursite.com/#/user/id//如请求,只会发送http://oursite.com/

所以hash模式下遇到根据url请求页面不会有问题

而history模式则将url修改的就和正常请求后端的url一样(history不带#)

oursite.com/user/id

如果这种向后端发送请求的话,后端没有配置对应/user/id的get路由处理,会返回404错误。

官方推荐的解决办法是在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。同时这么做以后,服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面。或者,如果是用 Node.js 作后台,可以使用服务端的路由来匹配 URL,当没有匹配到路由的时候返回 404,从而实现 fallback。

两种模式比较

一般的需求场景中,hash模式与history模式是差不多的,根据MDN的介绍,调用history.pushState()相比于直接修改hash主要有以下优势:

• pushState设置的新url可以是与当前url同源的任意url,而hash只可修改#后面的部分,故只可设置与当前同文档的url

• pushState设置的新url可以与当前url一模一样,这样也会把记录添加到栈中,而hash设置的新值必须与原来不一样才会触发记录添加到栈中

• pushState通过stateObject可以添加任意类型的数据记录中,而hash只可添加短字符串 pushState可额外设置title属性供后续使用

AbstractHistory

'abstract'模式,不涉及和浏览器地址的相关记录,流程跟'HashHistory'是一样的,其原理是通过数组模拟浏览器历史记录栈的功能

//abstract.js实现,这里通过栈的数据结构来模拟路由路径
export class AbstractHistory extends History {
 index: number;
 stack: Array<Route>;

 constructor (router: Router, base: ?string) {
  super(router, base)
  this.stack = []
  this.index = -1
 }

 // 对于 go 的模拟
 go (n: number) {
  // 新的历史记录位置
  const targetIndex = this.index + n
  // 小于或大于超出则返回
  if (targetIndex < 0 || targetIndex >= this.stack.length) {
   return
  }
  // 取得新的 route 对象
  // 因为是和浏览器无关的 这里得到的一定是已经访问过的
  const route = this.stack[targetIndex]
  // 所以这里直接调用 confirmTransition 了
  // 而不是调用 transitionTo 还要走一遍 match 逻辑
  this.confirmTransition(route, () => {
   this.index = targetIndex
   this.updateRoute(route)
  })
 }

//确认是否转化路由
 confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
  const current = this.current
  const abort = err => {
   if (isError(err)) {
    if (this.errorCbs.length) {
     this.errorCbs.forEach(cb => { cb(err) })
    } else {
     warn(false, 'uncaught error during route navigation:')
     console.error(err)
    }
   }
   onAbort && onAbort(err)
  }
  //判断如果前后是同一个路由,不进行操作
  if (
   isSameRoute(route, current) &&
   route.matched.length === current.matched.length
  ) {
   this.ensureURL()
   return abort()
  }
  //下面是各类钩子函数的处理
  //*********************
  })
 }

看到这里你已经对vue-router的路由基本掌握的差不多了,要是喜欢看源码可以 点击查 看

要是喜欢可以给我一个star,github

总结

以上所述是小编给大家介绍的vue-router原理浅析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • vue-router相关基础知识及工作原理

     前言 今天面试被问到 vue的动态路由,我竟然没有回答上来,感觉不是什么难得问题.好久没有看vue-router的文档,很多用的东西和概念没有对上.回来一看什么是动态路由就傻眼了.看来有必要把vue -router相关知识总结一下,好丢人的感觉. 单页面应用的工作原理 我理解的单页面工作原理是通过浏览器URL的#后面的hash变化就会引起页面变化的特性来把页面分成不同的小模块,然后通过修改hash来让页面展示我们想让看到的内容. 那么为什么hash的不同,为什么会影响页面的展示呢?浏览器在这里

  • 浅析前端路由简介以及vue-router实现原理

    路由这个概念最先是后端出现的.在以前用模板引擎开发页面时,经常会看到这样 http://www.xxx.com/login 大致流程可以看成这样: 浏览器发出请求 服务器监听到80端口(或443)有请求过来,并解析url路径 根据服务器的路由配置,返回相应信息(可以是 html 字串,也可以是 json 数据,图片等) 浏览器根据数据包的 Content-Type 来决定如何解析数据 简单来说路由就是用来跟后端服务器进行交互的一种方式,通过不同的路径,来请求不同的资源,请求不同的页面是路由的其中

  • Vue Router history模式的配置方法及其原理

    vue-router分为 hash和 history模式,前者为其默认模式,url的表现形式为 http://yoursite.com#home,比较难看.后者的url表现形式为 http://yoursite.com/home,比较美观. 但如果要使用 history模式,我们需要在后端进行额外配置.本文将讨论如何配置以及为什么要这样配置. history模式的配置方法 我们来看看官方文档是教我们怎么配置的:HTML5 History 模式. 首先要将 mode设置为 history: con

  • Vue Router深扒实现原理

    目录 回顾Vue Router的核心代码 代码实现 创建Vue-Router插件 构造函数 完整代码 Vue Router官网 前置知识:插件.slot插槽.mixins混入.render函数.运行时和完整版的Vue 回顾Vue Router的核心代码 // 注册插件 // Vue.use() 内部调用传入对象的 install 方法 Vue.use(VueRouter) // 创建路由对象 const router = new VueRouter({ routes: [ { name: 'ho

  • 浅析Vue中Virtual DOM和Diff原理及实现

    目录 0. 写在开头 1. vdom 2. Diff 0. 写在开头 本文将秉承Talk is cheap, show me the code原则,做到文字最精简,一切交由代码说明! 1. vdom vdom即虚拟DOM,将DOM映射为JS对象,结合diff算法更新DOM 以下为DOM <div id="app"> <div class="home">home</div> </div> 映射成VDOM { tag: '

  • vue router嵌套路由在history模式下刷新无法渲染页面问题的解决方法

    解决vue-router嵌套路由(子路由)在history模式下刷新无法渲染页面的问题,具体内容如下 一. 异常描述 本来使用的是vue-router的hash模式,但是hash模式下url需要带"#"符号,不仅看起来不舒服,而且有些场景下是会破坏路由中的"#"(微信分享页面就会把"#"后边的内容处理掉),所以就需要使用history模式,然后就让后端改下nginx配置: location / { try_files $uri $uri/ /in

  • vue router 源码概览案例分析

    源码这个东西对于实际的工作其实没有立竿见影的效果,不会像那些针对性极强的文章一样看了之后就立马可以运用到实际项目中,产生什么样的效果,源码的作用是一个潜移默化的过程,它的理念.设计模式.代码结构等看了之后可能不会立即知识变现(或者说变现很少),而是在日后的工作过程中悄无声息地发挥出来,你甚至都感觉不到这个过程 另外,优秀的源码案例,例如 vue . react 这种,内容量比较庞大,根本不是三篇五篇十篇八篇文章就能说完的,而且写起来也很难写得清楚,也挺浪费时间的,而如果只是分析其中一个点,例如

  • Vue Router的手写实现方法实现

    为什么需要前端路由 在前后端分离的现在,大部分应用的展示方式都变成了 SPA(单页面应用 Single Page Application)的模式.为什么会选择 SPA 呢?原因在于: 用户的所有操作都在同一个页面下进行,不进行页面的跳转.用户体验好. 对比多页面,单页面不需要多次向服务器请求加载页面(只请求一次.html文件),只需要向服务器请求数据(多亏了 ajax).因此,浏览器不需要渲染整个页面.用户体验好. 归根结底,还是因为 SPA 能够提供更好的用户体验. 为了更好地实现 SPA,前

  • Vue Router 实现动态路由和常见问题及解决方法

    个人理解:动态路由不同于常见的静态路由,可以根据不同的「因素」而改变站点路由列表.常见的动态路由大都是用来实现:多用户权限系统不同用户展示不同导航菜单. 如何利用Vue Router 实现动态路由 Vue项目实现动态路由的方式大体可分为两种: 前端将全部路由规定好,登录时根据用户角色权限来动态展示路由: 路由存储在数据库中,前端通过接口获取当前用户对应路由列表并进行渲染: 第一种方式在很多Vue UI Admin上都实现了,可以去读一下他们的源码理解具体的实现思路,这里就不过多展开.第二种方式现

  • Vue Router中应用中间件的方法

    中间件是我们在软件开发中的一个古老而强大的概念,当我们在应用程序中使用路由相关模式时,它非常有用. 如果您不太了解中间件的含义,Nodejs框架Express里的中间件可以帮助您了解它们的工作原理. 但是,中间件仅适用于后端吗? 不,当应用程序中有路由时,中间件在前端或后端中就会非常常见.比如现在流行的单页应用程序. 有一些示例可以说明,何时可以使用中间件: 不允许未登录用户访问您的网页. 仅允许某些类型的用户查看页面(角色:管理员,作者等) 数据采集. 重置设置或清理存储空间. 限制访问用户的

  • 如何手写简易的 Vue Router

    前言 还是那样,懂得如何使用一个常用库,还得了解其原理或者怎么模拟实现,今天实现一下 vue-router . 有一些知识我这篇文章提到了,这里就不详细一步步写,请看我 手写一个简易的 Vuex 基本骨架 Vue 里面使用插件的方式是 Vue.use(plugin) ,这里贴出它的用法: 安装 Vue.js 插件.如果插件是一个对象,必须提供 install 方法.如果插件是一个函数,它会被作为 install 方法.install 方法调用时,会将 Vue 作为参数传入.这个方法的第一个参数是

  • 浅析VUE防抖与节流

    防抖和节流到底是啥 函数防抖(debounce) 解释:当持续触发某事件时,一定时间间隔内没有再触发事件时,事件处理函数才会执行一次,如果设定的时间间隔到来之前,又一次触发了事件,就重新开始延时. 案例:持续触发scroll事件时,并不立即执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发一次handle函数. function debounce(fn, wait) { let timeout = null return function() { if(timeout

随机推荐