Vue3 面試題 (2023-09-26更新)

Vue3 對比 Vue2 做了那些改進?

1. 響應式系統

  • vue2 中使用的 Object.defineProperty 實現的響應式,劫持整個對象,遞歸遍歷所有屬性,給每個屬性添加 getter 和 setter
  • vue3 中使用的 Proxy 實現的響應式

2. 編譯階段

  • Fragment

Vue3 增加了一個 Fragment 抽象組件,本身不會被渲染到 DOM 中。主要的作用是:模板裏面不再需要創建唯一根節點。可以直接放同級標籤

  • 靜態節點提升

Vue2 中,每次數據更新重新渲染時,靜態節點,也會在虛擬 DOM 樹中重新創建一次。執行 diff 算法來比較舊的虛擬 DOM 樹和新的虛擬 DOM 樹,通過對靜態節點打上標記,來優化 diff 的過程
Vue3 中,是通過將靜態節點提升到渲染函數之外,渲染函數內部只會保持對靜態節點的引用。這樣重新渲染時,並不會創建靜態的虛擬節點,從而減少了不必要的計算和操作,提高了渲染性能

下面用一段代碼說明,假設我們有如下模板:

<div>
  <p>static text</p>
  <p>{{ title }}</p>
</div>

在沒有靜態提升的情況下,它對應的渲染函數是:

function render() {
  return (
    openClock(),
    createClock('div', null, [
      createVNode('p', null, 'static text'), // 靜態節點被重新創建
      createVNode('p', null, ctx.title, 1)
    ])
  )
}

有了靜態提升的情況:

const hoist1 = createVNode('p', null, 'text')
function render() {
  return (
    openBlock(),
    createBlock('div', null, [
      hoist1, // 靜態節點引用
      createVNode('p', null, ctx.title, 1)
    ])
  )
}

3. 源碼體積

  • Vue3 中移除了一些不常用的 API,整體體積變小了
  • 使用的函數方法,如 ref、reactive、computed 等,只有再用到的時候纔會進行打包

Vue3 有哪些變化?

  1. 響應式系統

用 Proxy 代替 Object.defineProperty 重構了響應式系統,可以監聽到數組下標的變化,對象屬性的變化

  1. 生命週期

用 setup 代替了 beforeCreate 和 created 這兩個生命週期

  1. 指令
  • 新增指令 v-memo 可以緩存 html 模板
  • 插槽 slot 和 slot-scope 屬性已被廢棄,用 v-slot 代替
  1. 內置 API

移除了 Vue.filter、Vue.mixin、Vue.set 和 Vue.delete

  1. 編譯

組件模板中不再需要唯一根節點,不需要根元素包裹

  1. 組合式 API

新增 Composition API 可以更好的組織代碼,同一功能的代碼不至於像以前一樣太分散。Vue2 中可以用 minxin 來實現複用代碼,但也會存在一些問題,比如方法或屬性名會衝突,代碼來源不明顯等

組合式 API 有什麼優缺點?

優點:

  • 提供了靈活性和邏輯複用方式,相關的邏輯代碼組織在一起,方便了閱讀

缺點:

  • 對於初學者或已經習慣於響應式 API 的開發者來說,切換到組合式 API 可能需要一些時間來熟悉用法和設計思想
  • 組合式 API 提供了更強大的靈活性,開發者可以自由的組織代碼和邏輯處理。但這可能會導致代碼結構不一致,代碼更難以理解和維護

選項式 API 有什麼優缺點?

優點:

  • 易於學習和使用,寫代碼的位置已經固定好

缺點:

  • 當組件變得複雜時,同一個功能的邏輯被拆分,關注點會變得很分散
  • 相似的邏輯代碼不便於複用

選項式 API 和 組合式 API 的區別?

  • 選項式 API:Vue2 中的 Options API 就是選項式 API。在一個文件內,只能固定使用datamethodscomputed等選項來組織代碼。在項目變得複雜時,一個功能的屬性和方法會存在文件的各個地方,很分散,變的越來越難維護。使用 mixin 重用共用代碼,也會有命名衝突,數據來源不清晰的問題
  • 組合式 API:Vue3 中的 Composition API 就是組合式 API。通常把一個功能所定義的屬性、方法放在一起,可以更靈活的組織邏輯。解決了選項式 API 不好拆分和重用的問題

Vue3 響應式原理 和 Vue2 的區別?

Vue2 數據響應式是通過 Object.defineProperty 劫持屬性,在數據變化時發佈消息給訂閱者,觸發相應的監聽回調,這其中存在幾個問題:

  • 初始化數據需要遞歸遍歷對象所有 key,性能不好
  • 通知更新需要維護大量的 dep 實例和 watcher 實例,佔用額外的內存
  • 無法監聽到數組元素的變化,只能通過重寫數組方法
  • 動態新增、刪除對象屬性無法攔截,只能通過特定 setdeleteAPI 代理
  • 不支持 MapSet 等數據結構

Vue3中爲了解決這些問題:

  • 使用原生proxy代理,可以監聽到數組下標的變化,對象屬性的變化,多達 13 種攔截方法
  • 新增數據結構全部支持
  • 對象嵌套屬性只代理第一層,只有訪問某個屬性的時候,纔會遞歸代理下一級的屬性
  • 也不需要維護特別多的依賴關係

Object.defineProperty 和 Proxy 的區別?

  • Object.definePropertyes5 的方法。Proxyes6 的方法
  • Object.defineProperty 不能監聽到數組下標變化和對象新增屬性。Proxy 可以
  • Object.defineProperty 是劫持對象屬性。Proxy 是代理整個對象
  • Object.defineProperty 是遞歸遍歷對象屬性,只能監聽單個屬性。Proxy 對象嵌套屬性運行時遞歸,用到的時候再進行代理,也不需要維護特別多的依賴關係,性能提升很大,且首次渲染更快
  • Object.defineProperty 會污染原對象,修改時是修改原對象。Proxy 是對原對象進行代理並會返回一個新的代理對象,修改的是代理對象
  • Object.defineProperty 不兼容 IE8。Proxy 不兼容 IE11

