诗号:六道同坠,魔劫万千,引渡如来。

/img/bdx/yiyeshu-001.jpg

https://github.com/vuejs/vue-next/blob/master/CHANGELOG.md

前言

TIP

本系列文说明:会不定期的更新本地的 vue-next 仓库,本系列文章则是针对 git pull 后 的更新内容的分析和记录, 。

可参过链接跳转到对应的分析文章

更新内容:

很久没更新了,惨~~~~~~~~~

捡点重要的来看下吧,以后还是要保持每周拉一次代码比较好!!!

最初分析拉取的代码是: 3.0.4, 现在都 3.1.2 了 尴尬!!!

3.0.8 (2021-03-26)

Important

  1. IMP: SFC style 中的 ::v-slotted:slotted 🔗

TODO Bug Fixes [0/44]

  • compiler: properly bail stringfication for nested slot elements (f74b16c)

  • compiler-core: allow unicode to appear in identifiers (#3443) (ebedccc), closes #3440

  • compiler-core: avoid generating useless createVNode helper (#2938) (7715c49), closes #2739

  • compiler-core: detect v-if branch root with comment as dev fragment (#2785) (4bf7ba1), closes #2780

  • compiler-core: fix the detection of forwarded slots with v-if or v-for (#3353) (602b58e), closes #3347

  • compiler-core: should not condense whitespace in RCDATA text mode (#3482) (b4b8215), closes #3479

  • compiler-dom: stringifyStatic should remove attribute bindings with null value (#3477) (ca6aa01), closes #3475

  • compiler-sfc: scope Id should not be attached to @keyframe breakpoint rules (#3308) (6cb9475), closes #3304

  • compiler-sfc: should not rewrite scope variable (#3449) (bbc5fe6), closes #3445

  • compiler-ssr: keep the order of imports expression for the fallback branch of SSR (#3448) (49f4072), closes #3447

  • component: prioritize registered component over implicit self-reference via filename (abd129d), closes #2827

  • hydration: handle camel-case tag name when performing match assertion (#3247) (9036f88), closes #3243

  • KeepAlive: adapt keepalive for ssr (#3259) (e8e9b00), closes #3255

  • reactivity: ensure computed can be wrapped by readonly (41e02f0), closes #3376

  • reactivity: ensure that shallow and normal proxies are tracked seperately (close #2843) (#2851) (22cc4a7)

  • reactivity: fix shallow readonly behavior for collections (#3003) (68de9f4), closes #3007

  • rumtime-core: custom dom props should be cloned when cloning a hoisted DOM (#3080) (5dbe834), closes #3072

  • runtime-core: cache props default values to avoid unnecessary watcher trigger (#3474) (44166b4), closes #3471

  • runtime-core: ensure only skip unflushed job (#3406) (bf34e33)

  • runtime-core: fix async component ref handling (#3191) (7562e72), closes #3188

  • runtime-core: fix erraneous emits warnings w/ mixins (60d777d), closes #2651

  • runtime-core: fix warning for absent props (#3363) (86ceef4), closes #3362

  • runtime-core: handle error in async setup (#2881) (d668d48)

  • runtime-core: handle error in async watchEffect (#3129) (eb1fae6)

  • runtime-core: should call chained mixins and extends (#3040) (b58bb16), closes #3038

  • runtime-core: should not cache property access during data() invocation (#3299) (6e88156), closes #3297

  • runtime-core: should not track deps in pre flush watcher callbacks (d5824b9), closes #2728

  • runtime-core: the select tag's multiple prop should be set before the children mounting (#3202) (2451dd8), closes #3199

  • runtime-dom: support mounting app to svg container (#2929) (8ffcde2), closes #2926

  • ssr: ensure async setup error handling work with suspense during ssr (2e71f07)

  • ssr: fix memory leak when vnode component render throws error (da944cb), closes #3100

  • ssr: properly update currentRenderingInstance state during ssr (8c3c14a), closes #2863

  • ssr: respect render function from extends/mixins in ssr (#3006) (0a583d5), closes #3004

  • ssr: watchEffect onInvalidate runner initialization (#3323) (e4b5fcc), closes #3322

  • ssr/hydration: handle ending empty text node (#3246) (420c8f4), closes #3245

  • teleport: targetAnchor should also be removed when unmounted (#2870) (21d1288)

  • Teleport: component with multi roots should be removed when unmounted (#3157) (7769513), closes #3156

  • Teleport: fallback to non-optimized mode when HRM performing updates (#3311) (9cb21d0), closes #3302

  • transition: toggling branches with in-out mode should be transitioned correctly (#3109) (67a0290), closes #3104

  • types: allow style to be an array in JSX (#2947) (13c9d2c)

  • types: union function prop (#3119) (3755e60), closes #3357

  • types: unwrap refs on public instance data (#3319) (2b588cf), closes #3315

  • types/jsx: llow tabindex to be a string (#3476) (e4a5712)

  • add display name for suspense component (#3312) (3b3a9a1)

DONE Performance Improvements [1/1]

CLOSED: [2021-08-09 Mon 21:23]

  • support only attaching slot scope ids when necessary (02cbbb7)

    SFC style 中使用

    :slotted(h1) { color: blue; }

    ::v-slotted(h1) { color: blue; }

    可以在当前组件中控制 slot 组件的样式。

    https://codesandbox.io/s/damp-cdn-k9eed?file=/src/main.js

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    const url =
        process.env.VNEXT_PKG_RC + "/../compiler-sfc/dist/compiler-sfc.cjs.js";
    const value = require(url.replace("stb-", ""));
    const { compileScript, parse, compileStyle } = value;
    
    function compileScoped(source, options) {
    const res = compileStyle({
      source,
      filename: 'test.css',
      id: 'data-v-test',
      scoped: true,
      ...options
    })
    return res.code
    }
    
    const src = `::v-slotted(h1) { color: red; } :slotted(h1) {font-size:12px;}`
    const code = compileScoped(src)
    console.log(code)
    return 0;
    
    h1[data-v-test-s] { color: red;
    }
    h1[data-v-test-s] {font-size:12px;}
    0
    

    下面会检查 style 中是不是含 ::v-slotted(...) {...}:slotted(...) {..} 指令

    1
    2
    3
    4
    
    // packages/compiler-sfc/src/parse.ts
    // check if the SFC uses :slotted
    const slottedRE = /(?:::v-|:)slotted\(/
    descriptor.slotted = descriptor.styles.some(s => slottedRE.test(s.content))
    

    根据上面的结果,在 parse SFC template 阶段给组件加上 scope-id-s 属性, 如:

    <div scope-id-s />

    1
    2
    3
    4
    5
    
    // packages/compiler-ssr/src/transforms/ssrTransformSlotOutlet.ts
    // inject slot scope id if current template uses :slotted
    if (context.scopeId && context.slotted !== false) {
      args.push(`"${context.scopeId}-s"`)
    }
    

3.0.7 (2021-08-08)

Important

  1. FIX: <script setup> 中 export default 和 class 语法 🔗

  2. FIX: v-show 的优先级比 {style: { display: 'block' }} 更高 🔗

  3. FIX: scheduler 中 组件更新任务总是保持以 job.id 的增序执行,在插入的时候找到 对应的索引插入 🔗

DONE Bug Fixes [6/6]

CLOSED: [2021-08-09 Mon 15:23]

  • ✅ compiler-sfc: handle more edge cases in default rewrite (1dedc19)

    处理 setup script 中更多语法情况:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    const url =
    process.env.VNEXT_PKG_RC + "/../compiler-sfc/dist/compiler-sfc.cjs.js";
    const value = require(url.replace("stb-", ""));
    const { rewriteDefault } = value;
    
    const test = (hint, code) => {
    console.log('> ' + hint + '\n');
    console.log(rewriteDefault(code, 'script'))
    }
    
    test('1. comments', `// export default\nexport default {}`)
    test('2. export default class', `export default class Foo {}`)
    test('3. export default class + commons', `// export default\nexport default class Foo {}`)
    test('4. export default class + comments 2',  `export default {}\n` + `// export default class Foo {}`)
    test('5. export default class + comments 3', `/*\nexport default class Foo {}*/\n` + `export default class Bar {}`)
    console.log('------ end ------ ');
    
    return 0;
    
    > 1. comments
    
    // export default
    const script = {}
    > 2. export default class
    
    class Foo {}
    const script = Foo
    > 3. export default class + commons
    
    // export default
    class Foo {}
    const script = Foo
    > 4. export default class + comments 2
    
    const script = {}
    // export default class Foo {}
    > 5. export default class + comments 3
    
    /*
    export default class Foo {}*/
    const script = class Bar {}
    ------ end ------
    0
    
  • ✅ deps: pin Rollup to 2.38 (34f354b), closes #3332

  • ✅ runtime-core: properties in methods should be writable and enumerable in DEV (#3301) (e3568ba), closes #3300

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    3  packages/runtime-core/src/componentOptions.ts
    @@ -610,7 +610,8 @@ export function applyOptions(
            Object.defineProperty(ctx, key, {
              value: methodHandler.bind(publicThis),
              configurable: true,
    -            enumerable: false
    +            enumerable: true,
    +            writable: true
            })
          } else {
            ctx[key] = methodHandler.bind(publicThis)
    
  • ✅ scheduler: ensure updates are always inserted in ascending id order (#3184) (45fae9d), closes #2768 #2829

    View isn't updated in a weird case (combination of many factors, transition, injection & computed)

    确保 updates 总是以升序被插入,那么在插入之前就得找到适当的 job 索引:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    // #2768
    // Use binary-search to find a suitable position in the queue,
    // so that the queue maintains the increasing order of job's id,
    // which can prevent the job from being skipped and also can avoid repeated patching.
    function findInsertionIndex(job: SchedulerJob) {
    // the start index should be `flushIndex + 1`
    let start = flushIndex + 1
    let end = queue.length
    const jobId = getId(job)
    
    while (start < end) {
      const middle = (start + end) >>> 1
      const middleJobId = getId(queue[middle])
      middleJobId < jobId ? (start = middle + 1) : (end = middle)
    }
    
    return start
    }
    

    在 update 进入任务队列的时候保证所有 jobs 是以升序排列

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    export function queueJob(job: SchedulerJob) {
    // the dedupe search uses the startIndex argument of Array.includes()
    // by default the search index includes the current job that is being run
    @@ -72,7 +91,12 @@ export function queueJob(job: SchedulerJob) {
        )) &&
      job !== currentPreFlushParentJob
    ) {
    -    queue.push(job)
    +    const pos = findInsertionIndex(job)
    +    if (pos > -1) {
    +      queue.splice(pos, 0, job)
    +    } else {
    +      queue.push(job)
    +    }
      queueFlush()
    }
    }
    
  • ✅ v-show: v-show takes higher priority than style attribute (#3230) (5ad4036), closes #2757

    v-show 也是通过 el.style.display 来实现的,这里意思是如果 style 属性中也有 display 的话,在 runtime-dom/src/modules/style.ts 中 patch 的时候应该 v-show 的优先级更高。

  • ✅ init devtools after feature flag checks (d0ea745)

DONE Performance Improvements [1/1]

CLOSED: [2021-08-09 Mon 15:22]

  • ✅ reactivity: only call Set.add if doesn't already have value (#3307) (9cd9883)

    对于 Set key-value 都是值,所以当有这个 value 的时候再添加就没有什么意义了, Set 又是不重复集合。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    packages/reactivity/src/collectionHandlers.ts
    @@ -76,8 +76,8 @@ function add(this: SetTypes, value: unknown) {
    const target = toRaw(this)
    const proto = getProto(target)
    const hadKey = proto.has.call(target, value)
    -  target.add(value)
    if (!hadKey) {
    +    target.add(value)
      trigger(target, TriggerOpTypes.ADD, value, value)
    }
    return this
    

3.0.6 (2021-02-24)

Important

此次更新重点内容(值得关注的点):

  1. ADD: BigInt 类型和 SFC 支持 🔗

  2. FIX: 修复 class: ['foo', false, undefined, 'bar'] 被解析成 <div class="foo bar"/> 问题 🔗

  3. FIX: 修复 foo-bar 事件名无法触发问题 🔗

  4. FIX: this.$watch(fn, callback)fn 第一个参数是 instance.proxy 🔗

DONE Bug Fixes [25/25]

CLOSED: [2021-08-05 Thu 23:42]

  • ✅ compiler-core: do not mark v-for as stable on const bindings (734c65b), closes vitejs/vite#1956

  • ✅ compiler-dom: ensure global build filename matches the one defined in package.json (close #3181) (#3185) (96b6433)

  • ✅ compiler-dom: fix cdn entries (fcb6c89), closes #3181 #3185

  • ✅ compiler-sfc: compiler blank srcset (#3005) (9dc816d)

    允许 <img src="./logo.png" srcset=""/>srcset 是个空值,编译后:

    1
    2
    3
    4
    
      _createVNode(\\"img\\", {
        src: \\"./logo.png\\",
        srcset: \\"\\"
      }),
    
  • ✅ compiler-sfc: removeSpecifier issue when removing initial imports (script-setup) (#2729) (6d762a8)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    const url =
    process.env.VNEXT_PKG_RC + "/../compiler-sfc/dist/compiler-sfc.cjs.js";
    const value = require(url.replace("stb-", ""));
    const parser = '/usr/local/lib/node_modules/@babel/parser/lib'
    const { parse: babelParse } = require(parser)
    const { compileScript, parse } = value;
    
    const src = `<script setup>
        import { defineProps, defineEmits, ref } from 'vue'
        defineProps(['foo'])
        defineEmits(['bar'])
        const r = ref(0)
        </script>`
    const { descriptor } = parse(src)
    const code = compileScript(descriptor, { id: 'xxxxx' }).content
    console.log('before babel > \n', code);
    babelParse(code, {
    sourceType: 'module',
    plugins: ['bigInt', 'optionalChaining', 'nullishCoalescingOperator', 'typescript']
    })
    
    console.log('after babel > \n', code);
    return 0;
    
    before babel >
     import { ref } from 'vue'
    
    export default {
      props: ['foo'],
      emits: ['bar'],
      setup(__props, { expose }) {
      expose()
    
    
    
          const r = ref(0)
    
    return { r, ref, __isScriptSetup: true }
    }
    
    }
    after babel >
     import { ref } from 'vue'
    
    export default {
      props: ['foo'],
      emits: ['bar'],
      setup(__props, { expose }) {
      expose()
    
    
    
          const r = ref(0)
    
    return { r, ref, __isScriptSetup: true }
    }
    
    }
    0
    
  • ✅ compiler-sfc: the empty lang attribute should be treated as no lang specified (#3051) (6d5b623)

  • compiler-sfc: transformAssetUrls.base should not affect known module requests (2ea9867)

  • ✅ compiler-sfc: treat const reactive() bindings as mutable (03360ce) const obj = reactive({}) 编译成 let 变量。

  • ✅ compiler-ssr: avoid duplicated asset imports merged from component slot client branch (c69f4ea), closes vitejs/vite#2034

    root.imports Set 改成了 Array,允许重复了.

  • ✅ devtools: init devtools in production (#2906) (4d9bcb7)

  • ✅ devtools: send instance to devtools when it's mounted instead of created (4fecb27)

  • ✅ docs: change reference to passed deadline (#2930) (de7f9d1)

  • ✅ hmr: deep clone reused hoisted trees during dev (5a7a1b8), closes vitejs/vite#2022

    开发过程中,对静态提升的组件树进行深拷贝。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    packages/runtime-core/src/vnode.ts
    @@ -459,7 +459,7 @@ export function cloneVNode<T, U>(
    ): VNode<T, U> {
    // This is intentionally NOT using spread or extend to avoid the runtime
    // key enumeration cost.
    -  const { props, ref, patchFlag } = vnode
    +  const { props, ref, patchFlag, children } = vnode
    const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props
    return {
      __v_isVNode: true,
    @@ -479,7 +479,10 @@ export function cloneVNode<T, U>(
            : normalizeRef(extraProps)
          : ref,
      scopeId: vnode.scopeId,
    -    children: vnode.children,
    +    children:
    +      __DEV__ && patchFlag === PatchFlags.HOISTED && isArray(children)
    +        ? (children as VNode[]).map(deepCloneVNode)
    +        : children,
      target: vnode.target,
      targetAnchor: vnode.targetAnchor,
      staticCount: vnode.staticCount,
    @@ -513,6 +516,18 @@ export function cloneVNode<T, U>(
    }
    }
    

    deep clone 函数:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    /**
    * Dev only, for HMR of hoisted vnodes reused in v-for
    * https://github.com/vitejs/vite/issues/2022
    */
    function deepCloneVNode(vnode: VNode): VNode {
    const cloned = cloneVNode(vnode)
    if (isArray(vnode.children)) {
      cloned.children = (vnode.children as VNode[]).map(deepCloneVNode)
    }
    return cloned
    }
    
  • ✅ runtime-core: align $parent/$root with the template ref when using expose (#3158) (f43a3b0)

    expose 特性详解:Vue3 功能拆解⑪ expose options&api

  • ✅ runtime-core: allow overriding properties other than props (#3105) (73117f6)

    允许重写非组件 props 的属性,比如:原生 API hasOwnProperty

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    packages/runtime-core/src/componentPublicInstance.ts
    @@ -368,7 +368,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
        setupState[key] = value
      } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
        data[key] = value
    -    } else if (key in instance.props) {
    +    } else if (hasOwn(instance.props, key)) {
        __DEV__ &&
          warn(
            `Attempting to mutate prop "${key}". Props are readonly.`,
    

    测试:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    packages/runtime-core/__tests__/componentProps.spec.ts
    @@ -295,6 +295,10 @@ describe('component props', () => {
        ;(instance!.proxy as any).foo = 2
      }).toThrow(TypeError)
      expect(`Attempting to mutate prop "foo"`).toHaveBeenWarned()
      // should not throw when overriding properties other than props
    +    expect(() => {
    +      ;(instance!.proxy as any).hasOwnProperty = () => {}
    +    }).not.toThrow(TypeError)
    })
    
  • ✅ runtime-core: check the DEV_ROOT_FRAGMENT flag correctly in the dev environment (#2750) (347a879)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
      // to have comments along side the root element which makes it a fragment
      let root = result
      let setRoot: ((root: VNode) => void) | undefined = undefined
    -    if (__DEV__ && result.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT) {
    +    if (
    +      __DEV__ &&
    +      result.patchFlag > 0 &&
    +      result.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT
    +    ) {
        ;[root, setRoot] = getChildRoot(result)
      }
    
  • ✅ runtime-core: component methods should override global properties in DEV (#3074) (2587f36)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    packages/runtime-core/src/componentOptions.ts
    @@ -604,7 +604,17 @@ export function applyOptions(
      for (const key in methods) {
        const methodHandler = (methods as MethodOptions)[key]
        if (isFunction(methodHandler)) {
    -        ctx[key] = methodHandler.bind(publicThis)
    +        // In dev mode, we use the `createRenderContext` function to define methods to the proxy target,
    +        // and those are read-only but reconfigurable, so it needs to be redefined here
    +        if (__DEV__) {
    +          Object.defineProperty(ctx, key, {
    +            value: methodHandler.bind(publicThis),
    +            configurable: true,
    +            enumerable: false
    +          })
    +        } else {
    +          ctx[key] = methodHandler.bind(publicThis)
    +        }
          if (__DEV__) {
            checkDuplicateProperties!(OptionTypes.METHODS, key)
          }
    

    测试:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    const url = process.env.VNEXT_PKG_RC +'/../runtime-test/dist/runtime-test.cjs.js'
    const value = require(url.replace('stb-', ''))
    const { nodeOps, render, h, serializeInner: s, createApp } = value
    
    const Comp = {
    methods: {
      foo() {
        return 'foo'
      }
    },
    render() {
      return this.foo()
    }
    }
    
    const app = createApp(Comp)
    app.config.globalProperties.foo = () => 'bar'
    
    const root = nodeOps.createElement('div')
    app.mount(root)
    
    console.log(s(root))
    return 0
    
    foo
    0
    
  • ✅ runtime-core: ensure app instance can be garbage collected after unmount (close #2907) (#2909) (60e05ef)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    packages/runtime-core/src/apiCreateApp.ts
    @@ -272,6 +272,7 @@ export function createAppAPI<HostElement>(
            if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
              devtoolsUnmountApp(app)
            }
    +          delete app._container.__vue_app__
          } else if (__DEV__) {
            warn(`Cannot unmount an app that is not mounted.`)
          }
    

    取消引用。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    function unmount() {
    if (isMounted) {
      render(null, app._container)
      if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
        app._instance = null
        devtoolsUnmountApp(app)
      }
      delete app._container.__vue_app__
    } else if (__DEV__) {
      warn(`Cannot unmount an app that is not mounted.`)
    }
    }
    

    mount 里面保存的 __vue_ap__

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    function mount(
    rootContainer: HostElement,
    isHydrate?: boolean,
    isSVG?: boolean
    ): any {
    if (!isMounted) {
      // ...
         vnode.appContext = context
    
      // HMR root reload ...
    
      if (isHydrate && hydrate) {
        hydrate(vnode as VNode<Node, Element>, rootContainer as any)
      } else {
        render(vnode, rootContainer, isSVG)
      }
      isMounted = true
      app._container = rootContainer
      // for devtools and telemetry
      ;(rootContainer as any).__vue_app__ = app
    
      if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
        app._instance = vnode.component
        devtoolsInitApp(app, version)
      }
    
      return vnode.component!.proxy
    }  // ...
    }
    
  • ✅ runtime-core: instanceWatch should pass this.proxy to source as the first argument (#2753) (ec8fd10)

    当 watch 一个函数的时候,将 instance.proxy 做为第一个参数传给这个函数。

    测试:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    
    const url = process.env.VNEXT_PKG_RC +'/../runtime-test/dist/runtime-test.cjs.js'
    const value = require(url.replace('stb-', ''))
    const { nodeOps, render, h, serializeInner: s, createApp } = value
    
    let instance = null
    const source = (proxy) => console.log(instance && ( proxy === instance.proxy ))
    const Comp = {
    created() {
      instance = this
      this.$watch(source, () => {})
    },
    render() {}
    }
    
    const root = nodeOps.createElement('div')
    createApp(Comp).mount(root)
    return 0
    
    false
    0
    

    修复:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    packages/runtime-core/src/apiWatch.ts
    @@ -181,7 +181,9 @@ function doWatch(
          } else if (isReactive(s)) {
            return traverse(s)
          } else if (isFunction(s)) {
    -          return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
    +          return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER, [
    +            instance && (instance.proxy as any)
    +          ])
          } else {
            __DEV__ && warnInvalidSource(s)
          }
    @@ -190,7 +192,9 @@ function doWatch(
      if (cb) {
        // getter with cb
        getter = () =>
    -        callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
    +        callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER, [
    +          instance && (instance.proxy as any)
    +        ])
      } else {
        // no cb -> simple effect
        getter = () => {
    
  • ✅ types: extract the correct props type for the DateConstructor (#2676) (48f0d29)

  • ✅ ensure all published packages contan a LICENCE file (close #2650) (#2857) (6a48d23)

  • ✅ remove superfluous spaces when normalizing class (#3083) (4b55142)

    问题如下代码,正常应该忽略 undefind, false

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    const url = process.env.VNEXT_PKG_RC +'/../runtime-test/dist/runtime-test.cjs.js'
    const value = require(url.replace('stb-', ''))
    const { nodeOps, render, h, serializeInner: s } = value
    
    const Comp = {
    props: { foo: BigInt },
    render() {
      return h('div', { class: ['foo', undefined, false, 'bar'] }, [this.foo])
    }
    }
    
    const root = nodeOps.createElement('div')
    render(h(Comp,  {
    foo: BigInt(BigInt(100000111)) + BigInt(2000000000) * BigInt(30000000)
    }), root)
    
    console.log(s(root))
    return 0
    
    <div class="foo   bar">60000000100000111</div>
    0
    

    修复:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    packages/shared/src/normalizeProp.ts
    @@ -62,7 +62,10 @@ export function normalizeClass(value: unknown): string {
      res = value
    } else if (isArray(value)) {
      for (let i = 0; i < value.length; i++) {
    -      res += normalizeClass(value[i]) + ' '
    +      const normalized = normalizeClass(value[i])
    +      if (normalized) {
    +        res += normalized + ' '
    +      }
      }
    } else if (isObject(value)) {
      for (const name in value) {
    
  • ✅ runtime-dom: enable set form attr to null on form-elements (#2840) (#2849) (f262438)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    packages/runtime-dom/src/patchProp.ts
    @@ -3,7 +3,13 @@ import { patchStyle } from './modules/style'
    -  // #1787 form as an attribute must be a string, while it accepts an Element as
    -  // a prop
    -  if (key === 'form' && typeof value === 'string') {
    +  // #1787, #2840 the form property is readonly and can only be set as an
    +  // attribute using a string value
    +  if (key === 'form' && isFormTag(el.tagName)) {
      return false
    }
    
    packages/shared/src/domTagConfig.ts
    @@ -30,6 +30,11 @@ const SVG_TAGS =
    const VOID_TAGS =
    'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'
    
    + const FORM_TAGS =
    +  'button,datalist,fieldset,input,keygen,label,legend,meter,optgroup,option,' +
    +  'output,progress,select,textarea'
    
    export const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS)
    export const isSVGTag = /*#__PURE__*/ makeMap(SVG_TAGS)
    export const isVoidTag = /*#__PURE__*/ makeMap(VOID_TAGS)
    export const isFormTag = /*#__PURE__*/ makeMap(FORM_TAGS, true)
    
  • ✅ toRef: ref created from union typed prop can't be used in watch (#3048) (4ca4666)

  • ✅ should prefix ShadowRoot with window. (#2943) (97d6f1a)

    通过 window 去使用 ShadowRoot,因为它不是 window 上可枚举的属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    packages/runtime-dom/src/index.ts
    @@ -119,7 +119,7 @@ function normalizeContainer(
    }
    if (
      __DEV__ &&
    -    container instanceof ShadowRoot &&
    +    container instanceof window.ShadowRoot &&
      container.mode === 'closed'
    ) {
    

DONE Features [5/5]

CLOSED: [2021-08-05 Thu 23:42]

  • ✅ remove useless option in KeepAlive (#3170) (bd1240c)

    删除了 KeepAlive 的 inheritRef: true 选项。

  • ✅ compiler-core: support BigInt in template (#2900) (c9f94fa)

    BigInt 标记为全局变量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    packages/shared/src/globalsWhitelist.ts
    @@ -3,6 +3,6 @@ import { makeMap } from './makeMap'
    const GLOBALS_WHITE_LISTED =
    'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
    'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
    -  'Object,Boolean,String,RegExp,Map,Set,JSON,Intl'
    +  'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'
    
    export const isGloballyWhitelisted = /*#__PURE__*/ makeMap(GLOBALS_WHITE_LISTED)
    

    测试结果:

  • compiler-sfc: upgrade to postcss 8 (#2710) (49bc2e4)

  • ✅ runtime-core: improve render context warning (#2496) (288ae0a)

    问题: 组件渲染期间去访问一个不存在的属性时候,报错信息:

    Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    packages/runtime-core/src/componentPublicInstance.ts
    @@ -349,7 +349,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
            )} must be accessed via $data because it starts with a reserved ` +
              `character ("$" or "_") and is not proxied on the render context.`
          )
    -      } else {
    +      } else if (instance === currentRenderingInstance) {
          warn(
            `Property ${JSON.stringify(key)} was accessed during render ` +
              `but is not defined on instance.`
    
  • ✅ runtime-core: props type support BigInt (#2891) (ffd5288)

    修改代码:

    1
    2
    3
    4
    
    const isSimpleType = /*#__PURE__*/ makeMap(
    -  'String,Number,Boolean,Function,Symbol'
    +  'String,Number,Boolean,Function,Symbol,BigInt'
    )
    

    测试

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    const url = process.env.VNEXT_PKG_RC +'/../runtime-test/dist/runtime-test.cjs.js'
    const value = require(url.replace('stb-', ''))
    const { nodeOps, render, h, serializeInner: s } = value
    
    const Comp = {
    props: { foo: BigInt },
    render() {
      return h('div', [this.foo])
    }
    }
    
    const root = nodeOps.createElement('div')
    render(h(Comp,  {
    foo: BigInt(BigInt(100000111)) + BigInt(2000000000) * BigInt(30000000)
    }), root)
    
    console.log(s(root))
    return 0
    
    <div>60000000100000111</div>
    0
    

DONE Performance Improvements [1/1]

CLOSED: [2021-08-05 Thu 23:42]

  • ✅ reactivity: should not track __isVue (#2940) (dd02cf3)

    @@ -93,7 +96,7 @@ function createGetter(isReadonly = false, shallow = false) {

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    +  const isNonTrackableKeys = /*#__PURE__*/ makeMap(`__proto__,__v_isRef,__isVue`)
    
      if (
        isSymbol(key)
          ? builtInSymbols.has(key as symbol)
    -        : key === `__proto__` || key === `__v_isRef`
    +        : isNonTrackableKeys(key)
      ) {
        return res
      }
    
    // 遇到 __isVue 也直接返回 Reflect.get 的结果,不往下 track 了。
    

3.0.5 (2020-12-30)

vue-next/CHANGELOG.md at master · vuejs/vue-next

TIP

Note: this release contains a type-only change that requires TypeScript 4.0+, which may cause build issues in projects still using TS 3.x.

只包含一些类型的变更。

DONE Bug Fixes [9/9]

CLOSED: [2021-08-05 Thu 23:44]

  • ✅ compiler-core: fix missing createVNode import on nested v-for (ad4d391), closes #2718

  • ✅ compiler-sfc: should keep template nodes with no content (#2468) (5b9b37f), closes #2463

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
     -> packages/compiler-sfc/src/parse.ts
    
     if (node.type !== NodeTypes.ELEMENT) {
        return
      }
    -    if (!node.children.length && !hasSrc(node)) {
    +    if (!node.children.length && !hasSrc(node) && node.tag !== 'template') {
        return
      }
      switch (node.tag) {
    
  • compiler-sfc: support transforming asset urls with full base url. (#2477) (db786b1)

    针对相对路径而言,当提供了 base 选项的时候,会使用这个值去拼接,如:

    { transformAssetUrls: { base: 'https://www.cheng92.com' } }

    <img src="./vue/img/test.png" /> 会被编译成:

    createVNode('img', { src: 'https://www.cheng92.com/vue/img/test.png' })

  • ✅ runtime-core: component mount anchor memory leak (#2459) (3867bb4), closes #2458

    /img/tmp/vue-bug-2459.png

  • ✅ runtime-core: skip patchBlockChildren if n1.dynamicChildren is null (#2717) (c59897c), closes #2715 #2485

    这个问题原因是,一开始 HelloWorld 的 dynamicChildren 是 null。

    当点击 ADD 按钮的时候增加了一项数据,会进入 mountChildren -> patchBlockChildren

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    const patchBlockChildren: PatchBlockChildrenFn = (
      oldChildren,
      newChildren,
      fallbackContainer,
      parentComponent,
      parentSuspense,
      isSVG,
      slotScopeIds
    ) => {
      for (let i = 0; i < newChildren.length; i++) {
        const oldVNode = oldChildren[i] // dynamicChildren
        const newVNode = newChildren[i]
    
        // ...
        }
      }
    

    dynamicChildren 在初始化的时候是个 null 值, 一开始就访问了 dynamicChildren[i] 所以导致报错。

    修复代码(c59897c),加个判断条件:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    if (
          patchFlag > 0 &&
          patchFlag & PatchFlags.STABLE_FRAGMENT &&
    -        dynamicChildren &&
    +        dynamicChildren &&
    +        n1.dynamicChildren
        ) {
          // a stable fragment (template root or <template v-for>) doesn't need to
          // patch children order, but it may contain dynamicChildren.
          patchBlockChildren(
    -         n1.dynamicChildren!,
    +          n1.dynamicChildren,
            dynamicChildren,
            container,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds
          )
    
  • runtime-dom: support mounting app on ShadowRoot (#2447) (b2189ba), closes #2399

    >3.2 中已经没有 __isShadowRoot 相关的代码了。

  • ✅ ssr: properly handle ssr empty slot and fallback (88f6b33)

  • transition: ensure manual style manipulation in transition leave hooks work (cbaa380), closes #2720

    在 onLeave hook 中增加

    1
    2
    
    forceReflow()
    addTransitionClass(el, leaveActiveClass)
    

    去强制渲染,触发 leaveActiveClass

  • transition: ensure styles from *-leave-active trigger transition (#2716) (3f8f9b6), closes #2712

DONE Features [1/1]

CLOSED: [2021-08-05 Thu 23:44]

  • ✅ devtools: send instance (3626ff0)

    将组件实例给开发工具。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    function createDevtoolsComponentHook(hook: DevtoolsHooks) {
    return (component: ComponentInternalInstance) => {
      if (!devtools) return
      devtools.emit(
        hook,
        component.appContext.app,
        component.uid,
    -      component.parent ? component.parent.uid : undefined
    +      component.parent ? component.parent.uid : undefined,
    +      component
      )
    }
    }