渲染篇(一):從零實現一個“微型React”:Virtual DOM的真面目

渲染篇(一):從零實現一個“微型React”:Virtual DOM的真面目

引子:前端性能的“永恒之問”

在前面兩章中,我們已經奠定了堅實的架構基礎。我們用“任務調度器”建立了聲明式和模塊化的編程范式,并通過對比MVC等模式論證了“組件化”是現代前端的唯一答案。我們的應用現在擁有了清晰、可組合的“邏輯組件”。

但它依然是“看不見”的。

現在,我們要開始搭建連接“邏輯世界”與“視覺世界”的橋梁。而這座橋梁的基石,就是我們要面對的前端性能優化領域一個幾乎“永恒”的問題:

如何高效地更新用戶界面(UI)?

想象一下,你的應用狀態發生了改變——比如,用戶數據更新了,一個列表項被刪除了,或者一個計數器增加了。你需要將這些變化反映到屏幕上。最直觀、最暴力的方式是什么?

很簡單:清空整個頁面,然后根據新的狀態重新渲染所有內容。

// 暴力更新法
function renderApp(state) {const appContainer = document.getElementById('app');// 簡單粗暴地清空appContainer.innerHTML = ''; // 根據新狀態,重新創建所有DOMconst header = document.createElement('h1');header.textContent = state.title;const list = document.createElement('ul');state.items.forEach(itemText => {const listItem = document.createElement('li');listItem.textContent = itemText;list.appendChild(listItem);});appContainer.appendChild(header);appContainer.appendChild(list);
}

這種方法在狀態簡單、UI規模小的時候或許可行。但對于今天復雜的單頁應用(SPA)來說,它是一場災難。因為直接操作真實DOM(Document Object Model)的開銷是極其昂貴的

每一次你對DOM進行增、刪、改,都可能引發瀏覽器的重排(Reflow)重繪(Repaint)

  • 重排:當DOM的幾何屬性(如寬度、高度、位置)發生變化時,瀏覽器需要重新計算所有受影響元素的幾何信息,這個過程非常耗費計算資源。
  • 重繪:當元素的視覺屬性(如顏色、背景)發生變化,但幾何屬性不變時,瀏覽器會重新繪制元素。開銷比重排小,但依然不可忽視。

頻繁地、大規模地操作DOM,就像是在一個精密的沙盤上,每次只改動一粒沙子,你卻選擇把整個沙盤推倒重來。這必然導致頁面卡頓、掉幀,用戶體驗直線下降。

那么,問題變成了:我們能否找到一種方法,只更新“真正改變”了的那部分DOM?

答案是肯定的,但這需要進行一次“新舊對比”。我們需要知道更新前的UI長什么樣,更新后的UI又長什么樣,然后找出它們之間的差異,只把這些差異應用到真實DOM上。

這就是Diff(差異比對)算法的用武之地。然而,直接在真實DOM樹上進行Diff操作,復雜度極高,因為DOM提供了太多無關的API和屬性,遍歷和比較的成本依然很大。

前端先驅們想出了一個絕妙的主意:我們為什么不先在“成本更低”的地方完成比對呢?

這個“成本更低”的地方,就是JavaScript的世界。于是,Virtual DOM (虛擬DOM) 應運而生。


第一幕:Virtual DOM的本質 - 用JS對象模擬DOM

Virtual DOM(簡稱VDOM)這個概念聽起來很高級,但它的本質思想卻異常樸素:

Virtual DOM 是真實DOM結構在JavaScript內存中的一種輕量級描述。

它不是什么魔法,它就是一個普普通通的JavaScript對象(Plain Old JavaScript Object, POJO)。這個對象通過嵌套,形成一棵“虛擬節點樹”,用以模擬真實DOM的樹形結構。

讓我們來定義一下一個“虛擬節點”(VNode)應該長什么樣。一個DOM元素,最核心的屬性是什么?

  1. 標簽名(Tag Name):比如'div', 'p', 'ul'
  2. 屬性(Attributes/Properties):比如class, id, style,以及事件監聽器如onclick。我們把它統稱為props
  3. 子節點(Children):它可以是其他虛擬節點組成的數組,也可以是純文本。

基于此,我們可以設計出這樣一個VNode結構:

