預覽圖
本層 BOSS 弱點
- 【特定技術】必須使用 AJAX 技術串接資料 API,不可直接寫死資料在變數上
- 【特定技術】上方切換城市 (高雄、台北) 後,下方會切換該城市的各地區
- 【解決問題】糟糕,BOSS 使用屏蔽魔法將 API 出處移除了,身為勇者的你必須查出 API 的下落,才能順利擊敗此 BOSS。
使用技術
- Vue.js
- SCSS
心得
不知不覺小菜雞也挑戰到第五層了,這一層樓的版面看起來也是蠻輕鬆就能搞定,所以為了給自己一點點挑戰性,我稍微添加了一點互動回饋:
- 使用 CSS animation 製作 Loading 畫面
- 點擊時補上內凹的陰影,使其更加真實
接著就是寫程式的部分了,不得不說做這種跟資料相關的版面,使用 Vue 真的是輕鬆很多,只要專注在資料的處理上就好了,所以當我看到這次的題目是 AQI 儀表板,想都沒想就決定要用 Vue 寫了。
所以我說那個 API 在哪?
身為一個前端菜雞,菜歸菜但基本的資料蒐集能力還是要有的,我下的關鍵字是「行政院環境保護署 API」,果不其然第一筆搜尋結果就是答案。
一進網站就馬上發現目標了,空氣品質指標 AQI ,接著只要切到資料檢視頁籤,在選擇 JSON 格式就可以囉。
CORS 怎麼處理?
以下引用自 MDN 的說明:跨來源資源共用 (Cross-Origin Resource Sharing (CORS)) 是一種使用額外 HTTP 標頭令目前瀏覽網站的使用者代理取得存取其他來源 (網域) 伺服器特定資源權限的機制。當使用者代理請求一個不是目前文件來源 - 例如來自於不同網域 (domain) 、通訊協定 (protocol) 或通訊埠 (port) 的資源時,會建立一個跨來源 HTTP 請求 (cross-origin HTTP request)
白話來說,就是這支空氣品質指標的 API 沒有打開 CORS ,所以如果我們把做好的網頁成品發佈到 GitHub 、 Codepen 的話是不能撈取資料的。
我之前的作法都是使用別人建立好的服務,像是這個:
用法相當容易,只需要在後方加入要使用的 API 即可。
但是這一次,我在討論區上看到其他大神們分享這個方法,讓我躍躍欲試。
使用 Google Apps Script 做中繼點跨網域遠端取得 API 資料
使用這個服務必須要申請一個 Google 帳號,接著我們來到雲端硬碟的畫面。
左上角有個「新增」按鈕點進去即可看到這個畫面,因為之前我已經有操作過了,如果是第一次使用要點選「連結更多應用程式」,接著於搜尋欄輸入「script」即可找到這個服務囉。
接著可以在程式碼內貼上這一段程式碼:
1 | function doGet(e){ |
接著按下發布 > 部署為網路應用程式
使用方式:部署的網址?參數名稱= API 網址
這樣子只要一次工,之後練習的作品全部都可以透過這個服務解決掉 CORS 的問題唷
如果是工作上遇到的 CORS ,可能就不適合這個方法囉。
本段圖片、程式碼引用自此,感謝大大的分享。
取得 API 資料的方式
這方式也是有蠻多種的,如果想方便一點可以使用 axios 這個非常強大的套件,而且討論區中也非常多人使用,而且它本身也有 Promise 的功能了。
用法相當簡單,像是這樣即可:
1 | axios({ |
更詳細可以參考 axios 的官方說明。
雖然說這樣就可以了,不過因為我不懂 Promise 是什麼,該如何與 Ajax 結合取得資料,所以決定自己動手做看看,用土法煉鋼的方式(?
首先因為我完全不懂,所以直接上網 google 了一下:
所以大概對於 Promise 有一點點的概念,總之就大概長得像這樣?
1 | let promise = new Promise((resolve, reject) => { |
大概就是理解成,建立一個 Promise 然後可以用兩個函式 resolve 、 reject 分別代表兌現或是失敗。
- 如果使用
resolve()
,接著程式會運行.then
的部分 - 如果使用
reject()
,接著程式會運行.catch
的部分
然後搭配 XMLHttpRequest()
應該就沒問題了
並將程式碼修改如下:
1 | let promise = new Promise((resolve, reject) => { |
這樣就完成了呢,不過這邊有個小小的問題,就是用 XMLHttpRequest()
取到的結果會是字串,要額外透過 JSON.parse()
轉成 JSON 才可以使用。
所以結論就是 axios 好用
使用 filter() 方法取出不重複的值
處理好 API 的問題後,再來就要實作內容了,這個部份很基本,不過我卻每次都會忘記該怎麼處理,所以決定這一次把它寫下來。
而取出陣列中不重複的值做法有很多種,我習慣用 filter()
就是了。
基本的 filter()
起手式是這樣,會回傳一個新陣列:
1 | let arr = ['apple', 'banana', 'lemon', 'apple', 'watermelon', 'grape']; |
而這些參數分別代表為:
item
— 當前是arr
陣列中的哪一個值,如「apple
」index
— 這個值在arr
陣列中的索引,如「apple
」的索引為 0array
— 這個陣列的內容
比方說想從陣列找出 apple ,可以在 return
後補上條件:
1 | let arr = ['apple', 'banana', 'lemon', 'apple', 'watermelon', 'grape']; |
如果想找出陣列中的不重複值,則條件就複雜多了:
1 | let arr = ['apple', 'banana', 'lemon', 'apple', 'watermelon', 'grape']; |
使用了一個方法 indexOf() ,這會回傳從陣列中第一個被找到的目標索引,若不存在於陣列中則回傳 -1。
也就是說這段程式碼實際上是這麼跑的:
arr
陣列的第一個元素是apple
,array.indexOf('apple')
的結果為 0,所以實際上會像這個樣子0 === index
,然而目前是陣列中的第一個元素,索引是 0 ,因此結果是true
,將apple
加入新陣列中。- 第二、第三個元素也同第一個元素,以此類推。
- 當跑到第四個元素時,
array.indexOf('apple')
的結果為 0,而當前的索引是 3 ,因此結果是false
,不加入。 - 第五個元素時,
array.indexOf('watermelon')
的結果為 4,而當前的索引是 4,因此結果是true
,將watermelon
加入新陣列中。 - 當所有元素都執行完時,回傳新陣列。
所以才能找出陣列中不重複的值,透過這樣的方式,要找出陣列中重複的值也很容易,只要 === 改成 !== 就可以了。