基于 Vue 和 Electron 的攔截登錄 Demo

vue-login-intercept

一個 Vue.js 的小demo

介紹

項目地址,有喜歡的歡迎 star 或提出問題。

該項目是根據 一個項目學會前端實現登錄攔截 項目的思想,基于 Element UI 完成一個登錄,攔截登錄,登出,利用 GitHub API 獲取數據等功能的一個小 demo ,利用了 vue,vue-router,vuex,axios,webpack,sass 等技術棧, 通過 eslint 進行語法檢查,是 vue 初學者實踐的一個很好的例子。

項目分為網頁版和桌面版,網頁版使用 vue-webpack 作為項目模板構建,桌面版使用基于 Electron 和 Vue 的 electron-vue 模板構建。

Electron 是一個能讓你通過 JavaScript、HTML 和 CSS 構建桌面應用的框架。這些應用能打包到 Mac、Windows 和 Linux 電腦上運行,當然它們也能上架到 Mac 和 Windows 的 app stores。Electron 結合了 Chromium、Node.js 和用于調用操作系統本地功能的 API(如打開文件窗口、通知、圖標等)?;?Electron 的開發,就好像開發一個網頁一樣,而且能夠無縫地使用 Node。或者說:就好像構建一個 Node app,并通過 HTML 和 CSS 構建界面。另外,你只需為一個瀏覽器(最新的 Chrome)進行設計,無需考慮兼容性。使用 Electron 可以快速開發桌面端程序,并且兼容性好,可移植性強,是使用前端技術開發桌面端應用程序的很好地選擇。

網頁版截圖

首頁截圖

登錄界面

倉庫列表

Electron版截圖

首頁

倉庫列表

技術棧

構建步驟

# 安裝依賴
npm install

#在本地服務器上的8080端口啟動熱加載調試
npm run dev

# 以最小化構建產品
npm run build

# 構建并查看分析器報告
npm run build --report=

項目結構

網頁版

.
├── README.md
├── build // 構建配置文件
│   ├── build.js
│   ├── check-versions.js
│   ├── dev-client.js
│   ├── dev-server.js
│   ├── utils.js
│   ├── vue-loader.conf.js
│   ├── webpack.base.conf.js // 基本的 webpack 配置文件
│   ├── webpack.dev.conf.js
│   └── webpack.prod.conf.js
├── config
│   ├── dev.env.js
│   ├── index.js
│   └── prod.env.js
├── index.html
├── package-lock.json
├── package.json
├── src // 源文件
│   ├── App.vue
│   ├── assets // 資源目錄
│   │   ├── layout.scss
│   │   └── logo.png
│   ├── components // vue 組件
│   │   ├── index.vue // 主頁
│   │   ├── login.vue // 登錄
│   │   └── repository.vue // 倉庫列表
│   ├── main.js // 入口文件
│   ├── router // 路由
│   │   ├── http.js // axios獲取數據
│   │   └── index.js
│   └── store // vuex 狀態管理的文件夾
│       ├── index.js
│       └── mutations.js
└── static // 靜態資源

項目邏輯

1. 組件

分析項目的目的,我們可以得出以下主要過程:

登錄 -> 驗證 -> 獲取 GitHub 上的倉庫信息 -> 注銷

由上面的過程,我們可以抽出 3 個組件:首頁,登錄,倉庫列表。index 組件顯示介紹信息,登錄和驗證功能在 login 組件中完成,獲取 GitHub 上的倉庫信息并進行展示由 repository 組件完成。注銷功能全局可見,直接放到導航欄中即可。

2. 攔截登錄

攔截登錄是這個項目的要點,主要分為兩步,一是通過路由攔截,二是通過 axios 的攔截器。

路由攔截

在定義 reposiroty 組件的路由時需要配置 meta 字段,在 meta 字段中添加一個屬性,用于判斷該路由的訪問是否需要登錄,如果用戶已經登錄,則順利進入路由, 否則就重定向到登錄頁面。

