Vite React問題

React Vite clear Cache on client side React Vite

客戶端清除緩存
瀏覽器緩存
在index.html新增

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />


# React Warning non-passive event listener to a scroll-blocking 'touchstart'
反應警告非被動事件偵聽器到滾動阻塞“touchstart”

Vite 打包優化

優化指標

  • FCP(First Contentful Paint):白屏時間(第一個文本繪製時間)
  • Speed Index:首屏時間
  • TTI(Time To Interactive): 第一次可交互的時
  • lighthouse score:Chrome瀏覽器審查工具性能評分

借助 Chrome 完成

  • Network 分析
  • Lighthouse 分析
  • Bundle 分析

Network 分析

網絡加載狀態「 1.9MB 」的 index.js 文件佔用了比較大的加載時間,阻塞了其它資源的加載,是一個優化點

Lighthouse 分析

  • Analyze page load 分析網站加載情形
  • Performance 性能
  • Accessibility 無障礙功能
  • Best Practices 最佳作法
  • Seo
  • Pwa漸進式Web應用

Bundle 分析

安裝 rollup-plugin-visualizer 構建(打包)分析插件

rollup-plugin-visualizer

1
npm i rollup-plugin-visualizer -D

vite vite.config.js引入visualizer()

1
2
3
4
5
6
7
8
9
//引入
import { visualizer } from "rollup-plugin-visualizer";

module.exports = {
plugins: [
vue(),
visualizer()
],
};

打包之後會在項目根目錄生成 stats.html 文件,打開

github說明rollup-plugin-visualizer 構建(打包)分析插件
github說明rollup-plugin-visualizer 構建(打包)分析插件

GZIP 配置

1
npm i vite-plugin-compression -D

//引入
import { vite-plugin-compression } from “vite-plugin-compression”;

1
2
3
4
5
6
7
8
9

//引入vite-plugin-compression
import viteCompression from 'vite-plugin-compression'
module.exports = {
plugins: [
vue(),
viteCompression()
],
};

打包後就會生成 gzip 文件了

github說明-gzip安裝與設置
github說明-gzip安裝與設置

圖片壓縮

1
2
安裝
npm i vite-plugin-imagemin -D

//引入
import viteImagemin from ‘vite-plugin-imagemin’

1
2
3
4
5
6
7
8
9
10
11
//引入
import viteImagemin from 'vite-plugin-imagemin'

export default defineConfig({
plugins: [
vue(),
visualizer(),//構建(打包)分析插件
viteCompression(), //GZIP 配置
viteImagemin(), // 圖片壓縮
],
})

打包以後出現以下數據

vite v5.1.3 building for production…
✓ 18 modules transformed.
dist/index.html 0.46 kB │ gzip: 0.30 kB
dist/assets/index-CKKEtT6e.css 1.27 kB │ gzip: 0.65 kB
dist/assets/index-CWKaKe3g.js 55.26 kB │ gzip: 22.43 kB
✓ built in 712ms

✨ [vite-plugin-imagemin]- compressed image resource successfully:
dist/vite.svg -17% 1.46kb / tiny: 1.22kb

✨ [vite-plugin-imagemin]- 壓縮圖片資源成功:
dist/vite.svg -17% 1.46kb / 小:1.22kb

✨ [vite-plugin-compression]:algorithm=gzip - compressed file successfully:
dist//Users/larahuang/Desktop/作品集/lara_webdesign/assets/index-CKKEtT6e.css.gz 1.24kb / gzip: 0.63kb
dist//Users/larahuang/Desktop/作品集/lara_webdesign/assets/index-CWKaKe3g.js.gz 53.99kb / gzip: 21.89kb

github壓縮說明 image

閉包 (Closure)

閉包範例與解釋

閉包是函式被建立時所在的環境,環境由任意的局域變數所組成,這些變數是由在閉包建立的時間點上存在於作用域裡的所有變數,特殊的環境變數--本來以為應該消失,可是卻依然存在的變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function addFn() {
let num = 10

function ClosureAddFn(newNum) {
num = num + newNum
return num
}
//函示內 return 函示就會變成 『閉包』
return ClosureAddFn
}
const useClosure = addFn()

console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function addFn() {
let num = 10

function ClosureAddFn(newNum) {
num = num + newNum
return num
}
//函示內 return 函示就會變成 『閉包』
return ClosureAddFn
}
const useClosure = addFn()
console.log(useClosure(10)) //20
console.log(useClosure(10)) //30
console.log(useClosure(10)) //40

const useClosure2 = addFn()
console.log(useClosure2(100)) //110
console.log(useClosure2(100)) //210

