YUIJZEON
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
3
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# VUE reactive() vs. ref() 都幾? 先說結論 reactive() 能做的事 ref() 也都能做 ref() 能做的事 reactive() 也都能做 真的要說差異的話只是 coding 風格的不同 ## 使用上的差異 這裡簡單列出最顯而易見的差異 ref() 定義出來的值在 <script/> 裡需要 .value 才取得到 但在 <template/> 裡不用 而 reactive() 在 <script/> 還是 <template/> 裡都不用 ref() 可以允許 primitive values (原始值, 如 string, number, boolean, undefined) 以及 Objects (包括任何種類的 Object 和 Array) 而 reactive() 只允許 Objects --- 在探討哪個比較好這個問題之前 我們先來看看 ref() 和 reactive() 背後真正做了些什麼事 ## [先看 ref()](https://github.com/vuejs/core/blob/main/packages/reactivity/src/ref.ts) ```typescript export function ref(value?: unknown) { return createRef(value, false) } function createRef(rawValue: unknown, shallow: boolean) { if (isRef(rawValue)) { return rawValue } return new RefImpl(rawValue, shallow) } class RefImpl<T> { private _value: T private _rawValue: T public dep?: Dep = undefined public readonly __v_isRef = true constructor(value: T, public readonly __v_isShallow: boolean) { this._rawValue = __v_isShallow ? value : toRaw(value) this._value = __v_isShallow ? value : toReactive(value) } get value() { trackRefValue(this) return this._value } set value(newVal) { const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal) newVal = useDirectValue ? newVal : toRaw(newVal) if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal this._value = useDirectValue ? newVal : toReactive(newVal) triggerRefValue(this, newVal) } } } export const toReactive = <T extends unknown>(value: T): T => isObject(value) ? reactive(value) : value ``` 由上面的 code 可以看出 ref.value 在 pass by reference (Objects) 的情況下實際上也是回傳 reactive() 的值 在 pass by value (primitive values) 的情況下則是直接回傳值 ## [再看 reactive()](https://github.com/vuejs/core/blob/main/packages/reactivity/src/reactive.ts) ```typescript export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap ) } function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any> ) { if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // target is already a Proxy, return it. // exception: calling readonly() on a reactive object if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } // target already has corresponding Proxy const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } // only specific value types can be observed. const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target } const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy } ``` 上面可以看出 最終 reactive() 會用傳入的值和 handlers 包出 [Proxy 物件](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy) handlers 最終會在對這個 Proxy 進行 set, get... 等動作時 trigger effects 關於 Proxy 是如何真的使資料響應的 不在本文的範疇內 想了解的我推薦閱讀[這篇文章](https://vue-js.com/topic/61403f1861c8f900316ae580) ## 所以到底該用哪一個呢? 沒有一定要用哪一種 因為都能達到一樣的目的 但是可以總結成一句話: > ### 如果將來這份資料需要被 re-assign 那就用 ref(), 反之用 reactive() 當然這只是我自己的看法 全都用 ref() 定義的人也大有人在 ### 我不推薦使用 ref() 定義響應式資料 ref() 是允許值被 re-assign 的 但我認為這種操作對響應式資料是不好的 下面有一段程式碼每秒都會在 console 印出 "john one year older" ```typescript const john = ref({ name: "John", age: 30, }); useIntervalFn(() => { john.value.age++; }, 1000); watch(john.value, () => { console.log("john one year older"); }); ``` 如果在這時候我多加這一段 ```typescript function Resurrection() { john.value = { name: "New John", age: 0, }; } ``` 當 Resurrection() 被呼叫的時候 因為 watch 是基於 reference 來監聽的 在 re-assign 這個操作時 reference 會被換成新的 導致再也無法監聽到 john (但是 john 年齡仍在每秒增加) 當然你可以只 watch john 並把 deep 設為 true, 這樣也可以監聽到, 但我覺得不優雅 當響應式資料為 Object 或 Array 時 我推薦使用 reactive() 和 const 來定義, 這會強迫工程師養成不 re-assign 響應式資料的習慣, 可以讓 vue 響應式資料莫名其妙解綁的問題少一點 Object 可以用 Object.assign(), Array 可以用 array.splice() ### 那如果是實值 (pass by value) 的話勒 我會特別把他包成 Object 以方便能 reactive() 假設原本有一個 isShow: boolean 的響應式資料 ```typescript const isShow = ref(false); ``` 用 reactive 會寫成 ```typescript const errorBox = reactive({ isShow: false, }); ``` 會這麼做的原因是絕大多數的響應式資料如果用基本型別定義的話 通常都是只作用在這個 vue component 的一小區塊 包成 Object 會在命名的時候給更多資訊, 之後再看通常會比較快看懂, 尤其是當某個 vue component 有一大堆由基本型別定義的資料時, 這個差距會更明顯 當然這只是我自己的習慣 我認為這個情況用 ref() 也是完全能被接受的 ### 蛤? 那什麼時候用 ref? 真的有時候需要 re-assign 值也是會用 ref() 啦 他沒有那麼十惡不赦, 只是我不愛用而已 :P ## 後記 ### 一些 ref() 派的論點 > * Accessing variables with .value may irritate you, but I see that as an indicator that a variable is reactive. > [source](https://medium.com/@bsalwiczek/ref-vs-reactive-in-vue-3-whats-the-right-choice-7c6f7265ce39) 靠 .value 當作識別響應式資料的做法也有 這算是不同的 coding style 但我個人認為被 .value 誤導的機率比使用 reactive 搞錯的機率大得多 畢竟 .value 算是一個蠻常見的屬性名字 > * 如果邏輯可以複用可以使用組合式函數,這樣其他組件也可以使用這個邏輯。reactive()函數返回的對象如果被解構的話,裏面的數據將會失去響應式,可以通過toRefs把對象裏面的每個屬性轉化成ref來使用。 > [source](https://www.readfog.com/a/1633537209551392768) 這個現象 ref() 也會發生 因為兩者都是套娃 Proxy 實作 只要是解構出來的東西不是 Proxy (當解構出來的值為原始值時) 就會失去響應 ### 網路上一些容易誤解的說法 > * ref : 可以接受任何型態的資料,但是不會對物件或陣列內部的屬性變動做監聽。 > * reactive : 只能接受 Object 或 Array,會對內部的屬性變動做深層的監聽,取資料時不需要 .value。 > * 所以你可以從這邊去推論出 reactive 會去監控物件內的每一個屬性,而 ref 對於內部的改變並不會每一個屬性都去監控變化 > [source1](https://medium.com/i-am-mike/vue-3-ref-%E8%B7%9F-reactive-%E6%88%91%E8%A9%B2%E6%80%8E%E9%BA%BC%E9%81%B8-2fb6b6735a3c), [source2](https://campus-xoops.tn.edu.tw/modules/tad_book3/page.php?tbsn=33&tbdsn=1726) 當定義 Object 或 Array 的響應式資料時 無論是 ref 還是 reactive 都是套娃 Proxy 實作 都是深層的響應式資料 在使用 watch() 時 監聽不到物件或陣列內部的屬性變動 這是 ref.value 還是 reactive 都會有的問題 因為 watch 是淺層監聽 只會監聽物件的第一層

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully