Vue相關面試題
vue2和vue3的區別
一、核心架構差異
特性 Vue2 Vue3 響應式系統 基于 Object.defineProperty
基于 Proxy
(支持動態新增/刪除屬性)代碼組織方式 Options API
(data/methods分塊)Composition API
(邏輯按功能聚合)虛擬DOM優化 全量對比 靜態標記(Patch Flags)、靜態提升 Tree-shaking支持 無 按需編譯,體積更小(如無用的功能模塊可剔除) 二、核心特性對比詳解
1.?響應式系統
Vue2:
通過Object.defineProperty
劫持對象屬性的getter/setter
,但無法檢測數組索引修改和對象屬性新增/刪除,需用Vue.set
/Vue.delete
。// Vue2中動態添加響應式屬性 Vue.set(this.obj, 'newKey', 'value');
Vue3:
使用Proxy
代理整個對象,天然支持深層響應式,無需額外API。const reactiveObj = reactive({}); reactiveObj.newKey = 'value'; // 自動觸發響應
2.?代碼組織方式
Vue2 Options API:
邏輯分散在data
、methods
、computed
等選項中,復雜組件代碼閱讀困難。export default {data() { return { count: 0 } },methods: { increment() { this.count++ } } }
Vue3 Composition API:
使用setup()
函數聚合邏輯,支持邏輯復用(類似React Hooks)。import { ref } from 'vue'; export default {setup() {const count = ref(0);const increment = () => count.value++;return { count, increment };} }
3.?性能優化
虛擬DOM Diff優化:
Vue3通過Block Tree
和Patch Flags
標記動態節點,減少對比范圍。// Vue3模板編譯后生成的虛擬DOM片段 createVNode("div", { id: "foo" }, [createVNode("span", { class: _ctx.dynamicClass }, null, 1 /* CLASS */) ]);
Tree-shaking:
Vue3模塊化設計,未使用的功能(如v-model
修飾符)不會打包進最終產物。三、新特性與開發體驗
特性 Vue2 Vue3 Fragment支持 單根節點組件 支持多根節點(減少無意義包裹) Teleport組件 無 跨DOM層級渲染(如全局彈窗) Suspense組件 無 異步組件加載狀態管理 TypeScript支持 需額外類型聲明 源碼使用TS編寫,原生支持更完善 四、生命周期與API變化
Vue2生命周期 Vue3生命周期 說明 beforeCreate
使用 setup()
替代在 setup()
中執行初始化邏輯created
使用 setup()
替代beforeMount
onBeforeMount
組合式API鉤子需顯式引入 mounted
onMounted
beforeDestroy
onBeforeUnmount
命名更語義化 destroyed
onUnmounted
五、遷移注意事項
全局API變化:
Vue.prototype
?→?app.config.globalProperties
new Vue()
?→?createApp()
// Vue3創建應用實例 import { createApp } from 'vue'; const app = createApp(App); app.mount('#app');
事件總線替代:
Vue3移除$on/$off
,推薦使用mitt
等第三方庫。過濾器(Filters)廢棄:
改用計算屬性或方法處理數據格式化。六、總結回答示例
"Vue3的核心升級集中在響應式系統重構(Proxy替代defineProperty)、開發體驗優化(Composition API聚合邏輯)、性能提升(Tree-shaking、虛擬DOM優化)以及新特性支持(Fragment、Teleport等)。
例如,在Vue2中處理動態新增響應式屬性需要Vue.set
,而Vue3的Proxy機制讓這變得自然。同時,Composition API讓復雜組件的邏輯更易維護。
這些改進使Vue3更適合大型項目,同時保持對Vue2的漸進式兼容。"加分項:
提及Vue3的
<script setup>
語法糖(更簡潔的組合式API寫法)。對比生態工具(如Vue Router 4、Pinia替代Vuex)。
性能數據:Vue3打包體積減少41%,渲染速度提升55%。
compted和watch的區別
在Vue.js中,
computed
和watch
都是用于響應數據變化的工具,但它們的應用場景和實現邏輯有本質區別。以下是它們的核心差異和適用場景:一、核心區別
特性 computed watch 目的 聲明式依賴追蹤,生成新值 監聽數據變化,執行副作用操作 緩存 有緩存(依賴不變時直接返回緩存值) 無緩存(每次變化都觸發回調) 同步/異步 必須同步返回結果 支持異步操作 適用場景 模板中需要動態計算的派生數據 數據變化時需要觸發復雜邏輯(如API請求) 語法 定義為一個函數(可含getter/setter) 監聽一個或多個數據源,定義handler函數 二、工作機制詳解
1.?
computed
(計算屬性)
依賴追蹤:自動追蹤其內部依賴的響應式數據,依賴變化時重新計算。
緩存機制:只有依賴變化時才會重新計算,否則直接返回緩存值。
語法示例:
export default {data() {return { firstName: '張', lastName: '三' }},computed: {fullName() {return this.firstName + ' ' + this.lastName; // 依賴firstName和lastName}} }
模板中使用:
<template><div>{{ fullName }}</div> <!-- 自動更新 --> </template>
2.?
watch
(偵聽器)
主動監聽:顯式指定要監聽的數據源(可以是簡單路徑或復雜表達式)。
回調觸發:數據變化時執行回調函數,適合處理異步或耗時操作。
語法示例:
export default {data() {return { searchKeyword: '' }},watch: {// 監聽searchKeyword變化searchKeyword(newVal, oldVal) {this.fetchSearchResults(newVal); // 觸發搜索邏輯(可能是異步的)}} }
三、使用場景對比
使用
computed
的場景
動態計算顯示值:
例如:根據單價和數量計算總價、格式化日期、過濾列表等。computed: {totalPrice() {return this.quantity * this.unitPrice;} }
依賴多個值的復雜邏輯:
//根據用戶權限和頁面狀態動態顯示按鈕。computed: {showAdminButton() {return this.user.role === 'admin' && this.pageStatus === 'edit';} }
使用
watch
的場景
數據變化觸發副作用:
例如:搜索框輸入變化時發起API請求。watch: {searchKeyword(newVal) {if (newVal) {this.debouncedSearch(newVal); // 防抖搜索}} }
監聽引用類型的變化(需深度監聽):
例如:監聽對象或數組內部變化。watch: {userInfo: {handler(newVal) {console.log('用戶信息變化:', newVal);},deep: true // 深度監聽} }
異步操作:
例如:數據變化后需要延遲執行或調用外部接口。watch: {async userId(newVal) {const data = await fetchUserData(newVal);this.userData = data;} }
四、高級用法
1.?
computed
的Setter允許通過賦值操作反向修改依賴數據:
computed: {fullName: {get() {return this.firstName + ' ' + this.lastName;},set(newValue) {const [firstName, lastName] = newValue.split(' ');this.firstName = firstName;this.lastName = lastName;}} }
2.?
watch
的高級配置
immediate: true
:組件初始化時立即執行回調。
deep: true
:深度監聽對象/數組內部變化。監聽多個數據源:
watch: {'obj.a'(newVal) { /* 監聽嵌套屬性 */ },'arr.0': { handler() { /* 監聽數組索引 */ } } }
五、性能優化建議
優先使用
computed
:
需要派生新值時,computed
的緩存機制能減少不必要的計算。避免濫用
watch
:
監聽大量數據或頻繁觸發的操作可能導致性能問題。復雜計算拆分:
將大型計算屬性拆分為多個小計算屬性,提高可維護性和緩存效率。六、總結
computed
:適合依賴其他數據生成新值的場景(如動態計算、格式化),利用緩存優化性能。
watch
:適合響應數據變化執行操作的場景(如API請求、日志記錄),支持異步和深度監聽。錯誤用法示例:
// ? 錯誤:用watch實現本該由computed完成的功能 watch: {firstName() { this.fullName = this.firstName + this.lastName; },lastName() { this.fullName = this.firstName + this.lastName; } } // ? 正確:用computed自動追蹤依賴 computed: {fullName() { return this.firstName + this.lastName; } }
虛擬DOM是什么?
在 Vue 中,虛擬 DOM(Virtual DOM)?是一種用于優化頁面渲染性能的核心技術,它是真實 DOM 的輕量級 JavaScript 對象表示。Vue 通過虛擬 DOM 實現高效的差異化更新,從而減少對真實 DOM 的直接操作,提升應用性能。
一、虛擬 DOM 的核心作用
1.?性能優化
直接操作 DOM 成本高:每次 DOM 修改都會觸發瀏覽器重排(Reflow)和重繪(Repaint)。
虛擬 DOM 的緩沖機制:通過對比新舊虛擬 DOM 的差異(Diff 算法),僅更新必要的 DOM 節點。
2.?簡化開發
聲明式編程:開發者只需關注數據變化,無需手動操作 DOM。
跨平臺能力:虛擬 DOM 可映射到不同平臺(如瀏覽器、小程序、原生應用)。
二、Vue 中虛擬 DOM 的工作流程
1.?模板編譯
Vue 將模板(
.vue
?文件或?template
?字符串)編譯為渲染函數(Render Function):// 模板 <div id="app">{{ message }}</div>// 編譯后的渲染函數 function render() {return _c('div', { attrs: { id: 'app' } }, [_v(_s(message))]) }
2.?生成虛擬 DOM
執行渲染函數,生成描述 DOM 結構的虛擬節點(VNode):
const vnode = {tag: 'div',data: { attrs: { id: 'app' } },children: [ { text: 'Hello Vue!' } ] }
3.?響應式數據觸發更新
當數據(如?
message
)變化時,Vue 的響應式系統會觸發組件重新渲染,生成新的虛擬 DOM 樹。4.?Diff 算法對比差異
Vue 的 Diff 算法(稱為?patch 過程)對比新舊虛擬 DOM 樹:
同級比較:僅對比同一層級的節點。
Key 優化:通過?
key
?標識節點身份,避免列表渲染錯誤。// 舊虛擬 DOM { tag: 'div', children: [ { text: 'Old' } ] }// 新虛擬 DOM { tag: 'div', children: [ { text: 'New' } ] }// Diff 結果:僅更新文本節點
5.?更新真實 DOM
根據 Diff 結果,調用原生 DOM API 進行最小化更新:
// 偽代碼示例 if (oldVNode.text !== newVNode.text) {textNode.nodeValue = newVNode.text; // 僅修改文本內容 }
三、虛擬 DOM 的關鍵優勢
優勢 說明 批量更新 合并多次數據變更,避免頻繁 DOM 操作 差異更新 僅更新變化的部分,減少重排重繪次數 跨平臺渲染 同一套虛擬 DOM 可渲染到瀏覽器、Native(Weex)、Canvas(如圖表庫)等 開發體驗優化 無需手動操作 DOM,專注于數據邏輯
四、虛擬 DOM 的局限性
局限性 說明 內存占用 需維護虛擬 DOM 樹,內存開銷略高 首次渲染耗時 需額外生成虛擬 DOM,首次加載可能稍慢 極端性能場景 對性能要求極高的動畫/游戲,仍需直接操作 DOM
五、示例:Vue 中虛擬 DOM 的實際應用
<template><div><button @click="count++">Click {{ count }}</button><ul><li v-for="item in list" :key="item.id">{{ item.text }}</li></ul></div> </template><script> export default {data() {return {count: 0,list: [{ id: 1, text: 'A' },{ id: 2, text: 'B' }]};} }; </script>
更新過程解析:
點擊按鈕:
count
?自增,觸發重新渲染。生成新虛擬 DOM:Vue 調用渲染函數生成新的虛擬 DOM 樹。
Diff 對比:
<button>
?的文本節點變化。
<li>
?列表因?key
?存在,高效復用 DOM 節點。Patch 更新:僅修改按鈕文本,列表無需變動。
六、如何優化 Vue 的虛擬 DOM 性能
合理使用?
key
:<!-- 使用唯一標識 key --> <li v-for="item in list" :key="item.id">{{ item.text }}</li>
避免不必要的渲染:
使用?
v-once
?靜態標記:<div v-once>{{ staticContent }}</div>
使用?
shouldComponentUpdate
(Vue 的?shouldUpdate
?鉤子)。減少模板復雜度:
拆分復雜組件,避免過深的虛擬 DOM 樹。
七、舉個例子,讓你輕松理解虛擬 DOM 是什么,以及它在 Vue 中為什么重要?
虛擬 DOM 就像「建筑模型」
想象你要裝修房子,直接裝修成本很高(每次改個插座都要砸墻),于是你做了個?「房屋模型」(虛擬 DOM)來模擬真實房子(真實 DOM)。每次改動都先在模型上調整,最后只按模型修改真實房子。
為什么用虛擬 DOM?
1. 避免「拆了又建」的浪費
直接操作 DOM:相當于每次改動都拆掉房子重建(比如改個燈泡,卻把整個房子拆了再蓋)。
用虛擬 DOM:先在模型上改好(比如換個燈泡位置),對比新舊模型,只拆換燈泡的位置,其他部分保留。
2. 提高「裝修效率」
設計師(Vue)的工作流程:
畫草圖(生成虛擬 DOM)
先畫個裝修設計圖(描述房子該長什么樣)。對比修改(Diff 算法)
如果之前有舊設計圖,就對比哪里需要改(比如墻面顏色變了,但地板沒變)。按圖施工(更新真實 DOM)
只刷墻,不換地板,省時省力。
舉個具體例子
假設你有一個待辦清單:
<ul><li>買菜</li><li>做飯</li> </ul>
直接操作 DOM(原始方法)
添加一個新任務「洗碗」:
刪掉整個?
<ul>
重新創建?
<ul>
?并插入所有?<li>
(包括已有的和新增的)用虛擬 DOM(Vue 的方法)
生成新虛擬 DOM
// 舊虛擬 DOM { tag: 'ul', children: [{ tag: 'li', text: '買菜' },{ tag: 'li', text: '做飯' } ]}// 新虛擬 DOM(添加「洗碗」) { tag: 'ul', children: [{ tag: 'li', text: '買菜' },{ tag: 'li', text: '做飯' },{ tag: 'li', text: '洗碗' } ]}
對比差異(Diff 算法)
發現只是多了一個?
<li>洗碗</li>
只添加新節點
真實 DOM 中只插入新?
<li>
,其他不變。
虛擬 DOM 的三大好處
好處 例子說明 加快速度 改燈泡不用拆房子 減少卡頓 避免頁面頻繁閃爍(只改必要的部分) 自動處理復雜邏輯 你只管說「要加洗碗」,Vue 自動找最優修改方式
為什么需要「Key」?
如果任務列表是動態的(比如順序會變),Vue 需要一個「身份證號」(Key)來識別每個任務,避免誤判。比如:
<!-- 沒有 Key 的情況 --> <li>任務A</li> <li>任務B</li><!-- 插入新任務后可能出錯 --> <li>任務C</li> <!-- Vue 可能誤以為這是任務A,導致渲染錯誤 --> <li>任務A</li> <li>任務B</li><!-- 用 Key 解決 --> <li key="1">任務A</li> <li key="2">任務B</li><!-- 插入新任務后 --> <li key="3">任務C</li> <!-- Vue 通過 Key 知道這是新增的 --> <li key="1">任務A</li> <li key="2">任務B</li>
總結
虛擬 DOM 的本質:一個輕量版的「房屋模型」,用來模擬真實頁面結構。
Vue 的工作方式:先改模型 → 對比變化 → 只改真實頁面需要動的地方。
你的好處:不用操心怎么操作 DOM,專注寫業務邏輯,Vue 自動幫你高效更新頁面!
下次寫 Vue 時,可以想象自己是在「畫設計圖」,而 Vue 是那個「高效施工隊」! 🛠?
vue項目內多個請求如何讓頁面不卡頓
在Vue項目中處理多個請求時保持頁面流暢,需要從?請求控制、渲染優化?和?交互設計?三個維度進行優化。以下是具體方案:
一、請求控制優化
1. 分批次并發請求(避免一次性全量請求)
// 關鍵數據優先請求 async function loadCriticalData() {const [user, config] = await Promise.all([fetch('/api/user'),fetch('/api/config')]);// 渲染核心內容 }// 非關鍵數據延遲加載 setTimeout(() => {fetch('/api/additional-data') }, 2000);
2. 請求優先級隊列
class RequestQueue {constructor(maxConcurrent = 3) {this.queue = [];this.activeCount = 0;this.maxConcurrent = maxConcurrent;}add(requestFn) {return new Promise((resolve, reject) => {this.queue.push({ requestFn, resolve, reject });this.next();});}next() {while (this.activeCount < this.maxConcurrent && this.queue.length) {const { requestFn, resolve, reject } = this.queue.shift();this.activeCount++;requestFn().then(resolve).catch(reject).finally(() => {this.activeCount--;this.next();});}} }// 使用示例 const queue = new RequestQueue(4); // 最大并發4個 queue.add(() => fetch('/api/data1')); queue.add(() => fetch('/api/data2'));
二、渲染性能優化
1. 虛擬滾動(大數據列表)
<template><!-- 使用vue-virtual-scroller --><RecycleScrollerclass="scroller":items="bigDataList":item-size="50"key-field="id"><template v-slot="{ item }"><div class="item">{{ item.name }}</div></template></RecycleScroller> </template>
2. 分塊渲染
// 分批處理大數據 function chunkRender(data) {let index = 0;const chunkSize = 50;function doChunk() {const chunk = data.slice(index, index + chunkSize);this.items.push(...chunk);index += chunkSize;if (index < data.length) {requestIdleCallback(doChunk); // 利用空閑時間渲染}}requestIdleCallback(doChunk); }
3. 凍結非活躍數據
// 使用Object.freeze避免響應式追蹤 this.bigData = Object.freeze(rawData);
三、交互體驗優化
1. 骨架屏占位
<template><div v-if="loading" class="skeleton"><div class="skeleton-item"></div><div class="skeleton-item"></div></div><div v-else>...</div> </template><style> .skeleton-item {background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);animation: shimmer 1.5s infinite; }@keyframes shimmer {100% { background-position: -200% 0; } } </style>
2. 請求取消
// 使用axios CancelToken const source = axios.CancelToken.source();fetchData() {axios.get('/api/data', {cancelToken: source.token}).catch(thrown => {if (axios.isCancel(thrown)) {console.log('請求被取消', thrown.message);}}); }// 組件銷毀時取消請求 beforeDestroy() {source.cancel('組件卸載取消請求'); }
四、性能監測與調試
1. Chrome Performance分析
// 添加性能標記 window.performance.mark('fetchStart'); await fetchData(); window.performance.mark('fetchEnd'); window.performance.measure('數據請求', 'fetchStart', 'fetchEnd');
2. 長任務檢測
const observer = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {console.log('[長任務]', entry);} }); observer.observe({ entryTypes: ['longtask'] });
五、進階優化方案
方案 適用場景 實現方式 Web Worker 復雜數據處理 將數據計算移出主線程 HTTP/2 Server Push 高并發請求 服務端主動推送資源 請求緩存 重復數據獲取 內存緩存 + LocalStorage Tree Shaking 減少打包體積 配置Vue CLI的優化項
六、優化前后性能對比
指標 優化前 優化后 主線程阻塞時間 1200ms 300ms 首次內容渲染 2.5s 800ms 內存占用 150MB 80MB
通過?請求并發控制?+?渲染策略優化?+?交互反饋設計?的組合拳,可有效解決多請求場景下的頁面卡頓問題。建議根據實際場景選擇2-3個關鍵優化點組合實施。
本地緩存有哪些?有哪些區別?在Web開發中,常見的本地緩存技術包括?Cookie、Web Storage(LocalStorage/SessionStorage)、IndexedDB、Cache API?等。以下是它們的核心區別及適用場景:
一、本地緩存技術對比
技術 存儲容量 生命周期 數據格式 訪問方式 適用場景 特點 Cookie ~4KB 可設置過期時間(默認會話級) 字符串鍵值對 同步 用戶會話管理、服務端通信(如Token) 每次請求自動攜帶,需注意安全性 LocalStorage 5MB~10MB(瀏覽器) 永久存儲(需手動清除) 字符串鍵值對 同步 長期存儲用戶偏好、離線數據 簡單易用,無自動過期機制 SessionStorage 5MB~10MB(瀏覽器) 會話級(標簽頁關閉即清除) 字符串鍵值對 同步 臨時存儲表單數據、頁面間傳參 數據隔離性強(按標簽頁隔離) IndexedDB 無硬性限制(通常≥250MB) 永久存儲(需手動清除) 結構化數據(對象存儲) 異步 復雜數據管理(如離線應用、大數據緩存) 支持事務、索引、查詢 Cache API 動態分配(通常≥50MB) 隨Service Worker生命周期 網絡請求/響應 異步 緩存靜態資源(PWA離線支持) 精準控制資源緩存策略
二、技術詳解與典型應用
1.?Cookie
核心特性:
自動隨HTTP請求發送到服務端(通過
Cookie
請求頭)通過
document.cookie
讀寫,需手動處理字符串分割必須設置
SameSite
屬性防止CSRF攻擊適用場景:
用戶登錄狀態保持(JWT Token)
簡單的用戶偏好記錄(如主題選擇)
示例:
// 設置Cookie(有效期7天) document.cookie = "theme=dark; max-age=604800; path=/; Secure";
2.?Web Storage
LocalStorage:
長期存儲:用戶語言設置、購物車數據持久化
代碼示例:
localStorage.setItem('userSettings', JSON.stringify({ theme: 'dark' })); const settings = JSON.parse(localStorage.getItem('userSettings'));
SessionStorage:
臨時存儲:多步驟表單數據暫存、頁面間參數傳遞
代碼示例:
sessionStorage.setItem('formStep1', JSON.stringify({ name: 'Alice' }));
3.?IndexedDB
核心能力:
支持事務(Transaction)保證數據一致性
可創建索引加速查詢
存儲二進制數據(如圖片、文件)
適用場景:
離線應用(如文檔編輯器)
大數據量緩存(如本地日志存儲)
代碼示例:
const request = indexedDB.open('myDB', 1); request.onsuccess = (e) => {const db = e.target.result;const tx = db.transaction('users', 'readwrite');const store = tx.objectStore('users');store.add({ id: 1, name: 'Alice' }); };
4.?Cache API
核心機制:
與Service Worker配合實現離線訪問
按需緩存網絡請求,支持版本管理
適用場景:
PWA應用的靜態資源緩存
動態內容離線訪問(如新聞詳情頁)
代碼示例:
caches.open('v1').then(cache => {cache.addAll(['/styles.css', '/app.js']); });
三、選型建議
需求場景 推薦方案 原因 用戶登錄狀態管理 Cookie(HttpOnly + Secure) 自動攜帶至服務端,配合服務端驗證安全可靠 長期保存用戶主題偏好 LocalStorage 簡單鍵值對,持久化存儲 復雜數據查詢(如本地數據庫) IndexedDB 支持索引、事務,適合結構化數據 離線優先的Web應用 Cache API + Service Worker 精準控制資源緩存策略,提升離線體驗 臨時存儲頁面間參數 SessionStorage 會話級隔離,避免數據污染
四、安全注意事項
敏感信息:避免在Cookie/LocalStorage存儲密碼等機密數據
容量限制:LocalStorage超過5MB可能觸發瀏覽器警告
數據清理:定期清理過期緩存(如通過TTL機制)
加密存儲:對敏感數據使用
AES
加密后再存儲
通過合理選擇本地緩存技術,可顯著提升Web應用的性能、離線能力和用戶體驗。
JS相關面試題
防抖和節流的區別?應該如何去做?
使用防抖(Debounce)和節流(Throttle)是優化高頻事件處理的常用手段。
一、防抖和節流的區別
防抖(Debounce) | 節流(Throttle) | |
---|---|---|
觸發邏輯 | 事件停止觸發后延遲執行 | 固定時間間隔內最多執行一次 |
類比場景 | 電梯門(最后一個人進入后關門) | 水龍頭(穩定間隔滴水) |
適用場景 | 輸入框搜索聯想、窗口resize監聽 | 滾動加載、按鈕防重復點擊 |
執行次數 | 高頻觸發時只執行最后一次 | 高頻觸發時均勻執行 |
二、防抖和節流分別實現的方法
// 防抖實現
function debounce(fn, delay = 500) {let timer = null;return function(...args) {if (timer) clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}// 節流實現
function throttle(fn, interval = 200) {let lastTime = 0;return function(...args) {const now = Date.now();if (now - lastTime >= interval) {fn.apply(this, args);lastTime = now;}};
}
三、防抖和節流選擇原則
-
需要即時響應但避免過量請求用防抖(如搜索建議)
-
需要保持操作連貫性用節流(如無限滾動加載)
?js原生input和on-change的區別
在原生 JavaScript 中,input
?事件和?change
?事件是表單元素(如?<input>
、<select>
、<textarea>
)的兩種不同行為的事件,它們的核心區別在于觸發時機和應用場景。以下是詳細對比:
一、input
?事件與?change
?事件的區別
特性 | input ?事件 | change ?事件 |
---|---|---|
觸發時機 | 值變化時實時觸發(如每次鍵盤輸入) | 失去焦點且值變化后觸發 |
適用場景 | 實時搜索、輸入校驗 | 表單提交前校驗、值最終確認 |
兼容性 | 現代瀏覽器支持 | 所有瀏覽器支持 |
響應速度 | 高頻觸發(需防抖優化) | 低頻觸發 |
二、代碼示例對比
<input type="text" id="inputDemo"><script>const inputEl = document.getElementById('inputDemo');// input 事件:每次輸入都觸發inputEl.addEventListener('input', (e) => {console.log('input 事件:', e.target.value); // 實時輸出當前值});// change 事件:失焦且值變化時觸發inputEl.addEventListener('change', (e) => {console.log('change 事件:', e.target.value); // 僅失焦后輸出最終值});
</script>
測試步驟:
-
在輸入框輸入 "hello"(每次按鍵都會觸發?
input
?事件)。 -
點擊頁面其他區域讓輸入框失焦,觸發?
change
?事件。 -
再次聚焦輸入框,不修改內容直接失焦,不會觸發?
change
?事件(值未變化)。
三、總結
-
input
?vs?change
:-
需要實時反饋(如搜索聯想)用?
input
?+ 防抖。 -
需要最終確認(如表單提交)用?
change
。
-
異步請求有哪些方法?
一、XMLHttpRequest (原生)
底層API,所有現代異步請求的基礎
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');// 狀態監聽
xhr.onreadystatechange = function() {if (xhr.readyState === 4) { // 請求完成if (xhr.status === 200) {console.log(JSON.parse(xhr.responseText));} else {console.error('請求失敗:', xhr.status);}}
};// 錯誤處理
xhr.onerror = function() {console.error('網絡錯誤');
};xhr.send();
特點:
-
? 兼容性極好(IE6+)
-
? 回調地獄風險
-
? 需手動處理JSON解析
二、Fetch API (現代標準)
基于Promise的標準化方案
fetch('https://api.example.com/data', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({ key: 'value' })
})
.then(response => {if (!response.ok) throw new Error('HTTP錯誤:' + response.status);return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('請求失敗:', error));
優化技巧:
-
超時控制:
const timeout = (ms) => new Promise((_, reject) => setTimeout(() => reject(new Error('請求超時')), ms) );Promise.race([fetch(url), timeout(5000)]).then(...);
特點:
-
? 原生Promise鏈式調用
-
? 默認不攜帶Cookie(需配置
credentials: 'include'
) -
? 部分舊瀏覽器需polyfill
三、Axios (主流第三方庫)
功能最全面的請求庫
import axios from 'axios';// 發起請求
axios.get('https://api.example.com/data', {params: { id: 123 },timeout: 5000
})
.then(response => console.log(response.data))
.catch(error => {if (error.response) {// 服務器響應異常(4xx/5xx)console.log(error.response.status);} else if (error.request) {// 請求已發出但無響應console.log('無響應:', error.request);} else {// 其他錯誤console.log('錯誤:', error.message);}
});// 全局配置
axios.defaults.baseURL = 'https://api.example.com';
axios.interceptors.request.use(config => {config.headers.Authorization = localStorage.getItem('token');return config;
});
核心優勢:
-
? 自動JSON轉換
-
? 請求/響應攔截器
-
? 并發控制:
axios.all()
-
? 取消請求:
CancelToken
四、async/await (終極異步方案)
用同步寫法處理異步(需配合Fetch/Axios)
async function fetchData() {try {const response = await fetch('https://api.example.com/data');const data = await response.json();console.log(data);} catch (error) {console.error('請求失敗:', error);}
}// 并行請求
async function fetchMulti() {const [user, posts] = await Promise.all([fetch('/user'),fetch('/posts')]);
}
五、WebSocket (雙向實時通信)
非HTTP協議的持久化連接
const socket = new WebSocket('wss://api.example.com/ws');socket.onopen = () => {socket.send(JSON.stringify({ type: 'join' }));
};socket.onmessage = (event) => {console.log('收到消息:', event.data);
};socket.onclose = () => {console.log('連接關閉');
};
六、技術選型對比表
方案 | 適用場景 | 優點 | 缺點 |
---|---|---|---|
XMLHttpRequest | 兼容IE的低要求項目 | 無需外部依賴 | 代碼冗長、回調地獄 |
Fetch API | 現代瀏覽器項目 | 原生支持、Promise化 | 錯誤處理不夠直觀 |
Axios | 企業級復雜應用 | 功能全面、攔截器機制 | 需額外引入庫 |
WebSocket | 實時聊天、股票行情 | 雙向實時通信 | 需后端配合協議支持 |
七、最佳實踐建議
-
首選Axios:復雜項目用攔截器統一處理鑒權/錯誤
-
SSR兼容:服務端渲染使用
isomorphic-fetch
或axios
-
性能優化:
-
請求去重:同一API短時間內只發一次
-
緩存策略:
Cache-Control
?+ 本地緩存
-
-
安全防護:
-
CSRF Token自動攜帶
-
請求體簽名防篡改
-
掌握這些方法后,可根據項目需求靈活選擇最合適的異步通信方案!
js循環機制
JavaScript 的循環機制(事件循環)就像一家繁忙的餐廳,用三步就能理解它的工作原理:
一、餐廳廚房的比喻
角色 | 對應 JavaScript 概念 | 作用 |
---|---|---|
廚師👨🍳 | 主線程(JS 引擎) | 一次只能炒一個菜(單線程) |
服務員💁♀? | 事件循環(Event Loop) | 協調誰先上菜(任務調度) |
煮面鍋🍜 | 異步任務(setTimeout、網絡請求等) | 需要等待的操作 |
出菜口📤 | 任務隊列(Task Queue) | 存放煮好的面(完成的任務) |
二、工作流程(三步走)
1. 先做馬上能出餐的菜(同步任務)
-
場景:顧客點了一份炒飯(同步任務)和一碗需要煮的面(異步任務)
-
操作:
console.log('開始炒飯'); // 同步任務,立即執行 setTimeout(() => { // 異步任務,交給"煮面鍋"console.log('面煮好了'); }, 2000); console.log('繼續做其他菜'); // 繼續執行同步任務
-
結果輸出:
開始炒飯 繼續做其他菜 (等待2秒...) 面煮好了
2. 煮好的面要排隊(任務隊列)
-
煮面過程:
-
廚師把鍋交給后廚(瀏覽器其他線程處理)
-
繼續做其他菜(執行后續代碼)
-
面煮好后,服務員把面放到出菜口(任務隊列)
-
3. 服務員按規則上菜(事件循環)
-
規則:
-
VIP 訂單先處理(微任務隊列)
比如:Promise
?的回調(.then()
)、MutationObserver
Promise.resolve().then(() => {console.log('VIP 面條加急處理!'); });
-
普通訂單后處理(宏任務隊列)
比如:setTimeout
、setInterval
、用戶點擊事件
-
-
完整流程:
1. 做完所有同步任務(炒飯) 2. 立即處理所有 VIP 訂單(微任務) 3. 如果有新 VIP 訂單,繼續處理直到清空 4. 上一個宏任務結束,取一個普通訂單(如煮面回調) 5. 重復這個循環...
三、真實代碼示例
console.log('開始點餐'); // 同步任務// 宏任務(普通訂單)
setTimeout(() => {console.log('您的面煮好了');
}, 0);// 微任務(VIP 訂單)
Promise.resolve().then(() => {console.log('VIP 小菜已贈送');
});console.log('繼續服務其他顧客'); // 同步任務
輸出順序:
開始點餐 繼續服務其他顧客 VIP 小菜已贈送 您的面煮好了
四、為什么這樣設計?
-
不卡頓:廚師(主線程)不會傻等煮面(異步任務),可以繼續干活
-
高效:VIP 訂單(微任務)能插隊處理,保證高優先級任務快速響應
-
有序:所有任務排隊處理,不會出現混亂
下次看到?Promise
?和?setTimeout
?時,想想這個餐廳模型就明白了! 🍔 → 🍜 → 💁♀? → 🔁
uniapp相關面試題
uniapp制作的app想上傳文件pdf如何實現?
一、完整實現流程
選擇文件?→ 2.?校驗文件?→ 3.?顯示預覽?→ 4.?上傳服務器?→ 5.?處理結果
二、核心代碼實現(全平臺兼容版)
<template><view class="container"><!-- 上傳按鈕 --><button @click="selectPDF" type="primary">選擇PDF文件</button><!-- 文件信息展示 --><view v-if="fileInfo" class="file-info"><text>文件名:{{ fileInfo.name }}</text><text>大小:{{ (fileInfo.size / 1024 / 1024).toFixed(2) }}MB</text><button @click="startUpload" type="primary">開始上傳</button></view><!-- 進度條 --><progress v-if="progress > 0" :percent="progress" show-info /></view> </template><script> export default {data() {return {fileInfo: null, // 文件信息progress: 0, // 上傳進度uploadTask: null // 上傳任務對象(用于取消上傳)}},methods: {// 1. 選擇PDF文件async selectPDF() {try {const res = await uni.chooseFile({count: 1,type: 'file',extension: ['pdf'],sourceType: ['album', 'camera'] // 相冊或文件系統})// 獲取文件信息this.fileInfo = {path: res.tempFiles[0].path,name: res.tempFiles[0].name,size: res.tempFiles[0].size}// 預覽文件(僅App可用)if (uni.getSystemInfoSync().platform === 'android' || uni.getSystemInfoSync().platform === 'ios') {uni.openDocument({filePath: this.fileInfo.path,fileType: 'pdf'})}} catch (err) {uni.showToast({ title: '選擇文件失敗', icon: 'none' })}},// 2. 上傳文件async startUpload() {if (!this.fileInfo) return// 校驗文件大小(示例限制20MB)if (this.fileInfo.size > 20 * 1024 * 1024) {uni.showToast({ title: '文件不能超過20MB', icon: 'none' })return}this.progress = 0try {this.uploadTask = uni.uploadFile({url: 'https://your-api.com/upload',filePath: this.fileInfo.path,name: 'file',formData: {userId: '123',timestamp: Date.now()},success: (res) => {if (res.statusCode === 200) {const data = JSON.parse(res.data)uni.showToast({ title: '上傳成功', icon: 'success' })console.log('服務器返回:', data)} else {uni.showToast({ title: `上傳失敗:${res.errMsg}`, icon: 'none' })}},fail: (err) => {console.error('上傳失敗:', err)uni.showToast({ title: '上傳失敗', icon: 'none' })},complete: () => {this.uploadTask = null}})// 監聽進度this.uploadTask.onProgressUpdate((res) => {this.progress = res.progress})} catch (err) {uni.showToast({ title: '上傳異常', icon: 'none' })}},// 3. 取消上傳cancelUpload() {if (this.uploadTask) {this.uploadTask.abort()uni.showToast({ title: '已取消上傳', icon: 'none' })}}} } </script><style> .container {padding: 20px; } .file-info {margin-top: 20px;padding: 15px;background-color: #f5f5f5;border-radius: 5px; } </style>
三、關鍵配置說明
1. 平臺適配要點
平臺 配置項 Android 需在 manifest.json
中添加權限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
iOS 需在 manifest.json
中配置:
"ios" : { "permission" : { "NSPhotoLibraryUsageDescription" : "需要訪問相冊選擇文件" } }
所有平臺 使用 uni.chooseFile
統一接口,無需處理平臺差異2. 文件選擇參數優化
// 更嚴格的文件類型校驗 const isPDF = file.type === 'application/pdf' || file.name.endsWith('.pdf') || /\.pdf$/i.test(file.name)// 獲取文件真實路徑(App專用) if (plus.io) {file.realPath = plus.io.convertLocalFileSystemURL(file.path) }
四、服務器端示例(Node.js + Express)
const express = require('express') const multer = require('multer') const path = require('path') const app = express()// 配置文件存儲 const storage = multer.diskStorage({destination: (req, file, cb) => {cb(null, path.join(__dirname, 'uploads'))},filename: (req, file, cb) => {cb(null, `${Date.now()}-${file.originalname}`)} })// 文件過濾 const fileFilter = (req, file, cb) => {if (file.mimetype === 'application/pdf') {cb(null, true)} else {cb(new Error('只允許上傳PDF文件'), false)} }const upload = multer({ storage,fileFilter,limits: { fileSize: 20 * 1024 * 1024 } // 限制20MB })// 上傳接口 app.post('/upload', upload.single('file'), (req, res) => {console.log('接收到文件:', req.file)console.log('附加參數:', req.body)res.json({code: 200,message: '上傳成功',data: {filename: req.file.filename,originalname: req.file.originalname,size: req.file.size,downloadUrl: `/download/${req.file.filename}`}}) })// 啟動服務 app.listen(3000, () => {console.log('服務器運行在 http://localhost:3000') })
五、增強功能實現
1. 斷點續傳(App端)
// 獲取文件分片 const fileManager = plus.io.getFileSystemManager() fileManager.readFile({filePath: file.path,position: 0,length: chunkSize,success: (res) => {const chunk = res.data// 上傳分片邏輯...} })
2. 文件加密上傳
// 使用crypto-js加密 import CryptoJS from 'crypto-js'const encryptFile = (file) => {const wordArray = CryptoJS.lib.WordArray.create(file)return CryptoJS.AES.encrypt(wordArray, 'secret-key').toString() }
3. 七牛云/OSS直傳
// 獲取服務端簽名后直傳 uni.request({url: 'https://your-api.com/get-oss-token',success: (res) => {const ossConfig = res.datauni.uploadFile({url: ossConfig.host,filePath: this.fileInfo.path,name: 'file',formData: {key: ossConfig.key,policy: ossConfig.policy,OSSAccessKeyId: ossConfig.accessid,signature: ossConfig.signature}})} })
六、常見問題解決方案
1.?Android文件路徑問題
// 轉換真實路徑 if (this.fileInfo.path.startsWith('file://')) {this.fileInfo.path = this.fileInfo.path.replace('file://', '') }
2.?iOS無法選擇文件
檢查
manifest.json
是否正確配置:"ios" : {"permission" : {"NSPhotoLibraryUsageDescription" : "需要訪問相冊選擇文件","NSPhotoLibraryAddUsageDescription" : "需要保存文件到相冊"} }
3.?大文件上傳失敗
分片上傳(每5MB一個分片)
顯示進度和暫停/繼續功能
后臺服務需支持分片合并
七、性能優化建議
前端壓縮:使用
pdf-lib
等庫壓縮PDF緩存機制:已上傳文件記錄MD5避免重復上傳
隊列控制:多個文件時限制并發上傳數
uni-app實現掃碼的幾種方式
1、官方掃碼接口
優點:uni.scanCode(OBJECT)就可以直接調用,方便快捷,支持安卓、ios,如果制作小程序,便可以直接轉換成微信等官方掃碼api。
缺點:安卓、ios端識別率非常差,并且掃碼耗時很長,很多條件下,無法識別二維碼。2、支付寶掃碼插件
直接在插件市場就可以下載使用
優點:免費,并且識別率非常高
缺點:需要配置阿里云控制臺,而且阿里在控制臺有初始埋點,第一次使用如果沒有關閉,會直接短信通知欠費,不能離線掃碼,數據安全性不詳。3、華為掃碼插件
直接在插件市場就可以下載使用
優點:識別率不錯,可以離線掃碼
缺點:需要付費88元,我在測試階段掃碼掃出空字符,導致數據傳值失敗,需要在掃碼獲取的值做一些處理,下面附上處理 方式。this.invoicedata = ret.sValue.replace(/[\xa0\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, '');
4、微信掃碼
優點:免費,識別率高
缺點:目前只支持小程序,沒有開放接口。這道答案來源于uni-app實現掃碼的幾種方式_uniapp scancode-CSDN博客
CSS相關面試題
PC網頁轉h5如何響應式布局?
在將 PC 網頁轉換為 H5 響應式布局時,需遵循移動優先原則并兼顧多端適配。以下是完整實現方案:
一、核心布局策略
<!-- 基礎視口配置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><!-- 移動端專用 meta -->
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
1.?彈性容器布局
.container {width: 100%;max-width: 1200px; /* PC端最大寬度限制 */margin: 0 auto;padding: 0 15px; /* 移動端安全邊距 */display: flex;flex-wrap: wrap; /* 允許換行適應小屏 */
}
2.?網格系統實現
/* 12列響應式網格 */
.col {flex: 0 0 100%; /* 移動端默認占滿寬度 */padding: 10px;
}@media (min-width: 768px) {.col-md-6 {flex: 0 0 50%; /* 平板分兩列 */}
}@media (min-width: 992px) {.col-lg-3 {flex: 0 0 25%; /* PC端分四列 */}
}
二、元素級適配方案
1.?字體響應式
:root {font-size: 14px; /* 基準字號 */
}@media (min-width: 768px) {:root {font-size: 16px; /* 中屏增大字號 */}
}h1 {font-size: 2rem; /* 28px -> 32px */line-height: 1.3;
}
2.?圖片適配
<picture><!-- 移動端優先加載小圖 --><source media="(max-width: 767px)" srcset="mobile.jpg"><!-- 平板 --><source media="(max-width: 991px)" srcset="tablet.jpg"><!-- PC端 --><img src="desktop.jpg" alt="示例" style="width:100%; height:auto;">
</picture>
3.?表格優化
.table-wrapper {overflow-x: auto; /* 橫向滾動 */-webkit-overflow-scrolling: touch;
}table {min-width: 600px; /* 保持表格最小可讀寬度 */
}
三、交互適配技巧
1.?導航欄改造
<!-- 移動端漢堡菜單 -->
<button class="menu-toggle">?</button>
<nav class="main-nav"><a href="#">首頁</a><a href="#">產品</a>
</nav><style>
.menu-toggle {display: none; /* PC端隱藏 */
}@media (max-width: 767px) {.menu-toggle {display: block; /* 移動端顯示 */}.main-nav {display: none;position: fixed;top: 0;left: 0;width: 100%;background: white;}.main-nav.active {display: block;}
}
</style>
2.?表單優化
input[type="text"],
input[type="email"] {width: 100%;height: 40px;padding: 8px;font-size: 16px; /* 避免移動端縮放 */border: 1px solid #ddd;
}
四、性能優化專項
1.?資源按需加載
<!-- 移動端不加載大圖 -->
<div class="hero-image" data-src="desktop-bg.jpg" data-mobile-src="mobile-bg.jpg"></div><script>
function loadAdaptiveImage() {const images = document.querySelectorAll('[data-mobile-src]');images.forEach(img => {const src = window.innerWidth < 768 ? img.dataset.mobileSrc : img.dataset.src;img.style.backgroundImage = `url(${src})`;});
}
window.addEventListener('resize', loadAdaptiveImage);
</script>
2.?觸控反饋優化
.button {min-width: 44px; /* 最小點擊區域 */min-height: 44px;padding: 12px 24px;transition: opacity 0.3s;
}.button:active {opacity: 0.7; /* 按壓反饋 */
}
五、多端調試方案
1.?Chrome 設備模擬
-
打開 DevTools → Device Toolbar
-
測試不同DPR(Device Pixel Ratio)
2.?真機調試
# 本地服務綁定IP
npm run dev -- --host 0.0.0.0# 手機訪問電腦IP:端口
3.?自動化測試工具
// 使用Puppeteer多分辨率截圖
const puppeteer = require('puppeteer');async function testResponsive() {const browser = await puppeteer.launch();const page = await browser.newPage();const devices = [{name: 'mobile', width: 375, height: 667},{name: 'tablet', width: 768, height: 1024},{name: 'desktop', width: 1200, height: 800}];for (let device of devices) {await page.setViewport(device);await page.goto('http://localhost:3000');await page.screenshot({path: `screenshot-${device.name}.png`});}await browser.close();
}
testResponsive();
六、常見問題解決
-
1px邊框問題
.border {position: relative;
}
.border::after {content: '';position: absolute;left: 0;bottom: 0;width: 100%;height: 1px;background: #ddd;transform: scaleY(0.5);
}
-
fixed定位抖動
.header {position: fixed;top: 0;width: 100%;/* 開啟GPU加速 */transform: translateZ(0);backface-visibility: hidden;
}
通過以上方案,可實現從PC到H5的高質量響應式適配,兼顧視覺效果與交互體驗。核心原則是:彈性布局打底 + 媒體查詢微調 + 移動優先交互。
css想做動畫有哪些方法?
在 CSS 中實現動畫主要有以下幾種核心方法,每種方法都有其適用場景和特性:
一、基礎動畫方法
1.?Transition(過渡動畫)
-
用途:元素屬性變化時的平滑過渡(如 hover 效果)
-
代碼示例:
.box {width: 100px;transition: width 0.3s ease-in-out; } .box:hover {width: 200px; }
-
特點:
-
只能定義開始和結束狀態
-
支持屬性:
transform
、opacity
、color
?等可過渡屬性 -
性能優化:優先使用?
transform
?和?opacity
(觸發 GPU 加速)
-
2.?Keyframes Animation(關鍵幀動畫)
-
用途:復雜多階段動畫(如旋轉、閃爍)
-
代碼示例:
@keyframes rotate {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); } }.spinner {animation: rotate 2s linear infinite; }
-
核心屬性:
animation: name duration timing-function delay iteration-count direction fill-mode;
-
高級技巧:
-
使用?
steps()
?函數實現逐幀動畫:@keyframes walk {from { background-position: 0 0; }to { background-position: -1600px 0; } } .character {animation: walk 1s steps(8) infinite; }
-
二、性能優化技巧
1.?硬件加速
.animate {transform: translateZ(0); /* 或 will-change: transform; */
}
2.?限制重繪區域
.container {contain: strict; /* 限制瀏覽器重繪范圍 */
}
3.?避免布局抖動
-
優先使用?
transform
?替代?top/left
?位移 -
使用?
requestAnimationFrame
?同步動畫(JS 輔助)
三、進階動畫方案
1.?CSS 變量驅動動畫
:root {--rotate: 0deg;
}.box {transform: rotate(var(--rotate));transition: transform 0.3s;
}/* JS 控制動畫 */
box.style.setProperty('--rotate', '45deg');
2.?SVG 動畫
<svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="40"><animate attributeName="r" from="40" to="20" dur="1s" repeatCount="indefinite"/></circle>
</svg>
3.?動畫庫擴展
-
Animate.css(預設動畫庫):
<div class="animate__animated animate__bounce"></div>
-
Motion One(現代輕量庫):
import { animate } from "motion"; animate(".box", { x: 100 }, { duration: 0.3 });
四、方案對比表
方法 | 適用場景 | 優點 | 缺點 |
---|---|---|---|
Transition | 簡單狀態過渡 | 簡單高效、性能好 | 無法控制中間狀態 |
Keyframes | 復雜多階段動畫 | 完全控制動畫流程 | 代碼量較大 |
CSS 變量 | 動態控制動畫參數 | 靈活、易于JS交互 | 兼容性要求 IE11+ |
SVG 動畫 | 矢量圖形動畫 | 分辨率無關、路徑動畫 | 學習成本較高 |
動畫庫 | 快速實現復雜效果 | 開箱即用、效果豐富 | 增加項目體積 |
五、調試工具
-
Chrome DevTools 動畫面板:
-
查看動畫時間軸
-
修改動畫參數實時預覽
-
-
性能監測:
-
使用 Performance 面板分析幀率
-
檢查 Layout/Paint 耗時
-
六、最佳實踐建議
-
優先使用 CSS 原生動畫(而非 JS 動畫)
-
限制同時運行的動畫數量(一般不超過 5 個)
-
移動端優化:
-
動畫時長建議 200-500ms
-
使用?
@media (prefers-reduced-motion)
?適配無障礙
@media (prefers-reduced-motion: reduce) {* { animation: none !important; } }
-
掌握這些方法后,可根據具體需求選擇最合適的動畫實現方案!🎉
其他綜合問題
如何不讓網頁上的水印被去除?
在PC端頁面實現防篡改水印需要綜合多種技術手段,以下是一套較為全面的解決方案:
一、動態Canvas水印方案
function createDynamicWatermark(text) {const canvas = document.createElement('canvas');const ctx = canvas.getContext('2d');canvas.width = 300;canvas.height = 200;// 繪制基礎水印ctx.font = '14px Arial';ctx.fillStyle = 'rgba(200,200,200,0.2)';ctx.rotate(-20 * Math.PI / 180);ctx.fillText(text, 10, 100);// 添加防偽波紋for(let i=0;i<canvas.width;i+=30){ctx.beginPath();ctx.moveTo(i, 0);ctx.lineTo(i, canvas.height);ctx.strokeStyle = 'rgba(0,0,0,0.02)';ctx.stroke();}// 生成動態背景圖const dataUrl = canvas.toDataURL();const watermarkDiv = document.createElement('div');watermarkDiv.style.cssText = `position: fixed;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;z-index: 999999;background: url(${dataUrl}) left top repeat;mix-blend-mode: multiply;`;// 防刪除保護watermarkDiv.setAttribute('id', 'secure-watermark');Object.defineProperty(watermarkDiv, 'id', {configurable: false,writable: false});document.body.appendChild(watermarkDiv); }
二、防篡改保護機制
DOM變動監控
const observer = new MutationObserver(mutations => {mutations.forEach(mutation => {Array.from(mutation.removedNodes).forEach(node => {if (node.id === 'secure-watermark') {document.body.appendChild(node);}});}); });observer.observe(document.body, {childList: true,subtree: true });
樣式篡改防御
const styleGuard = setInterval(() => {const watermark = document.getElementById('secure-watermark');if (!watermark) {createDynamicWatermark();return;}const styles = window.getComputedStyle(watermark);const protectedProps = {'display': 'block','opacity': '1','visibility': 'visible'};Object.entries(protectedProps).forEach(([prop, value]) => {if (styles[prop] !== value) {watermark.style[prop] = value;}}); }, 1000);
三、用戶行為監控
window.addEventListener('keydown', (e) => {// 屏蔽開發者工具快捷鍵const devtoolKeys = [e.ctrlKey && e.keyCode === 85, // Ctrl+Ue.ctrlKey && e.shiftKey && e.keyCode === 73, // Ctrl+Shift+Ie.keyCode === 123 // F12];if (devtoolKeys.some(Boolean)) {e.preventDefault();alert('安全警告:禁止開發者工具操作!');window.location.reload();} });// 防止右鍵檢查 document.addEventListener('contextmenu', (e) => {e.preventDefault();return false; });// 防截圖干擾 const noiseCanvas = document.createElement('canvas'); noiseCanvas.style.cssText = `position: fixed;top:0;left:0;pointer-events: none;z-index: 9999999; `; document.body.appendChild(noiseCanvas);function generateNoise() {const ctx = noiseCanvas.getContext('2d');const imageData = ctx.createImageData(noiseCanvas.width, noiseCanvas.height);const data = imageData.data;for(let i=0;i<data.length;i+=4){const val = Math.random()*255;data[i] = data[i+1] = data[i+2] = val;data[i+3] = 5; }ctx.putImageData(imageData, 0, 0);requestAnimationFrame(generateNoise); }noiseCanvas.width = window.innerWidth; noiseCanvas.height = window.innerHeight; generateNoise();
四、服務端配合方案
動態水印信息
// 每次請求攜帶動態token async function getWatermarkText() {const res = await fetch('/api/watermark-token');const { token } = await res.json();return `機密 ${token} ${Date.now()}`; }// 定期更新水印 setInterval(async () => {const newText = await getWatermarkText();updateWatermark(newText); }, 300000);
內容加密驗證
// 使用CryptoJS進行內容簽名 const contentHash = CryptoJS.HmacSHA256(document.body.innerText, 'secretKey' ).toString();// 后臺校驗示例 app.post('/verify-content', (req, res) => {const clientHash = req.body.hash;const serverHash = CryptoJS.HmacSHA256(req.body.content,'secretKey').toString();if(clientHash !== serverHash) {// 觸發內容篡改警報sendAlert('檢測到內容篡改!');} });
五、防御等級說明
防御層級 技術手段 防御能力 初級防御 基礎CSS水印 ★☆☆☆☆ 中級防御 Canvas動態水印+DOM監控 ★★★☆☆ 高級防御 行為監控+服務端驗證 ★★★★☆ 終極防御 硬件級DRM方案 ★★★★★ 注意事項:
定期更新水印算法(建議每月迭代)
結合業務日志記錄用戶操作行為
重要數據建議使用PDF等不可逆格式展示
法律層面增加水印聲明條款
任何前端方案都無法做到絕對防篡改,建議根據業務安全等級選擇適當方案,關鍵數據應結合后端驗證和權限控制。
HTTP和HTTPS的區別
HTTP(超文本傳輸協議)和 HTTPS(安全超文本傳輸協議)的核心區別在于數據傳輸的安全性,以下是兩者的詳細對比:
一、核心區別總結
特性 HTTP HTTPS 安全性 明文傳輸,數據可被竊聽/篡改 加密傳輸,防竊聽/防篡改 協議 應用層協議 HTTP + SSL/TLS 安全層 默認端口 80 443 證書 無需證書 需CA機構頒發的SSL證書 性能 無加密開銷,速度稍快 有加密計算開銷,但現代硬件影響可忽略 SEO 搜索引擎可能降權 搜索引擎優先收錄(如Google明確支持)
二、技術實現差異
1.?加密機制(核心)
HTTP:
數據以明文形式傳輸,可直接被中間人讀取:GET /login HTTP/1.1 Host: example.com username=admin&password=123456 ← 明文傳輸!
HTTPS:
通過SSL/TLS協議加密數據,傳輸內容為密文:���]�k�M�I�4��Q�V��x�d� ← 加密后的亂碼數據
2.?SSL/TLS工作流程
握手階段(非對稱加密):
客戶端驗證服務器證書有效性
協商加密算法(如RSA、ECDHE)
生成會話密鑰(Session Key)
數據傳輸(對稱加密):
使用會話密鑰加密通信內容
三、HTTPS核心優勢
1.?防竊聽(Encryption)
所有數據加密傳輸,即使被截獲也無法解密
示例:公共WiFi下登錄賬號時,HTTPS可防止密碼泄露
2.?防篡改(Integrity)
使用MAC(消息認證碼)驗證數據完整性
示例:阻止運營商插入廣告代碼
3.?身份認證(Authentication)
SSL證書驗證網站真實身份,防止釣魚網站
示例:瀏覽器地址欄顯示鎖標志和公司名稱
四、部署HTTPS的要求
1.?SSL證書類型
證書類型 驗證等級 適用場景 DV(域名驗證) 驗證域名所有權 個人博客、小型網站 OV(組織驗證) 驗證企業/組織身份 企業官網 EV(擴展驗證) 嚴格身份驗證(地址欄綠標) 銀行、電商等敏感領域 2.?部署步驟
購買或申請免費證書(如Let's Encrypt)
在服務器安裝證書(Nginx示例):
server {listen 443 ssl;ssl_certificate /path/to/cert.pem;ssl_certificate_key /path/to/private.key; }
強制HTTP跳轉HTTPS:
server {listen 80;return 301 https://$host$request_uri; }
五、性能優化技巧
會話復用(Session Resumption)
減少SSL握手次數,降低延遲:ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m;
啟用HTTP/2
HTTPS下可啟用HTTP/2協議提升性能:listen 443 ssl http2;
六、常見誤解澄清
誤區1:"HTTPS會顯著拖慢網站速度"
→ 現代服務器性能下,TLS握手開銷僅增加約1 RTT(經優化可降至0 RTT)誤區2:"只有支付頁面需要HTTPS"
→ 現代Web標準(如PWA、Geolocation API)要求全站HTTPS誤區3:"HTTPS無法被劫持"
→ 若客戶端安裝偽證書(如企業監控),仍可能被中間人攻擊
七、如何驗證HTTPS安全性
瀏覽器地址欄顯示鎖形圖標
通過SSL Labs測試檢查配置:
Rating: A+ (最佳評級)
總結:HTTPS通過加密和身份驗證構建安全傳輸通道,是現代Web應用的必備基礎,所有新建網站都應默認啟用HTTPS。
前端瀏覽器安全如何注意?
為了保障前端瀏覽器的安全性,需要從代碼防護、數據傳輸、用戶輸入處理等多方面入手。以下是系統化的安全防護方案及具體實施方法:
一、基礎防護體系
1. 啟用HTTPS強制加密
# Nginx配置(強制跳轉HTTPS) server {listen 80;server_name example.com;return 301 https://$host$request_uri; }
附加措施:
使用HSTS頭(強制瀏覽器使用HTTPS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
2. 安全頭配置
# 安全響應頭示例 add_header X-Frame-Options "SAMEORIGIN"; # 防點擊劫持 add_header X-Content-Type-Options "nosniff"; # 禁止MIME嗅探 add_header Referrer-Policy "strict-origin-when-cross-origin"; # 控制Referer泄露 add_header Permissions-Policy "geolocation=()"; # 禁用不必要權限
二、攻擊防御專項
1. XSS(跨站腳本攻擊)防御
輸入過濾:
// 使用DOMPurify過濾HTML import DOMPurify from 'dompurify'; const cleanHTML = DOMPurify.sanitize(userInput);
輸出轉義:
<!-- Vue自動轉義 --> <div>{{ userContent }}</div><!-- 手動轉義 --> <div v-html="safeHTML"></div>
CSP策略:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'">
2. CSRF(跨站請求偽造)防御
Token驗證:
// 后端生成Token const csrfToken = crypto.randomBytes(32).toString('hex'); res.cookie('XSRF-TOKEN', csrfToken);// 前端發送請求攜帶Token axios.defaults.headers.common['X-XSRF-TOKEN'] = getCookie('XSRF-TOKEN');
SameSite Cookie:
// 設置Cookie屬性 res.cookie('sessionID', '123', { sameSite: 'Strict', secure: true });
3. 點擊劫持防御
add_header X-Frame-Options "DENY"; # 完全禁止嵌入 # 或 add_header Content-Security-Policy "frame-ancestors 'self'";
三、數據安全防護
1. 敏感數據處理
前端加密:
// 使用Web Crypto API加密 const encryptedData = await crypto.subtle.encrypt({ name: 'AES-GCM', iv },key,new TextEncoder().encode(data) );
安全存儲:
// 避免localStorage存儲敏感信息 sessionStorage.setItem('tempData', encryptedData);
2. 第三方資源校驗
<!-- 使用SRI校驗CDN資源 --> <script src="https://cdn.example.com/jquery.js" integrity="sha384-..."> </script>
四、用戶輸入與認證
1. 輸入驗證
// 郵箱格式校驗 const isValidEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);// 防止SQL注入 const sanitizeInput = (input) => input.replace(/['"\\;]/g, '');
2. 認證安全
密碼策略:
// 前端密碼強度校驗 const isStrongPassword = (pwd) => pwd.length >= 8 && /[A-Z]/.test(pwd) && /[0-9]/.test(pwd);
雙因素認證:
// 集成Google Authenticator import { authenticator } from 'otplib'; const secret = authenticator.generateSecret(); const token = authenticator.generate(secret);
五、持續監控與更新
1. 依賴安全掃描
# 使用npm audit檢查漏洞 npm audit# 使用Snyk深度檢測 npx snyk test
2. 實時監控
錯誤追蹤:接入Sentry監控XSS等異常
入侵檢測:部署WAF(Web應用防火墻)
六、安全等級對照表
安全等級 適用場景 防護措施 基礎級 小型展示網站 HTTPS + CSP + XSS過濾 標準級 企業級應用 基礎級 + CSRF Token + 輸入驗證 + 安全頭 高級級 金融/政務系統 標準級 + 雙因素認證 + 實時監控 + 前端加密 + 人工滲透測試
七、應急響應方案
漏洞響應流程:
發現漏洞 → 評估影響 → 熱修復 → 通知用戶
數據泄露處理:
立即重置相關憑證
啟用日志審計追溯攻擊路徑
根據法律要求通知受影響用戶
通過以上綜合措施,可構建多層次的前端安全防護體系。關鍵點在于:默認安全原則 + 縱深防御 + 持續監控,同時需結合具體業務場景調整防護策略。
PC端開發大文件如何上傳?卡住了怎么辦?失敗了后如何繼續上傳?在前端PC端實現大文件上傳并處理中斷和續傳的問題,可以采用分塊上傳與斷點續傳的策略。以下是詳細的解決方案:
1. 分塊上傳(Chunked Upload)
將大文件分割為多個小塊(如1MB/塊),分別上傳,降低單次請求壓力,便于失敗后重試。
實現步驟:
前端分塊處理:
const CHUNK_SIZE = 1 * 1024 * 1024; // 1MB const file = document.getElementById('file-input').files[0]; const chunks = []; let start = 0;while (start < file.size) {const chunk = file.slice(start, start + CHUNK_SIZE);chunks.push(chunk);start += CHUNK_SIZE; }
生成文件唯一標識:
使用文件內容的哈希(如SHA-256)作為唯一標識,避免同名文件沖突。async function computeFileHash(file) {const buffer = await file.arrayBuffer();const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join(''); }
上傳單個塊:
async function uploadChunk(chunk, chunkIndex, fileHash) {const formData = new FormData();formData.append('chunk', chunk);formData.append('chunkIndex', chunkIndex);formData.append('fileHash', fileHash);formData.append('totalChunks', totalChunks);await fetch('/upload-chunk', {method: 'POST',body: formData,}); }
2. 斷點續傳(Resumable Upload)
記錄已上傳的塊,失敗后僅上傳缺失部分。
實現步驟:
查詢服務器已上傳的塊:
async function getUploadedChunks(fileHash) {const response = await fetch(`/uploaded-chunks?fileHash=${fileHash}`);return await response.json(); // 返回已上傳的塊索引數組,如[0,1,2] }
過濾未上傳的塊并繼續上傳:
const uploadedChunks = await getUploadedChunks(fileHash); const chunksToUpload = chunks.filter((_, index) => !uploadedChunks.includes(index));// 控制并發上傳(如最多5個并行) const MAX_CONCURRENT = 5; const queue = chunksToUpload.map((chunk, index) => () => uploadChunk(chunk, index, fileHash));// 使用p-limit庫控制并發 import pLimit from 'p-limit'; const limit = pLimit(MAX_CONCURRENT); await Promise.all(queue.map(task => limit(task)));
3. 失敗處理與重試
網絡錯誤重試:?為每個塊的上傳添加重試機制。
async function uploadWithRetry(chunk, chunkIndex, fileHash, retries = 3) {try {await uploadChunk(chunk, chunkIndex, fileHash);} catch (error) {if (retries > 0) {await uploadWithRetry(chunk, chunkIndex, fileHash, retries - 1);} else {throw error;}} }
服務端錯誤處理:?返回明確的錯誤狀態碼,前端根據狀態碼決定重試或報錯。
4. 服務器端實現
關鍵接口:
上傳塊接口(/upload-chunk):
接收塊文件、塊索引、文件哈希。
將塊保存到臨時目錄(按文件哈希組織)。
查詢已上傳塊接口(/uploaded-chunks):
根據文件哈希返回已上傳的塊索引列表。
合并文件接口(/merge-file):
所有塊上傳完成后,按順序合并成完整文件。
示例偽代碼(Node.js):
// 保存塊 app.post('/upload-chunk', (req, res) => {const { chunk, chunkIndex, fileHash } = req.body;const chunkDir = `./temp/${fileHash}`;fs.mkdirSync(chunkDir, { recursive: true });fs.writeFileSync(`${chunkDir}/${chunkIndex}`, chunk.buffer);res.status(200).send('Chunk uploaded'); });// 合并文件 app.post('/merge-file', (req, res) => {const { fileHash, fileName } = req.body;const chunkDir = `./temp/${fileHash}`;const chunks = fs.readdirSync(chunkDir).sort((a, b) => a - b);const filePath = `./uploads/${fileName}`;chunks.forEach(chunk => {fs.appendFileSync(filePath, fs.readFileSync(`${chunkDir}/${chunk}`));});fs.rmdirSync(chunkDir, { recursive: true }); // 清理臨時文件res.status(200).send('File merged'); });
5. 用戶體驗優化
進度顯示:?監聽每個塊的上傳進度,計算總進度。
let uploadedSize = 0; chunks.forEach((chunk, index) => {uploadChunk(chunk, index).then(() => {uploadedSize += chunk.size;const progress = (uploadedSize / file.size) * 100;updateProgressBar(progress);}); });
暫停/恢復:?使用
AbortController
取消請求,保存已上傳狀態。const controllers = new Map();function uploadChunk(chunk, index) {const controller = new AbortController();controllers.set(index, controller);return fetch(url, {signal: controller.signal,// ...}); }// 暫停上傳 function pauseUpload() {controllers.forEach(controller => controller.abort()); }
6. 注意事項
文件一致性校驗:?合并后對比服務器端文件的哈希與前端計算的哈希。
清理臨時文件:?設置定時任務清理未完成的過期上傳。
分塊大小調整:?根據網絡情況動態調整塊大小(如1MB-5MB)。
通過上述方案,可有效解決大文件上傳中的中斷、續傳和失敗重試問題,提升用戶體驗。
如何記錄接口報錯并且展示?在前端中記錄接口報錯并展示錯誤信息,是提升用戶體驗和排查問題的重要手段。以下是分步驟的實現方案:
一、錯誤捕獲與記錄
1. 使用請求攔截器(以 Axios 為例)
// 創建 axios 實例 const instance = axios.create();// 響應攔截器 instance.interceptors.response.use((response) => response,(error) => {const { config, response } = error;const errorInfo = {time: new Date(),url: config?.url,method: config?.method,status: response?.status,message: response?.data?.message || error.message,};// 記錄錯誤(可擴展為上報服務器)logError(errorInfo);// 展示錯誤(可選,部分錯誤可能不需要立即展示)showErrorUI(errorInfo);return Promise.reject(error);} );
2. 錯誤日志記錄方式
本地存儲(臨時記錄)
function logError(error) {const MAX_LOG_SIZE = 50;const logs = JSON.parse(localStorage.getItem('api_errors') || []);logs.push(error);if (logs.length > MAX_LOG_SIZE) logs.shift();localStorage.setItem('api_errors', JSON.stringify(logs)); }
上報到服務器(推薦生產環境使用)
function reportErrorToServer(error) {navigator.sendBeacon('/api/logs', JSON.stringify(error)); }
二、錯誤展示策略
1. 通用錯誤提示組件(React 示例)
import { Toast } from 'antd-mobile';function showErrorUI(error) {let message = '請求失敗,請稍后重試';// 根據錯誤類型定制提示if (error.status === 401) {message = '登錄已過期,請重新登錄';redirectToLogin();} else if (error.status >= 500) {message = '服務器開小差了,請聯系管理員';}Toast.show({content: message,duration: 3000,icon: 'fail',}); }
2. 專用錯誤信息面板
function ErrorPanel() {const [errors, setErrors] = useState([]);useEffect(() => {const errors = JSON.parse(localStorage.getItem('api_errors')) || [];setErrors(errors);}, []);return (<div className="error-panel"><h3>最近接口錯誤 ({errors.length})</h3>{errors.map((err, i) => (<div key={i} className="error-item"><p>[{err.time}] {err.method} {err.url}</p><p>狀態碼: {err.status} | 信息: {err.message}</p></div>))}</div>); }
三、高級優化方案
錯誤分類處理
function handleErrorByType(error) {if (!navigator.onLine) {showNetworkError();} else if (error.code === 'ECONNABORTED') {showTimeoutError();} else {showGenericError();} }
錯誤頻率限制
let lastToastTime = 0; function showError(message) {if (Date.now() - lastToastTime > 5000) {Toast.show(message);lastToastTime = Date.now();} }
敏感信息過濾
function sanitizeError(error) {const sanitized = { ...error };delete sanitized.headers.Authorization;delete sanitized.request.body.password;return sanitized; }
四、技術棧擴展建議
錯誤監控平臺集成
使用 Sentry/Bugsnag 進行專業錯誤監控
import * as Sentry from '@sentry/browser'; Sentry.init({ dsn: 'your_dsn' });function logError(error) {Sentry.captureException(new Error(error.message), {extra: error.context}); }
性能監控結合
const startTime = Date.now(); axios.get('/api/data').then(() => {logApiPerformance(Date.now() - startTime);});
五、注意事項
生產環境應避免顯示原始錯誤堆棧
401 錯誤需要特殊處理(清除登錄態)
重要接口可加入自動重試機制
function retryRequest(config, retries = 3) {return axios(config).catch(err => {return retries > 0 ? retryRequest(config, retries - 1): Promise.reject(err);}); }
通過以上方案,可以實現完整的接口錯誤處理流程,平衡用戶體驗與開發調試需求。建議根據具體業務場景選擇適合的優化點進行擴展。
?前端輸入url后瀏覽器會先做什么?
以下是前端輸入 URL 后瀏覽器的主要處理流程,按順序分階段說明:
一、URL 解析階段(URL Parsing)
協議補全
自動補全協議頭(如輸入?google.com
?→ 補全為?https://google.com
)格式校驗
檢查 URL 合法性(如特殊字符轉換?空格→%20
)
二、緩存檢查階段(Cache Check)
緩存類型 檢查順序 典型場景 Service Worker 最先檢查 PWA 離線緩存 Memory Cache 內存緩存(短期) 前進后退頁面 Disk Cache 磁盤緩存(長期) CSS/JS 文件緩存 Push Cache HTTP/2 推送緩存(會話級) 服務器推送資源 *觸發強制刷新(Ctrl+F5)時會跳過所有緩存*
三、DNS 解析階段(DNS Lookup)
優化手段
<!-- 預解析特定域名 --> <link rel="dns-prefetch" href="//cdn.example.com">
四、建立連接階段(TCP Handshake)
三次握手建立 TCP 連接
Client->Server: SYN Server->Client: SYN-ACK Client->Server: ACK
TLS 握手(HTTPS)
協商加密協議版本
驗證證書有效性
交換對稱加密密鑰
五、發送請求階段(HTTP Request)
GET /index.html HTTP/1.1 Host: www.example.com Accept: text/html Cookie: session_id=abc123
關鍵過程
攜帶 Cookie 等身份信息
檢查?
Connection: keep-alive
?復用連接處理?
Content-Encoding
?壓縮格式
六、響應處理階段(Response Processing)
狀態碼處理
// 常見狀態碼處理邏輯 switch(statusCode) {case 301:handleRedirect(response.headers.Location);break;case 404:show404Page();break;case 500:logServerError();break; }
解壓響應體
自動處理 gzip/deflate 壓縮
七、解析渲染階段(Rendering)
關鍵阻塞點
<script>
?標簽會阻塞 DOM 構建(除非添加?async/defer
)CSS 文件會阻塞渲染樹合成(CSSOM 構建)
八、持續通信階段(Keep-Alive)
TCP 連接復用
// HTTP/1.1 默認開啟 keep-alive // 同一域名最多維持 6 個 TCP 連接(瀏覽器差異)
HTTP/2 多路復用
單個連接并行傳輸多個請求
頭部壓縮優化
關鍵性能優化點
減少 DNS 查詢(使用 dns-prefetch)
復用 TCP 連接(合理配置 keep-alive)
壓縮資源體積(Gzip/Brotli)
避免渲染阻塞(CSS 內聯/JS 異步加載)
利用 CDN 加速(減少網絡延遲)
理解整個流程可以幫助前端開發者針對性優化關鍵路徑,提升頁面加載性能。