目錄
什麼是響應式
響應式原理
定制响应式数据
Vueuse 工具包
click
首頁 web前端 Vue.js Vue3中的響應式機制是什麼

Vue3中的響應式機制是什麼

May 12, 2023 pm 05:07 PM
vue3

什麼是響應式

響應式一直都是Vue 的特色功能之一;與之相比,JavaScript 裡面的變量,是沒有響應式這個概念的;你在學習JavaScript 的時候首先被灌輸的概念,就是程式碼是自上而下執行的;

我們看下面的程式碼,程式碼執行後,列印輸出的兩次double 的結果也都是2 ;即使我們修改了程式碼中count 的值後,double 的值也不會有任何改變

let count = 1
let double = count * 2
count = 2
登入後複製

double 的值是根據count 的值乘以二計算而得到的,如果現在我們想讓doube能夠跟著count 的變化而變化,那麼我們就需要在每次count 的值修改後,重新計算double

比如,在下面的程式碼,我們先把計算doube 的邏輯封裝成函數,然後在修改完count 之後,再執行一遍,你就會得到最新的double 值

let count = 1
// 计算过程封装成函数
let getDouble = n=>n*2 //箭头函数
let double = getDouble(count)
count = 2
// 重新计算double ,这里我们不能自动执行对double的计算
double = getDouble(count)
登入後複製

實際開發中的計算邏輯會比計算doube 複雜的多,但是都可以封裝成一個函數去執行;下一步,我們要考慮的是,如何讓double 的值得到自動計算

如果我們能讓getDouble 函數自動執行,也就是如下圖所示,我們使用JavaScript 的某種機制,把count 包裹一層,每當對count 進行修改時,就去同步更新double 的值,那麼就有一種double 自動跟著count 的變化而變化的感覺,這就算是響應式的雛形了

Vue3中的響應式機制是什麼


響應式原理

響應式原理是什麼呢? Vue 中用過三種響應式解決方案,分別是defineProperty、Proxy 和value setter我們先來看Vue 2 的defineProperty API

這裡我結合一個例子來說明,在下面的程式碼中,我們定義個一個物件obj,使用defineProperty 代理了count 屬性;這樣我們就對obj 物件的value 屬性實作了攔截,讀取count 屬性的時候執行get 函數,修改count 屬性的時候執行set 函數,並在set  函數內部重新計算了double

let getDouble = n=>n*2
let obj = {}
let count = 1
let double = getDouble(count)
Object.defineProperty(obj,'count',{
    get(){
        return count
    },
    set(val){
        count = val
        double = getDouble(val)
    }
})
console.log(double) // 打印2
obj.count = 2
console.log(double) // 打印4 有种自动变化的感觉
登入後複製

這樣我們就實現了簡易的響應式功能,在課程的第四部分,我還會帶著你寫一個更完善的響應式系統

但defineProperty API 作為Vue 2 實作響應式的原理,它的語法中也有一些缺陷;例如在下面程式碼中,我們刪除obj.count 屬性,set 函數就不會執行,double 還是之前的數值;這也是為什麼在Vue 2中,我們需要$delete 一個專門的函數去刪除資料

delete obj.count
console.log(double) // doube还是4
登入後複製

Vue 3 的響應式機制是基於Proxy 實現的;就Proxy 這個名字來說,你也能看出來這就是代理的意思,Proxy 的重要意義在於它解決了Vue 2 響應式的缺陷

我們看下面的程式碼,在其中我們透過new Proxy 代理了obj 這個對象,然後透過get、set 和deleteProperty 函數代理了物件的讀取、修改和刪除操作,從而實現了響應式的功能

let proxy = new Proxy(obj,{
    get : function (target,prop) {
        return target[prop]
    },
    set : function (target,prop,value) {
        target[prop] = value;
        if(prop==='count'){
            double = getDouble(value)
    }
},
deleteProperty(target,prop){
    delete target[prop]
    if(prop==='count'){
        double = NaN
    }
   }
})
console.log(obj.count,double)
proxy.count = 2
console.log(obj.count,double)
delete proxy.count
// 删除属性后,我们打印log时,输出的结果就会是 undefined NaN
console.log(obj.count,double)
登入後複製

