专栏分享:vue2源码专栏vue3源码专栏vue router源码专栏玩具项目专栏,硬核推荐

欢迎各位ITer关注点赞收藏

Vue3中响应数据核心是 reactive , reactive 的实现是由 proxy 加 effect 组合,上一章节我们利用 proxy 实现了一个简易版的 reactive,# 【源码系列#01】Vue3响应式原理(Reactive)。接下来让我们一起手写下 effect 的源码

effect

effect 作为 reactive 的核心,主要负责收集依赖,更新依赖

在学习 effect之前,我们再来看下这张图

  • targetMap:存储了每个 "响应性对象属性" 关联的依赖;类型是 WeakMap
  • depsMap:存储了每个属性的依赖;类型是 Map
  • dep:存储了我们的 effects ,一个 effects 集,这些 effect 在值发生变化时重新运行;类型是 Set

编写effect函数

  1. // 当前正在执行的effect
  2. export let activeEffect = undefined
  3. export class ReactiveEffect {
  4. // @issue2
  5. // 这里表示在实例上新增了parent属性,记录父级effect
  6. public parent = null
  7. // 记录effect依赖的属性
  8. public deps = []
  9. // 这个effect默认是激活状态
  10. public active = true
  11. // 用户传递的参数也会传递到this上 this.fn = fn
  12. constructor(public fn, public scheduler) {}
  13. // run就是执行effect
  14. run() {
  15. // 这里表示如果是非激活的情况,只需要执行函数,不需要进行依赖收集
  16. if (!this.active) {
  17. return this.fn()
  18. }
  19. // 这里就要依赖收集了 核心就是将当前的effect 和 稍后渲染的属性关联在一起
  20. try {
  21. // 记录父级effect
  22. this.parent = activeEffect
  23. activeEffect = this
  24. // 当稍后调用取值操作的时候 就可以获取到这个全局的activeEffect了
  25. return this.fn()
  26. } finally {
  27. // 还原父级effect
  28. activeEffect = this.parent
  29. }
  30. }
  31. }
  32. export function effect(fn, options: any = {}) {
  33. // 这里fn可以根据状态变化 重新执行, effect可以嵌套着写
  34. const _effect = new ReactiveEffect(fn) // 创建响应式的effect
  35. // issue1
  36. _effect.run() // 默认先执行一次
  37. }

@issue1 effect 默认会先执行一次

依赖收集

  1. const targetMap = new WeakMap()
  2. export function track(target, type, key) {
  3. // @issue3
  4. // 我们只想在我们有activeEffect时运行这段代码
  5. if (!activeEffect) return
  6. let depsMap = targetMap.get(target) // 第一次没有
  7. if (!depsMap) {
  8. targetMap.set(target, (depsMap = new Map()))
  9. }
  10. let dep = depsMap.get(key) // key -> name / age
  11. if (!dep) {
  12. depsMap.set(key, (dep = new Set()))
  13. }
  14. // 单向指的是 属性记录了effect, 反向记录,应该让effect也记录他被哪些属性收集过,这样做的好处是为了可以清理
  15. trackEffects(dep)
  16. }
  17. export function trackEffects(dep) {
  18. if (activeEffect) {
  19. let shouldTrack = !dep.has(activeEffect) // 去重了
  20. if (shouldTrack) {
  21. dep.add(activeEffect)
  22. // @issue4
  23. // 存放的是属性对应的set
  24. activeEffect.deps.push(dep) // 让effect记录住对应的dep, 稍后清理的时候会用到
  25. }
  26. }
  27. }

@issue3 当activeEffect有值时,即只在effect运行时执行track依赖收集

@issue4 双向记录 ,一个属性对应多个effect,一个effect对应多个属性

一个属性对应多个 effect: 在之前的 depsMap 图中,我们得知,一个属性映射一个 dep(即 effect 集合,类型为 Set)

一个effect对应多个属性: 在 effect 中,有一个 deps 属性,她记录了此 effect 依赖的每一个属性所对应的 dep。让 effect 记录对应的 dep, 目的是在稍后清理的时候会用到

