sort()

sort() 方法以字母順序對陣列的元素進行排序。

1
2
3
4
5
6
// Create an Array
const fruits = ["Banana", "Orange", "Apple", "Mango"];

// Sort the Array
fruits.sort();
console.log(fruits.sort())

用匿名函式當排序條件

如果希望完全依照自己給的條件排序,最好是給sort()一個帶有條件的匿名函式來當參數,這個匿名函數必須要有兩個參數,然後再依照這兩個參數比較回傳的值,來當排序依據。

sort()會依匿名函式的參數與回傳的值為精確的排序規則:

當回傳值為負數時,那麼前面的數放在前面
當回傳值為正整數,那麼後面的數在前面
當回傳值為零,保持不動。
這個函式會每次都先拿兩組陣列裡的元素來比較,當回傳值為正,會讓後面的數跑到前面,以上述的規則來移動元素,大家有沒覺得這種方式很眼熟,其實就是使用冒泡排序法來達到排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 沒有給參數的預設排序
const arr = [5, 9, 1, 3, 2, 6];
arr.sort(); // [1, 2, 3, 5, 6, 9]

// 以匿名函式回參數做「升序」排序
arr.sort(function(a, b) {
return a - b; // a - b > 0
});
// [1, 2, 3, 5, 6, 9]

// 如果要反過來做「降序」排序
arr.sort(function(a, b) {
return b - a;
});
// [9, 6, 5, 3, 2, 1]

參考資料
sort()

try...catch 流程控制與例外處理

try…catch 流程控制與例外處理

  • try...catch可以使用在錯誤或是例外情形(expection),錯誤可能是亂呼叫不存在的變數or函式,但例外情形是可以自己定義的
  • throw new Error() 要放在try區塊裡面才會被catch接到,丟出例外的throw
  • new Error() JS內建提供錯誤處理的物件,會連接到catch的error.message
  • try...catch...finally
1
2
3
4
5
6
7
try {
// 欲執行的程式碼
} carch(error) {
// 當錯誤發生時,欲執行的程式碼
} finally {
//錯誤與否都會被執行的區塊
}

Node express 設定 CORS

Node express 設定 CORS

Web確保客戶端與伺服器之間的安全無縫隙通訊至關重要。
跨來源共享的CORS需要正確的配置

安裝 cors

curs npm

1
npm install cors

引入與使用

1
2
3
4
5
6
7
const express = require('express');
const cors = require('cors');

const app = express();

// 將其實作為中間件
app.use(cors());
配置來源
根據應用程式的要求自訂 CORS 設定
1
2
3
4
5
6
7
8
9
10
11
12
13
//origin單個時
const corsOptions = {
origin: 'https://www.nileshblog.tech/',
optionsSuccessStatus: 200,
};

//origin多個時
const corsOptions = {
origin: ['https://localhost:5175', 'https://192.168.220.20:5175','https://www.nileshblog.tech/'],
optionsSuccessStatus: 200,
};
//使用
app.use(cors(corsOptions))
處理路由中的 CORS
如果需要,將 CORS 設定套用至特定路由:
1
2
3
app.get('/authenticated-route', cors(corsOptions), (req, res) => {
// Your route logic here
});

Css Img Filter

Demo

blur() 模糊效果

1
filter: blur(5px);

brightness() 調整亮度

函示內放入 % 數來調整亮度,< 100% 會減少亮度,> 100% 會增加亮度。0% 會呈現全黑,預設值為 1 。

1
filter: brightness(2)

contrast() 調整對比度

函示內放入 % 數來調整對比度,< 100% 會減少對比度,> 100% 會增加對比度。

1
filter: contrast(200%)

這個函數的特性和 box-shadow 的屬性很相似,但 drop-shadow 可以製作和物件不透明區域完全相同形狀的陰影,詳細比較可以參考這篇文章

drop-shadow() 可帶入的 4 個參數分別為:

  • (必填)offset-x:陰影水平偏移量的長度
  • (必填)offset-y:陰影垂直偏移量的長度
  • blur-radius:隱影模糊程度,值越大越模糊
  • color:陰影顏色

grayscale() 調整灰階程度

函示內放入 % 數或、0-1 來調整圖像灰度,值介於 0-100% 或 0-1,0% 和 0 代表顏色不變,為預設值,100% 和 1 代表變黑白的。

1
filter:grayscale(1);

hue-rotate() 色相相轉

透過色相旋轉來調整顏色呈現,函示內放入 deg 色環角度值來調整圖像灰度,值介於 0deg - 360deg 間,超過 360deg 的值相當於繞了一圈。

1
filter: hue-rotate(90deg);

invert() 負面效果

所謂的負片就是反轉成顏色和明暗程度,函示內放入 % 數設定反轉的程度,值介於 0-100% 間,預設值為 0%。

1
filter: invert(100%);

opactiy 調整透明度

函示內放入 % 數設定透明度,值介於 0-100% 間,預設值為 100%。

1
filter: opacity(50%);

saturate() 調整飽和度

函示內放入 % 數設定飽和度,預設值為 1<100% 越來越不飽和>100% 高度飽和

1
filter: saturate(200%);

sepia() 深褐色效果

函示內放入 % 數設定深褐色的程度,值介於 0-100% 之間,預設值是 0。

1
filter: sepia(100%)

複合效果

1
2
/* 同時設定對比度、亮度、飽和度*/
filter: contrast(175%) brightness(3%) saturate(200%);

Css Gap 格線容器

  • display:grid
  • grid-template-columns: repeat(行的數量, minmax(0, 1fr)),fr 代表格線容器內,可用空間的分塊(fraction),repeat::重複標記是軌道表列一部分。
  • gap:間距
1
2
3
4
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
// grid-template-columns: 200px 200px 200px;
gap: 1.25rem;
  • display:grid
  • grid-template-columns: repeat(行的數量, minmax(0, 1fr)),fr 代表格線容器內,可用空間的分塊(fraction),repeat::重複標記是軌道表列一部分。
  • grid-column-gap指定網格欄之間的間距。
  • grid-row-gapp指定網格列之間的間距。
1
2
3
4
5
6
7
8
9
10
11
12
.two-wrapper{
display: grid;
grid-template-columns: repeat(3, 1fr);
// grid-auto-rows: minmax(50px, auto);
grid-column-gap: 10px; //指定網格欄之間的間距。
grid-row-gap: 1em; //指定網格列之間的間距。
margin-top:30px;
div{
border:1px solid green;
display: flex;
}
}

React Hook Form

React Hook Form 是一個專門針對 React functional component 設計的,用來更好管理 form 表單的 library。

官方更推薦我們使用 Controlled Component,我想主因可能是希望讓 state 成為表單欄位值的唯一來源
使用 Controlled Component 就必須面對容易頻繁 re-render 的問題,如果要寫一個欄位數量龐大的 form 表單,就會遇到 render 效率的問題。
因為每一個欄位都要一組 state 來控制,就會需要寫相當多非常類似的 state 與 useState,也是一個滿費工的過程。

優點
  • 即便遇到龐大欄位的 Form 表單,依然不會頻繁 re-render,有較好的 UX
  • 提供方便的介面處理 validation (透過 yup)
  • 就像是在使用 hook 一樣去使用 useForm 即可,不需額外 import component
缺點
  • 因為是 hook,僅限 React functional component 使用
  • 若已經習慣 Controlled Component,反而會覺得有點難轉換到 React Hook Form
1
npm install --save react-hook-form 

新增Register.jsx

  • 載入import { useForm } from "react-hook-form"
  • 載入axios
  • const { register, handleSubmit, formState: { errors } } = useForm();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import React from "react";
import { useForm } from "react-hook-form";
import axios from 'axios';
import "../assets/scss/form.scss";
const Register = () =>{
const { register, handleSubmit, formState: { errors }, } = useForm();
//表單onSubmit
const onSubmit = async (data) => {
const api = `${import.meta.env.VITE_API_DOMAIN}/auth/register`;
try{
await axios.post(api,data)
.then(res =>{
if (res.status===200) {
console.log(res.data)
alert('註冊成功')
}
})
}
catch (error) {
console.log(error,"GET Error!!")
}
}
return (
<div className="register_area">
<div className="title"><b>註冊帳號</b></div>
<form onSubmit={handleSubmit(onSubmit)}>
<input type="text" className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
placeholder="username" { ...register('username', {required: { value: true, message: '欄位必填'}})} />
{ errors.username && <span> {errors.username?.message}</span> }
<input type="text" className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" placeholder="email" { ...register('email', {required: { value: true, message: '欄位必填'},pattern: { value: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g, message: '不符合 Email 規則'}})}/>
{ errors.email && <span> {errors.email?.message}</span> }
<input type="password" className="block w-full rounded-md border-0 px-3.5 py-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" placeholder="password" { ...register('password', {required: { value: true, message: '欄位必填'},})} />
{ errors.password && <span> {errors.password?.message}</span> }
<input type="submit" value="登入" className="block w-full rounded-md bg-indigo-600 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" />
</form>
</div>
)


}
//輸出Register
export default Register

React Owl Carousel

1
npm install --save react-owl-carousel 

在vite.config.js新增

  • import inject from '@rollup/plugin-inject'
  • inject({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', }),
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { defineConfig } from 'vite'
import inject from '@rollup/plugin-inject'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
inject({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
}),
]
})

vite 發生錯誤訊息如圖 安裝
1
npm install @rollup/plugin-inject --save

在使用的組件或是頁面載入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { useEffect, useState } from "react"
import $ from 'jquery';
import OwlCarousel from "react-owl-carousel";
import "owl.carousel/dist/assets/owl.carousel.css";
import "owl.carousel/dist/assets/owl.theme.default.css";

const App = () =>{
const [banners, setBanners] = useState([]);
const api = "https://xxx/api/banners";
const getBanners = () => {
fetch(api, { method: "GET" })
.then((res) => res.json())
.then((data) => {
setBanners(data);
})
.catch((err) => {
console.log(err);
});
};

useEffect(() => {
getBanners();
}, []);
return (
<div>
<OwlCarousel className='owl-theme' loop items={1} autoplay={true} nav>
{banners.map(item => (
<div className="item" key={item._id}>
<img src={item.image} />
<div>{item.subject}</div>
</div>
))}
</OwlCarousel>
</div>
)
}

export default App

Vite React router

安裝react-router

Single Page Application,中文又稱單頁式應用程式( SPA)

1
npm install --save react-router-dom@6.4.1

React Router 起手式

  • HashRouter用於 Web 瀏覽器,因為某些原因不應(或不能)將 URL 發送到服務器。
打開main.jsx檔案
  1. 引入 import { HashRouter } from "react-router-dom"
  2. HashRouter 是一個元件概念,接著只需要使用它將整個 < App /> 包起來即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as React from "react";
import * as ReactDOM from "react-dom";
import { HashRouter } from "react-router-dom";
import App from './App'
import './index.css'

const root=ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<HashRouter>
<App />
</HashRouter>
</React.StrictMode>
)

以上成功引入了 React Router

React Router 基本使用

使用 Route

  • path:定義路徑
  • element:定義對應的元件
1
<Route path="/todolist" element={ <ToDoList/> }>

< Route > 只能包在 < Routes > 底下使用,因此正確來講必須這樣寫才對

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React from 'react'
import { HashRouter, Route, Routes } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
import ToDoList from "./TodoList";

const root=ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<HashRouter>
<Routes>
<Route path="/" element={ <App /> } />
<Route path="/todolist" element={ <ToDoList /> } />
</Routes>
</HashRouter>
</React.StrictMode>
)

重新 npm run dev

打開http://localhost:3000/#/todolist

巢狀路由

路徑位置App.jsx

  • 載入import { Link } from "react-router-dom"
  • Link to="/"
