### Bundle
在使用 React 時是一定會使用到 Webpack 等等工具來把 JSX、CSS、IMAGES... 等等進行打包。如果你的 APP 已經建立得非常大時,產生的 Bundle 檔案也會非常巨大。使用者在下載時就會花上很多時間了喔 !
### 解決方法
我們可以通過使用 React 內建的 Lazy loading 來解決一次過下載巨大 Bundle 檔案的問題。它的原理是把單一的 Bundle 檔案依不同的 package 來分割開,然後在使用者實際使用時才動態載入。
### 內裏是 Promise
我們有以下的 module ,作用把兩個傳入的數字相加。
```js
// math.js
export default sum(a, b) => a + b;
```
在傳統的使用方法,我們是以下這樣 :
```js
import sum from './math.js';
console.log(sum(1, 2)); // 3
```
使用 Lazy Loading 的話,載入的 object 就會變成了 Promise 物件:
```js
import('./math.js')
.then(sum => console.log(sum(1, 2))); // 3
```
### 實際使用
在實際使用上,我們可以通過使用 `React.lazy` 方法來簡單化整個載入的流程。
```js
// 載入 SomeComponent
const SomeComponent = React.lazy(() => import('./SomeComponent.jsx'));
```
然可以作為一般的 Component 來使用,但是需要放在 React.Suspense 元件內。
```js
<React.Suspense fallback={<div>Loading...</div>}>
<SomeComponent />
</React.Suspense>
```
React.Suspense 是處理其子 Component 如果有 Lazy Loading 動作時,就會劃出 fallback 的元件出來,等待 Lazy Load 完成後,就會顯示元件。
### 筆者推介使用方法
因為在 React 內得多元件都可以重用,但是如果有使用 React Router 的話,不同 URL 之間的元件是一定不會重用的,所以我們可以簡單把 React.Suspense 包住整個 Router。然後把不同 Route 內的 Component 都轉成使用 Lazy Load,這樣就已經可以簡單快速地分割出合適的 Bundle 檔案了。
### 官方文件
官方文件 : https://zh-hant.reactjs.org/docs/code-splitting.html
### Image slider / Carousels
在網站上很多時都會使用流水式的 Banner 來告訴使用者最近的事情。

### 選擇現成的 Library
初時筆者找到了 `react-alice-carousel` 這個套件,好像很簡單易用,所以後快速的試了一下。不過發現有個問題到現時最新的 Version 都好像還沒有修正好,就是在圖片滑動的中途,如果整個 Component 出現 unmount 的情況,就會出現 Error : Updated on unmounted component。
雖然在它們的 github 上好像沒有人 report 過這樣的 issue。不過筆者確實是遇到了,所以還是選擇另家的好了。
### nuka-carousel
這是另一家大神寫出來的 Image slider,使用上也是非常之簡單。
Github: https://github.com/FormidableLabs/nuka-carousel
下面會簡單記錄一下使用的方法。
### 安裝
可以使用 npm 來安裝。
```sh
$ npm install nuka-carousel
```
### 使用
使用上也是十分之簡單,只要用 Carousel 包住想要用來 Slide 的內容就可以了,高度是自動的,內容可以用 Array 填入就可以。配上 Div 使用 background 來使用就非常得心應手。
```js
import React from 'react';
import Carousel from 'nuka-carousel';
export default class extends React.Component {
render() {
return (
<Carousel>
<img src="https://via.placeholder.com/400/ffffff/c0392b/&text=slide1" />
<img src="https://via.placeholder.com/400/ffffff/c0392b/&text=slide2" />
<img src="https://via.placeholder.com/400/ffffff/c0392b/&text=slide3" />
<img src="https://via.placeholder.com/400/ffffff/c0392b/&text=slide4" />
<img src="https://via.placeholder.com/400/ffffff/c0392b/&text=slide5" />
<img src="https://via.placeholder.com/400/ffffff/c0392b/&text=slide6" />
</Carousel>
);
}
}
```
送上 div 顯示圖片的咒語:
```js
<div
style={{
background: '#FFFFFF url(https://via.placeholder.com/400/ffffff/c0392b/&text=slide1) center center / cover no-repeat',
height: '0px',
paddingBottom: '30%'
}}
/>
```
以下是筆者使用的 Config:
```js
<Carousel
autoplay={ true }
autoplayInterval={ 4000 }
withoutControls={ false }
wrapAround={ true }
speed={ 1000 }
renderCenterLeftControls={ () => {} }
renderCenterRightControls={ () => {} }
>
{items}
</Carousel>
```
### 設定 Nginx
要在 nginx 上取得 proxy 的 client address,需要事先在設定 nginx 時就要加入下
`--with-http_realip_module` 的設定。如果安裝的 nginx 是由 apt 上下載的,就可以使用以下指令來查看你的 nginx 有沒有設定 `--with-http_realip_module` 模組。
```sh
$ nginx -V
nginx version: nginx/1.18.0 (Ubuntu)
built with OpenSSL 1.1.1f 31 Mar 2020
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-5J5hor/nginx-1.18.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-compat --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module
```
如果在輸出的結看看到了模組的名稱,就代表已經設定好了。
### 設定來自 Cloudflare 的 IP address
然後需要把來自 Cloudflare 的 IP address 設定到 nginx.conf 檔案內。
參考 Cloudflare 的網站 : https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses-with-mod-cloudflare-
```txt
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
```
再設定 Cloudflare 的 IP 寫入 header 欄位名稱。
```txt
real_ip_header CF-Connecting-IP;
#real_ip_header X-Forwarded-For;
```
設定好後,在 Nginx 的 Virtual Host Config 檔案內使用變數 $remote_addr 時,就可以取得 Cloudflare 傳入的真正地址了。
下面是 Virtual Host Config 檔案使用 Reverse proxy 的例子 :
```txt
location / {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass https://localhost:81;
}
```
由於不同之間的 browser 都有差異,所以如果要保險起見,需要以多種的語法來最最得 scroll top 值來確保安全。而已經有大神寫好了這個簡單的語法 !
```js
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
```