談談你對 setup 函數的瞭解?

  • setup 會在 beforeCreate() 之前執行
  • setup 不能使用 this
  • setup 內部的屬性、方法,必須 return 暴露出來。否則沒法使用
  • setup 內部數據不具有響應式
  • setup 不能調用生命週期相關函數,但生命週期函數可以調用 setup 內的函數

setup 方法和 setup 語法糖的區別?

// 方法
setup(props.context) {}

// 語法糖
<script setup></script>

ref 和 reactive 區別?

  • 定義數據
    ref 通常用來定義基本類型數據,也可以傳入引用類型的數據,內部會通過reactive轉化爲代理對象,只是不推薦這麼做
    reactive 用來定義引用類型數據。不可以傳入基本類型數據,如果傳入則不會返回proxy對象,由於reactive是基於proxy實現響應式的,如果返回的不是proxy對象,則該數據不會具有響應式
  • 實現原理
    ref 通過Object.defineProperty來實現數據代理
    reactive 使用proxy實現數據代理,並且通過reflect操作原對象內部的數據
  • 操作數據
    ref 在 js 中,獲取和修改數據需要.valuetemplate中不需要
    reactive 不需要

怎麼重置 reactive 數據?

  1. 第一種
import { reactive } from 'vue'

class InitFormData {
  userName: string = ''
  age: number = 0
}

let formData = reactive(new InitFormData())

// 重置數據
Object.assign(formData, new InitFormData())
  1. 第二種
import { reactive } from 'vue'

const initFormData = () => {
  return {
    userName: '',
    age: 0
  }
}

let formData = reactive(initFormData())

// 重置數據
Object.assign(formData, initFormData())
  1. 第三種
import {reactive} from 'vue'

const initFormData = {
  userName: '',
  age: 0
}

const _initFormData = Object.assign({}, initFormData)

let formData = reactive{formData: initFormData}

// 重置數據
formData.formData = Object.assign({}, _initFormData)

原理

打包

說說 wepack 和 vite 的區別?

https://www.inte.net/news/281889.html
https://worktile.com/kb/p/53704

  • 打包過程
    • webpack 會對項目進行全量構建,首先會分析各個模塊之間的依賴關係,形成一個依賴樹,然後打包成bundle.js文件,將打包後的代碼在本地服務器進行渲染。這樣存在的問題就是,隨着模塊的增多,打包體積變大,影響熱更新的速度
    • vite 不需要向webpack一樣先打包再渲染。它可以直接啓動本地服務器進行渲染,它利用瀏覽器原生的 ES 模塊加載功能(現代瀏覽器本身支持ES-Module),會自動向依賴的模塊發出請求,按需動態編譯顯示(每個模塊可以獨立的進行編譯和緩存)
  • 熱更新方面
    • webpack 當改動了某個模塊時,需要將模塊以及依賴的模塊全部編譯一次
    • vite 只需讓瀏覽器重新請求該模塊

結果:通過比較可以看出vite在啓動的時候不需要打包,不用分析模塊之間的依賴關係,瀏覽器請求某個模塊時,再對模塊進行編譯。當項目越複雜,模塊越多的情況,vite越優於webpack

怎麼開啓 gzip?

1. Nginx 在線壓縮
通過 CPU 負載換取寬度,客戶端請求靜態資源時,通過 Nginx 在線壓縮 gz 文件返回瀏覽器,瀏覽器自動解壓縮(支持 gzip 的瀏覽器)

gzip on;
gzip_min-length 1K;
gzip_buffers 4 16K;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";

2. Koa2 開啓 gzip
node 直接對源文件進行 gzip 壓縮,返回壓縮後的資源
[koa-compress]https://www.npmjs.com/package/koa-compress

2.1 安裝 koa-compress 中間件

npm install koa-compress --save

2.2 配置 koa-compress 中間件

const app = require('koa')()
const compress = require('koa-compress')
const options = { threshold: 2048 }
app.use(compress(options))

3. webpack 開啓 gzip 壓縮
服務端進行壓縮,如果壓縮的文件比較大,壓縮的這個過程也比較耗時,體驗也不是太好
可以在前端打包的時候,直接打包成 .gz 的文件

3.1 安裝 compression-webpack-plugin

npm install compression-webpack-plugin --save-dev

3.2 Vue.config.js 配置

const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
 configureWebpack: {
   plugins: [
     new CompressionPlugin({
       algorithm: 'gzip', // 使用 gzip 壓縮
       test: /\.(js|css|html)?$/i, // 匹配文件名
       filename: '[path][base].gz[query]', // 壓縮後的文件名(保持原文件名,後綴加.gz)
       minRatio: 0.8, // 默認 0.8,壓縮率小於 1 纔會進行壓縮
       threshold: 10 \* 1024, // 超過 10K 的文件會進行壓縮
       deleteOriginalAssets: false // 是否刪除未壓縮的源文件。如果希望提供非 gzip 的資源,可不設置或設置爲 false
     })
   ]
 }
}

3.3 nginx 配置

gzip_static on;

4. vite 開啓 gzip 打包
4.1 安裝

npm i vite-plugin-compression

4.2 vite.config.js 配置

import viteCompression from 'vite-plugin-compression'
const plugins = [
  vue(),
  vueJsx(),
  viteCompression({
    threshold: 10 * 1024 // 對大於 10K 的文件進行壓縮
  })
]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章