App=>載入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Outlet, Link } from 'react-router-dom';

const App = () => {
return (
<>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<Link to="/" className="border p-3 hover:bg-indigo-600 duration-500">Home</Link>
</li>
<li className="mr-3">
<Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
</li>
<li className="mr-3">
<Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
</li>
</ul>
</nav>
<h1>App</h1>
<Outlet />
</>
)
}

export default App;

路徑內容main.jsx
以下App Route 包裹 ToDoList元件
< Route path=”/“ element={} >
<Route path=”todolist” element={ } >
< /Route>
以下Admin Route 包裹 AdminProducts元件
< Route path=”/admin” element={} >
<Route path=”products” element={ } >
< /Route>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React from 'react'
import ReactDOM from 'react-dom/client'
import { HashRouter, Route, Routes } from "react-router-dom";
import App from './App'
import './index.css'

import ToDoList from "./views/ToDoList";
import Admin from "./views/Admin";
import AdminProducts from './views/AdminProducts';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<HashRouter>
<Routes>
<Route path="/" element={<App />} >
<Route path="todolist" element={ <ToDoList /> } />
</Route>
<Route path="/admin" element={ <Admin /> } >
<Route path="products" element={<AdminProducts /> } />
</Route>
</Routes>
</HashRouter>
</React.StrictMode>
)

Admin =>載入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Outlet, Link } from 'react-router-dom';

const Admin = () => {
return (
<>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
</li>
<li className="mr-3">
<Link to="/admin/products" className="border p-3 hover:bg-indigo-600 duration-500">AdminProducts</Link>
</li>
</ul>
</nav>
<h1>Admin</h1>
<Outlet />
</>
)
}

export default Admin;

NavLink 切換頁面的方式

檔案App.jsx,將 Link 通通改成 NavLink

<NavLink to=”/“ className={({ isActive }) =>
[
‘border p-3 hover:bg-indigo-600 duration-500’,
isActive ? ‘router-link-active’ : null
].join(‘ ‘)
}>Home

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { Outlet, Link ,NavLink} from 'react-router-dom';

const App = () =>
{

return (
<>
<nav className="px-5 flex items-center h-[60px] bg-indigo-500 text-white">
<h1 className="mr-auto text-2xl">React TodoList</h1>
<ul className="flex">
<li className="mr-3">
<NavLink to="/" className={({ isActive }) =>
[
'border p-3 hover:bg-indigo-600 duration-500',
isActive ? 'router-link-active' : null
].join(' ')
}>Home</NavLink>
</li>
<li className="mr-3">
<Link to="/todolist" className="border p-3 hover:bg-indigo-600 duration-500">ToDoList</Link>
</li>
<li className="mr-3">
<Link to="/admin" className="border p-3 hover:bg-indigo-600 duration-500">Admin</Link>
</li>
</ul>
</nav>
<h1>App</h1>
<Outlet />
</>
)
}

export default App;

檔案index.css新增以下

1
2
3
.router-link-active {
@apply bg-indigo-800;
}

React Router - Hook

//載入
import { useEffect } from “react”;
常用的 React Router Hook

  • useLocation: 載入
  • useParams: 載入
  • useNavigate: 載入
  • useRoutes: 載入

useLocation

在App.jsx內舉例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//載入
import { useEffect } from "react";
import { useLocation } from "react-router";

const App = () =>
{
const { hash, key, pathname, search, state } = useLocation();
useEffect(() => {
console.log('hash:', hash); // hash: #products
console.log('key:', key); // key: ry5x4mjc
console.log('pathname:', pathname); // pathname: /products
console.log('search:', search); // search: ?q=ray
console.log('state:', state); // state: { products: { id: '1', name: 'QQ 產品' } }
},[]);

return (
<>
<div>App</div>
</>
)
}
export default App;

useParams 動態參數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { useEffect } from "react";
import { useParams,Navigate } from 'react-router-dom';