我們從這裡可以看出Proxy 實現的功能和Vue 2 的definePropery 類似,它們都能夠在用戶修改資料的時候觸發set 函數,從而實現自動更新double 的功能。而且Proxy 也完善了幾個definePropery 的缺陷,比如說可以監聽到屬性的刪除

#Proxy 是針對物件來監聽,而不是針對某個具體屬性,所以不僅可以代理那些定義時不存在的屬性,也可以代理更豐富的資料結構,像是Map、Set 等,我們也能透過deleteProperty 實作對刪除操作的代理

當然,為了幫助你理解Proxy,我們也可以把double 相關的程式碼都寫在set 和deleteProperty 函數裡進行實現,在課程的後半程我會帶你做好更完善的封裝;比如下面程式碼中,Vue 3 的reactive 函數可以把一個物件變成響應式數據,而reactive 就是基於Proxy 實現的;我們也可以透過watchEffect,在obj.count 修改之後,執行資料的列印

import {reactive,computed,watchEffect} from 'vue'
let obj = reactive({
    count:1
})
let double = computed(()=>obj.count*2)
obj.count = 2
watchEffect(()=>{
    console.log('数据被修改了',obj.count,double.value)
})
登入後複製

有了Proxy 後,響應式機制就比較完備了;但是在Vue 3 中還有另一個響應式實現的邏輯,就是利用物件的get 和set 函數來進行監聽,這種響應式的實作方式,只能攔截某一個屬性的修改,這也是Vue 3 中ref 這個API 的實作

在下面的程式碼中,我們攔截了count 的value 屬性,並且攔截了set 操作,也能實現類似的功能

let getDouble = n => n * 2
let _value = 1

double = getDouble(_value)
let count = {
    get value() {
        return _value
    },
    set value(val) {
        _value = val
        double = getDouble(_value)
    }
}

console.log(count.value,double)
count.value = 2
console.log(count.value,double)
登入後複製

三種實現原理的對比表格如下,幫助你理解三種響應式的差異

实现原理definePropertyProxyvalue setter
实际场景Vue 2 响应式Vue 3 reactiveVue 3 ref
优势兼容性基于proxy实现真正的拦截实现简单
劣势数组和属性删除等拦截不了兼容不了 IE11只拦截了 value 属性
实际应用Vue 2Vue 3 复杂数据结构Vue 3 简单数据结构

定制响应式数据

简单入门响应式的原理后,接下来我们学习一下响应式数据在使用的时候的进阶方式;我们看下使用 <script setup> 重构之后的 todolist 的代码;这段代码使用 watchEffect,数据变化之后会把数据同步到 localStorage 之上,这样我们就实现了 todolist 和本地存储的同步