// 一個VNode的結構示例
{type: 'div', // 標簽名props: {     // 屬性id: 'container',class: 'main-content',onclick: () => alert('clicked!')},children: [ // 子節點{type: 'h1',props: { class: 'title' },children: ['Hello, Virtual DOM!'] // 文本節點},{type: 'p',props: {},children: ['This is a paragraph.']}]
}

看,這就是一個VDOM節點。它就是一個JS對象,比真實的DOM節點(那個包含了成百上千個屬性和方法的龐然大物)要輕量得多。在內存中創建、遍歷、比較這些JS對象,速度飛快,幾乎沒有性能開銷。

createElement: VDOM的“制造工廠”

為了方便地創建這種VNode對象,React定義了一個眾所周知的函數:createElement(在Vue中,它通常被稱為h函數)。我們現在就從零實現一個我們自己的createElement

它的功能很簡單:接收type, props, 和 children,然后返回一個符合我們定義的VNode結構的對象。

createElement.js

// CSDN @ 你的用戶名
// 系列: 前端內功修煉:從零構建一個“看不見”的應用
//
// 文件: /src/v3/createElement.js
// 描述: 實現一個簡單的createElement函數,用于創建虛擬DOM節點(VNode)。/*** 創建并返回一個虛擬DOM節點 (VNode)。** @param {string | Function} type - 節點的類型。*   - 如果是字符串,如 'div', 'p',代表一個原生DOM標簽。*   - (在后續章節中)如果是一個函數或類,代表一個組件。* @param {object | null} props - 節點的屬性對象,如 { id: 'app', class: 'main' }。* @param {...(object | string)} children - 子節點。可以是其他VNode對象,也可以是字符串。* @returns {object} 一個VNode對象。*/
function createElement(type, props, ...children) {// 核心就是返回一個結構一致的JS對象return {type,props: props || {}, // 保證props不為null// children參數是一個數組,里面包含了所有子節點。// 我們需要對它進行一些處理:// 1. 數組扁平化:有時候children可能是個數組,比如 .map() 的結果 [[VNode1, VNode2]]// 2. 過濾掉null或boolean等無效節點,這些在條件渲染中很常見 (e.g., { condition && <p/> })// 3. 將文本子節點(string, number)也包裝成VNode對象,以便統一處理。children: children.flat().filter(Boolean).map(child => {if (typeof child === 'object') {// 如果已經是VNode對象,直接返回return child;} else {// 如果是字符串或數字,創建一個特殊的“文本VNode”return createTextVNode(String(child));}})};
}/*** 創建一個文本類型的VNode。* 這是一種內部輔助函數,用于統一數據結構。* @param {string} text - 文本內容。* @returns {object} 一個文本VNode對象。*/
function createTextVNode(text) {return {type: 'TEXT_ELEMENT', // 特殊類型,用于標識文本節點props: {nodeValue: text // 文本內容存儲在nodeValue中,與真實DOM的屬性對應},children: [] // 文本節點沒有子節點};
}// 導出函數
module.exports = { createElement };

這個createElement函數雖然簡單,但非常關鍵。它做了幾件重要的事情:

  • 統一結構:確保所有VNode都有type, props, children這三個屬性。
  • 處理子節點:優雅地處理了map生成的嵌套數組(flat())、條件渲染產生的nullfalsefilter(Boolean)),并將原始的字符串或數字子節點轉換成了統一的文本VNode結構。

現在,我們可以像使用React一樣,來“描述”我們的UI了:

app.js (使用createElement)

