入行至今也一段時間啦,雖不是多坎坷但也沒順遂到哪去,踩坑、debug、體驗純靠北工程師上的各種職場鳥事,導致 blog 呈現半放置狀態都是藉口

回到正題,用 Vue.js 前端框架的理由很多,大概可以另外寫一篇騙文章數,是最近替公司後台頁面做重構,有機會用上現代框架,藉此寫一下開發流程,也讓這裡少長些草。

搭一個Vue app

使用 vue-cli

Vue.js 官方提供快速搭建工具 vue-cli,類似 React.js 的 creat-react-app,Github 上安裝與設定文檔寫得很清楚就不贅述,設定集個人是偏好較乾淨快速、有需求套件自己再裝的webpack-simple。小黑窗輸入vue init webpack-simple login 建立一個叫做 login 的專案資料夾,再安裝完裡面的套件後就能用npm run dev 讓他跑起來跑起來

架構

先簡單規劃一下頁面流程:

  • 使用者會到 login 頁進行登入
  • 登入成功後進到主畫面
  • 主畫面包含 header 導航列,能切換主頁與使用者資訊頁,與一個登出鈕

拆分component

Vue.js 很大的優點就是能將頁面 UI 元件模組化。一頁一檔、維護有方。由以上需求,我們在/src資料夾下建一個/components資料夾,裡面建立各頁面的 vue 檔長這樣:

1
2
3
4
5
└ components
├ Login.vue
├ Header.vue
├ Home.vue
└ UserInfo.vue

而在單頁應用SPA,達成這些頁面的切換就是路由的範疇,是該 vue-router 出場了。

路由必備 vue-router

vue 官方提供 vue-router(文件) 幫助我們實現 routing ,安裝成專案的 dependency:

1
npm i vue-router -s

定義路由

個人傾向將路由規則獨立一個檔案管理,在/src資料夾下新建一個 routes.js。裡面先寫好簡單的兩個路由:

routes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
import Login from './components/Login.vue';
import Home from './components/Home.vue';

export const routes = [
{
path: '/login',
component: Login
},
{
path: '/',
component: Home
}
];

這段想寫在main.js裡也行,只要讓 router 帶入正確的 routes array 就好,這裡只是 export 出來

main.js改成:

main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import { routes } from './routes' //路由規則

Vue.use(VueRouter);

const router = new VueRouter({
routes,
mode: 'history'
});

new Vue({
el: '#app',
router,
render: h => h(App)
});

vue-router 預設使用 # 字模擬 anchor 的 url 導向方式,避免整頁刷新,這裡改成 history mode 以呈現乾淨的 url path,相應的 server 端也必須設定總是回應index.html的內容,以避免導向錯誤,可以參考文件

所見所得

App.vue中頁面加入<router-view>,路由規則定義的 component 將會 mount 在上面:

App.vue
1
2
3
4
5
6
7
8
9
10
11
<template>
<div id="app">
<router-view></router-view>
</div>
</template>

<script>
export default {
name: 'app',
}
</script>

然後簡單的寫一下Home.vue,跑一下npm run dev,應該就能看到 Home 字出現在瀏覽器中:

Home.vue
1
2
3
<template>
<h1>Home</h1>
</template>

接著編輯登入頁面Login.vue

Login.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<template>
<div>
<h1>Login Page</h1>
<form @submit.prevent="login">
<label>User Name</label>
<input type="text" v-model="userName" required>
<br>
<label>Password</label>
<input type="password" v-model="password" required>
<br>
<button type="submit">Log In</button>
</form>
</div>
</template>
<script>
export default {
data () {
return {
userName: '',
password: '',
}
},
methods: {
login(){
//-- write login authencation logic here! --
let auth = true;

if( auth )
this.$router.push('/');
else
alert('login failed')
}
}
}
</script>

v-on 綁定<form>元素的 submit 事件,.prevent後綴防止原生方法產生頁面重整,而改用自定的login method。

手動在網址後打上 /login,就能看到登入表格,利用this.$router.push('/')導向至主頁。

說好的 header 呢?

Header.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<div>
<router-link to="/">Home</router-link>
<router-link to="/userInfo">User Info</router-link>
<a href="" @click.prevent="logout">Logout</a>
</div>
</template>
<script>
export default {
methods: {
logout(){
this.$router.push('/login');
}
}
}
</script>

template 的 router-link 會 render 成<a href="..."></a>的超連結,但不會重整頁面,而是改變路由、滑順的 mount 元件上去,SPA 要的就是這個感覺

命名 router-view

可是瑞凡, Header 是只有登入後才會看到的元件,怎麼辦?

對於路由條件式的掛載元件,簡單的實作是在App.vue,加上另外指定 name 的<router-view>

1
2
3
4
<div id="app">
<router-view name="nav"></router-view>
<router-view></router-view>
</div>

然後在路由規則裡,把需要 Header 的路由改成 components ( 加個s )並使用物件, key 名將會自動匹配到指定 name 的 router-view,沒指定的會 fallback 至 default

routes.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import Login from './components/Login.vue';
import Header from './components/Header.vue';
import Home from './components/Home.vue';
import UserInfo from './components/UserInfo.vue';

export const routes = [
{
path: '/login',
component: Login
},
{
path: '/',
components: {
default: Home,
nav: Header
}
},
{
path: '/userInfo',
components: {
default: UserInfo,
nav: Header
}
},
];

順便完成 UserInfo.vue

UserInfo.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<h1>UserInfo</h1>
<h3>user name:</h3>
<p>{{ userName }}</p>
<h3>ID:</h3>
<p>{{ userId }}</p>
</div>
</template>
<script>
export default {
data () {
return {
userName: 'Steven Chou',
userId: 9527,
}
}
}
</script>

中場休息

大致完成了半殘的登入系統,各種點擊也能正常作動,但目前直接透過 URL 都能造訪任一個頁面,不用yoyodiy就能繞過登入機制看光光,完全沒卵用。

下一篇將繼續實作完整的登入驗證功能