const App = () =>
{
const { id } = useParams();
useEffect(() => {
console.log('params id:', id); // params id: 123
},[]);

return (
<>
<div>App</div>
</>
)
}
export default App;

useNavigate 重新導向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { useEffect } from "react";
import { useNavigate } from 'react-router-dom';

const App = () =>
{
const navigate = useNavigate();
useEffect(() => {
console.log('params id:', id); // params id: 123
},[]);

return (
<>
<h1>Products</h1>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate('/products/123')}
>
點我跳轉
</button>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate(-1) }
>
前一頁
</button>
<button
className="border-1 bg-indigo-500 p-3 text-white"
onClick={ () => navigate(1) }
>
後一頁
</button>
<Outlet />
</>
)
}
export default App;

Lazy Loading:可以將我們的程式碼分割成不同的 chunk,然後在你需要或請求的時候才去載入相對應檔案

將以上改寫,新增routes資料夾index.jsx

  • 載入lazy
  • 載入useRoutes
  • 頁面載入與lazy寫法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//載入lazy
import { lazy } from 'react';
//載入useRoutes
import { useRoutes } from 'react-router-dom';
//頁面載入,路由懶加載
const App = lazy(() => import('../App'));
const ToDoList = lazy(() => import('../views/toDoList'));
const Products = lazy(() => import('../views/Products'));
const Product = lazy(() => import('../views/Product'));
const Admin = lazy(() => import('../views/admin/Admin'));

const routes = [
{
path: '/',
element: <App />,
},
{
path: '/todoList',
element: <ToDoList />,
},
{
path: "/product/:id",
element: <Product />,
},
{
path: '/admin',
element: <Admin />,
//巢狀
children: [
{
path: 'products',
element: <Products />,
}
],
}
];

export default () => useRoutes(routes);

在views /Product.jsx內
載入useParams
const { id } = useParams();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useEffect } from "react";
import { useParams } from 'react-router-dom';
const Product = () =>{
const { id } = useParams();
useEffect(() => {
console.log('params id:', id);
},[]);
return (
<div>
<p className="text-center text-3xl mt-20">The post ID is {id}</p>
</div>
)
}

export default Product

Vite React Tailwind CSS

Vite React 安裝 Tailwind CSS

Tailwind

1
npm install --save tailwindcss postcss autoprefixer

建立Tailwind CSS config & PostCSS config

1
npx tailwindcss init -p

Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js

專案底下會創建出 tailwind.config.cjs 與 postcss.config.cjs 兩個檔案

打開 tailwind.config.js
加入
“./index.html”,
“./src/**/*.{js,ts,jsx,tsx}”,

1
2
3
4
5
6
7
8
9
10
11
12
13
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}


index.css 內容全部刪除,改成以下

1
2
3
@tailwind base;
@tailwind components;
@tailwind utilities;

Vite React第一個專案與環境變數

使用終端機 vite 安裝react

Vite 需要在 Node.js 版本 14.18+、16+
vite版本必須使用3.1.0

1
npm create vite@3.1.0 專案名稱
  1. Select a framework:React
  2. Select a variant:Javascript
1
2
3
4
5
cd 專案名稱

npm install
//啟動
npm run dev

http://localhost:5173/

環境變數設定

修改vite.config.js 檔案,加入以下

  • 載入import path from "path"
  • 載入import { resolve } from 'path'
  • export default defineConfig內新增envDir: path.resolve(__dirname, "./env")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from "path"
import { resolve } from 'path';

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
base: "/",
resolve: {
alias: {
'@': resolve(__dirname, '/src')
}
},
envDir: path.resolve(__dirname, "./env")
})

在專案內新增env資料夾,資料夾內新增:.env.development,.env.production,.env.staging

開發端環境.env.development

1
VITE_API_URL = '開發端環境Api網址'

正式主機位置.env.production

1
2
VITE_API_URL = '正式主機位置Api網址'

測試環境.env.staging

1
2
VITE_API_URL = '測試環境位置Api網址'

使用

1
import.meta.env.VITE_API_URL