爲何 Vue3 的 ref 讓不少大佬操碎了心?

做者:瑞哥
來源:瑞哥聊前端

最近 Vue3 關於 ref-sugar 的提案引發了普遍的討論:juejin.im/post/689417…[1]前端

<script setup>
import Foo from './Foo.vue'

// declaring a variable that compiles to a ref
ref: count = 1
const inc = () => {
  count++
}
// access the raw ref object by prefixing with $
console.log($count.value)
</script>

<template>
  <Foo :count="count" @click="inc" /
>
</template>

感興趣的同窗能夠先閱讀上面的討論,本文再也不重複討論。提案目的是將 ref.value 的寫法進一步簡化,但由於修改了 js 語言自己的語義,引發了不少的爭論。vue

本文但願用一種最保守的思路提供另外一種心智負擔可能更小的解決方案react

1. 回顧 vue-composition-api-rfc

讀寫 ref 的操做比普通值的更冗餘,由於須要訪問 .value。尤大大對使用編譯時的語法糖來解決這個問題很是謹慎,曾明確表示不默認提供此類支持。vue-composition-api-rfc.netlify.app/zh/#\%E5\%BC\%…[2]git

對此我很是贊同。github

但在 js 中必需要加 .value ,在模板中又不須要加 .value,這無疑形成了必定程度的混亂和割裂感。web

2. 解決思路:轉成對象 new String('foo') 再攔截

爲何會這樣?究其根本,咱們沒法對基礎數據類型值作攔截。行呀,那咱們把基礎數據類型轉成對應的包裝類實例,再進行攔截。例如:let str = 'foo' 改寫成 let strObj = new String('foo'),此時 strObj 是對象,接下來咱們嘗試攔截,我寫了一個庫re-primitive,使用示例以下:面試

const { rePrimitive, watchEffect } = require('../dist/re-primitive.cjs')

// 用 rePrimitive 代替 ref; 並傳入String的包裝對象  new String('foo')
let proxy = rePrimitive(new String('foo'))
// let proxy = rePrimitive('foo')  // 內部作了裝箱,可簡寫去掉 new String()
// rePrimitive 的做用是將對象設置成響應式,並增長 setValue() 修改數據的方法

watchEffect(() => {
  console.log('輸出 proxy instanceof String:', proxy instanceof String// true , 能夠看出 proxy是String的實例,能夠使用全部的String的原型方法
  console.log('輸出 proxy.valueOf():' + proxy.valueOf())
  console.log('輸出:proxy.substring(1): ' + proxy.substring(1))
})
console.log('==========')
proxy.setValue('bar'// 響應式修改數據,從新執行 effect 函數

// 輸出結果

// 輸出 proxy instanceof String:true
// 輸出 proxy.valueOf():foo
// 輸出:proxy.substring(1): oo
// ==========
// 輸出 proxy instanceof String:true
// 輸出 proxy.valueOf():bar
// 輸出:proxy.substring(1): ar

  • rePrimitive: 至關於 reactive, ref。將基本數據類型設置成響應式,若是傳遞的是 new String('foo'), 則 proxy 仍然是 String 的實例;支持 String | Number | Boolean 三種類型,可簡寫去掉主動裝箱。
let proxyStr = rePrimitive('foo'// 等價於 let proxyStr = rePrimitive(new String('foo'))
let proxyNum = rePrimitive(123// 等價於 let proxyNum = rePrimitive(new Number(123))
let proxyBool = rePrimitive(false// 等價於 let proxyBool = rePrimitive(new Boolean(false))

  • setValue: 響應式修改數據,從新執行 effect 函數

3. 總結

  • 取值用.valueOf() 。開發者須要時刻注意 proxy 是 String, Number, Boolean 對象,記得調用 .valueOf()方法才能取到實際值。配合 eslint 插件可幫助檢查漏寫.valueOf()方法
  • 修改用.setValue() 。這種解決方案完整保留 js 語言特性,僅僅只是增長了 .setValue() 方法,對開發者的心智負擔可能略少於 ref
  • 實現源碼倉庫:https://github.com/ruige24601/re-primitive.git

參考資料

[1]

juejin.im/post/689417…: https://juejin.im/post/6894175515515551752算法

[2]

vue-composition-api-rfc.netlify.app/zh/#%E5%BC%…: https://vue-composition-api-rfc.netlify.app/zh/#%E5%BC%95%E5%85%A5-ref-%E7%9A%84%E5%BF%83%E6%99%BA%E8%B4%9F%E6%8B%85api

最後

歡迎關注「前端瓶子君」,回覆「 交流 」加入前端交流羣!
歡迎關注「前端瓶子君」,回覆「 算法 」自動加入,從0到1構建完整的數據結構與算法體系!
另外,每週還有手寫源碼題,瓶子君也會解答喲!
》》面試官也在看的算法資料《《
「在看和轉發」 就是最大的支持

本文分享自微信公衆號 - 前端瓶子君(pinzi_com)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。微信

相關文章
相關標籤/搜索