惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
博客园 - Franky
博客园 - 三生石上(FineUI控件)
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Apple Machine Learning Research
Apple Machine Learning Research
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
爱范儿
爱范儿
罗磊的独立博客
博客园_首页
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
量子位
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
V
Visual Studio Blog
T
Tailwind CSS Blog

Aimee's Blog

秒杀怎么防机器人:验证码之外的几道防线 防薅羊毛怎么做:号码风控、设备指纹与分层策略 接口限流怎么选:固定窗口、滑动窗口、令牌桶 批量打包下载怎么设计:流式 ZIP 与异步任务 图片处理怎么做:异步处理与 CDN 实时参数 大文件上传怎么设计:分片、断点续传、秒传 报表可重跑的设计:快照、水位线与幂等 UV 统计为什么有误差:精确计数与近似计数怎么选 大数据量导出怎么做:流式写入与异步任务 排行榜的两类需求,难度差了一个数量级 内容审核怎么选:先审后发,还是先发后审 实时热榜怎么设计:Redis ZSet 与热度分值 互关状态怎么保持同步:双向关系的维护与缓存失效 并发点赞的计数设计:原子操作与最终对账 删了父评论,子评论怎么处理——先想清楚用户看到的是什么 定时消息任务触发了两次 消息量大,写扩散还是读扩散——接到这个需求先问清楚规模 同一条消息推送了三次 活动结束了,用户还在收短信 接到注销需求,先问两个问题 微服务与服务拆分:何时拆、怎么拆 异步与事件驱动架构:把协作从「打电话」改成「发消息」 高可用设计:怎么让系统尽量不宕机 可扩展性设计:怎么让系统加机器就能扛更多 缓存架构:多级缓存怎么搭 高并发三板斧:限流、熔断、降级 架构设计到底在设计什么 —— 从单体到微服务的演进 服务成本账:一个服务一个月烧多少钱 可观测性:线上出问题怎么查 API 设计:好接口长什么样
vue2.0 router及简易实现
Aimee · 2021-12-28 · via Aimee's Blog

分类

hash

  • URL中 # 后面的内容作为路径地址
  • hash的模式是基于锚点,以及onhashchange 事件
  • 根据当前路由地址找到对应组件重新渲染

history

  • history 模式是基于html5中的history api
  • history.pushState() ie10 以后才支持
  • history.replaceState()
  • 通过history.pushState()方法改变地址栏
  • 监听popstate 事件
  • 根据当前路由地址找到对应组件重新渲染

服务端配置history

express配置

const path = require('path')
const history = require('connect-history-api-fallback')
const express = require('express')

const app = express()
// 注册处理history 模式的中间件
app.use(history())
// 处理金泰资源的中间件,网站根目录 ../web
app.use(express.static(path.join(__dirname, '../web')))

app.listen(3000, ()=>{
 console.log('服务器开启,端口:3000')
})

nginx 配置

server{
 ...
 location / {
 	 root html;
   index index.html index.html;
   try_files $uri $uri/ index.html;
 }
 ...
}

history 实现

简易版说明

  • 只支持routes 参数
  • 只支持router-link 和router-view 组件
  • 不支持push replace 等功能

实现

  • 写一个VueRouter class类
  • 提供静态 install 方法
    • 接受一个Vue参数
    • 判断当前 router是否已经被安装
    • 把Vue的构造函数记录在全局
    • 把创建Vue的实例传入的router对象 注入到Vue实例中
      • 使用mixin的方式注入
      • 在beforeCreate 钩子中注入
  • 提供构造函数constructor 处理参数和初始化
    • 初始化参数options
    • 将routes存放在routeMap中
    • 记录当前的current, 并设置为响应式
      • 通过Vue.observable来实现
    • 初始化组件信息
  • 处理组件 router-link 和router-view 注册为全局组件
    • router-link
      • 处理router-link 组件 接受一个参数to
      • router-link即为a标签,to即为href的参数
      • 拦截a标签的onClick事件,将history中pushState 记录跳转记录
      • 设置当前的current为to的值
    • router-view
      • 处理router-view 组件
      • router-view组件,用于展示当前路由的对应的component内容
      • 获取当前的current,通过routerMap获取对应的component
      • render 函数中return component vnode
      • 由于current 是响应式的,router-view中的render 会实时 获取最新的component来渲染
  • 处理监听事件
    • 由于router-link中 点击的时候,在history中添加了state,浏览器会根据hsitory的state自动处理前进和后退箭头
    • 箭头浏览器的前进后退事件及popstate,重新设置对应的current
    • 触发了router-view中component的渲染