// CSDN @ 你的用戶名
// 系列: 前端內功修煉:從零構建一個“看不見”的應用
//
// 文件: /src/v3/app.js
// 描述: 使用我們自己的createElement來描述一個UI結構。const { createElement } = require('./createElement');// 假設這是我們的應用狀態
const state = {title: 'My Awesome App',items: ['Learn Virtual DOM', 'Implement Diff Algorithm', 'Build a Framework']
};/*** 一個“組件”函數,它接收state并返回一個VNode樹。* 這就是React/Vue組件的核心工作:State in -> UI out.* @param {object} state - 應用狀態* @returns {object} VNode*/
function App(state) {return createElement('div',{ id: 'app-container', class: 'theme-dark' },createElement('h1',{ style: 'color: skyblue;' },state.title),createElement('ul',{ class: 'item-list' },...state.items.map(item => createElement('li', { class: 'item' }, item))),createElement('footer',null, // props可以為null`Total items: ${state.items.length}`));
}// 生成我們的VDOM樹
const virtualDom = App(state);// 打印出來看看它的真面目
console.log(JSON.stringify(virtualDom, null, 2));

運行node app.js,你會在控制臺看到一個巨大的、結構清晰的JSON對象。這就是我們應用的“UI藍圖”,完全存在于JavaScript內存中。

{"type": "div","props": {"id": "app-container","class": "theme-dark"},"children": [{"type": "h1","props": {"style": "color: skyblue;"},"children": [{"type": "TEXT_ELEMENT","props": {"nodeValue": "My Awesome App"},"children": []}]},// ... 其他子節點]
}

我們成功地用輕量的JS對象,完整地描述了我們想要的UI。這是通往高效渲染的第一步,也是最重要的一步。


第二幕:render函數 - 將“虛擬”照進“現實”

有了“UI藍圖”(VDOM),下一步就是根據這張藍圖來建造“真實的房子”(DOM)。這個過程,我們需要一個render函數來完成。

render函數接收兩個參數:一個VNode對象,和一個真實的DOM容器節點。它的工作就是遞歸地遍歷VNode樹,并將每個VNode都轉換成對應的真實DOM節點,然后插入到容器中

render.js

// CSDN @ 你的用戶名
// 系列: 前端內功修煉:從零構建一個“看不見”的應用
//
// 文件: /src/v3/render.js
// 描述: 實現一個render函數,將VNode渲染成真實的DOM。/*** 將VNode渲染到指定的DOM容器中。* @param {object} vnode - 要渲染的虛擬DOM節點。* @param {HTMLElement} container - 真實DOM容器。*/
function render(vnode, container) {// 第一步:清空容器,這是最簡單的實現方式// 在后續章節我們會用diff算法來替代它container.innerHTML = '';// 第二步:創建真實DOM并追加const dom = createDom(vnode);container.appendChild(dom);
}/*** 遞歸地將VNode轉換成真實DOM。* @param {object} vnode - 虛擬DOM節點。* @returns {HTMLElement | Text} 真實DOM節點。*/
function createDom(vnode) {// 處理文本節點if (vnode.type === 'TEXT_ELEMENT') {return document.createTextNode(vnode.props.nodeValue);}// 處理普通元素節點const dom = document.createElement(vnode.type);// 將VNode的props應用到真實DOM上applyProps(dom, vnode.props);// 遞歸處理子節點if (vnode.children && vnode.children.length > 0) {vnode.children.forEach(childVNode => {// 遞歸調用,并將子DOM追加到父DOM上dom.appendChild(createDom(childVNode));});}return dom;
}/*** 將props應用到DOM元素上。* @param {HTMLElement} dom - 真實DOM元素。* @param {object} props - 屬性對象。*/
function applyProps(dom, props) {Object.keys(props).forEach(key => {const value = props[key];// 處理事件監聽,如 onClick -> onclickif (key.startsWith('on')) {const eventType = key.slice(2).toLowerCase();dom.addEventListener(eventType, value);}// 處理樣式對象,如 { style: { color: 'red' } }else if (key === 'style' && typeof value === 'object') {Object.assign(dom.style, value);}// 處理classNameelse if (key === 'class') {dom.className = value;}// 處理其他HTML屬性else {dom.setAttribute(key, value);}});
}// 在Node.js環境中,沒有真實的document對象。
// 為了能讓我們的代碼在Node中“運行”并看到結果,
// 我們來模擬一個“渲染成字符串”的版本。
// 這在服務器端渲染(SSR)中非常有用。/*** [Node.js環境專用] 將VNode渲染成HTML字符串。* @param {object} vnode - 虛擬DOM節點。* @returns {string} HTML字符串。*/
function renderToString(vnode) {if (vnode.type === 'TEXT_ELEMENT') {return escapeHtml(vnode.props.nodeValue);}const { type, props, children } = vnode;const propsString = Object.keys(props).map(key => {// 忽略事件監聽和復雜對象if (key.startsWith('on') || typeof props[key] === 'object') return '';if (key === 'class') return ` class="${props[key]}"`; // 處理classreturn ` ${key}="${props[key]}"`;}).join('');const childrenString = children.map(child => renderToString(child)).join('');return `<${type}${propsString}>${childrenString}</${type}>`;
}function escapeHtml(str) {return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
}module.exports = { render, renderToString };

這里的render.js提供了兩個版本的渲染器:

  1. render(vnode, container):這是用于瀏覽器環境的,它會創建真實的DOM元素。
  2. renderToString(vnode):這是我們為了在Node.js中看到結果而創建的。它不依賴document對象,而是將VDOM樹直接轉換成一個HTML字符串。這和React的服務器端渲染(SSR)中的ReactDOMServer.renderToString()原理完全一致。

現在,讓我們把所有東西串聯起來:

main.js (最終執行文件)

// CSDN @ 你的用戶名
// 系列: 前端內功修煉:從零構建一個“看不見”的應用
//
// 文件: /src/v3/main.js
// 描述: 串聯所有模塊,將VDOM渲染成字符串并打印。const { createElement } = require('./createElement');
const { renderToString } = require('./render'); // 我們使用renderToString// 1. 定義應用狀態
const state = {title: 'My Awesome App',items: ['Learn Virtual DOM', 'Implement Diff Algorithm', 'Build a Framework']
};// 2. 定義App“組件”
function App(state) {return createElement('div',{ id: 'app-container', class: 'theme-dark' },createElement('h1',{ style: 'color: skyblue;' },state.title),createElement('ul',{ class: 'item-list' },...state.items.map(item => createElement('li', { class: 'item', onClick: () => console.log(`${item} clicked!`) }, item))),createElement('footer',null,`Total items: ${state.items.length}`));
}// 3. 生成VDOM
console.log('--- Generating Virtual DOM ---');
const virtualDom = App(state);// 4. 將VDOM渲染成HTML字符串
console.log('\n--- Rendering to HTML String ---');
const htmlString = renderToString(virtualDom);// 5. 打印最終結果
console.log('\n--- Final HTML Output ---');
console.log(htmlString);/*最終輸出:<div id="app-container" class="theme-dark"><h1 style="color: skyblue;">My Awesome App</h1><ul class="item-list"><li class="item">Learn Virtual DOM</li><li class="item">Implement Diff Algorithm</li><li class="item">Build a Framework</li></ul><footer>Total items: 3</footer></div>
*/

運行node main.js,你將得到一段格式完美的HTML字符串。我們成功地將一個用JS對象描述的UI藍圖,轉化成了最終的產物。雖然我們沒有在瀏覽器里看到它,但我們已經完成了從0到1的最關鍵一步。

第三章總結:我們搭建了怎樣的橋梁?

在這一章,我們親手揭開了Virtual DOM的神秘面紗。它不是黑魔法,而是一種優雅而務實的設計模式,其核心思想在于用計算成本低的JavaScript操作,來代替計算成本高的DOM操作

我們完成了兩件核心的事情:

  1. 實現了createElement函數:它讓我們能夠用一種聲明式、結構化的方式,在JavaScript中描述我們想要的UI。這是“描述”階段。
  2. 實現了renderrenderToString函數:它能夠讀取VDOM這個“藍圖”,并將其轉化為最終的產物(真實DOM或HTML字符串)。這是“執行”階段。

這套“描述”->“執行”的流程,是所有現代前端框架的渲染核心。

核心要點:

  1. 直接操作DOM性能開銷巨大,是導致頁面卡頓的主要原因之一。
  2. Virtual DOM的本質是一個輕量級的JavaScript對象,用于模擬DOM樹的結構。
  3. 在內存中對Virtual DOM進行操作(未來將進行Diff比較)遠比直接操作真實DOM要快。
  4. createElement是創建VNode的工廠函數,它統一了UI的描述方式。
  5. render函數是VDOM和真實DOM之間的“翻譯官”,負責將虛擬結構具象化。

然而,我們目前的render函數還是“暴力”的——每次渲染都清空容器再全部重建。這并沒有完全解決我們最初提出的性能問題。

這正是我們下一章要去征服的高地。在 《渲染篇(二):解密Diff算法:如何用“最少的操作”更新UI》 中,我們將基于本章創建的VDOM體系,親手實現一個核心的diff算法。我們將學習如何比對兩棵VDOM樹,找出最小化的“補丁”(Patches),并只將這些補丁應用到真實DOM上,從而實現真正意義上的“高效更新”。這將是整個系列中技術挑戰最大,但也是回報最高的一章。敬請期待!

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

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

相關文章

SWC 深入全面講解

一、核心功能與原理 1. 高性能編譯 Rust 架構優勢&#xff1a;SWC 基于 Rust 編寫&#xff0c;利用 Rust 的性能和并發性優勢&#xff0c;編譯速度比 Babel 快約 20 倍&#xff0c;比 TypeScript 編譯器更快。并行編譯&#xff1a;支持多線程并行處理&#xff0c;在四核基準測試…

XML Expat Parser:深入解析與高效應用

XML Expat Parser:深入解析與高效應用 引言 XML(可擴展標記語言)作為一種廣泛使用的標記語言,在數據交換、存儲和表示中扮演著重要角色。XML Expat Parser 是一個高性能、可擴展的XML解析庫,廣泛應用于各種編程語言中。本文將深入探討XML Expat Parser 的原理、特性以及…

【Python】自動化GIT提交

在日常開發中&#xff0c;我們經常需要頻繁地向 Git 倉庫提交代碼。雖然 git add、git commit、git push 這幾個命令并不復雜&#xff0c;但重復操作容易出錯&#xff0c;也浪費時間。本文將介紹如何使用 Python 腳本自動化完成 Git 提交流程&#xff0c;讓開發更高效&#xff…

基于Qlearning強化學習的水下無人航行器路徑規劃與避障系統matlab性能仿真

目錄 1.引言 2.算法仿真效果演示 3.數據集格式或算法參數簡介 4.算法涉及理論知識概要 5.參考文獻 6.完整算法代碼文件獲得 1.引言 水下無人航行器 (Autonomous Underwater Vehicle, AUV) 的路徑規劃與避障是海洋探索、資源開發和軍事應用中的關鍵技術。傳統的路徑規劃方…

模塊自由拼裝!Python重構DSSAT作物模塊教程(以雜交水稻為例)

基于過程的作物生長模型&#xff08;Process-based Crop Growth Simulation Model&#xff09;在模擬作物對氣候變化的響應與適應、農田管理優化、作物品種和株型篩選、農業碳中和、農田固碳減排等領域扮演著越來越重要的作用。Decision Support Systems for Agrotechnology Tr…

Java項目接口權限校驗的靈活實現

引言 在Java Web開發中&#xff0c;接口權限校驗是保護系統資源安全的關鍵機制。本文將介紹一種靈活、可配置的接口權限校驗方案&#xff0c;通過注解驅動和攔截器實現&#xff0c;既能保證安全性&#xff0c;又能靈活控制哪些接口需要校驗。 設計思路 實現方案的核心設計要點&…

瀚高DB兼容MySQL if函數

文章目錄環境癥狀問題原因解決方案環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 癥狀 MySQL if函數在瀚高DB當中沒有&#xff0c;源應用在用到if函數時&#xff0c;就會報if函數不存在的錯誤信息。為此&#xff0c;我們需要根據業…

基于深度學習的胸部 X 光圖像肺炎分類系統(六)

目錄 結果指標解讀 一、為什么選擇這些指標&#xff1f; 二、各指標的定義和解讀 1. 準確率&#xff08;Accuracy&#xff09; 2. 損失&#xff08;Loss&#xff09; 3. 精確率&#xff08;Precision&#xff09; 4. 召回率&#xff08;Recall&#xff09; 三、這些指標…

區塊鏈性能優化策略:從理論到實踐

目錄 區塊鏈性能優化策略:從理論到實踐 1. 引言:區塊鏈性能的挑戰 2. 性能評估指標 2.1 核心性能指標 2.2 性能瓶頸分析 3. 分層優化策略 3.1 網絡層優化 3.1.1 Gossip協議改進 3.1.2 網絡分片 3.2 共識層優化 3.2.1 PBFT優化 3.3 數據層優化 3.3.1 狀態樹優化 3.3.2 區塊數據…

【VLLM】open-webui部署模型全流程

目錄 前言 一、租用服務器到服務器連接VScode全流程(可選) 二、下載模型到本地服務器 2.1 進入魔塔社區官網 2.2 選擇下載模型 2.3 執行下載 三、部署VLLM 3.1 參考vllm官網文檔 3.2 查看硬件要求 3.3 安裝vLLM框架 3.4 啟動模型服務 方法1:直接啟動下載的本地模…

辦公自動化入門:如何高效將圖片整合為PDF文檔

將多張圖片合并到一個PDF文件中可以幫助保持特定的順序和布局&#xff0c;同時確保圖像的質量不會因為格式轉換而下降。它是免費&#xff0c;不限次數&#xff0c;批量導入也毫無壓力。操作堪比發朋友圈&#xff1a;拖圖進來 → 選個紙張尺寸 → 點擊轉換 → 指定保存路徑&…

使用寶塔面板搭建 PHP 環境開發一個簡單的 PHP 例子

目錄一、引言二、準備工作2.1 服務器選擇2.2 下載安裝寶塔面板三、使用寶塔面板搭建 PHP 環境3.1 登錄寶塔面板3.2 選擇 Web Server3.3 安裝 PHP3.4 安裝 MySQL 數據庫四、開發一個簡單的 PHP 例子4.1 創建 PHP 文件4.2 編寫 PHP 代碼4.3 設置站點4.4 訪問 PHP 頁面五、常見問題…

AWS WebRTC:我們的業務模式

拉流、卡錄基本流程 設備端&#xff08;攝像機&#xff09; 與 App端 是通過 AWS KVS WebRTC 信令服務進行“點對點連接”的&#xff0c;真正的媒體數據&#xff08;音視頻&#xff09;是通過 WebRTC 的 ICE 通道&#xff08;P2P 或 TURN&#xff09;直接傳輸的&#xff0c;而不…

使用Python,OpenCV,K-Means聚類查找圖像中最主要的顏色

使用Python&#xff0c;OpenCV&#xff0c;K-Means聚類查找圖像中最主要的顏色 分別把跑圖聚類選取1, 2, 3&#xff0c;4, 5, 6, 7&#xff0c;8, 9種主要顏色并繪制colormap顏色圖; 效果圖 分別把跑圖聚類選取3&#xff0c;4, 5&#xff0c;7&#xff0c;9種主要顏色并繪制…

DBAPI 實現分頁查詢的兩種方法

DBAPI 實現分頁查詢的兩種方法 背景 在進行分頁查詢時&#xff0c;用戶通常需要傳入當前頁碼 pageNo 和每頁顯示的條數 pageSize 參數。根據這兩個參數&#xff0c;我們可以從數據庫中查詢出當前頁的數據。以 MySQL 為例&#xff0c;分頁查詢的 SQL 語句如下&#xff1a; se…

第五天上課 SSLPolicy策略和Network Discovery技術

SSL Policy場景1:擁有自家服務器的私鑰&#xff0c;解密訪問自家服務器的ssl流量場景2: 內部用戶訪問互聯網的ssl流量&#xff0c;需要解密并重簽名Correlation and Compliance相關性與合規性配置相關性與合規性策略&#xff0c;在10.1.1.0/24網絡中&#xff0c;當通過Network …

進階07:C#與通用OPC UA通信范例

本節目標&#xff1a; 1&#xff09;安裝軟件&#xff0c;搭建虛擬OPC UA服務器&#xff1b; 2&#xff09;使用UaExpert&#xff0c;讀取OPC UA服務器中的變量&#xff1b; 3&#xff09;編寫Winform程序&#xff0c;讀寫服務器中變量值&#xff0c;創建訂閱觸發事件&#…

大模型微調學習筆記(基于訊飛星辰MaaS速學版)

文章目錄參考資料說明大模型微調入門微調簡介微調步驟數據準備模型選擇訓練方式效果評估模型部署大模型微調&#xff08;基于訊飛星辰Maas&#xff09;構建數據集方法1&#xff1a;預置數據集方法2&#xff1a;創建數據集數據輔助工具數據集劃分模型微調數據配置參數配置模型部…

[CSS]讓overflow不用按shift可以滾輪水平滾動(純CSS)

前言 我不爽前端無法直接滾輪橫向滾動很久了 明明瀏覽器可以直接判斷 x滾動且y不滾動的時候滾輪事件可以直接操作橫向滾動 這個是我探究出來的方法,尤其適合這種很多很多小tag的情況解析 原理是將豎向排列的overflow旋轉成橫向,實際操作的還是豎向overflow.繼而實現鼠標滾輪不用…

截稿倒計時 TrustCom‘25大會即將召開

會議資訊IEEE TrustCom-2025&#xff08;第24屆IEEE計算與通信領域信任、安全與隱私國際會議&#xff09;是一個展示可信計算、通信、網絡和機器學習領域前沿成果的學術平臺。會議聚焦計算機系統、網絡及人工智能在信任、安全、隱私、可靠性、可依賴性、生存性、可用性和容錯性…