---
title: 'VueJS 2.0 教學筆記: 生命週期與AXIOS API'
disqus: hackmd
---
VueJS 2.0 教學筆記: 生命週期與AXIOS API
===
綱要
[TOC]
一. Vue Life Circle 生命週期
---
![](https://i.imgur.com/MLYrjeX.jpg)
當瀏覽器開始new Vue()階段時,會依序經過以下步驟,且每個步驟都可以根據需要的階段來掛載methods:
==**beforeCreate()**==
觀察`data()`結構與初始化 events
==**created()**==
是否有"el"的 option 被調用,假如有則判斷是否有"template"元素出現,沒有的話則 view model 呼叫 mount 掛載 el 。如果有 `template`,將 `template` 中的 html 模板編譯到 `render()` 中,若沒有 `template` 出現在環境則編譯 el 上一層的 HTML 作為 `template` 模板。
**注意:`created()`階段時頁面 DOM 還不會生成**
==**beforeMount()**==
Vue掛載初始化並處理好的組件設定與事件之前,會建構一個`vm.$el`屬性物件替換el。
==**mounted()**==
掛載階段,如果data有被更動,則進入`beforeUpdate()`。
==**beforeUpdate()**==
將VDOM重新render並更新。
==**updated()**==
更新data後的狀態。
==**beforeDestroy()**==
當`mounted()`後如果有呼叫`vm.$destroy()`的行為,則進入`beforeDestroy()`
此時拆卸`watcher`(比如compted的getter或watch)和子組件、以及事件偵聽後進入`destroyed()`消滅。
二、router 路由新增頁面練習
---
[GitHub範例](https://github.com/fortes1219/vue_0803/blob/0803/src/router.js)
:::info
注意,假如你也有跟我一樣要把 Home 設定起始畫面就是 Dashboard 才跟著做,如果要改成獨立分頁,寫在跟 home 平行同層物件下即可。
:::
```javascript=
{
path: 'newpage' //路徑。也就是網址打什麼後綴字會出現
name: 'newPage', //名稱,router切換頁面$router.push('newPage')會使用這個名字
component: () => import './views/newPage.vue' // 將新增頁面的vue檔案拉到這
}
```
三、Vue 使用 axios 的封裝方式
---
:::info
除了不需要 Call API 取得 Data List 或 Select Options 而會將設定寫入 data() 外,儘早讓各位同學習慣從 JSON Server 模擬環境 Get 資料,提前安排 Axios + vue-axios 在這個禮拜講解實作一次。這個例子只是方便大家從JSON Server中取資料來操作一遍,實際環境專案使用的封裝方式最後會提到。
:::
先安裝以下套件:
`npm install axios`
`npm install vue-axios`
`npm install json-server`
然後在專案的根目錄下新增 `db.json`,並且新增一些內容。[GitHub範例](https://github.com/fortes1219/vue_0803/blob/0803/db.json)
接下來開啟終端程式輸入 `json-server --watch db.json`
就會看到這個畫面
```
\{^_^}/ hi!
Loading db.json
Done
Resources
http://localhost:3000/posts
http://localhost:3000/comments
http://localhost:3000/profile
http://localhost:3000/tableData ##剛剛在db.json新增的資料
Home
http://localhost:3000
```
==1. main.js當中引入並且設定global prototype==
[GitHub範例](https://github.com/fortes1219/vue_0803/blob/0803/src/main.js)
```javascript=
/* main.js */
import axios from 'axios'
import api from '@/service/api' // 第二步驟會用到
import VueAxios from 'vue-axios'
// 教學專案採用element UI組件來切版和建置表單,所以先裝好
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
// 套用並設定prototype
Vue.use(ElementUI)
Vue.use(VueAxios, axios)
Vue.config.productionTip = false
Vue.prototype.$api = api // 定義api這個常數給AXIOS存取json-server或實際api環境用
```
==2. 在src裡面創建一個叫做「service」的目錄,此目錄下再創建一個「api.js」==
[GitHub範例](https://github.com/fortes1219/vue_0803/blob/0803/src/service/api.js)
```javascript=
/* api.js */
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:3000/'
const api = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
get: (url, params) => {
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
})
.then((response) => {
resolve(response.data)
})
.catch((error) => {
reject(error)
})
})
},
post: (url, params) => {
return new Promise((resolve, reject) => {
axios.post(url, params)
.then((response) => {
resolve(response.data)
})
.catch((error) => {
reject(error)
})
})
},
put: (url, params) => {
return new Promise((resolve, reject) => {
axios.put(url, params)
.then((response) => {
resolve(response.data)
})
.catch((error) => {
reject(error)
})
})
},
delete: (url, params) => {
return new Promise((resolve, reject) => {
axios.delete(url)
.then((response) => {
resolve(response.data)
})
.catch((error) => {
reject(error)
})
})
}
}
export default api
```
==3. 封裝好axios後回到頁面檔案==
[GitHub範例](https://github.com/fortes1219/vue_0803/blob/0803/src/views/ApiTemp.vue)
```htmlmixed=
<template>
<!--ApiTemp.vue-->
<div>
API測試
</div>
</template>
```
```javascript=
<script>
export default {
data() {
return{
tableData: [] //宣告個空陣列
}
},
methods: {
async packageGetData() {
const url = 'tableData' // json-server API 位置
let res = await this.$api.get(url)
this.tableData = [...res] // 透過ES6語法將res的內容直接繼承到tableData
console.log(res)
},
},
created() {
this.packageGetData() // 在created 階段把API資料叫進來
}
}
</script>
```
接著在開發人員工具畫面的Console中就可以看到我們從 `tableData` Get 回來的資料了。
真實專案上接 API 是什麼樣的狀況?
---
實際專案使用Axios多以POST為主,包含取得資料(GET)和修改(UPDATE)、刪除(DELETE)等行為都是給一個包裝好指定參數的物件,送到後端驗證之後回傳新的結果,真正在有後端同事配合的狀況下,會需要一些環境變數,也要把串接的CODE改寫,包含封裝和攔截器設定等。
**QS套件**
通常主要用來處理原生form物件POST行為當中 application/x-www-form-urlencoded 編碼上的問題,先轉字串後再使用這個套件做`Qs.stringify`,但說句實話,「Server Side 可以完全無視這件事」,僅在這稍稍提一下它的存在是幹嘛的。
`npm install qs`
**.env.development**
這邊視部署情況去更改不同檔名的參數,比如QAT就是staging,PROD就是production
```shell=
ENV = 'development'
VUE_APP_BASE_API = 'http://my-api-server.com'
VUE_APP_VERSION = '0.0.0'
```
**api.js**
```javascript=
import axios from 'axios'
import Cookies from 'js-cookie'
// QS用不用其實都可以,前面說過這個機制Server Side可以無視
import Qs from 'qs'
export function request(config) {
const service = axios.created({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
timeout: 60000,
transformRequest: [(data) => {
let ret = ''
const tempData = getJwtData(data)
for (const it in tempData) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(tempData[it]) + '&';
}
return ret
}]
})
const getJwtData = (data) => {
// 如果jwt的key中含有空字串或者undefined,刪除該條jwt
for (const key in data) {
if (data.hasOwnProperty(key)) {
const val = data[key]
if (val === '' || val === undefined){
delete data[key]
}
}
}
const userInfo = Cookies.get('userInfo')
let token = ''
if (userInfo) {
token = JSON.parse(userInfo).token
}
const jwt = token + '.' + encodeURIComponent(btoa(encodeURIComponent(JSON.stringify(data))))
return {data:jwt}
}
// --->
// 設置 request 攔截器
axios.interceptors.request.use((config) => {
const token = JSON.parse(Cookies.get('userInfo').token)
// token本身是會過期的,需要返回狀態查詢是不是過期
token && (config.headers.Authorization = token)
return config
})
// 設置 response 攔截器
axios.interceptors.response.use(
(response) => {
// 如果回應200表示正常連線,可以返回資料結果,反之reject
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
// 根據不同的回應碼來訂製不同的錯誤訊息
(error) => {
if (error && error.response) {
switch (error.response.status) {
case 400:
error.message = 'Request Error!'
break
case 401:
error.message = 'No permission, need login.'
break
case 403:
error.message = 'Access denied!'
break
case 404:
// 自動帶入 request 地址的寫法
error.message = `Address not exist: ${error.response.config.url}`
break
case 408:
error.message = 'Request timeout!'
break
case 500:
error.message = 'Server inside error!'
break
case 501:
error.message = 'Service not allowed!'
break
case 502:
error.message = 'Bad gateway!'
break
case 503:
error.message = 'No service!'
break
case 504:
error.message = 'Gateway timeout!'
break
case 505:
error.message = 'http version not supported!'
break
default:
break
}
}
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
console.log('error',error)
return Promise.reject(error)
}
)
return service(config)
}
```
**api_path.js**
```javascript=
import request from '@/api' // api.js
export const GetDataList = (data) =>
request({
url: '/post/tableData',
method: 'post',
data,
})
```
**component.vue**
```htmlmixed=
<template>
<div id="page">
<el-table :data="tableData" >
...
...
</el-table>
</div>
</template>
```
```javascript=
<script>
import { GetDataList } from '@/api_path'
export default {
data() {
return {
tableData: [],
currentPage: 1,
pageLimit: 20
}
},
computed: {
getLanguage() {
return this.$store.lang
}
},
methods: {
async fetchDataList() {
const jwt = {
"language": this.getLanguage(),
"page": this.currentPage,
"page_limit": this.pageLimit
}
console.log('post jwt: ', jwt)
let res = await GetDataList(jwt)
if (res.result == '1') {
this.tableData = res.data
console.log('get result: ', res.data)
} else {
alert(res.data.errMessage)
}
}
},
created() {
// created 階段就開始發出request
this.fetchDataList()
}
}
</script>
```
###### tags: `VueJS` `Axios`