let _Vue = null;
export default class VueRouter {
  static install(Vue) {
    //1 判断当前插件是否被安装
    if (VueRouter.install.installed) {
      return
    }

    VueRouter.install.installed = true
    //2 把Vue的构造函数记录在全局
    _Vue = Vue
    //3 把创建Vue的实例传入的router对象注入到Vue实例
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router
        }
      },
    })

  }
  constructor(options) {
    // 初始化参数
    this.options = options;
    // 将routes 存放在routeMap中
    this.routeMap = {}
    // 记录当前的current,并设置为响应式
    this.data = _Vue.observable({
      current: '/'
    })
    this.init()
  }
  init() {
    this.createRouteMap()
    this.initComponent(_Vue)
    this.initEvent()
  }
  // 创建routeMap的数据
  createRouteMap() {
    this.options.routes.forEach(item => {
      this.routeMap[item.path] = item.component
    })
  }
  // 处理组件
  initComponent(Vue) {
    Vue.component('router-link', {
      props: {
        to: String
      },
      render(h) {
        console.log('this.$slots.default', this.$slots.default)
        return h('a', {
          attrs: {
            href: this.to
          },
          on: {
            click: this.clickhander
          }
        }, [this.$slots.default])
      },
      methods: {
        clickhander(e) {
          e.preventDefault();
          history.pushState({}, '', this.to)
          this.$router.data.current = this.to
        }
      }
    })


    Vue.component("router-view", {
      render: (h) => {
        const cm = this.routeMap[this.data.current]
        return h(cm)
      }
    })
  }
  // 监听事件 替换的current
  initEvent() {
    window.addEventListener('popstate', () => {
      this.data.current = window.location.pathname
    })
  }
}

hash实现

简易版说明

  • 只支持routes参数
  • 只支持router-link和router-view组件
  • 不支持push replace等功能

实现

  • 和history实现整理类似, 可以继续使用history的模式
  • 或者通过hashchange监听路由变化,设置current的值
 window.addEventListener('hashchange', () => {
   this.data.current = window.location.hash
 })
let _Vue = null;
export default class VueRouter {
  static install(Vue) {
    //1 判断当前插件是否被安装
    if (VueRouter.install.installed) {
      return
    }

    VueRouter.install.installed = true
    //2 把Vue的构造函数记录在全局
    _Vue = Vue
    //3 把创建Vue的实例传入的router对象注入到Vue实例
    _Vue.mixin({
      beforeCreate() {
        if (this.$options.router) {
          _Vue.prototype.$router = this.$options.router
        }
      },
    })

  }
  constructor(options) {
    // 初始化参数
    this.options = options;
    // 将routes 存放在routeMap中
    this.routeMap = {}
    // 记录当前的current,并设置为响应式
    this.data = _Vue.observable({
      current: '#/'
    })
    this.init()
  }
  init() {
    this.createRouteMap()
    this.initComponent(_Vue)
    this.initEvent()
  }
  // 创建routeMap的数据
  createRouteMap() {
    this.options.routes.forEach(item => {
      this.routeMap[item.path] = item.component
    })
  }
  // 处理组件
  initComponent(Vue) {
    Vue.component('router-link', {
      props: {
        to: String
      },
      render(h) {
        return h('a', {
          attrs: {
            href: this.to
          },
          on: {
            click: this.clickhander
          }
        }, [this.$slots.default])
      },
      methods: {
        clickhander() {
          this.$router.data.current = this.to
        }
      }
    })


    Vue.component("router-view", {
      render: (h) => {
        const cm = this.routeMap[this.data.current]
        return h(cm)
      }
    })
  }
  // 监听事件 替换的current
  initEvent() {
    // window.addEventListener('popstate', () => {
    //   this.data.current = window.location.hash
    // })

    window.addEventListener('hashchange', () => {
      console.log(window.location)
      this.data.current = window.location.hash
    })

  }
}