Coder Social home page Coder Social logo

code-for-vue-3-book's Introduction

#code-for-vue3-book

code-for-vue-3-book's People

Contributors

hcysunyang avatar sudongyuer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

code-for-vue-3-book's Issues

【232】代码注释

代码中的注释”新的虚拟节点“有的使用"newVnode"有的使用"newVNode"

【57 页】`data.foo` 应为 `obj.foo`

在这种情况下,我们希望当修改 data.foo 时会触发 effectFn1 执行

应该是,“在这种情况下,我们希望当修改 obj.foo 时会触发 effectFn1 执行”

【213页】错别字

213页 倒数第二段最后一行 “我们就可以用它来描述Itmes.vue组件的模板了”
Itmes => Items

【221页】代码错误

221页示例代码第15行

patch(oldChildren[i], newChildren[i])

应改为

patch(oldChildren[i], newChildren[i], container)

否则oldChildren 中如果有 Fragment 会出错。

【74页】watch 的实现原理 建议增加读者注意

目前书中所实现的代码,只能在监听有返回的 getter 函数时,才能将 newValue,oldValue 正确传入,其他情况都是 undefined。
有返回的函数是指:

// 1. 简写箭头函数
() => obj.foo; // 收集 obj.foo 的值为 newValue, oldValue

// 2. 完整写法
() => {return obj.foo};

// 3. 监听多个 getter
// 收集 obj.bar 的值为 newValue, oldValue;如果没有返回值,则 newValue 和 oldValue 都是 undefined
() => {obj.foo; return obj.bar};

测试示例图:

image

原因分析:

代码中 newValueoldValue 都是通过 effectFn() 的执行结果赋值的。而 effectFn() 的执行结果又依赖于 副作用函数 的执行结果。在当前上下文中 getter() 的返回值决定了 newValue,oldValue 的值

image

【137页】注释错误

新137 页,第一段代码,第 7 行注释,由 “删除。。。” 改为 "添加。。。"

【387页】语病

第二行,“把回调函数存储到 context.nodeTransforms” 改为 “把回调函数存储到 transforms”

【111页】正文第2行

我们还修改了 get 拦截函数和 deleteProperty 拦截函数;

期望

我们还修改了 set 拦截函数和 deleteProperty 拦截函数;

【261 页】代码有误

while (...) {
  if (!oldStartVNode) {
    oldStartVNode = oldChildren[++oldStartIdx]
  } else if (!oldEndVNode) {
    oldEndVNode = newChildren[--oldEndIdx]
  }
  ...
}

第五行应该为 oldEndVNode = oldChildren[--oldEndIdx]

【44页】语句不通顺

下面划线的部分,是不是改成“我们可以按照如下所示的方式使用”更合适一些。

image

【77页】立即执行的 watch 与回调执行时机 代码调整建议

建议将 flush is post 时的原始代码:

    scheduler: () => {
      // 在调度函数中判断 flush 是否为 'post',如果是,将期放到微任务队列中执行
      if (options.flush === "post") {

        const p = Promise.resolve();
        p.then(job);

      } else {
        job();
      }
    },

变更为:

    scheduler: () => {
      // 在调度函数中判断 flush 是否为 'post',如果是,将期放到微任务队列中执行
      if (options.flush === "post") {

        jobQueue.add(job);
        flushJob();

      } else {
        job();
      }
    },

原始代码的运行结果,容易使读者产生困惑,并且新代码因为采用微任务队列,所以只会执行一次。

运行对比图:

image

【198页】事件的处理逻辑有遗漏

if (/^on/.test(key)) {
    const invokers = el._vei || (el._vei = {})
    let invoker = invokers[key]
    const name = key.slice(2).toLowerCase()
    if (nextValue) {
      if (!invoker) {
        invoker = el._vei[key] = (e) => {
          if (Array.isArray(invoker.value)) {
            invoker.value.forEach(fn => fn(e))
          } else {
            invoker.value(e)
          }
        }
        invoker.value = nextValue
        el.addEventListener(name, invoker)
      } else {
        invoker.value = nextValue
      }
    } else if (invoker) {
      el.removeEventListener(name, invoker)
      // 移除绑定的同时需要置空或删除 el._vei
    }
  }

如上面代码注释出所述,如果新的绑定事件不存在,且之前绑定的invoker 存在,则移除绑定。但是移除绑定后invoker 依旧存在,所以下次再添加新的绑定事件时,只会触发invoker.value = newValue,而不会执行addEventListener(name, invoker)。因此,在这边需要同时置空或删除 el._vei,如el._vei = null

如下面例子所示:

const vnode = {
  type: 'p',
  props: {
    onClick: () => {
      alert('clicked 1')
    }
  },
  children: 'text'
}
renderer.render(vnode, document.querySelector('#app'))

const newVnode = {
  type: 'p',
  props: {},
  children: 'text'
}
renderer.render(newVnode, document.querySelector('#app'));
renderer.render(vnode, document.querySelector('#app'));

点击p 标签不会触发click 事件,不符合预期。

同样的问题在事件的处理章节后续的代码示例中同样存在。

【86页】代码有误

image

这里有段代码里,由于没有访问 receiver,所以返回值仍然是一,需要将

const obj = { foo: 1 }
console.log(Reflect.get(obj, 'foo', { foo: 2 }))

改为:

const obj = {
  get foo() {
    return this.foo
  }
}
console.log(Reflect.get(obj, 'foo', { foo: 2 }))

【259页】图 10-19 错误

该图的 newStartIdx 应该指向 p-4。
因为前文说的是 “经过上述两个步骤后”,此时队头指针已经移动了。所以应该修改

[Bug] 第 2 章 - 2.6 错误处理 - callWithErrorHandling(page23)

当用户不去注册错误处理程序 utils.registerErrorHandler((e) => {}),handleError 为 null,执行报错 handleError is not a function

let handleError = null
export default {
  foo(fn) {
    callWithErrorHandling(fn)
  },

  bar(fn) {
    callWithErrorHandling(fn)
  },

  registerErrorHandler(fn) {
    handleError = fn
  }
}

function callWithErrorHandling(fn) {
  try {
    fn && fn()
  } catch (e) {
    handleError(e)
  }
}
function callWithErrorHandling(fn) {
  try {
    fn && fn()
  } catch (e) {
    handleError && handleError(e)
  }
}

【21页】代码缩进不一致

在本书第 21 页的第二段代码(用 Composition API 来编写代码)中,第 4 行是否应当与第 3 行的缩进相一致?

image

[109页] 实例代码16行,代码顺序错误

const res = Reflect.get(target, key, receiver);
if (isShallow) {
  return res;
}

track(target, key);             // 这一行应该放在isshlallow 判断之前,否则如果是浅响应就无法收集依赖了

【109-110页】示例代码,浅响应没有调用track收集响应依赖

浅响应不会执行 track 函数收集依赖,因此 target 本身的属性也不是响应式的
track 函数执行时机应在 if (isShallow) { ... } 判断语句之前

书上:

// 封装 createReactive 函数,接收一个参数 isShallow,代表是否为浅响应,默认为 false,即非浅响应
function createReactive(obj, isShallow = false) {
  return new Proxy(obj, {
    // 拦截读取操作
    get(target, key, receiver) {
      // 代理对象可以通过 raw 属性访问原始数据
      if (key === 'raw') {
        return target
      }

      const res = Reflect.get(target, key, receiver)
      // 如果是浅响应,则直接返回原始值
      if (isShallow) {
        return res
      }

      track(target, key)

      if (typeof res === 'object' && res !== null) {
        return reactive(res)
      }

      return res
    }
    // 省略其他拦截函数
  })
}

调整了下 track(target, key) 位置
修改后:

// 封装 createReactive 函数,接收一个参数 isShallow,代表是否为浅响应,默认为 false,即非浅响应
function createReactive(obj, isShallow = false) {
  return new Proxy(obj, {
    // 拦截读取操作
    get(target, key, receiver) {
      // 代理对象可以通过 raw 属性访问原始数据
      if (key === 'raw') {
        return target
      }
      track(target, key)

      const res = Reflect.get(target, key, receiver)
      // 如果是浅响应,则直接返回原始值
      if (isShallow) {
        return res
      }

      if (typeof res === 'object' && res !== null) {
        return reactive(res)
      }

      return res
    }
    // 省略其他拦截函数
  })
}

【60页】避免无限递归循环 的处理逻辑有遗漏

书中代码解决了单个副作用函数 obj.foo++ 造成的死循环问题。

effect(() => obj.foo++)

但是没有解决或提及多个副作用函数造成的死循环问题。

effect(() => obj.foo++);
effect(() => obj.foo++);

运行示例图

image

【96页】最底部示例代码 setter 缺少 receiver 入参

line 03 缺少 receiver 入参

书中:

set(target, key, newVal) {
  // 设置属性值
  const res = Reflect.set(target, key, newVal, receiver)
  // ...
}

应修改为:

set(target, key, newVal, receiver) {
  // 设置属性值
  const res = Reflect.set(target, key, newVal, receiver)
  // ...
}

【67页-69页】计算属性 computed 与 lazy 思维有点跳

【67页】无缓存的 computed 实现,当 obj.fooobj.bar 值变更时都会执行副作用函数,这个执行没有任何意义,因为执行后的结果并不会被收集。

  • 处理方案:添加调度器,在调度器中不做任何处理。
const effectFn = effect(getter, {
  lazy: true,
  scheduler() {}
})

【69页】增加调度器的配置,这里调度器是 ”空参“,看书时比较困扰为什么要用 “空参”。

  • 建议对空参进行说明。或者就是先解决上面提到的问题自然就引出了空参调度器的配置。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.