Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/cj-yang0225/cj-newsapp

An app that collects news from various media in Taiwan
https://github.com/cj-yang0225/cj-newsapp

axios babel es6 koa2 scss vanilla-javascript vanilla-js vercel webpack webpack5

Last synced: 23 days ago
JSON representation

An app that collects news from various media in Taiwan

Awesome Lists containing this project

README

        

# CJ-NewsApp

有時想看些新聞、時事,但不想被單一媒體的觀點所侷限,所以自製一個新聞的 Web App,藉由串接集成的 [Taiwan News API](https://newsapi.org/s/taiwan-news-api),獲取各方新聞媒體的報導。

## Client [連結](https://cj-news.vercel.app)

原生 [Vanilla.js](http://vanilla-js.com/)🤣 撰寫前端 Client App,受到 React.js 和 Vue.js 的啟發,嘗試運用框架的核心理念,自訂 Webpack 環境,打造出專屬的專案架構。一方面可強化原生控制資料、事件、畫面三者的能力,另一方面能反思框架存在的意義、想解決什麼問題呢?

### Features & Technologies

#### 首頁(index.html)

- 使用自己簡單封裝的 [XMLHttpRequest 工具](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/utils/http.js),發送 AJAX GET 請求,獲取類別(top, entertainment, sports, business, health, technology, science)對應的新聞,預設每頁為 10 筆報導(會根據裝置尺寸調整)。

- 已獲取的資料會利用記憶體快取(memory cache)並附加一個能判斷資料是否過期的 timestamp(類似 Cookie 的 `Max-Age`),再將這個加工過的資料儲存至 `localStorage`,設定 5 分鐘後過期,需要重新請求資料,以確保新聞的即時性。([程式碼 L67~L127](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/utils/data.js#L67-L127))

- 標題區塊 [`Header`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/Header/index.js) 根據不同頁面顯示相應的訊息和連結。

- 點擊上方導覽列 [`Navbar`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/Navbar/index.js),更換新聞的類別,並即時更新 URL Search parameters(`?category=technology`),操控瀏覽器歷史紀錄(`history.pushState`)和監聽 `popstate` 事件。

- 點擊新聞卡 [`NewsCard`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/NewsCard/index.js) 開啟有獨特 name 的分頁,讓點擊相同新聞卡時跳至同樣的分頁,不會重複開啟同一則新聞;另外用滑鼠中鍵或是右鍵(context menu)的「在新分頁中開啟連結」則可重複開啟。

- 啟用圖片 lazy loading 以及增設圖片 `load` 事件,首次載入完成時觸發 fade-in 動畫;若瀏覽器有啟用快取(cache)機制則用 `HTMLImageElement.complete` 屬性判斷載入完成的時機來觸發 fade-in 動畫。

- 行動裝置(Mobile device)裝置可透過左滑、右滑的手勢切換新聞類別。([程式碼](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/NewsContainer/index.js))

- 當滾動至底部時觸發 [`loadMoreNews()`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/pages/index.js#L136-L162) 載入更多新聞,如果還有資料就加入提示 [`PullHint`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/PullHint/index.js)「載入更多新聞中」,更新頁數並填充下 10 筆報導。

- 可對想追蹤、收藏的新聞報導點擊書籤圖示,使用 `localStorage` 儲存已收藏的新聞資料陣列,實現跨頁攜帶資料,加到**書籤收藏頁(collection.html)**。

- 固定在左下角的部件組 [`Widgets`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/Widgets/index.js),hover 後會展開內部組件,其一可進行主題模式(theme mode)的切換(預設採用使用者偏好的系統設定 `prefers-color-scheme`);另一點擊後回到頁面頂部。

- 手機、平板與電腦的 RWD(Responsive Web Design)。

#### 書籤收藏頁(collections.html)

- 取出 `localStorage` 的新聞資料陣列,展示出已收藏的全部新聞報導。

- 點擊新聞卡 [`NewsCard`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/NewsCard/index.js) 開啟有獨特 name 的分頁,讓點擊相同新聞卡時跳至同樣的分頁,不會重複開啟同一則新聞;另外用滑鼠中鍵或是右鍵(context menu)的「在新分頁中開啟連結」則可重複開啟。

- 可切換成管理模式進行整個 `localStorage` 新聞資料陣列的操作。

- 固定在左下角的部件組 [`Widgets`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/components/Widgets/index.js),hover 後會展開內部組件,其一可進行主題模式(theme mode)的切換(預設採用使用者偏好的系統設定 `prefers-color-scheme`);其二點擊後回到頁面頂部。

- 手機、平板與電腦的 RWD(Responsive Web Design)。

#### 實用的工具函式們(utils)

- 使用 RegExp 注入資料到類 html 格式的自訂 .tpl 檔案。([`injectTpl()`](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/utils/data.js#L7-L11))

- 幫助整理 object 的結構、URL 的參數、form data 的格式等等。([程式碼 L13~L65](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/utils/data.js#L13-L65))

- 操作卷軸和 DOM 相關屬性、新增 Web Components。([程式碼](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/utils/dom.js))

- 建立事件管理用的 class、實作 debounce 和 throttle。([程式碼](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/src/utils/event.js))

### Module Bundler / Dependencies

- Webpack5

- plugins

- [webpack.DefinePlugin](https://webpack.js.org/plugins/define-plugin/)

定義全域的環境變數 `NODE_ENV`,可判斷環境是 `"development"` 或 `"production"` 做出相應處理。

- [html-webpack-plugin](https://www.npmjs.com/package/html-webpack-plugin)

進入點(entry)主要分成 index.html 和 collections.html 兩頁面,共同的依賴包為 common.js。可以根據設定自動注入 title 名稱、SEO-friendly 標籤和解決需手動引入 JavaScript 與 CSS 的困擾。

- [mini-css-extract-plugin](https://www.npmjs.com/package/mini-css-extract-plugin)

`"development"` 環境下會以 CSS 注入 style 標籤的形式加到 HTML 中,但在 `"production"` 環境需要將 CSS 獨立抽離成靜態檔案,有利於提升效能與維護性,因此使用此 plugin。

- [webpack-bundle-analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer)

視覺化分析工具,查看套件、模組的體積。

- loaders

- [babel-loader](https://www.npmjs.com/package/babel-loader)

搭配 Webpack 並與以下核心、preset 和 plugin 進行語法轉譯(transpiling)。

- [@babel/core](https://www.npmjs.com/package/@babel/core)

babel 的核心,負責讀取設定,提供執行轉譯的 API。

- [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env)

讓專案使用新的 JavaScript 語法,並根據瀏覽器環境(browserslist)添加需要的 polyfill,進而節省處理兼容所花費的時間。

- [@babel/plugin-transform-runtime](https://babeljs.io/docs/en/babel-plugin-transform-runtime)

進一步減少 bundle size。處理語法兼容經常會加上 helper function,此 plugin 可以在使用相同 helper function 時,預設以 import 的方式。

- [raw-loader(內建)](https://webpack.js.org/guides/asset-modules/)

載入自訂的 .tpl 檔案之原始內容(utf-8)

- [file-loader(內建)](https://webpack.js.org/guides/asset-modules/)

將特定檔案拷貝到目標資料夾,並返回對應的 URL

- [sass-loader](https://www.npmjs.com/package/sass-loader)
- [postcss-loader](https://www.npmjs.com/package/postcss-loader)
- [css-loader](https://www.npmjs.com/package/css-loader)
- [style-loader](https://www.npmjs.com/package/style-loader)

- Others
- [node-sass](https://www.npmjs.com/package/node-sass)
- [autoprefixer](https://www.npmjs.com/package/autoprefixer)
- [browserslist](https://www.npmjs.com/package/browserslist)
- [webpack-cli](https://www.npmjs.com/package/webpack-cli)
- [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
- [webpack-merge](https://www.npmjs.com/package/webpack-merge)
- [cross-env](https://www.npmjs.com/package/cross-env)

## Proxy Server [連結](https://cj-news-server.vercel.app/)

由於 News API 需要驗證 API Key 才能使用,為避免 API Key 在前端洩漏以及方便後續功能的擴充,所以用 Koa2 架設一個 Proxy Server,簡單路由後使用 Axios 帶上 API Key 到 Request Header 中,然後對目標 API 發送請求,最後獲得所需的新聞資料並回傳給前端。

### Technologies & Features

- 環境變數(Environment variables)

使用 `dotenv` 讀取 .env 和 .env.local 的環境變數。

- .env: 新增檔案命名為 .env,裡面放上本地開發用伺服器的 HOSTNAME 和 PORT。

```plain
HOSTNAME=127.0.0.1
PORT=8080
```

- .env.local: 放上 [Taiwan News API](https://newsapi.org/s/taiwan-news-api) Request Header 之 `X-Api-Key` 要驗的 `NEWS_API_KEY`,可放多支 key 增加請求次數(要用 NEWS_API_KEY 開頭)。

```plain
NEWS_API_KEY0=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEWS_API_KEY1=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEWS_API_KEY2=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEWS_API_KEY3=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NEWS_API_KEY4=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

- [Axios 使用](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/server/src/config/index.js)

- 創建 `Axios` 的實例(instance),設定預設的選項,方便重複使用。
- 使用實例的 Interceptors,管理 Request 和 Response 過程發生的事情,例如處理 429 Too many requests 狀況(每支 API Key 有限制請求次數),更換成另一支 API Key。

- [路由(routes)](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/server/src/routes/news.js)

`/api` 或 `/api/news`: 接收前端的 GET Request,調用中介層函式的 `getNews`

- [控制器(controllers)](https://github.com/CJ-Yang0225/CJ-NewsApp/blob/main/server/src/controllers/news.js)

`getNews(ctx)`: koa2 會調用此中介層函式,放入包裝過、便於開發的 `ctx` 參數(HTTP `req` 和 `res` 等)。用於回傳新聞資料及處理例外事件。

### Dependencies

- [axios](https://www.npmjs.com/package/axios)
- [dotenv](https://www.npmjs.com/package/dotenv)
- [koa](https://www.npmjs.com/package/koa)
- [koa-router](https://www.npmjs.com/package/koa-router)
- [nodemon](https://www.npmjs.com/package/nodemon)