參考資料

  • 閉包(Closure)的細節:共用與非共用變數
  • 閉包(Closure)的細節(二):模擬私有變數
  • 閉包(Closure)的細節(三):閉包與記憶體洩漏
  • 閉包(Closure)的細節(四):閉包效能與prototype

共用與非共用變數

一般函式在執行完畢時,內部的區域變數會被釋放掉,但如果在執行時產生了閉包。那閉包會保留其所在巢狀函式內的變數。讓他不至於被系統釋放掉。現在有一個問題是,閉包保留下來的變數,會與其他閉包共用嗎?這其實取決於閉包產生的位置與時間點。

同一個呼叫期間內的閉包共享變數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function closuretest(){
x=10;
function changex(cx){
x=cx;
}
function showx(){
console.log(x)
}
f1= changex;
f2= showx;
}
closuretest();
console.log(f2())//顯示10
console.log(f1(5))
console.log(f2())//顯示5

不同呼叫期間的閉包各自擁有變數

1
2
3
4
5
6
7
8
9
10
function closuretest(ox){
var x=ox;
return function(){
console.log(x)
}
}
var f1=closuretest(5);
var f2=closuretest(10);
console.log(f1());//顯示5
console.log(f2());//顯示10

閉包效能與prototype

閉包有效能上的影響,主要是反映在處理速度與記憶體的占用。
以下誤用的的寫法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPlayer(name,race,sex) {
this.name = name.toString();
this.race = race.toString();
this.sex = sex.toString();
this.getName = function() {
return this.name;
};
this.getRace = function() {
return this.race;
};
this.getSex = function() {
return this.sex;
};
}

不同呼叫期間的閉包各自擁有變數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPlayer(name,race,sex) {
this.name = name.toString();
this.race = race.toString();
this.sex = sex.toString();
}
MyPlayer.prototype.getName = function() {
return this.name;
};
MyPlayer.prototype.getRace = function() {
return this.race;
};
MyPlayer.prototype.getSex = function() {
return this.sex;
};

利用原型可以讓物件共享方法,這比上面有效率的多。
這範例說明了prototype與閉包的使用時機,不過嚴格來說上面兩種做法都有點不切實際,因為其實變數本身就是公開屬性了,在為其增加公開方法其實沒必要。比較常的做法是用閉包來模擬並且存取私有變數。用prototype來實作通用方法以及存取通用的私有變數。

Array.of()

Array.of() 方法從任何可迭代物件傳回一個陣列。

Array.of() 方法從任意數量的參數建立一個新數組。

Array.of() 方法可以採用任何類型的參數。

1
2
3
let fruits = Array.of("Banana", "Orange", "Apple", "Mango");
console.log(fruits)
//(4) ['Banana', 'Orange', 'Apple', 'Mango']

參考資料
Array.of()

entries()

entries()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

const fruits = ["Banana", "Orange", "Apple", "Mango"];

// 建立條目
const list = fruits.entries();

// 列出條目
let text = "";
for (let x of list) {
console.log(x)
}
//(2) [0, 'Banana']
//(2) [1, 'Orange']
//(2) [2, 'Apple']
//(2) [3, 'Mango']

參考資料
entries()

fetch 串接

fetch基本介紹

Response(請求回應)對象
  • json()它返回一個承諾,該承諾以將正文文本解析為JSON.
  • text()USVString 它返回一個以(en-US) (文本)解析的承諾。
  • formData()它返回一個用對象解析的承諾FormData。
  • blob返回一個以 .resolve 解決的承諾Blob
  • arrayBuffer()它返回一個用 .resolve 解決的承諾ArrayBuffer
  • redirect()使用不同的 URL 創建新的響應。
  • clone()建立一份Response的複製品。
  • error() 返回與網絡錯誤關聯的新對象。
參考 MDN 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const 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 為範例:
1
2
3
4
5
6
7
8
9
10
11
12
13
async 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 為範例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async 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()
參考文獻

join()

join()

join() 方法以字串形式傳回數組。
join() 方法不會更改原始陣列。
可以指定任何分隔符號。 預設為逗號 (,)。

1
2
3
4
5
6
7
8
const fruits = ["Banana", "Orange", "Apple", "Mango"];
let text = fruits.join();
console.log(text);
//Banana,Orange,Apple,Mango
let result = Array.isArray(fruits);
console.log(result);
console.log(Array.isArray(text));
//false

參考資料
join()

1
2
3
4
const fruits = ["Banana", "Orange", "Apple", "Mango"];
let text = fruits.join(" and ");
console.log(text);
//Banana and Orange and Apple and Mango

join()