触发更新

  1. export function trigger(target, type, key) {
  2. const depsMap = targetMap.get(target)
  3. if (!depsMap) return // 触发的值不在模板中使用
  4. let effects = depsMap.get(key) // 找到了属性对应的effect
  5. // 永远在执行之前 先拷贝一份来执行, 不要关联引用
  6. if (effects) {
  7. triggerEffects(effects)
  8. }
  9. }
  10. export function triggerEffects(effects) {
  11. effects.forEach(effect => {
  12. // 我们在执行effect的时候 又要执行自己,那我们需要屏蔽掉,不要无限调用,【避免由activeEffect触发trigger,再次触发当前effect。 activeEffect -> fn -> set -> trigger -> 当前effect】
  13. // @issue5
  14. if (effect !== activeEffect) {
  15. effect.run() // 否则默认刷新视图
  16. }
  17. })
  18. }

@issue5 避免由run触发trigger,无限递归循环

我们在执行 effect 的时候,又要执行自己,那我们需要屏蔽掉,不要无限调用【避免由 activeEffect 触发 trigger,再次触发当前 effect。 activeEffect -> fn -> set -> trigger -> 当前effect】

举个栗子

  1. const { effect, reactive } = VueReactivity
  2. const data = { name: '柏成', age: 13, address: { num: 517 } }
  3. const state = reactive(data)
  4. // vue3中的代理都是用proxy来解决的
  5. // 此effect函数默认会先执行一次, 对响应式数据取值(取值的过程中数据会依赖于当前的effect)
  6. effect(() => {
  7. state.age = Math.random()
  8. document.getElementById('app').innerHTML = state.name + '今年' + state.age + '岁了'
  9. })
  10. // 稍后name和age变化会重新执行effect函数
  11. setTimeout(() => {
  12. state.age = 18
  13. }, 1000)

分支切换与cleanup

  1. // 每次执行effect的时候清理一遍依赖,再重新收集,双向清理
  2. function cleanupEffect(effect) {
  3. // deps 里面装的是name对应的effect, age对应的effect
  4. const { deps } = effect
  5. for (let i = 0; i < deps.length; i++) {
  6. // 解除effect,重新依赖收集
  7. deps[i].delete(effect)
  8. }
  9. effect.deps.length = 0
  10. }
  11. export class ReactiveEffect {
  12. // @issue3
  13. // 这里表示在实例上新增了parent属性,记录父级effect
  14. public parent = null
  15. // 记录effect依赖的属性
  16. public deps = []
  17. // 这个effect默认是激活状态
  18. public active = true
  19. // 用户传递的参数也会传递到this上 this.fn = fn
  20. constructor(public fn, public scheduler) {} // @issue8 - scheduler
  21. // run就是执行effect
  22. run() {
  23. // 这里表示如果是非激活的情况,只需要执行函数,不需要进行依赖收集
  24. if (!this.active) {
  25. return this.fn()
  26. }
  27. // 这里就要依赖收集了 核心就是将当前的effect 和 稍后渲染的属性关联在一起
  28. try {
  29. // 记录父级effect
  30. this.parent = activeEffect
  31. activeEffect = this
  32. // 这里我们需要在执行用户函数之前将之前收集的内容清空
  33. cleanupEffect(this) // @issue6
  34. // 当稍后调用取值操作的时候 就可以获取到这个全局的activeEffect了
  35. return this.fn() // @issue1
  36. } finally {
  37. // 还原父级effect
  38. activeEffect = this.parent
  39. }
  40. }
  41. }
  42. export function triggerEffects(effects) {
  43. // 先拷贝,防止死循环,new Set 后产生一个新的Set
  44. effects = new Set(effects) // @issue7
  45. effects.forEach(effect => {
  46. // 我们在执行effect的时候 又要执行自己,那我们需要屏蔽掉,不要无限调用,【避免由activeEffect触发trigger,再次触发当前effect。 activeEffect -> fn -> set -> trigger -> 当前effect】
  47. if (effect !== activeEffect) {
  48. effect.run() // 否则默认刷新视图
  49. }
  50. })
  51. }