meta: {
  requiresAuth: true // 用于判斷進入這個路由是否需要認證
}
// 在每個路由生效之前,先進行一些處理,請參考 vue-router官方文檔-導航鉤子
router.beforeEach((to, from, next) => {
  // 對 to.matched 數組中的每個路由調用箭頭函數
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 判斷登錄狀態
    if (store.state.token) {
      // 繼續路由
      next()
    } else {
      // 重定向到登錄界面
      next({
        path: '/login',
        query: { redirect: to.fullPath }
      })
    }
  } else {
    // 繼續路由
    next()
  }
})

router.beforeEach 是 vue-router 提供的導航鉤子,導航鉤子的作用主要用來攔截導航,讓它完成跳轉或取消??梢允褂?router.beforeEach 來注冊一個全局的 before 鉤子,當一個導航觸發時,全局的 before 鉤子會被按照創建順序調用,鉤子是異步解析執行,在所有的鉤子解析完成之前,導航一直處于等待之中。

官方文檔

每個鉤子方法接收三個參數:

to: Route: 即將要進入的目標路由對象

from: Route: 當前導航正要離開的路由

next: Function: 一定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。

next(): 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。

next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了(可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。

next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。

注意: 一定要調用 next 方法,否則鉤子就不會被解析,也就不會起作用。

tofrom 是路由信息對象,包含了一些基本屬性,其中 matched 屬性是一個數組,記錄了當前路由的所有嵌套路徑片段的 路由記錄。路由記錄就是 routes 配置數組中的對象副本(還有在 children 數組)。

const router = new VueRouter({
  routes: [
    // 下面的對象就是路由記錄
    { path: '/foo', component: Foo,
      children: [
        // 這也是個路由記錄
        { path: 'bar', component: Bar }
      ]
    }
  ]
})

我們通過對 matched 數組調用 some 方法,遍歷數組,檢查所有路由記錄的 meta 字段中是否有需要登錄的標志屬性,如果有,就判斷是否登錄,做進一步處理,否則直接進行路由導航。

axios 攔截器

路由攔截只能進行簡單的攔截,若用戶惡意進行登錄,路由攔截并不能起到很好地作用,還需要根據服務器的返回信息進行攔截,這時就需要時使用 axios 的攔截器。

axios 的攔截器(interceptors)可以在 請求(request) 或者 返回(response)then 或者 catch 處理之前對他們進行攔截,即可以在正式向服務器發送請求之前和使用 then 或者 catch 處理服務器的請求之前對信息進行處理。

部分源代碼

// 請求攔截器
axios.interceptors.request.use(
  config => {
    if (store.state.token) {
      config.headers.Authorization = `token ${store.state.token}`
    }
    return config
  },
  err => {
    return Promise.reject(err)
  })

// 返回攔截器
axios.interceptors.response.use(
  response => {
    return response
  },
  error => {
    if (error.response) {
      switch (error.response.status) {
        case 401:
          // 401 清除token信息并跳轉到登錄頁面
          store.commit(Mutations.LOGOUT)
          router.replace({
            path: '/login',
            query: {redirect: router.currentRoute.fullPath}
          })
      }
    }
    return Promise.reject(error.response.data)
  })

這里使用 axios 的請求攔截器,在發起請求前判斷用戶的登錄狀態(通過 token 信息),如果登錄的話則在 http header 上加上 token 信息,否則拒絕發送請求。在處理返回的信息時,通過返回的狀態碼,判斷是否為非法登錄(401),如果是非法登錄,則清除登錄信息(本地存儲的 token 信息),重定向到登錄界面。

3.狀態管理(Vuex)

在這個項目里,多個組件(indexlogin)都需要用到 token 信息,也會有多個組件(loginrepository)對 token 或者 title 信息進行更改的情況, 這個時候就涉及到兩個問題:

  • 多個視圖依賴于同一狀態。
  • 來自不同視圖的行為需要變更同一狀態。

讓我們來看看官方文檔的對這種問題的解釋:

對于問題一,傳參的方法對于多層嵌套的組件將會非常繁瑣,并且對于兄弟組件間的狀態傳遞無能為力。對于問題二,我們經常會采用父子組件直接引用或者通過事件來變更和同步狀態的多份拷貝。以上的這些模式非常脆弱,通常會導致無法維護的代碼。

因此,我們為什么不把組件的共享狀態抽取出來,以一個全局單例模式管理呢?在這種模式下,我們的組件樹構成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態或者觸發行為!

另外,通過定義和隔離狀態管理中的各種概念并強制遵守一定的規則,我們的代碼將會變得更結構化且易維護。

這就是 Vuex 背后的基本思想,借鑒了 Flux、Redux、和 The Elm Architecture。與其他模式不同的是,Vuex 是專門為 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。

通過上面的解釋,我們可以看出來,Vuex 能夠很好地解決以上問題。

每一個 Vuex 應用的核心就是 store(倉庫)。"store" 基本上就是一個容器,它包含著你的應用中大部分的狀態(state)。Vuex 和單純的全局對象有以下兩點不同:

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那么相應的組件也會相應地得到高效更新。

  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交(commit) mutations。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地了解我們的應用。

創建 store 的過程很簡單,只需要提供一些初始 state 對象和一些 mutations

const store = new Vuex.Store({
  state: {
    title: '攔截登錄',
    user: 'wuyiqinng',
    token: ''
  },
  mutations: {
    // 使用 ES6 的計算屬性
    [Mutations.LOGIN] (state, token) {
      localStorage.token = token
      state.token = token
    },
    [Mutations.LOGOUT] (state) {
      localStorage.removeItem('token')
      state.token = ''
    },
    [Mutations.TITLE] (state, title) {
      state.title = title
    }
  }
})

現在,你可以通過 store.state 來獲取狀態對象,以及通過 store.commit 方法觸發狀態變更

store.commit(Mutations.LOGOUT)
console.log(store.state.title)

Vuex 主要有幾個核心概念:

  • State: 狀態,也就是數據來源,可以看作是組件中的 data,不過是抽離的公共數據。
  • Getters:可以理解為 store 的計算屬性。
  • Mutations:更改 store 中的 state 的方法,類似于事件,每個 mutation 都有一個字符串的事件類型 (類似于事件的名稱)和一個回調函數 (handler),這個回調函數就是我們實際進行狀態更改的地方,并且它會接受 state 作為第一個參數。 mutation 必須是同步函數。
  • Actions:Action 類似于 mutation,不過 Action 提交的是 mutation ,而不是直接變更狀態,并且 Action 可以包含任何異步操作。
  • Modules:Vuex 允許我們將 store 分割成模塊,避免當狀態較多時, store 對象過于臃腫。

更多關于 Vuex 的內容請看官方文檔

項目文件

網頁版

main.js

main.js 文件是 webpack 打包的入口文件,也是 vue 應用程序的入口文件,在這里要完成一些項目所需模塊的加載,實例化并掛載 vue。

App.vue

App.vue是主要的應用組件,也是最先被加載的組件,可以認為是傳統網頁中的首頁,但是不同的是,其他組件會被加載到 App.vue 中的路由視圖區 <router-view></router-view> 中,也就是說 App.vue 中的其他內容并不會消失,而是存在整個應用的生命周期中,所以 App.vue 中適合寫一些存在于全部頁面中的結構,如導航欄。

這里,我在 App.vue 中直接寫入了導航欄,因為導航欄結構比較簡單,如果是比較復雜的結構,還是抽離為單頁面組件比較好。

router/index.js

路由的官方介紹:

用 Vue.js + vue-router 創建單頁應用,是非常簡單的。使用 Vue.js ,我們已經可以通過組合組件來組成應用程序,當你要把 vue-router 添加進來,我們需要做的是,將組件(components)映射到路由(routes),然后告訴 vue-router 在哪里渲染它們。

在這里進行基本的路由配置,將組件映射到路由,使得組件能夠被正確的加載并渲染,路由和傳統網頁中的 <a href=""></a> 標簽意義比較接近,不過是針對 vue 的組件進行加載,也可以作為傳統的標簽使用,功能上更加強大。

router/http.js

http.js 文件是 axios 的包裝文件,包裝了 axios 的登錄攔截方法,

components/

components/ 目錄下是構成應用的單文件組件,主要有首頁,登錄,倉庫列表三個組件,會被加載到 App.vue 中的路由視圖區中。

stroe/

狀態管理模式 vuex 的配置文件 index.js 和管理 mutation 事件類型的文件 mutations.jsmutations.js 的作用是:使用常量代替 mutation 事件類型,把這些常量放在單獨的文件中可以讓你的代碼合作者對整個 app 包含的 mutation 一目了然。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容