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
    • Invite by email
      Invitee

      This note has no invitees

    • 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
    • Note Insights
    • 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 Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
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
  • Invite by email
    Invitee

    This note has no invitees

  • 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