@issue6 分支切换 - cleanupEffect。我们需要在执行用户函数之前将之前收集的内容清空,双向清理,在渲染时我们要避免副作用函数产生的遗留,举个栗子,我们再次修改name,原则上不应更新页面

每次副作用函数执行时,可以先把它从所有与之关联的依赖集合中删除。当副作用函数执行完毕后,响应式数据会与副作用函数之间建立新的依赖关系,而分支切换后,与副作用函数没有依赖关系的响应式数据则不会再建立依赖,这样副作用函数遗留的问题就解决了;

  1. const { effect, reactive } = VueReactivity
  2. const state = reactive({ flag: true, name: '柏成', age: 24 })
  3. effect(() => {
  4. // 我们期望的是每次执行effect的时候都可以清理一遍依赖,重新收集
  5. // 副作用函数 (effect执行渲染了页面)
  6. console.log('render')
  7. document.body.innerHTML = state.flag ? state.name : state.age
  8. })
  9. setTimeout(() => {
  10. state.flag = false
  11. setTimeout(() => {
  12. // 修改name,原则上不更新页面
  13. state.name = '李'
  14. }, 1000)
  15. }, 1000)

@issue7 分支切换 - 死循环。遍历 set 对象时,先 delete 再 add,会出现死循环

在调用循环遍历 Set 集合时,如果一个值已经被访问过了,但该值被删除,并重新添加到集合,如果此时循环遍历没有结束,那该值会被重新访问

参考资料:ECMAScript Language Specification

提示:语言规范说的是forEach时是这样的,实测 for of 遍历Set会有同样的问题。

看一下 triggerEffects 方法,遍历了 effects

  1. export function triggerEffects(effects) {
  2. effects.forEach(effect => { effect.run() })
  3. }

effect.run 方法中

  • 执行 cleanupEffect(effect),清理一遍依赖
  1. deps[i].delete(effect)
  • 执行 this.fn(),重新执行函数,重新收集依赖
  1. // track() 方法中
  2. dep.add(activeEffect) // 将副作用函数activeEffect添加到响应式依赖中

解决方法:

  1. let effect = () => {};
  2. let deps = new Set([effect])
  3. deps.forEach(item=>{
  4. console.log('>>>')
  5. deps.delete(effect);
  6. deps.add(effect)
  7. }); // 这样就导致死循环了
  8. // 解决方案如下,先拷贝一份,遍历的Set对象 和 操作(delete、add)的Set对象不是同一个即可
  9. let effect = () => {};
  10. let deps = new Set([effect])
  11. const newDeps = new Set(deps)
  12. newDeps.forEach(item=>{
  13. console.log('>>>')
  14. deps.delete(effect);
  15. deps.add(effect)
  16. });

effect嵌套

  1. // 当前正在执行的effect
  2. export let activeEffect = undefined
  3. export class ReactiveEffect {
  4. // @issue2
  5. // 这里表示在实例上新增了parent属性,记录父级effect
  6. public parent = null
  7. // 记录effect依赖的属性
  8. public deps = []
  9. // 这个effect默认是激活状态
  10. public active = true
  11. // 用户传递的参数也会传递到this上 this.fn = fn
  12. constructor(public fn, public scheduler) {}
  13. // run就是执行effect
  14. run() {
  15. // 这里表示如果是非激活的情况,只需要执行函数,不需要进行依赖收集
  16. if (!this.active) {
  17. return this.fn()
  18. }
  19. // 这里就要依赖收集了 核心就是将当前的effect 和 稍后渲染的属性关联在一起
  20. try {
  21. // 记录父级effect
  22. this.parent = activeEffect
  23. activeEffect = this
  24. // 当稍后调用取值操作的时候 就可以获取到这个全局的activeEffect了
  25. return this.fn()
  26. } finally {
  27. // 还原父级effect
  28. activeEffect = this.parent
  29. }
  30. }
  31. }
  32. export function effect(fn, options: any = {}) {
  33. // 这里fn可以根据状态变化 重新执行, effect可以嵌套着写
  34. const _effect = new ReactiveEffect(fn) // 创建响应式的effect
  35. // issue1
  36. _effect.run() // 默认先执行一次
  37. }

@issue2 利用 parent 解决effect嵌套问题,effect 嵌套的场景在 Vue.js 中常常出现,如:Vue中的渲染函数(render)就是在一个effect中执行的,嵌套组件就会伴随着嵌套 effect

  1. 解决effect嵌套问题----栈方式------------------------vue2/vue3.0初始版本
  1. // 运行effect,此effect入栈,运行完毕,最后一个effect出栈,属性关联栈中的最后一个effect
  2. [e1] -> [e1,e2] -> [e1]
  3. effect(() => { // activeEffect = e1
  4. state.name // name -> e1
  5. effect(() => { // activeEffect = e2
  6. state.age // age -> e2
  7. })
  8. // activeEffect = e1
  9. state.address // address = e1
  10. })
  1. 解决effect嵌套问题----树形结构方式----------------vue3后续版本
  1. // 这个执行流程 就类似于一个树形结构
  2. effect(()=>{ // parent = null activeEffect = e1
  3. state.name // name -> e1
  4. effect(()=>{ // parent = e1 activeEffect = e2
  5. state.age // age -> e2
  6. effect(()=> { // parent = e2 activeEffect = e3
  7. state.sex // sex -> e3
  8. }) // activeEffect = e2
  9. }) // activeEffect = e1
  10. state.address // address -> e1
  11. effect(()=>{ // parent = e1 activeEffect = e4
  12. state.age // age -> e4
  13. })
  14. })

停止effect和调度执行


  1. export class ReactiveEffect {
  2. // @issue8 - stop
  3. stop() {
  4. if (this.active) {
  5. this.active = false
  6. cleanupEffect(this) // 停止effect的收集
  7. }
  8. }
  9. }
  10. export function effect(fn, options: any = {}) {
  11. // 这里fn可以根据状态变化 重新执行, effect可以嵌套着写
  12. const _effect = new ReactiveEffect(fn, options.scheduler) // 创建响应式的effect @issue8 - scheduler
  13. _effect.run() // 默认先执行一次
  14. // @issue8 - stop
  15. // 绑定this,run方法内的this指向_effect,若不绑定,这样调用run方法时,runner(),则指向undefined
  16. const runner = _effect.run.bind(_effect)
  17. // 将effect挂载到runner函数上,调用stop方式时可以这样调用 runner.effect.stop()
  18. runner.effect = _effect
  19. return runner
  20. }
  21. export function triggerEffects(effects) {
  22. // 先拷贝,防止死循环,new Set 后产生一个新的Set
  23. effects = new Set(effects) // @issue7
  24. effects.forEach(effect => {
  25. // 我们在执行effect的时候 又要执行自己,那我们需要屏蔽掉,不要无限调用,【避免由activeEffect触发trigger,再次触发当前effect。 activeEffect -> fn -> set -> trigger -> 当前effect】
  26. if (effect !== activeEffect) {
  27. // @issue8 - scheduler
  28. if (effect.scheduler) {
  29. effect.scheduler() // 如果用户传入了调度函数,则执行调度函数
  30. } else {
  31. effect.run() // 否则默认刷新视图
  32. }
  33. }
  34. })
  35. }

如何使用 stop 和 scheduler ?举个小栗子

  • 当我们调用 runner.effect.stop() 时,就双向清理了 effect 的所有依赖,后续 state.age 发生变化后,将不再重新更新页面
  • 基于 scheduler 调度器,我们可以控制页面更新的周期,下面例子中,会在1秒后,页面由 30 变为 5000
  1. let waiting = false
  2. const { effect, reactive } = VueReactivity
  3. const state = reactive({ flag: true, name: 'jw', age: 30, address: { num: 10 } })
  4. let runner = effect(
  5. () => {
  6. // 副作用函数 (effect执行渲染了页面)
  7. document.body.innerHTML = state.age
  8. },
  9. {
  10. scheduler() {
  11. // 调度 如何更新自己决定
  12. console.log('run')
  13. if (!waiting) {
  14. waiting = true
  15. setTimeout(() => {
  16. runner()
  17. waiting = false
  18. }, 1000)
  19. }
  20. },
  21. },
  22. )
  23. // 清理 effect 所有依赖,state.age 发生变化后,将不再重新更新页面
  24. // runner.effect.stop()
  25. state.age = 1000
  26. state.age = 2000
  27. state.age = 3000
  28. state.age = 4000
  29. state.age = 5000

effect.ts

完整代码如下

  1. /**
  2. * @issue1 effect默认会先执行一次
  3. * @issue2 activeEffect 只在effect运行时执行track保存
  4. * @issue3 parent 解决effect嵌套问题
  5. * @issue4 双向记录 一个属性对应多个effect,一个effect对应多个属性 √
  6. * @issue5 避免由run触发trigger,递归循环
  7. * @issue6 分支切换 cleanupEffect
  8. * @issue7 分支切换 死循环,set循环中,先delete再add,会出现死循环
  9. * @issue8 自定义调度器 类似Vue3中的effectScope stop 和 scheduler
  10. */
  11. // 当前正在执行的effect
  12. export let activeEffect = undefined
  13. // @issue6
  14. // 每次执行effect的时候清理一遍依赖,再重新收集,双向清理
  15. function cleanupEffect(effect) {
  16. // deps 里面装的是name对应的effect, age对应的effect
  17. const { deps } = effect
  18. for (let i = 0; i < deps.length; i++) {
  19. // 解除effect,重新依赖收集
  20. deps[i].delete(effect)
  21. }
  22. effect.deps.length = 0
  23. }
  24. export class ReactiveEffect {
  25. // @issue3
  26. // 这里表示在实例上新增了parent属性,记录父级effect
  27. public parent = null
  28. // 记录effect依赖的属性
  29. public deps = []
  30. // 这个effect默认是激活状态
  31. public active = true
  32. // 用户传递的参数也会传递到this上 this.fn = fn
  33. constructor(public fn, public scheduler) {} // @issue8 - scheduler
  34. // run就是执行effect
  35. run() {
  36. // 这里表示如果是非激活的情况,只需要执行函数,不需要进行依赖收集
  37. if (!this.active) {
  38. return this.fn()
  39. }
  40. // 这里就要依赖收集了 核心就是将当前的effect 和 稍后渲染的属性关联在一起
  41. try {
  42. // 记录父级effect
  43. this.parent = activeEffect
  44. activeEffect = this
  45. // 这里我们需要在执行用户函数之前将之前收集的内容清空
  46. cleanupEffect(this) // @issue6
  47. // 当稍后调用取值操作的时候 就可以获取到这个全局的activeEffect了
  48. return this.fn() // @issue1
  49. } finally {
  50. // 还原父级effect
  51. activeEffect = this.parent
  52. }
  53. }
  54. // @issue8 - stop
  55. stop() {
  56. if (this.active) {
  57. this.active = false
  58. cleanupEffect(this) // 停止effect的收集
  59. }
  60. }
  61. }
  62. export function effect(fn, options: any = {}) {
  63. // 这里fn可以根据状态变化 重新执行, effect可以嵌套着写
  64. const _effect = new ReactiveEffect(fn, options.scheduler) // 创建响应式的effect @issue8 - scheduler
  65. _effect.run() // 默认先执行一次
  66. // @issue8 - stop
  67. // 绑定this,run方法内的this指向_effect,若不绑定,这样调用run方法时,runner(),则指向undefined
  68. const runner = _effect.run.bind(_effect)
  69. // 将effect挂载到runner函数上,调用stop方式时可以这样调用 runner.effect.stop()
  70. runner.effect = _effect
  71. return runner
  72. }
  73. // 对象 某个属性 -》 多个effect
  74. // WeakMap = {对象:Map{name:Set-》effect}}
  75. // {对象:{name:[]}}
  76. // 多对多 一个effect对应多个属性, 一个属性对应多个effect
  77. const targetMap = new WeakMap()
  78. export function track(target, type, key) {
  79. // 我们只想在我们有activeEffect时运行这段代码
  80. if (!activeEffect) return // @issue2
  81. let depsMap = targetMap.get(target) // 第一次没有
  82. if (!depsMap) {
  83. targetMap.set(target, (depsMap = new Map()))
  84. }
  85. let dep = depsMap.get(key) // key -> name / age
  86. if (!dep) {
  87. depsMap.set(key, (dep = new Set()))
  88. }
  89. // 单向指的是 属性记录了effect, 反向记录,应该让effect也记录他被哪些属性收集过,这样做的好处是为了可以清理
  90. trackEffects(dep)
  91. }
  92. export function trackEffects(dep) {
  93. if (activeEffect) {
  94. let shouldTrack = !dep.has(activeEffect) // 去重了
  95. if (shouldTrack) {
  96. dep.add(activeEffect)
  97. // @issue4
  98. // 存放的是属性对应的set
  99. activeEffect.deps.push(dep) // 让effect记录住对应的dep, 稍后清理的时候会用到
  100. }
  101. }
  102. }
  103. export function trigger(target, type, key) {
  104. const depsMap = targetMap.get(target)
  105. if (!depsMap) return // 触发的值不在模板中使用
  106. let effects = depsMap.get(key) // 找到了属性对应的effect
  107. // 永远在执行之前 先拷贝一份来执行, 不要关联引用
  108. if (effects) {
  109. triggerEffects(effects)
  110. }
  111. }
  112. export function triggerEffects(effects) {
  113. // 先拷贝,防止死循环,new Set 后产生一个新的Set
  114. effects = new Set(effects) // @issue7
  115. effects.forEach(effect => {
  116. // 我们在执行effect的时候,有时候会改变属性,那我们需要屏蔽掉,不要无限调用,【避免由activeEffect触发trigger,再次触发当前effect。 activeEffect -> fn -> set -> trigger -> 当前effect】
  117. // @issue5
  118. if (effect !== activeEffect) {
  119. // @issue8 - scheduler
  120. if (effect.scheduler) {
  121. effect.scheduler() // 如果用户传入了调度函数,则执行调度函数
  122. } else {
  123. effect.run() // 否则默认刷新视图
  124. }
  125. }
  126. })
  127. }

参考资料

Vue3响应式系统实现原理(二) - CherishTheYouth - 博客园

【源码系列#02】Vue3响应式原理(Effect)的更多相关文章

  1. vue 源码自问自答-响应式原理

    vue 源码自问自答-响应式原理 最近看了 Vue 源码和源码分析类的文章,感觉明白了很多,但是仔细想想却说不出个所以然. 所以打算把自己掌握的知识,试着组织成自己的语言表达出来 不打算平铺直叙的写清 ...

  2. Vue 源码解析:深入响应式原理(上)

    原文链接:http://www.imooc.com/article/14466 Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScri ...

  3. 由浅入深,带你用JavaScript实现响应式原理(Vue2、Vue3响应式原理)

    由浅入深,带你用JavaScript实现响应式原理 前言 为什么前端框架Vue能够做到响应式?当依赖数据发生变化时,会对页面进行自动更新,其原理还是在于对响应式数据的获取和设置进行了监听,一旦监听到数 ...

  4. vue3剖析:响应式原理——effect

    响应式原理 源码目录:https://github.com/vuejs/vue-next/tree/master/packages/reactivity 模块 ref: reactive: compu ...

  5. vue3响应式原理以及ref和reactive区别还有vue2/3生命周期的对比,第二天

    前言: 前天我们学了 ref 和 reactive ,提到了响应式数据和 Proxy ,那我们今天就来了解一下,vue3 的响应式 在了解之前,先复习一下之前 vue2 的响应式原理 vue2 的响应 ...

  6. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

  7. 【Vue2.x源码系列07】监听器watch原理

    上一章 Vue2计算属性原理,我们介绍了计算属性是如何实现的?计算属性缓存原理?以及洋葱模型是如何应用的? 本章目标 监听器是如何实现的? 监听器选项 - immediate.deep 内部实现 初始 ...

  8. java官网门户源码 SSM框架 自适应-响应式 freemarker 静态模版引擎

    来源:http://www.fhadmin.org/webnewsdetail3.html 前台:支持(5+1[时尚单页风格])六套模版,可以在后台切换 官网:www.fhadmin.org 系统介绍 ...

  9. Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

    刚开始使用Mybaits的同学有没有这样的疑惑,为什么我们没有编写Mapper的实现类,却能调用Mapper的方法呢?本篇文章我带大家一起来解决这个疑问 上一篇文章我们获取到了DefaultSqlSe ...

  10. vue2响应式原理与vue3响应式原理对比

    VUE2.0 核心 对象:通过Object.defineProtytype()对对象的已有属性值的读取和修改进行劫持 数组:通过重写数组更新数组一系列更新元素的方法来实现元素的修改的劫持 Object ...

随机推荐

  1. python教程 入门学习笔记 第1天 初识python python语言环境安装 python编写器

    初识python 一.python语言简介: 1.起源:1989年由荷兰的前谷歌程序员吉多.范罗苏姆(龟叔)创造,python的命名来源于英国电视喜剧Monty Python's Flying Cir ...

  2. C#.NET 国密SM2 加密解密 与JAVA互通 ver:20230805

    C#.NET 国密SM2 加密解密 与JAVA互通 ver:20230805 .NET 环境:.NET6 控制台程序(.net core). JAVA 环境:JAVA8,带maven 的JAVA控制台 ...

  3. CodeForces 1388C Uncle Bogdan and Country Happiness

    题意 给一棵\(n\)节点的树,每个节点有\(a[i]\)个人住,他们从\(1\)号节点回家,回家路上可能从开心的状态变成不开心的状态(但不可以由不开心变为开心),每个节点有个探测器,会探测经过该节点 ...

  4. 如何平息WPS for linux启动后,笔记本风扇的怒吼

    create:2022-09-06 20:02:45 WPS启动后,点击菜单栏右上角[未同步]按钮,不登录,直接关掉.几秒后,笔记本风扇嗷嗷叫.桌面conky显示wpscloudsvr进程CPU占用8 ...

  5. 文心一言 VS 讯飞星火 VS chatgpt (93)-- 算法导论9.2 1题

    一.用go语言,证明:在 RANDOMIZED-SELECT 中,对长度为0的数组,不会进行递归调用. 文心一言: 在 Go 语言中,我们可以通过实现一个函数来检查数组的长度,如果长度为0,则不进行递 ...

  6. 【uniapp】【微信小程序】wxml-to-canvas

    真是搞吐了,研究了整整两天,困死我了 本来使用生成二维码插件好好的,插件页也支持导出二维码图片,可是领导说要带上文件的名称,那就涉及html转图片了,当然也可以改二维码插件的源码,不过源码做了混淆,看 ...

  7. Solution -「CF 724F」Uniformly Branched Trees

    Description Link. 给定三个数 \(n,d,mod\),求有多少种 \(n\) 个点的不同构的树满足:除了度数为 \(1\) 的结点外,其余结点的度数均为 \(d\).答案对质数 \( ...

  8. CUDA C编程权威指南:2.1-CUDA编程模型

      本文主要通过例子介绍了CUDA异构编程模型,需要说明的是Grid.Block和Thread都是逻辑结构,不是物理结构.实现例子代码参考文献[2],只需要把相应章节对应的CMakeLists.txt ...

  9. 11g编译bbed

    报错如下: make -f ins_rdbms.mk $ORACLE_HOME/rdbms/lib/bbed Linking BBED utility (bbed) rm -f /u01/app/or ...

  10. 维修道路(repair)

    维修道路(repair) 时间限制: 1 Sec  内存限制: 128 MB 题目描述 由于在十多年前道路改建时的突出贡献, Bob 被任命为维修道路的承包商, 他可以任意 选择两条路径去修理. Bo ...