JavaScript性能優化實戰(11):前沿技術在性能優化中的應用

引言

隨著Web應用復雜度和性能需求不斷提高,傳統的JavaScript優化技術已經無法滿足某些高性能計算場景的需求。本文將深入探討前沿Web技術如何突破JavaScript的性能瓶頸,為Web應用提供接近原生應用的性能體驗。從底層計算到圖形渲染,從并發處理到動畫優化,我們將通過實際案例展示這些技術如何在真實項目中發揮作用。

WebAssembly在計算密集型場景中的應用

WebAssembly (Wasm) 作為一種低級的類匯編語言,為Web平臺提供了接近原生性能的執行能力。它能夠以接近機器代碼的效率運行,特別適合計算密集型任務。

WebAssembly基礎與性能特性

WebAssembly的核心性能優勢包括:

  1. 編譯執行而非解釋執行:WebAssembly以二進制格式分發,瀏覽器可以直接編譯成機器碼而無需解析和優化。
  2. 靜態類型系統:明確的類型信息使編譯器可以生成高效代碼。
  3. 內存安全:采用沙箱執行模型,內存訪問受限于分配的內存區域。
  4. 與JavaScript的高效互操作:可以與JavaScript無縫集成。
// 加載WebAssembly模塊的基本流程
async function loadWasmModule(wasmUrl, importObject = {}) {try {// 獲取WebAssembly二進制代碼const response = await fetch(wasmUrl);const buffer = await response.arrayBuffer();// 編譯和實例化WebAssembly模塊const wasmModule = await WebAssembly.instantiate(buffer, importObject);// 返回導出的函數和變量return wasmModule.instance.exports;} catch (error) {console.error('加載WebAssembly模塊失敗:', error);throw error;}
}// 使用示例
async function initWasmCalculator() {const wasmExports = await loadWasmModule('/calculator.wasm');// 調用WebAssembly函數const result = wasmExports.fibonacci(40);console.log('Fibonacci計算結果:', result);return wasmExports;
}

計算密集型場景性能對比

在計算密集型任務中,WebAssembly表現出明顯的性能優勢:

算法/操作JavaScript執行時間WebAssembly執行時間性能提升
斐波那契數列(n=45)12,450ms980ms12.7倍
圖像處理(4K圖像模糊)2,800ms230ms12.2倍
物理引擎(1000個物體碰撞)75ms/幀8ms/幀9.4倍
數據加密(AES-256)180ms/MB15ms/MB12倍

WebAssembly實戰:圖像處理優化

以圖像處理為例,我們可以將計算密集的操作從JavaScript遷移到WebAssembly:

// ImageProcessor.js
class ImageProcessor {constructor() {this.wasmReady = false;this.wasmModule = null;this.init();}async init() {try {// 加載WebAssembly模塊const importObject = {env: {memory: new WebAssembly.Memory({ initial: 256, maximum: 512 }),abort: () => console.error('WebAssembly內存分配失敗')}};this.wasmModule = await loadWasmModule('/image_processor.wasm', importObject);this.wasmReady = true;console.log('WebAssembly圖像處理器已準備就緒');} catch (error) {console.error('初始化WebAssembly圖像處理器失敗:', error);}}// 使用WebAssembly進行高斯模糊處理async applyGaussianBlur(imageData, radius) {if (!this.wasmReady) {await new Promise(resolve => {const checkReady = () => {if (this.wasmReady) {resolve();} else {setTimeout(checkReady, 50);}};checkReady();});}const { width, height, data } = imageData;const size = width * height * 4; // RGBA每像素4字節// 分配內存并復制數據const wasmMemoryOffset = this.wasmModule.allocateMemory(size);const wasmMemory = new Uint8Array(this.wasmModule.memory.buffer, wasmMemoryOffset, size);wasmMemory.set(new Uint8Array(data.buffer));// 調用WebAssembly函數處理圖像this.wasmModule.gaussianBlur(wasmMemoryOffset, width, height, radius);// 復制結果回ImageDataconst resultData = new Uint8ClampedArray(wasmMemory.buffer, wasmMemoryOffset, size);const result = new ImageData(resultData, width, height);// 釋放WebAssembly內存this.wasmModule.freeMemory(wasmMemoryOffset);return result;}// 使用JavaScript實現的對照組applyGaussianBlurJS(imageData, radius) {// JavaScript實現的高斯模糊(性能較差)const { width, height, data } = imageData;const result = new Uint8ClampedArray(data.length);// ... 高斯模糊的JavaScript實現 ...return new ImageData(result, width, height);}
}// 使用示例
async function processImage() {const processor = new ImageProcessor();const canvas = document.getElementById('sourceCanvas');const ctx = canvas.getContext('2d');const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);console.time('WebAssembly模糊處理');const processedData = await processor.applyGaussianBlur(imageData, 5);console.timeEnd('WebAssembly模糊處理');console.time('JavaScript模糊處理');const jsProcessedData = processor.applyGaussianBlurJS(imageData, 5);console.timeEnd('JavaScript模糊處理');// 顯示處理結果const resultCanvas = document.getElementById('resultCanvas');const resultCtx = resultCanvas.getContext('2d');resultCtx.putImageData(processedData, 0, 0);
}

WebAssembly與Rust/C++集成

WebAssembly的真正強大之處在于它可以將其他高性能語言編譯到Web環境:

// image_processor.rs (Rust實現)
use wasm_bindgen::prelude::*;#[wasm_bindgen]
pub fn gaussian_blur(data_ptr: *mut u8, width: u32, height: u32, radius: u32) {let buffer_size = (width * height * 4) as usize;let data = unsafe { std::slice::from_raw_parts_mut(data_ptr, buffer_size) };// 實現高斯模糊算法// ...計算高斯核// ...應用卷積
}#[wasm_bindgen]
pub fn allocate_memory(size: usize) -> *mut u8 {// 分配內存let mut buffer = Vec::with_capacity(size);buffer.resize(size, 0);let ptr = buffer.as_mut_ptr();std::mem::forget(buffer); // 防止內存被回收ptr
}#[wasm_bindgen]
pub fn free_memory(ptr: *mut u8, size: usize) {// 釋放之前分配的內存unsafe {let _ = Vec::from_raw_parts(ptr, size, size);// Vector超出作用域后會自動釋放內存}
}

生成WebAssembly模塊:

wasm-pack build --target web

實際應用場景與性能優化策略

WebAssembly在以下場景中表現出色:

  1. 圖像和視頻處理:濾鏡、轉碼、實時特效
  2. 3D渲染和物理模擬:游戲引擎、AR/VR應用
  3. 科學計算:數據分析、機器學習推理
  4. 加密和安全:端到端加密、區塊鏈操作

優化策略:

  1. 最小化JavaScript與WebAssembly之間的數據傳輸

    • 使用共享內存而非復制數據
    • 批量處理數據而非頻繁調用
  2. 合理分配任務

    • 將計算密集型任務交給WebAssembly
    • 將DOM操作和UI邏輯留給JavaScript
  3. 使用AssemblyScript降低開發門檻

    • 類TypeScript語法,更容易上手
    • 直接編譯為WebAssembly
// AssemblyScript示例
export function fibonacci(n: i32): i32 {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);
}export function processArray(array: Int32Array): Int32Array {const length = array.length;const result = new Int32Array(length);for (let i = 0; i < length; i++) {result[i] = array[i] * 2 + 1;}return result;
}

WebAssembly未來發展趨勢

WebAssembly正在迅速發展,未來方向包括:

  1. 垃圾回收提案:簡化與高級語言的集成
  2. 多線程支持:結合SharedArrayBuffer實現并行計算
  3. SIMD指令集:加速向量化運算
  4. 異常處理機制:改進錯誤處理流程
  5. 與Web API更深入集成:直接訪問DOM、WebGL等API

WebAssembly已經從單純的性能優化工具,逐漸發展為構建高性能Web應用的重要基礎設施。隨著工具鏈的完善和社區的發展,它將在Web性能優化領域發揮越來越重要的作用。

Web Worker多線程架構設計

JavaScript傳統上以單線程模型運行,這意味著計算密集型任務會阻塞UI渲染和用戶交互。Web Worker提供了在瀏覽器中實現真正多線程的能力,允許將耗時操作遷移到后臺線程,保持主線程的響應性。

Web Worker基礎與性能特性

Web Worker的核心特性包括:

  1. 真正的并行計算:Worker在獨立線程中運行,不會阻塞主線程
  2. 隔離的執行環境:Worker有自己的全局上下文,獨立于主線程
  3. 消息傳遞通信機制:通過結構化克隆算法傳遞數據
  4. 有限的API訪問:無法直接操作DOM,但可訪問部分Web API
// 創建Worker
const myWorker = new Worker('/path/to/worker.js');// 發送消息到Worker
myWorker.postMessage({type: 'PROCESS_DATA',data: largeDataArray
});// 接收Worker的處理結果
myWorker.onmessage = function(e) {const result = e.data;console.log('Worker處理完成,結果:', result);
};// 處理Worker錯誤
myWorker.onerror = function(error) {console.error('Worker錯誤:', error.message);
};

Worker腳本(worker.js):

// worker.js
self.onmessage = function(e) {const { type, data } = e.data;if (type === 'PROCESS_DATA') {// 執行耗時操作const result = processLargeData(data);// 將結果發送回主線程self.postMessage(result);}
};function processLargeData(data) {// 耗時計算,不會阻塞主線程// ...處理邏輯return processedData;
}

多線程架構設計模式

工作池模式(Worker Pool)

當需要處理大量并行任務時,維護一個Worker池可以提高資源利用率:

class WorkerPool {constructor(workerScript, numWorkers = navigator.hardwareConcurrency || 4) {this.workerScript = workerScript;this.workers = [];this.queue = [];this.activeWorkers = new Map();// 創建Worker池for (let i = 0; i < numWorkers; i++) {const worker = new Worker(workerScript);worker.onmessage = (e) => {const { jobId, result } = e.data;const { resolve } = this.activeWorkers.get(jobId);this.activeWorkers.delete(jobId);resolve(result);// 處理隊列中的下一個任務this.processQueue();};worker.onerror = (error) => {const { jobId } = this.activeWorkers.get(jobId) || {};if (jobId) {const { reject } = this.activeWorkers.get(jobId);this.activeWorkers.delete(jobId);reject(error);}// 替換出錯的Workerconst index = this.workers.indexOf(worker);if (index !== -1) {this.workers[index] = new Worker(this.workerScript);}this.processQueue();};this.workers.push(worker);}}processQueue() {if (this.queue.length === 0) return;// 查找空閑Workerconst availableWorkerIndex = this.workers.findIndex(worker => !Array.from(this.activeWorkers.values()).some(job => job.worker === worker));if (availableWorkerIndex !== -1) {const { jobId, payload, resolve, reject } = this.queue.shift();const worker = this.workers[availableWorkerIndex];this.activeWorkers.set(jobId, { worker, resolve, reject });worker.postMessage({ jobId, ...payload });}}exec(payload) {return new Promise((resolve, reject) => {const jobId = `job_${Date.now()}_${Math.random()}`;this.queue.push({ jobId, payload, resolve, reject });this.processQueue();});}terminate() {this.workers.forEach(worker => worker.terminate());this.workers = [];this.queue = [];this.activeWorkers.clear();}
}// 使用示例
const pool = new WorkerPool('worker.js', 4);async function processImages(images) {const results = [];for (const image of images) {const result = await pool.exec({type: 'PROCESS_IMAGE',data: image});results.push(result);}return results;
}

Worker腳本(worker.js):

// worker.js for worker pool
self.onmessage = function(e) {const { jobId, type, data } = e.data;let result;if (type === 'PROCESS_IMAGE') {result = processImage(data);} else if (type === 'ANALYZE_DATA') {result = analyzeData(data);}self.postMessage({ jobId, result });
};
專用Worker模式

對于特定功能或服務,可以創建專用Worker,使其作為應用的基礎設施:

// 數據處理Worker
class DataProcessor {constructor() {this.worker = new Worker('/workers/data-processor.js');this.callbacks = new Map();this.requestId = 0;this.worker.onmessage = (e) => {const { id, result, error } = e.data;if (this.callbacks.has(id)) {const { resolve, reject } = this.callbacks.get(id);this.callbacks.delete(id);if (error) {reject(new Error(error));} else {resolve(result);}}};}process(method, params) {return new Promise((resolve, reject) => {const id = this.requestId++;this.callbacks.set(id, { resolve, reject });this.worker.postMessage({ id, method, params });});}async sortData(data, options) {return this.process('sortData', { data, options });}async filterData(data, criteria) {return this.process('filterData', { data, criteria });}async aggregateData(data, groupBy) {return this.process('aggregateData', { data, groupBy });}
}// 使用示例
const processor = new DataProcessor();async function updateDashboard() {const rawData = await fetchData();// 在Worker中處理數據const sortedData = await processor.sortData(rawData, { key: 'timestamp', direction: 'desc' });const filteredData = await processor.filterData(sortedData, { region: 'APAC', status: 'active' });const aggregatedData = await processor.aggregateData(filteredData, 'category');// 在主線程中更新UIrenderCharts(aggregatedData);
}

Worker腳本(data-processor.js):

// data-processor.js
const handlers = {sortData({ data, options }) {const { key, direction } = options;const multiplier = direction === 'desc' ? -1 : 1;return [...data].sort((a, b) => {return multiplier * (a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0);});},filterData({ data, criteria }) {return data.filter(item => {return Object.entries(criteria).every(([key, value]) => item[key] === value);});},aggregateData({ data, groupBy }) {return data.reduce((acc, item) => {const key = item[groupBy];if (!acc[key]) {acc[key] = [];}acc[key].push(item);return acc;}, {});}
};self.onmessage = function(e) {const { id, method, params } = e.data;try {if (handlers[method]) {const result = handlers[method](params);self.postMessage({ id, result });} else {throw new Error(`未知方法: ${method}`);}} catch (error) {self.postMessage({ id, error: error.message });}
};

性能優化與數據傳輸

Web Worker通信存在性能開銷,主要與數據傳輸有關:

1. 結構化克隆開銷

當使用postMessage傳遞數據時,數據會被結構化克隆,這意味著會創建數據的深拷貝:

// 測量傳輸大型數據的開銷
function measureTransferTime(data) {const worker = new Worker('transfer-test.js');return new Promise(resolve => {const start = performance.now();worker.onmessage = () => {const end = performance.now();worker.terminate();resolve(end - start);};worker.postMessage(data);});
}async function compareTransferMethods() {// 創建大型數據(100MB TypedArray)const size = 100 * 1024 * 1024;const largeArray = new Float64Array(size / 8);// 填充隨機數據for (let i = 0; i < largeArray.length; i++) {largeArray[i] = Math.random();}console.log(`數據大小: ${size / (1024 * 1024)} MB`);// 常規傳輸(克隆)const cloneTime = await measureTransferTime(largeArray);console.log(`常規傳輸時間: ${cloneTime.toFixed(2)}ms`);// 可轉移對象(zero-copy)const transferTime = await measureTransferTime({ data: largeArray.buffer,transfer: [largeArray.buffer]});console.log(`可轉移對象傳輸時間: ${transferTime.toFixed(2)}ms`);
}

Worker腳本(transfer-test.js):

// transfer-test.js
self.onmessage = function(e) {// 簡單確認接收到數據self.postMessage('received');
};
2. 使用可轉移對象(Transferable Objects)

對于ArrayBuffer和MessagePort等可轉移對象,可以避免克隆:

// 使用可轉移對象優化數據傳輸
function processLargeImageData(imageData) {return new Promise((resolve, reject) => {const worker = new Worker('image-processor.js');worker.onmessage = (e) => {const { processedBuffer } = e.data;const processedData = new Uint8ClampedArray(processedBuffer);const imageData = new ImageData(processedData, e.data.width, e.data.height);worker.terminate();resolve(imageData);};worker.onerror = (error) => {worker.terminate();reject(error);};// 轉移ArrayBuffer所有權worker.postMessage({buffer: imageData.data.buffer,width: imageData.width,height: imageData.height}, [imageData.data.buffer]);// 注意:傳輸后,原始imageData.data將不再可用});
}

Worker腳本(image-processor.js):

// image-processor.js
self.onmessage = function(e) {const { buffer, width, height } = e.data;const data = new Uint8ClampedArray(buffer);// 進行圖像處理...invertColors(data);// 將處理后的buffer傳回主線程self.postMessage({processedBuffer: data.buffer,width,height}, [data.buffer]);
};function invertColors(data) {for (let i = 0; i < data.length; i += 4) {data[i] = 255 - data[i];         // Rdata[i + 1] = 255 - data[i + 1]; // Gdata[i + 2] = 255 - data[i + 2]; // B// 保持Alpha通道不變}
}
3. 共享內存優化

使用SharedArrayBuffer允許主線程和Worker線程共享內存,避免數據復制:

// 使用SharedArrayBuffer共享內存
function setupSharedMemoryProcessing() {// 創建一個共享內存緩沖區(4MB)const sharedBuffer = new SharedArrayBuffer(4 * 1024 * 1024);const sharedArray = new Float64Array(sharedBuffer);// 創建Workerconst worker = new Worker('shared-memory-worker.js');// 發送共享內存引用worker.postMessage({ sharedBuffer });return {processData(data) {// 將數據復制到共享內存for (let i = 0; i < data.length; i++) {sharedArray[i] = data[i];}// 通知Worker處理指定范圍的數據return new Promise(resolve => {worker.onmessage = (e) => {if (e.data.status === 'DONE') {// 從共享內存讀取結果const result = new Float64Array(sharedArray.buffer, 0, data.length);resolve(Array.from(result));}};worker.postMessage({command: 'PROCESS',length: data.length});});},terminate() {worker

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/81327.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/81327.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/81327.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

package.json 和 package-lock.json 的區別

package.json?? ??作用?? ??聲明項目元數據??&#xff1a;如項目名稱、版本、描述、入口文件等。??定義依賴范圍??&#xff1a;在 dependencies 和 devDependencies 中聲明項目??直接依賴??的包及其??版本范圍??&#xff08;如 ^1.2.3&#xff09;。??…

Rollup入門與進階:為現代Web應用構建超小的打包文件

我們常常面臨Webpack復雜配置或是Babel轉譯后的冗余代碼&#xff0c;結果導致最終的包體積居高不下加載速度也變得異常緩慢&#xff0c;而在眾多打包工具中Rollup作為一個輕量且高效的選擇&#xff0c;正悄然改變著這一切&#xff0c;本文將帶你深入了解這個令人驚艷的打包工具…

基于C#的MQTT通信實戰:從EMQX搭建到發布訂閱全解析

MQTT(Message Queueing Telemetry Transport) 消息隊列遙測傳輸&#xff0c;在物聯網領域應用的很廣泛&#xff0c;它是基于Publish/Subscribe模式&#xff0c;具有簡單易用&#xff0c;支持QoS&#xff0c;傳輸效率高的特點。 它被設計用于低帶寬&#xff0c;不穩定或高延遲的…

Mysql數據庫之集群進階

一、日志管理 5.7版本自定義路徑時的文件需要自己提前創建好文件&#xff0c;不會自動創建&#xff0c;否則啟動mysql會報錯 錯誤日志 rpm包(yum) /var/log/mysql.log 默認錯誤日志 ###查詢日志路徑 [rootdb01 ~]# mysqladmin -uroot -pEgon123 variables | grep -w log_e…

當硅基存在成為人性延伸的注腳:論情感科技重構社會聯結的可能性

在東京大學機器人實驗室的檔案室里&#xff0c;保存著一份泛黃的二戰時期設計圖——1943年日本陸軍省秘密研發的“慰安婦替代品”草圖。這個誕生于戰爭陰霾的金屬軀體&#xff0c;與2025年上海進博會上展出的MetaBox AI伴侶形成時空對話&#xff1a;當人類將情感需求投射于硅基…

5月17日

這幾天不知道為啥沒更新。可能是玩得太瘋了。或者是考試有點集中&#xff1f;&#xff1f; 線性代數開課了&#xff0c;英語昨天完成了debate 昨天中午debate結束我們就出去玩了&#xff0c;去的那里時光民俗&#xff0c;別墅很好&#xff0c;770平米&#xff0c;但是缺點是可…

FIFO的應用案例(基于Zephyr OS )

目錄 概述 1. 軟硬件環境 1.1 軟件開發環境 1.2 硬件環境 2 FIFO的函數接口 3 FIFO的應用函數實現 3.1 實現步驟 3.2 代碼設計 3.3 測試代碼實現 3.4 源代碼文件 4 編譯和測試 4.1 編譯代碼 4.2 測試 概述 本文介紹了在nRF52832開發板上使用Zephyr操作系統進行…

AWS Elastic Beanstalk部署極簡Spring工程(EB CLI失敗版)

棄用 這里我沒有走通EB CLI方式部署。 問題 最近又加入了AWS項目組&#xff0c;又要再次在AWS云上面部署Spring服務&#xff0c;我這里使用的使用AWS中國云。需要使用AWS Elastic Beanstalk部署一個極簡Spring工程。 EB CLI安裝 安裝EB CLI之前需要先在本地安裝好Git&…

粒子群算法(PSO算法)

粒子群算法概述 1.粒子群優化算法&#xff08;Particle Swarm Optimization&#xff0c;簡稱PSO&#xff09;。粒子群優化算法是在1995年由Kennedy博士和Eberhart博士一起提出的&#xff0c;它源于對鳥群捕食行為的研究。 2.基本核心是利用群體中的個體對信息的共享從而使得整…

leetcode2934. 最大化數組末位元素的最少操作次數-medium

1 題目&#xff1a;最大化數組末位元素的最少操作次數 官方標定難度&#xff1a;中 給你兩個下標從 0 開始的整數數組 nums1 和 nums2 &#xff0c;這兩個數組的長度都是 n 。 你可以執行一系列 操作&#xff08;可能不執行&#xff09;。 在每次操作中&#xff0c;你可以選…

Elasticsearch 官網閱讀之 Term-level Queries

Term-level Queries 參考&#xff1a;https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-exists-query 一、Term Query Term Query 是 term 精準查詢。需要注意的是&#xff0c;在進行 Term Query 的時候&#xff0c;要避免 text 類型的字段&#x…

信貸域——互聯網金融業務

摘要 本文深入探討了信貸域全托與半托業務的定義、特點、適用場景及注意事項&#xff0c;并分析了互聯網金融核心信息流的多個方面&#xff0c;包括資金流、信息流、風險流、合規流、物流、技術流和商流&#xff0c;還闡述了金融系統“斷直連”業務的相關內容&#xff0c;以及…

科技晚報 AI 速遞:今日科技熱點一覽 丨 2025 年 5 月 17 日

科技晚報AI速遞:今日科技熱點一覽 丨2025年5月17日 我們為您匯總今日的科技領域最新動向&#xff0c;帶您快速了解前沿技術、突破性研究及行業趨勢。 黃仁勛勸特朗普&#xff1a;AI 芯片出口規則得改&#xff0c;中國緊追其后&#xff1a;英偉達 CEO 黃仁勛在華盛頓 “山與谷論…

使用streamlit實現vLLM多實例信息統一監控

本文代碼和配置文件實現了一個基于 Streamlit 和 FastAPI 的前后端分離的應用程序&#xff0c;用于管理和展示 VLLM&#xff08;Very Large Language Model&#xff09;實例的信息。以下是代碼和配置文件的總結摘要&#xff1a; 概要 功能概述 前后端啟動方式&#xff1a; 使用…

搭建一個WordPress網站需要多少成本

WordPress 最初可能只是一個簡單的博客平臺。但近年來&#xff0c;它不僅成為了最好的博客平臺&#xff0c;還成為了一個全面的內容管理系統。白宮、jQuery、NGINX、《紐約時報》等企業都把 WordPress 作為自己的網上家園。 不過&#xff0c;它們只是其中的佼佼者。根據 Built…

飛帆控件 post or get it when it has get

我在這里分享兩個鏈接&#xff1a; post_get_it 設計 - 飛帆 有人看出來這個控件是干什么用嗎&#xff1f; 控件的配置&#xff1a;

AI智能體 | 使用Coze一鍵制作“假如書籍會說話”視頻,18個作品狂吸17.6萬粉,讀書博主新標桿!(附保姆級教程)

目錄 一、整體工作流設計 二、制作工作流 2.1 開始節點 2.2 大模型_生成對話文案 2.3 代碼_字幕切割 2.4 畫板_對話背景 2.5 循環_對話語音01 2.5.1 選擇器_2 2.5.2 語音合成02 2.5.3 語音合成03 2.5.4 變量聚合_1 2.5.5 視頻合成01 2.6 循環_3 2.6.1 選擇器_3 …

mysql中4種掃描方式和聚簇索引非聚簇索引【爽文一篇】

目錄 一 mysql的聚簇索引&非聚簇索引 1.1 數據表 1.2 聚簇索引 1.3 非聚簇索引 1.4 覆蓋索引 二 mysql的4種掃描查詢 2.1 全表掃描 2.2 索引掃描 2.3 覆蓋索引掃描 2.4 回表掃描 2.5 總結 三 mysql的回表查詢詳解 3.1 回表查詢 一 mysql的聚簇索引&非聚簇…

泛微對接金蝶云星空實戰案例技術分享

前言 在企業信息化建設中&#xff0c;OA系統與ERP系統對接往往是一個復雜而關鍵的環節。OA系統通常具有高度的自定義性&#xff0c;其基礎資料和單據可能與ERP系統存在字段不一致等問題。同時&#xff0c;OA系統涉及審批流程及流程發起方定義&#xff0c;增加了對接的復雜性。…

一種資源有限單片機處理cJSON數據的方法

一般單片機處理cJSON格式的數據都直接使用cJSON庫&#xff0c;但對于Ram較小的單片機&#xff0c;由于資源有限&#xff0c;這并不合適&#xff0c;但我們可以根據cJSON數據的特定格式&#xff0c;使用土方法&#xff0c;直接對字符進行查找裁剪即可 //截取字符串str中字符a與…