import { ref, watchEffect, computed } from "vue";
let title = ref("");
let todos = ref(JSON.parse(localStorage.getItem(&#39;todos&#39;)||&#39;[]&#39;));
watchEffect(()=>{
    localStorage.setItem(&#39;todos&#39;,JSON.stringify(todos.value))
})
function addTodo() {
    todos.value.push({
        title: title.value,
        done: false,
    });
    title.value = "";
}
登入後複製

更进一步,我们可以直接抽离一个 useStorage 函数,在响应式的基础之上,把任意数据响应式的变化同步到本地存储;我们先看下面的这段代码,ref 从本地存储中获取数据,封装成响应式并且返回,watchEffect 中做本地存储的同步,useStorage 这个函数可以抽离成一个文件,放在工具函数文件夹中

function useStorage(name, value=[]){
    let data = ref(JSON.parse(localStorage.getItem(name)||&#39;[]&#39;))
    watchEffect(()=>{
        localStorage.setItem(name,JSON.stringify(data.value))
    })
    return data
}
登入後複製

在项目中我们使用下面代码的写法,把 ref 变成 useStorage,这也是 Composition API 最大的优点,也就是可以任意拆分出独立的功能

let todos = useStorage(&#39;todos&#39;,[])
function addTodo() {
    ...code
}
登入後複製

现在,你应该已经学会了在 Vue 内部进阶地使用响应式机制,去封装独立的函数;在后续的实战应用中,我们也会经常对通用功能进行封装;如下图所示,我们可以把日常开发中用到的数据,无论是浏览器的本地存储,还是网络数据,都封装成响应式数据,统一使用响应式数据开发的模式;这样,我们开发项目的时候,只需要修改对应的数据就可以了

Vue3中的響應式機制是什麼

基于响应式的开发模式,我们还可以按照类似的原理,把我们需要修改的数据,都变成响应式;比如,我们可以在 loading 状态下,去修改浏览器的小图标 favicon;和本地存储类似,修改 favicon 时,我们需要找到 head 中有 icon 属性的标签

在下面的代码中,我们把对图标的对应修改的操作封装成了 useFavicon 函数,并且通过 ref 和 watch 的包裹,我们还把小图标变成了响应式数据

import {ref,watch} from &#39;vue&#39;
export default function useFavicon( newIcon ) {
    const favicon = ref(newIcon)
    const updateIcon = (icon) => {
        document.head
        .querySelectorAll(`link[rel*="icon"]`)
        .forEach(el => el.href = `${icon}`)
    }
watch( favicon,
    (i) => {
        updateIcon(i)
    }
)
    return {favicon,reset}
}
登入後複製

这样在组件中,我们就可以通过响应式的方式去修改和使用小图标,通过对 faivcon.value 的修改就可以随时更换网站小图标;下面的代码,就实现了在点击按钮之后,修改了网页的图标为 geek.png 的操作

<script setup>
    import useFavicon from &#39;./utils/favicon&#39;
    let {favicon} = useFavicon()
    function loading(){
        favicon.value = &#39;/geek.png&#39;
    }
</script>
<template>
    <button @click="loading">123</button>
</template>
登入後複製

Vueuse 工具包

我们自己封装的 useStorage,算是把 localStorage 简单地变成了响应式对象,实现数据的更新和localStorage 的同步;同理,我们还可以封装更多的类似 useStorage 函数的其他 use 类型的函数,把实际开发中你用到的任何数据或者浏览器属性,都封装成响应式数据,这样就可以极大地提高我们的开发效率

Vue 社区中其实已经有一个类似的工具集合,也就是 VueUse,它把开发中常见的属性都封装成为响应式函数

VueUse 趁着这一波 Vue 3 的更新,跟上了响应式 API 的潮流;VueUse 的官方的介绍说这是一个 Composition API 的工具集合,适用于 Vue 2.x 或者 Vue 3.x,用起来和 React Hooks 还挺像的

在项目目录下打开命令行里,我们输入如下命令,来进行 VueUse 插件的安装:

npm install @vueuse/core
登入後複製

然后,我们就先来使用一下 VueUse;在下面这段代码中,我们使用 useFullscreen 来返回全屏的状态和切换全屏的函数;这样,我们就不需要考虑浏览器全屏的 API,而是直接使用 VueUse 响应式数据和函数就可以很轻松地在项目中实现全屏功能

<template>
    <h2 id="click">click</h2>
</template>
<script setup>
import { useFullscreen } from &#39;@vueuse/core&#39;
const { isFullscreen, enter, exit, toggle } = useFullscreen()
</script>
登入後複製

useFullscreen 的封装逻辑和 useStorage 类似,都是屏蔽了浏览器的操作,把所有我们需要用到的状态和数据都用响应式的方式统一管理,VueUse 中包含了很多我们常用的工具函数,我们可以把网络状态、异步请求的数据、动画和事件等功能,都看成是响应式的数据去管理。

以上是Vue3中的響應式機制是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

vue3專案中怎麼使用tinymce vue3專案中怎麼使用tinymce May 19, 2023 pm 08:40 PM

tinymce是一個功能齊全的富文本編輯器插件,但在vue中引入tinymce並不像別的Vue富文本插件一樣那麼順利,tinymce本身並不適配Vue,還需要引入@tinymce/tinymce-vue,並且它是國外的富文本插件,沒有透過中文版本,需要在其官網下載翻譯包(可能需要翻牆)。 1.安裝相關依賴npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下載中文包3.引入皮膚和漢化包在項目public資料夾下新建tinymce資料夾,將下載的

vue3+vite:src使用require動態匯入圖片報錯怎麼解決 vue3+vite:src使用require動態匯入圖片報錯怎麼解決 May 21, 2023 pm 03:16 PM

vue3+vite:src使用require動態導入圖片報錯和解決方法vue3+vite動態的導入多張圖片vue3如果使用的是typescript開發,就會出現require引入圖片報錯,requireisnotdefined不能像使用vue2這樣imgUrl:require(' …/assets/test.png')導入,是因為typescript不支援require所以用import導入,下面介紹如何解決:使用awaitimport

Vue3如何實作刷新頁面局部內容 Vue3如何實作刷新頁面局部內容 May 26, 2023 pm 05:31 PM

想要實現頁面的局部刷新,我們只需要實現局部元件(dom)的重新渲染。在Vue中,想要實現這效果最簡單的方式方法就是使用v-if指令。在Vue2中我們除了使用v-if指令讓局部dom的重新渲染,也可以新建一個空白元件,需要刷新局部頁面時跳轉至這個空白元件頁面,然後在空白元件內的beforeRouteEnter守衛中又跳轉回原來的頁面。如下圖所示,如何在Vue3.X中實現點擊刷新按鈕實現紅框範圍內的dom重新加載,並展示對應的加載狀態。由於Vue3.X中scriptsetup語法中組件內守衛只有o

Vue3怎麼解析markdown並實現程式碼高亮顯示 Vue3怎麼解析markdown並實現程式碼高亮顯示 May 20, 2023 pm 04:16 PM

Vue實作部落格前端,需要實作markdown的解析,如果有程式碼則需要實作程式碼的高亮。 Vue的markdown解析函式庫很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。這些庫都大同小異。這裡選用的是marked,程式碼高亮的函式庫選用的是highlight.js。具體實現步驟如下:一、安裝依賴庫在vue專案下開啟命令窗口,並輸入以下命令npminstallmarked-save//marked用於將markdown轉換成htmlnpmins

怎麼使用vue3+ts+axios+pinia實現無感刷新 怎麼使用vue3+ts+axios+pinia實現無感刷新 May 25, 2023 pm 03:37 PM

vue3+ts+axios+pinia實作無感刷新1.先在專案中下載aiXos和pinianpmipinia--savenpminstallaxios--save2.封裝axios請求-----下載js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfigig ,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

Vue3復用元件怎麼使用 Vue3復用元件怎麼使用 May 20, 2023 pm 07:25 PM

前言無論是vue還是react,當我們遇到多處重複程式碼的時候,我們都會想著如何重複使用這些程式碼,而不是一個檔案裡充斥著一堆冗餘程式碼。實際上,vue和react都可以透過抽組件的方式來達到復用,但如果遇到一些很小的程式碼片段,你又不想抽到另外一個檔案的情況下,相比而言,react可以在相同文件裡面宣告對應的小元件,或透過renderfunction來實現,如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

Vue3中怎麼實現選取頭像並裁剪 Vue3中怎麼實現選取頭像並裁剪 May 29, 2023 am 10:22 AM

最終效果安裝VueCropper組件yarnaddvue-cropper@next上面的安裝值針對Vue3的,如果時Vue2或想使用其他的方式引用,請訪問它的npm官方地址:官方教程。在元件中引用使用時也很簡單,只需要引入對應的元件和它的樣式文件,我這裡沒有在全域引用,只在我的元件檔案中引入import{userInfoByRequest}from'../js/api' import{VueCropper}from'vue-cropper&

vue3項目打包發佈到伺服器後訪問頁面顯示空白怎麼解決 vue3項目打包發佈到伺服器後訪問頁面顯示空白怎麼解決 May 17, 2023 am 08:19 AM

vue3專案打包發佈到伺服器後存取頁面顯示空白1、處理vue.config.js檔案中的publicPath處理如下:const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({publicPath :process.env.NODE_ENV==='production'?'./':'/&

See all articles