2023-05-06 Vue fetch 串接 fetch基本介紹Response(請求回應)對象 json()它返回一個承諾,該承諾以將正文文本解析為JSON. text()USVString 它返回一個以(en-US) (文本)解析的承諾。 formData()它返回一個用對象解析的承諾FormData。 blob返回一個以 .resolve 解決的承諾Blob arrayBuffer()它返回一個用 .resolve 解決的承諾ArrayBuffer redirect()使用不同的 URL 創建新的響應。 clone()建立一份Response的複製品。 error() 返回與網絡錯誤關聯的新對象。 參考 MDN 文件 1234567891011121314const api='https://api-node-mongo.onrender.com/api/banners';fetch(api,{ method: "GET",headers: { "Content-Type": "application/json" },}) .then((res) => { // 當 fetch 成功後,會回傳 ReadableStream 物件,回傳 ReadableStream 物件,可使用 .json() 等等方法,取得對應資料。 return res.json(); }) .then((data) => { console.log(data); }) .catch((error) => { console.log(error)}) fetch 搭配 async/await 範例 狀況一:需等 Ajax1 完成後,才執行 Ajax2 這種狀況使用 async/await 或是 原生 Promise 鏈式寫法都能達成,這邊以 async/await 為範例: 12345678910111213async function useAjax() { try { const ajax1 = await fetch('https://api-node-mongo.onrender.com/api/banners') const data1 = await ajax1.json() console.log('data1', data1) const ajax2 = await fetch('https://api-node-mongo.onrender.com/api/news') const data2 = await ajax2.json(); console.log('data2', data2) } catch (error) { console.log('錯誤:', err) }}useAjax() 狀況二: 需等 Ajax1、Ajax2 完成後,才執行 Ajax3 這種狀況其實繼續使用 async/await 來寫也可以達成,不過會變成: 執行 Ajax1 ⇒ Ajax1 完成 ⇒ 執行 Ajax2 ⇒ Ajax2 完成 ⇒ 執行 Ajax3 這樣就會浪費較多時間再等待 Ajax 回傳,所以比較好的方法就是使用 Promise.all() 搭配 async/await ,或是單純 Promise.all() 加上 Promise 鏈式寫法,這邊以 Promise.all() 搭配 async/await 為範例。 1234567891011121314151617async function useAjax() { try { const ajax1 = fetch('https://api-node-mongo.onrender.com/api/banners') const ajax2 = fetch('https://api-node-mongo.onrender.com/api/news') const [res1, res2] = await Promise.all([ajax1, ajax2]) const data1 = await res1.json() const data2 = await res2.json() console.log('data1', data1) console.log('data2', data2) const ajax3 = await fetch('https://api-node-mongo.onrender.com/api/products') const data3 = await ajax3.json() console.log('data3', data3) } catch(error) { console.log('錯誤:', err) }}useAjax() 參考文獻 鐵人賽:ES6 原生 Fetch 遠端資料方法 MDN 文件 - fetch
2023-05-06 Vue flatMap() 1234const myArr = [1, 2, 3, 4, 5,6];const newArr = myArr.flatMap((x) => x * 2);console.log(newArr)//(6) [2, 4, 6, 8, 10, 12]
2023-05-06 Vue join() join()join() 方法以字串形式傳回數組。join() 方法不會更改原始陣列。可以指定任何分隔符號。 預設為逗號 (,)。 12345678const fruits = ["Banana", "Orange", "Apple", "Mango"];let text = fruits.join();console.log(text);//Banana,Orange,Apple,Mangolet result = Array.isArray(fruits);console.log(result);console.log(Array.isArray(text));//false 參考資料join() 1234const fruits = ["Banana", "Orange", "Apple", "Mango"];let text = fruits.join(" and ");console.log(text);//Banana and Orange and Apple and Mango join()
2023-05-06 Vue sort() sort() 方法以字母順序對陣列的元素進行排序。123456// Create an Arrayconst fruits = ["Banana", "Orange", "Apple", "Mango"];// Sort the Arrayfruits.sort();console.log(fruits.sort()) 用匿名函式當排序條件如果希望完全依照自己給的條件排序,最好是給sort()一個帶有條件的匿名函式來當參數,這個匿名函數必須要有兩個參數,然後再依照這兩個參數比較回傳的值,來當排序依據。 sort()會依匿名函式的參數與回傳的值為精確的排序規則: 當回傳值為負數時,那麼前面的數放在前面當回傳值為正整數,那麼後面的數在前面當回傳值為零,保持不動。這個函式會每次都先拿兩組陣列裡的元素來比較,當回傳值為正,會讓後面的數跑到前面,以上述的規則來移動元素,大家有沒覺得這種方式很眼熟,其實就是使用冒泡排序法來達到排序。 123456789101112131415// 沒有給參數的預設排序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()
2023-05-06 Vue try...catch 流程控制與例外處理 try…catch 流程控制與例外處理 try...catch可以使用在錯誤或是例外情形(expection),錯誤可能是亂呼叫不存在的變數or函式,但例外情形是可以自己定義的 throw new Error() 要放在try區塊裡面才會被catch接到,丟出例外的throw new Error() JS內建提供錯誤處理的物件,會連接到catch的error.message try...catch...finally 1234567try { // 欲執行的程式碼} carch(error) { // 當錯誤發生時,欲執行的程式碼} finally { //錯誤與否都會被執行的區塊}
2023-05-04 Vue Node express 設定 CORS Node express 設定 CORS Web確保客戶端與伺服器之間的安全無縫隙通訊至關重要。 跨來源共享的CORS需要正確的配置 安裝 corscurs npm 1npm install cors 引入與使用 1234567const express = require('express');const cors = require('cors');const app = express();// 將其實作為中間件app.use(cors()); 配置來源 根據應用程式的要求自訂 CORS 設定 12345678910111213//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 設定套用至特定路由: 123app.get('/authenticated-route', cors(corsOptions), (req, res) => { // Your route logic here });
2023-05-02 Style Css Img Filter Demo blur() 模糊效果1filter: blur(5px); brightness() 調整亮度函示內放入 % 數來調整亮度,< 100% 會減少亮度,> 100% 會增加亮度。0% 會呈現全黑,預設值為 1 。 1filter: brightness(2) contrast() 調整對比度函示內放入 % 數來調整對比度,< 100% 會減少對比度,> 100% 會增加對比度。 1filter: 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 代表變黑白的。 1filter:grayscale(1); hue-rotate() 色相相轉透過色相旋轉來調整顏色呈現,函示內放入 deg 色環角度值來調整圖像灰度,值介於 0deg - 360deg 間,超過 360deg 的值相當於繞了一圈。 1filter: hue-rotate(90deg); invert() 負面效果所謂的負片就是反轉成顏色和明暗程度,函示內放入 % 數設定反轉的程度,值介於 0-100% 間,預設值為 0%。 1filter: invert(100%); opactiy 調整透明度函示內放入 % 數設定透明度,值介於 0-100% 間,預設值為 100%。 1filter: opacity(50%); saturate() 調整飽和度函示內放入 % 數設定飽和度,預設值為 1<100% 越來越不飽和>100% 高度飽和 1filter: saturate(200%); sepia() 深褐色效果函示內放入 % 數設定深褐色的程度,值介於 0-100% 之間,預設值是 0。 1filter: sepia(100%) 複合效果12/* 同時設定對比度、亮度、飽和度*/filter: contrast(175%) brightness(3%) saturate(200%);
2023-05-02 Vue Css Gap 格線容器 display:grid grid-template-columns: repeat(行的數量, minmax(0, 1fr)),fr 代表格線容器內,可用空間的分塊(fraction),repeat::重複標記是軌道表列一部分。 gap:間距 1234display: 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指定網格列之間的間距。 123456789101112.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; } }
2023-05-01 Vue 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 1npm install --save react-hook-form 新增Register.jsx 載入import { useForm } from "react-hook-form" 載入axios const { register, handleSubmit, formState: { errors } } = useForm(); 12345678910111213141516171819202122232425262728293031323334353637383940414243import 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> )}//輸出Registerexport default Register
2023-04-28 Vue React Owl Carousel 1npm install --save react-owl-carousel 在vite.config.js新增 import inject from '@rollup/plugin-inject' inject({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', }), 12345678910111213141516import { 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 發生錯誤訊息如圖 安裝 1npm install @rollup/plugin-inject --save 在使用的組件或是頁面載入 123456789101112131415161718192021222324252627282930313233343536373839import { 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