likes
comments
collection
share

从pinia源码中能学到什么

作者站长头像
站长
· 阅读数 4

1 前言

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

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

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

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

2 知识点

2.1 effectScope

2.1.1 什么是effectScope

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

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也会全部接触监听,类似子组件卸载

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断开关系

function effectScope(detached?: boolean): EffectScope;
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的返回值就是函数执行的返回值

const scope1 = effectScope()

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

  return 1

}))   // 1

2.2 runWithContext

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

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
export let count = 1

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

import { count } from './test.js'

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

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

export let count = 1

export function add(){
  count++
}
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
转载自:https://juejin.cn/post/7360857598805508147
评论
请登录