从pinia源码中能学到什么

1 前言

真的好久好久没写文章了,看了下发帖记录,上次还是半年前,这半年真的发生了太多的事,对这个社会有了新的理解和想法,也重新审视了下自己,决定重新出发,重拾起键盘,做个总结。

我毕业之后就开始使用React做为主要前端框架进行开发任务,中途跳槽到一家公司用了几个月的Vue2,当时整天就是写增删改查,觉得没意思就走了,下一家继续回归React,最近几个月由于公司业务需要,切换到Vue3开发,但是因为对Vue不熟悉,做一些"骚操作"的时候会感觉格外的力不从心,不知道这么写好不好,会不会出问题。之前看了类React框架的源码就让我对React有了深刻的理解(官方React还没看,打算之后有时间再看),所以源码还是要看的。

需要注意的是,看源码并不是为了让你有吹牛逼的资本,而是让你在遇到具体问题的时候有具体解决方案,并且知道这么干一定是没错的。而不是为什么会这样呢,不知道诶,反正做出来了,搞不懂,就这样吧。如果你看过源码,能非常自信的说,这么干一定行!什么,不行?我的前端崩塌了,尤雨溪又搞了什么骚操作!

之前写源码文章说实话我就是在写流水账,什么多少多少行实现一个啥,然后把代码贴出来,我现在感觉没啥必要,因为会去看源码的人不需要你的流水账,不会去看源码的人也看不懂或者也不关心你怎么实现的。所以现在,我打算写我看了一个库的源码,我学到了一些什么东西,因为我才用Vue3没多久,算是一个Vue小白,所以如果写了一些很基础的内容,请大神轻喷。那么今天先从pinia的源码开始

2 知识点

2.1 effectScope

2.1.1 什么是effectScope

能用于管理某个作用域下的所有副作用,并对其进行统一的处理

ts 复制代码
let count1 = ref(0)

// 创建一个响应式作用域
const scope1 = effectScope()

scope1.run(() => {
  
  watch(count1, v => {
    console.log('xxx count1 changed', v)
  })

})

// 触发watch
count1.value++

queueMicrotask(() => {

  // 关闭监听
  scope1.stop()

  queueMicrotask(() => {
    // 下面就不会触发监听了(全部解除绑定)
    count1.value++
  })

})

2.1.2 effectScope与effectScope嵌套

需要知道的是,effectScope出现的目的是方便库给响应式做一次性接触监听绑定,那么如果scopescope互相嵌套,那么只需要取消第一个scope的监听,所有子scope也会全部接触监听,类似子组件卸载

ts 复制代码
let count1 = ref(0)
let count2 = ref(0)

// 创建一个响应式作用域
const scope1 = effectScope()

scope1.run(() => {
  
  let scope2 = effectScope()

  watch(count1, v => {
    console.log('xxx count1 changed', v)
  })
  
  scope2.run(() => {
    watch(count2, v => {
      console.log('xxx count2 changed', v)
    })
  })


})

// 触发watch
count1.value++
count2.value++

queueMicrotask(() => {

  // 关闭监听
  scope1.stop()

  queueMicrotask(() => {
    count1.value++    // 不会触发watch
    count2.value++    // 不会触发watch
  })

})

2.1.3 detached

创建effectScope作用域的时候可以传入一个参数,这个值默认是false,这个参数可以让互相嵌套的scope断开关系

ts 复制代码
function effectScope(detached?: boolean): EffectScope;
ts 复制代码
let count1 = ref(0)
let count2 = ref(0)

// 创建一个响应式作用域
const scope1 = effectScope()

scope1.run(() => {
  
  let scope2 = effectScope(true)

  watch(count1, v => {
    console.log('xxx count1 changed', v)
  })
  
  scope2.run(() => {
    watch(count2, v => {
      console.log('xxx count2 changed', v)
    })
  })


})

// 触发watch
count1.value++
count2.value++

queueMicrotask(() => {

  // 关闭监听
  scope1.stop()
  console.log('关闭监听')

  queueMicrotask(() => {
    count1.value++    // 不会触发watch
    count2.value++    // 会触发watch
  })

})

2.1.4 effectScope.run的返回值

effectScope的目的是为了给setup中的所有监听解绑,那么setup有个特性,就是返回值可以被模板使用到,那么effectScope同理,run的返回值就是函数执行的返回值

ts 复制代码
const scope1 = effectScope()

console.log(scope1.run(() => {

  return 1

}))   // 1

2.2 runWithContext

允许在非setup中访问inject,如果你需要开发Vue相关的库,同一个页面中存在多个实例,但是你的方法却是公用的,那么这个api会非常的有用,这防止了单例的诞生,而且全局可以公用同一个方法,我们来看下面的例子

ts 复制代码
import { type App, createApp } from 'vue'

const testSymbol = Symbol() 

const myPlugin = {
  install(app: App) {
    app.provide(testSymbol, { test: 'testData' })
    console.log(inject(testSymbol))    // undefined
    app.runWithContext(()=>{
      console.log(inject(testSymbol))  // { test: 'testData' }
    })
  }
}

createApp(App)
  .use(myPlugin)
  .mount('#app')

2.3 es module变量可修改

其实这个特性我很早就知道,是为了让更多的人知道(为了凑字数),尤雨溪似乎特别喜欢使用这个特性,Vue源码中很多地方都用到

比如现在有下面一个模块,导出了一个 count 变量

  • test.js
ts 复制代码
export let count = 1

我们是不能在别的模块中去修改count的值的

ts 复制代码
import { count } from './test.js'

try {
  count++
} catch (e) {
  console.log(e)  // TypeError: Assignment to constant variable.
}

但是,我们却可以调用test.js中的方法,去修改count的值

js 复制代码
export let count = 1

export function add(){
  count++
}
js 复制代码
import { count, add } from './test.js'

console.log(count)  // 1
try {
  count++
} catch (e) {
  console.log(e)  // TypeError: Assignment to constant variable.
}
add()
console.log(count)  // 2
相关推荐
小磊哥er9 分钟前
【前端工程化】前端工作中的业务规范有哪些
前端
旷世奇才李先生14 分钟前
Next.js 安装使用教程
开发语言·javascript·ecmascript
ᥬ 小月亮20 分钟前
webpack基础
前端·webpack
YongGit39 分钟前
探索 AI + MCP 渲染前端 UI
前端·后端·node.js
慧一居士1 小时前
<script setup>中的setup作用以及和不带的区别对比
前端
RainbowSea2 小时前
NVM 切换 Node 版本工具的超详细安装说明
java·前端
读书点滴2 小时前
笨方法学python -练习14
java·前端·python
Mintopia2 小时前
四叉树:二维空间的 “智能分区管理员”
前端·javascript·计算机图形学
慌糖2 小时前
RabbitMQ:消息队列的轻量级王者
开发语言·javascript·ecmascript
Mintopia2 小时前
Three.js 深度冲突:当像素在 Z 轴上玩起 "挤地铁" 游戏
前端·javascript·three.js