成果 & 程式碼
Introduction & 前言
超級菜鳥這次來挑戰第五層的BOSS了,這次必須串接API取的資料後Show在網頁上,對剛接觸JS不久以及根本沒碰過API的我是一大挑戰啊...

Summary & 摘要
由於JS地下城每個BOSS的弱點都不一樣,每一層都要由弱點去進行攻略,本次BOSS的弱點有三項。
第四點是自己加上的(笑
- 【特定技術】必須使用 AJAX 技術串接資料 API,不可直些寫死資料在變數上。 
- 【特定技術】上方切換城市(高雄、台北)後,下方會切換該城市的各地區。 
- 【解決問題】糟糕,BOSS 使用屏蔽魔法將 API 出處移除了,身為勇者的你必須查出 API 的下落,才能順利擊敗此 BOSS。什麼,你說會有 CORS 問題?嗯… 身為勇者的你,一定可以找各種服務來解決的,畢竟你是「勇者」嘛 (燦笑。 
- 【書寫能力】請寫一篇 BLOG 來介紹你的挑戰過程,你攻略此 BOSS 的攻略過程心得,底層 XMLHttpRequest、Fetch API 的差異,使用 Promise 來優化 XMLHttpRequest JAX,探討 CORS 問題解決方案。 
尋找API?
作為第一次尋找API的我很幸運的,收尋了關鍵字就出現了,一切都要感謝谷歌大神(跪

進入後只需要再收尋欄位輸入關鍵字,或者到下面的各種標籤點擊搜尋就能找到了!

關於CORS & XMLHttpRequest & Promise 優化相關
跨來源資源共用(Cross-Origin Resource Sharing (CORS))是一種使用額外 HTTP 標頭令目前瀏覽網站的使用者代理取得存取其他來源(網域)伺服器特定資源權限的機制。當使用者代理請求一個不是目前文件來源 — — 例如來自於不同網域(domain)、通訊協定(protocol)或通訊埠(port)的資源時,會建立一個跨來源 HTTP 請求(cross-origin HTTP request)。
這邊引用MDN Web docs的文章,簡單來說,因為安全考量,API端如果沒有開啟CORS的話,我們把做好的網頁發布到公開網站上,資料是沒辦法撈到的。

處理的方法有幾種,一種是透過後端,利用伺服器端程式來繞過此問題,但是必須自己架設一個伺服器,亦或者使用國外免費架設伺服器,但是這邊採取其他方式。
前人種樹後人乘涼
感謝各種前端大神的幫忙,像是pvt5r486大大的文章內就提到使用別人建立好的服務,例如這個:
但是這個方法我在使用上發現速度似乎載入的比較慢,有時還有撈不到資料的問題
最後我使用了另一種大神推薦的方法,那就是 Google App Script!
使用 Google Apps Script 做中繼點取得跨網域API資料

首先必須要有一個google帳號,然後進入雲端硬碟,點擊左上角新增,第一次會看不到 Google App Script 這個程式,必須點擊最下面的連結更多應用程式。

然後再裡面貼上一段程式碼:
| 1 | function doGet(e){ | 
- 接著點下 發布 → 部署為網絡應用程式


部署方式為:部署的網址?參數名稱= API 網址
這邊這樣說我也是理解了大半天,我資質愚鈍呀…
簡單說 部署的網址? 不用理它參數名稱就是 url ,你只需要在後面輸入 url=API 網址,之後他會產出一組網址給你 把那組網址跟你的這串 url=API 網址 結合後就可以使用了。
 就是這串啦~
就是這串啦~
留下這串網址之後,以後只要有API的問題,加上他就對啦。
這邊感謝Mandy大神還有prt5r486大神的文章
XMLHttpRequest 和 Fetch API 差異
一般Ajax指的就是XMLHttpRequest(XHR),而Fetch API 是基于 Promise 設計的,後面我們會提到 Promise。
有興趣可以點我看文章
XMLHttpRequest 本質上但並不是一個設計優良的 API: + 不符合關注分離(Separation of Concerns)的原則 + 配置和調用方式非常混亂 + 使用事件機制來跟蹤狀態變化 + 基於事件的異步模型沒有現代的 Promise,generator/yield,async/await 友好 。
Fetch API 旨在修正上述缺陷,它提供了與 HTTP 語義相同的 JS 語法,簡單來說,它引入了 fetch() 這個實用的方法來獲取網絡資源。
Promise 優化 XMLHttpRequest JAX
再接觸前端部分之前,有讀過PHP的書,裡面也有用到Ajax,在朋友的建議下,想著第一次串接API不如就直接學習AXIOS,因為他本身也有 Promise 功能了。
| 1 | function getapi() { | 
有關Axios可以上官方GitHub查看
因為以前完全沒接觸過Ajax及Axios,在查看了各位大神的文章後,
Promise的架構大致如下。
| 1 | let promise = new Promise((resolve, reject) => { | 
而白話一點就是可以理解成式子的可能為:
承諾 被兌現 (fulfilled)
→用 resolve() 來兌現
承諾 被打破 (rejected)
→用 reject() 來表示失敗
承諾 一直沒有回應 (pending)
→一直沒有回傳
而在這些承諾之後:
承諾被兌現 就 繼續做預定好的下一件事
使用 .then()
承諾被打破 就 根據這個原因去做對應的動作
使用 .catch(),或是 .then 的第二個參數
承諾 一直都沒有回應 就 繼續等下去
resolve() 運行 → .then 承諾被兌現
reject() 運行 → .catch 承諾失敗
然而因為我們使用 XMLHttpRequest() 取得結果會是字串,必須使用JSON.parse() 轉成 JSON 才能使用。
善用 filter() Method 點我前往
參考各位大神文章後,了解了基本方法如下:
| 1 | let arr = ['apple', 'banana', 'lemon', 'apple', 'watermelon', 'grape']; | 
- item — 當前是 arr 陣列中的哪一個值,如「apple」 
- index — 這個值在 arr 陣列中的索引,如「apple」的索引為 0 
- array — 這個陣列的內容 
function* alldata() {
  let* result = data.filter((*item*, *index*, *array*)    => {
      if ($.inArray(item.County, Country) == -1) {
        Country.push(item.County); //判斷有無重複並放入縣市名稱
      $('.select-box').append('<option>' + item.County + '</option>');
      return true;
    } else {
      return false;
    }
  });
}
在 item 裡我們已經將所有資料分開了,方便我們過濾資料,而這邊我們使用if條件是判斷不讓相同縣市的資料出現第二次以上,因為這邊要把縣市放到上方的 Select 內的 Option 。
不得不說真的是常常用到組字串呢
下方的資料一樣使用 filter() 及 組字串方式,把各縣市的資料放上 div,造出一個一個的 div(誰叫我不會Vue呢TAT
jQuery.data() 方法點我
這邊值得一提,因為使用重組字串的方法,所以用到了 jQuery 的 data() ,而造出來的字,會被HTML自動翻成小寫,資質愚鈍如我花了大把時間再找為何我的onclick事件抓不到呢…

Conclusion & 結論
這系列的地下城真的幫助我 JS 成長了許多,每每覺得要卡關了,還是能慢慢爬文慢慢吸收,然後破關,我想最主要的還是邏輯部分,簡化自己的程式碼,另外在這次做API之前我根本毫無頭緒,這是很好的練習,也是我的第一次(羞
最後的最後一樣要加強的也是文筆部分,看各位大神都精簡扼要,我都還是露露長。雖然這只是剛開始寫 Blog 的第二篇廢文,還是要列出檢討的地方,然後朝下一關繼續加油,完畢。
- JS 
- 邏輯 
- 文筆 
- 體重
我的其他範例
- Hero Of UnderGround — 1F Multiplicatio 九九乘法表 
- Hero Of UnderGround — 2F Clock 時鐘 
- Hero Of UnderGround — 3F Calculator 計算機 
- Hero Of UnderGround — 4F World Clock 各國時區 
參考網站
- pvt5r486大神的Blog — 全台空氣指標攻略心得
- Mandy大神的Bolg — 利用google apps script做中繼點跨網域遠端取得api資料* * )* henry35208大神的鐵人邦 — Promise介紹
- axios的GitHub — axios
- camsong大神的GitHubg Bolg — 傳統Ajax已死,Fetch永生
- W3school 教學網站
- 行政院環保署資料開放平臺
- *巴哈姆特 舒壓用*
