1. 程式人生 > >Vuejs 自定義路由實現

Vuejs 自定義路由實現

對於單頁面應用,前端路由是必不可少的,官方也提供了 vue-router 庫 供我們方便的實現,但是如果你的應用非常簡單,就沒有必要引入整個路由庫了,可以通過 Vuejs 動態渲染的API來實現。

我們知道元件可以通過 template 來指定模板,對於單檔案元件,可以通過 template 標籤指定模板,除此之外,Vue 還提供了我們一種自定義渲染元件的方式,那就是 渲染函式 render,具體 render 的使用,請閱讀官方文件。

接下來我們開始實現我們的前端路由了。

簡易實現

我們先執行 vue init webpack vue-router-demo 命令來初始化我們的專案(注意初始化的時候,不要選擇使用 vue-router)。

首先,在 src 目錄先建立 layout/index.vue 檔案,用來作為頁面的模板,程式碼如下:

<template>
  <div class="container">
    <ul>
      <li><a :class="{active: $root.currentRoute === '/'}" href="/">Home</a></li>
      <li><a :class="{active: $root.currentRoute === '/hello'}" href="/hello"
>
HelloWord</a></li> </ul> <slot></slot> </div> </template> <script> export default { name: 'Layout', }; </script> <style scoped> .container { max-width: 600px; margin: 0 auto; padding: 15px 30px; background: #f9f7f5; } a.active
{ color: #42b983; }
</style>

然後,將 components/HelloWorld.vue 移動到 src/pages,並修改其程式碼,使用上面建立的頁面模板包裹:

<template>
  <layout>
      <!-- 原模板內容 -->
  </layout>
</template>

<script>
import Layout from '@/layout';

export default {
  name: 'HelloWorld',
  components: {
    Layout,
  },
  // ...
};
</script>
<!-- ... -->

當然還需要新增一個 404頁面,用來充當當用戶輸入不存在的路由時的介面。

最後就是我們最重要的步驟了,改寫 main.js,根據頁面 url 動態切換渲染元件。

  1. 定義路由對映:
// url -> Vue Component
const routes = {
  '/': 'Home',
  '/hello': 'HelloWorld',
};
  1. 新增 VueComponent 計算屬性,根據 window.location.pathname 來引入所需要元件。
const app = new Vue({
  el: '#app',
  data() {
    return {
      // 當前路由
      currentRoute: window.location.pathname,
    };
  },
  computed: {
    ViewComponent() {
      const currentView = routes[this.currentRoute];
      /* eslint-disable */
      return (
        currentView
          ? require('./pages/' + currentView + '.vue')
          : require('./pages/404.vue')
      );
    },
  },
});
  1. 實現渲染邏輯,render 函式提供了一個引數 createElement,它是一個生成 VNode 的函式,可以直接將動態引入元件傳參給它,執行渲染。
const app = new Vue({
  // ...
  render(h) {
    // 因為元件是以 es module 的方式引入的,
    // 此處必須使用 this.ViewComponent.default 屬性作為引數
    return h(this.ViewComponent.default);
  }
});

history 模式

簡易版本其實並沒有實現前端路由,點選頁面切換會重新全域性重新整理,然後根據 window.location.pathname 來初始化渲染相應元件而已。

接下來我們來實現前端路由的 history 模式。要實現頁面 URL 改變,但是頁面不重新整理,我們就需要用到 history.pushState() 方法,通過此方法,我們可以動態的修改頁面 URL,且頁面不會重新整理。該方法有三個引數:一個狀態物件,一個標題(現在已被忽略),以及可選的 URL 地址,執行後會觸發 popstate 事件。

那麼我們就不能在像上面一樣直接通過標籤 a 來直接切換頁面了,需要在點選 a 標籤是,禁用預設事件,並執行 history.pushState() 修改頁面 URL,並更新修改 app.currentRoute,來改變我們想要的 VueComponent 屬性,好了原理就是這樣,我們來實現一下。

首先,編寫通用 router-link 元件,實現上面說的的 a 標籤點選邏輯,新增 components/router-link.vue,程式碼如下:

<template>
  <a
    :href="href"
    :class="{active: isActive}"
    @click="go"
  >
    <slot></slot>
  </a>
</template>
<script>
import routes from '@/routes';

export default {
  name: 'router-link',
  props: {
    href: {
      type: String,
      required: true,
    },
  },
  computed: {
    isActive() {
      return this.href === this.$root.currentRoute;
    },
  },
  methods: {
    go(e) {
      // 阻止預設跳轉事件
      e.preventDefault();
      // 修改父級當前路由值
      this.$root.currentRoute = this.href;
      window.history.pushState(
        null,
        routes[this.href],
        this.href,
      );
    },
  },
};
</script>

對於 src/main.js 檔案,其實不需要做什麼修改,只需要將 routes 物件修改為模組引入即可。如下:

import Vue from 'vue';

// 這裡將 routes 物件修改為模組引入方式
import routes from './routes';

Vue.config.productionTip = false;

/* eslint-disable no-new */
const app = new Vue({
  el: '#app',
  data() {
    return {
      currentRoute: window.location.pathname,
    };
  },
  computed: {
    ViewComponent() {
      const currentView = routes[this.currentRoute];
      /* eslint-disable */
      return (
        currentView
          ? require('./pages/' + currentView + '.vue')
          : require('./pages/404.vue')
      );
    },
  },
  render(h) {
    // 因為元件是以 es module 的方式引入的,
    // 此處必須使用 this.ViewComponent.default 屬性作為引數
    return h(this.ViewComponent.default);
  },
});

好了,我們的 history 模式的路由已經修改好了,點選頭部的連結,頁面內容改變了,並且頁面沒有重新整理。

但是有個問題,就是當我們點選瀏覽器 前進/後退 按鈕時,頁面 URL 變化了,但是頁面內容並沒有變化,這是怎麼回事呢?因為當我們點選瀏覽器 前進/後退 按鈕時,app.currentRoute 並沒有發生改變,但是它會觸發 popstate 事件,所以我們只要監聽 popstate 事件,然後修改 app.currentRoute 就可以了。

既然需要監聽,我們就直接新增程式碼吧,在 src/main.js 檔案末尾新增如下程式碼:

window.addEventListener('popstate', () => {
  app.currentRoute = window.location.pathname;
});

這樣我們現在無論是點選頁面中連結切換,還是點選瀏覽器 前進/後退 按鈕,我們的頁面都可以根據路由切換了。

hash 模式

既然實現 history 模式,怎麼又能少得了 hash 模式 呢?既然你這麼問了,那我還是不辭勞苦的帶著大家實現一遍吧(賣個萌~)。

什麼是 URL hash 呢?來看看 MDN 解釋:

Is a DOMString containing a '#' followed by the fragment identifier of the URL.

也就是說它是頁面 URL中 以 # 開頭的一個字串標識。而且當它發生變化時,會觸發 hashchange 事件。那麼我們可以跟 history 模式 一樣對其進行監聽就行了,對於 history 模式,這裡需要做的修改無非是 src/routes.js 的路由對映如下:

export default {
  '#/': 'Home',
  '#/hello': 'HelloWorld',
};

src/layout/index.vue 中的連結都新增 # 字首,然後在 src/main.js 中監聽 hashchange 事件,當然還需要將 window.location.hash 賦值給 app.currentRoute

window.addEventListener('hashchange', () => {
  app.currentRoute = window.location.hash;
});

最後還有個問題,就是單個面初始化的時候,window.location.hash 值為空,這樣就會找不到路由對映。所以當頁面初始化的時候,需要新增判斷,如果 window.location.hash 為空,則預設修改為 #/,這樣就全部完成了。

不同模式切換版本

實際開發中,我們會根據不同專案需求,使用不同的路由方式,這裡就需要我們新增一個 mode 引數,來實現路由方式切換,這裡就不做講解了,感興趣的讀者,可以自己嘗試實現下。

總結

實際上,一個完整的路由庫,遠遠不止我們上面演示的程式碼那麼簡單,還需要考慮很多問題,但是如果你的專案非常簡單,不需要很複雜的路由機制,自己實現一遍還是可以的,畢竟 vue-router.min.js 引入進來程式碼體積就會增加 26kb,具體如何取捨,還是視需求而定。

盡信書,不如無書,當面對 問題/需求 時,多點自主的思考和實踐,比直接接受使用要有用的多。

專題目錄

相關推薦

Vuejs 定義路由實現

對於單頁面應用,前端路由是必不可少的,官方也提供了 vue-router 庫 供我們方便的實現,但是如果你的應用非常簡單,就沒有必要引入整個路由庫了,可以通過 Vuejs 動態渲染的API來實現。我們知道元件可以通過 template 來指定模板,對於單檔案元件,可以通過 template 標籤指定模板,除此

基於ES6和原生nodejs實現定義路由,靜態檔案伺服器和增刪查改的MVC架構分享

基於ES6和原生nodejs來實現一個基於MVC的增刪查改功能示例分享 自定義路由的解耦實現 首先分別處理不同方式的請求: const http = require('http'); const url = require('url')

golang定義路由控制實現(一)

    由於本人之前一直是Java Coder,在Java web開發中其實大家都很依賴框架,所以當在學習Golang的時候,自己便想著在Go開發中脫離框架,自己動手造框架來練習。通過學習借鑑Java的思想還有部分框架的原始碼,在golang上面進行實現,從而達到對Java和

MVC定義路由實現二級域名類導致的找到多個與名為xxx的控制器匹配的型別的錯誤

今日在網站錯誤日誌中發現手機站頁面訪問:http://m.jinrimianshi.com/company/395961 時出現找到多個與名為“company”的控制器匹配的型別。如果為此請求(“{controller}/{action}/{id}”)提供服務的路由沒有指

Vuejs定義select2指令

del node ace upd direct prop fault 綁定 get   在做select2插件的時候遇到一些坑,最終解決如下: Vue.directive(‘select2‘, { inserted: function (el, bin

Android -- 定義view實現keep歡迎頁倒計時效果

super onfinish -m use new getc awt ttr alt 1,最近打開keep的app的時候,發現它的歡迎頁面的倒計時效果還不錯,所以打算自己來寫寫,然後就有了這篇文章。 2,還是老規矩,先看一下我們今天實現的效果   相較於我們常見的倒計時

Android定義View——實現水波紋效果類似剩余流量球

string 三個點 pre ber block span 初始化 move 理解 最近突然手癢就想搞個貝塞爾曲線做個水波紋效果玩玩,終於功夫不負有心人最後實現了想要的效果,一起來看下吧: 效果圖鎮樓 一:先一步一步來分解一下實現的過程 需要繪制一個正弦曲線(sin

Mvc Api 定義路由

return pic mbo tro val 定義 精確 efi post // [RoutePrefix("api/ssm")]// public class ValuesController : ApiController// {// ///&

Android定義processor實現bindView功能

lis dds 定義 java代碼 cli 註冊 文章 type() mage 一、簡介 在現階段的Android開發中,註解越來越流行起來,比如ButterKnife,Retrofit,Dragger,EventBus等等都選擇使用註解來配置。按照處理時期,註解又分為兩

定義toast實現

web javascript html5 toast ys_toast.css.ys-toast{ position:fixed; left:0; right:0; top:0; bottom:0; z-index: 999999; } .ys-toast>em{ pos

MVC路由學習:定義路由參數(用戶看不到參數名),重新定義路由規則

route sys 工具 str optional href clas local amp 一,項目有需求將項目地址中的參數名不顯示給用戶看 在MVC定義一個方法:     public ActionResult GetUserInfo(string Name, str

SpringVC 攔截器+定義註解 實現權限攔截

json.js 加載 bean media tar attr esp 權限 encoding 1.springmvc配置文件中配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://w

定義ScrollView 實現上拉下拉的回彈效果--並且子控件中有Viewpager的情況

是否 AS abs pri tar utils lda animation ted onInterceptTouchEvent就是對子控件中Viewpager的處理:左右滑動應該讓viewpager消費 1 public class MyScrollView ext

[python]RobotFramework定義實現UI自動化

bubuko output source 自動 封裝 9.png 全局變量 詳細 變量 1.安裝教程 環境搭建不多說,網上資料一大堆,可參考https://www.cnblogs.com/puresoul/p/3854963.html,寫的比較詳細,值得推薦。目前pyt

NPOI+反射+定義特性實現上傳excel轉List及驗證

type set custom pre script private property xssf don 1.自定義特性 [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited

Android bc信用盤搭建定義behavior 實現上滑 隱藏底部view

退出 Y軸 log rect app sum string dsl oss 布局 <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent"

13、定義Analyzer實現字長過濾

import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.a

OC 定義tabBar實現tabBar上帶有圓形按鈕

1.建立繼承自UITabBar控制元件的類CustomTabBar,程式碼如下: #import "CustomTabBar.h" @interface CustomTabBar () @property (nonatomic, strong)UIButton *roundButton;

定義按鈕實現 video暫停和播放的方法

注:兩個方法只能用於原生獲取的<video></video>元素,對jquery獲取的元素不管用  1.play(); 實現播放 // dom元素如下 <video width="800" height="400" id="video"

如何使用定義模板實現個性化多維分析

自定義表格樣式 多維分析展現報表時,潤乾報表提供了一套預設的表格樣式,統一的表格樣式可以使業務人員減少報表美化的工作量。然而預設的樣式不可能迎合所有使用者的審美,為此潤乾提供了自定義表格樣式的功能,供使用者實現個性化的需求,下面小編就來教你如何改變預設表格的樣式。 先來看下預設的表格樣式,下