Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ This repository employs a [monorepo](https://en.wikipedia.org/wiki/Monorepo) set

- `compiler-dom`: Compiler with additional plugins specifically targeting the browser.

- `compiler-sfc`: Lower level utilities for compiling Vue Single File Components.

- `compiler-ssr`: Compiler that produces render functions optimized for server-side rendering.

- `template-explorer`: A development tool for debugging compiler output. You can run `nr dev template-explorer` and open its `index.html` to get a repl of template compilation based on current source code.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"jest": "^27.1.0",
"lint-staged": "^10.2.10",
"lodash": "^4.17.15",
"marked": "^0.7.0",
"marked": "^4.0.10",
"minimist": "^1.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.1",
Expand Down
5 changes: 5 additions & 0 deletions packages/reactivity/__tests__/ref.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '../src/index'
import { computed } from '@vue/runtime-dom'
import { shallowRef, unref, customRef, triggerRef } from '../src/ref'
import { isShallow } from '../src/reactive'

describe('reactivity/ref', () => {
it('should hold a value', () => {
Expand Down Expand Up @@ -227,6 +228,10 @@ describe('reactivity/ref', () => {
expect(dummy).toBe(2)
})

test('shallowRef isShallow', () => {
expect(isShallow(shallowRef({ a: 1 }))).toBe(true)
})

test('isRef', () => {
expect(isRef(ref(1))).toBe(true)
expect(isRef(computed(() => 1))).toBe(true)
Expand Down
33 changes: 32 additions & 1 deletion packages/reactivity/__tests__/shallowReactive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { isReactive, reactive, shallowReactive } from '../src/reactive'
import {
isReactive,
isShallow,
reactive,
shallowReactive,
shallowReadonly
} from '../src/reactive'

import { effect } from '../src/effect'

Expand All @@ -24,6 +30,31 @@ describe('shallowReactive', () => {
expect(isReactive(reactiveProxy.foo)).toBe(true)
})

test('isShallow', () => {
expect(isShallow(shallowReactive({}))).toBe(true)
expect(isShallow(shallowReadonly({}))).toBe(true)
})

// #5271
test('should respect shallow reactive nested inside reactive on reset', () => {
const r = reactive({ foo: shallowReactive({ bar: {} }) })
expect(isShallow(r.foo)).toBe(true)
expect(isReactive(r.foo.bar)).toBe(false)

r.foo = shallowReactive({ bar: {} })
expect(isShallow(r.foo)).toBe(true)
expect(isReactive(r.foo.bar)).toBe(false)
})

test('should respect shallow/deep versions of same target on access', () => {
const original = {}
const shallow = shallowReactive(original)
const deep = reactive(original)
const r = reactive({ shallow, deep })
expect(r.shallow).toBe(shallow)
expect(r.deep).toBe(deep)
})

describe('collections', () => {
test('should be reactive', () => {
const shallowSet = shallowReactive(new Set())
Expand Down
11 changes: 8 additions & 3 deletions packages/reactivity/src/baseHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
reactiveMap,
shallowReactiveMap,
shallowReadonlyMap,
isReadonly
isReadonly,
isShallow
} from './reactive'
import { TrackOpTypes, TriggerOpTypes } from './operations'
import {
Expand Down Expand Up @@ -84,6 +85,8 @@ function createGetter(isReadonly = false, shallow = false) {
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.IS_SHALLOW) {
return shallow
} else if (
key === ReactiveFlags.RAW &&
receiver ===
Expand Down Expand Up @@ -148,8 +151,10 @@ function createSetter(shallow = false) {
): boolean {
let oldValue = (target as any)[key]
if (!shallow && !isReadonly(value)) {
value = toRaw(value)
oldValue = toRaw(oldValue)
if (!isShallow(value)) {
value = toRaw(value)
oldValue = toRaw(oldValue)
}
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
Expand Down
1 change: 1 addition & 0 deletions packages/reactivity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export {
readonly,
isReactive,
isReadonly,
isShallow,
isProxy,
shallowReactive,
shallowReadonly,
Expand Down
8 changes: 7 additions & 1 deletion packages/reactivity/src/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
IS_SHALLOW = '__v_isShallow',
RAW = '__v_raw'
}

export interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
[ReactiveFlags.IS_READONLY]?: boolean
[ReactiveFlags.IS_SHALLOW]?: boolean
[ReactiveFlags.RAW]?: any
}

Expand Down Expand Up @@ -87,7 +89,7 @@ export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRefSimple<T>
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
if (isReadonly(target)) {
return target
}
return createReactiveObject(
Expand Down Expand Up @@ -226,6 +228,10 @@ export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
}

export function isShallow(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_SHALLOW])
}

export function isProxy(value: unknown): boolean {
return isReactive(value) || isReadonly(value)
}
Expand Down
14 changes: 5 additions & 9 deletions packages/reactivity/src/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ export interface Ref<T = any> {
* autocomplete, so we use a private Symbol instead.
*/
[RefSymbol]: true
/**
* @internal
*/
_shallow?: boolean
}

type RefBase<T> = {
Expand Down Expand Up @@ -102,9 +98,9 @@ class RefImpl<T> {
public dep?: Dep = undefined
public readonly __v_isRef = true

constructor(value: T, public readonly _shallow: boolean) {
this._rawValue = _shallow ? value : toRaw(value)
this._value = _shallow ? value : toReactive(value)
constructor(value: T, public readonly __v_isShallow: boolean) {
this._rawValue = __v_isShallow ? value : toRaw(value)
this._value = __v_isShallow ? value : toReactive(value)
}

get value() {
Expand All @@ -113,10 +109,10 @@ class RefImpl<T> {
}

set value(newVal) {
newVal = this._shallow ? newVal : toRaw(newVal)
newVal = this.__v_isShallow ? newVal : toRaw(newVal)
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal
this._value = this._shallow ? newVal : toReactive(newVal)
this._value = this.__v_isShallow ? newVal : toReactive(newVal)
triggerRefValue(this, newVal)
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/apiWatch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
isRef,
isShallow,
Ref,
ComputedRef,
ReactiveEffect,
Expand Down Expand Up @@ -205,7 +206,7 @@ function doWatch(

if (isRef(source)) {
getter = () => source.value
forceTrigger = !!source._shallow
forceTrigger = isShallow(source)
} else if (isReactive(source)) {
getter = () => source
deep = true
Expand Down
7 changes: 4 additions & 3 deletions packages/runtime-core/src/customFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isReactive, isReadonly, isRef, Ref, toRaw } from '@vue/reactivity'
import { EMPTY_OBJ, extend, isArray, isFunction, isObject } from '@vue/shared'
import { isShallow } from '../../reactivity/src/reactive'
import { ComponentInternalInstance, ComponentOptions } from './component'
import { ComponentPublicInstance } from './componentPublicInstance'

Expand Down Expand Up @@ -38,7 +39,7 @@ export function initCustomFormatter() {
return [
'div',
{},
['span', vueStyle, 'Reactive'],
['span', vueStyle, isShallow(obj) ? 'ShallowReactive' : 'Reactive'],
'<',
formatValue(obj),
`>${isReadonly(obj) ? ` (readonly)` : ``}`
Expand All @@ -47,7 +48,7 @@ export function initCustomFormatter() {
return [
'div',
{},
['span', vueStyle, 'Readonly'],
['span', vueStyle, isShallow(obj) ? 'ShallowReadonly' : 'Readonly'],
'<',
formatValue(obj),
'>'
Expand Down Expand Up @@ -181,7 +182,7 @@ export function initCustomFormatter() {
}

function genRefFlag(v: Ref) {
if (v._shallow) {
if (isShallow(v)) {
return `ShallowRef`
}
if ((v as any).effect) {
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export {
isProxy,
isReactive,
isReadonly,
isShallow,
// advanced
customRef,
triggerRef,
Expand Down
36 changes: 36 additions & 0 deletions packages/runtime-dom/__tests__/customElement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,42 @@ describe('defineCustomElement', () => {
})
})

describe('attrs', () => {
const E = defineCustomElement({
render() {
return [
h('div', null, this.$attrs.foo as string)
]
}
})
customElements.define('my-el-attrs', E)

test('attrs via attribute', async () => {
container.innerHTML = `<my-el-attrs foo="hello"></my-el-attrs>`
const e = container.childNodes[0] as VueElement
expect(e.shadowRoot!.innerHTML).toBe('<div>hello</div>')

e.setAttribute('foo', 'changed')
await nextTick()
expect(e.shadowRoot!.innerHTML).toBe('<div>changed</div>')
})

test('attrs via properties', async () => {
const e = new E() as any
e.foo = 'one'
container.appendChild(e)
expect(e.shadowRoot!.innerHTML).toBe('<div>one</div>')

e.foo = 'two'
await nextTick()
expect(e.shadowRoot!.innerHTML).toBe('<div>two</div>')

e.foo = true
await nextTick()
expect(e.shadowRoot!.innerHTML).toBe('<div>true</div>')
})
})

describe('emits', () => {
const E = defineCustomElement({
setup(_, { emit }) {
Expand Down
13 changes: 9 additions & 4 deletions packages/runtime-dom/src/apiCustomElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,10 @@ export class VueElement extends BaseClass {

const resolve = (def: InnerComponentDef) => {
const { props, styles } = def
const hasOptions = !isArray(props)
const rawKeys = props ? (hasOptions ? Object.keys(props) : props) : []
const hasOptions = props && !isArray(props)
const propsVal = props ? (hasOptions ? Object.keys(props) : props) : []
const attrs = Object.keys(this._props).filter(p => !propsVal.includes(p))
const rawKeys = [...propsVal.concat(attrs)]

// cast Number-type props set before resolve
let numberProps
Expand All @@ -235,7 +237,10 @@ export class VueElement extends BaseClass {
// check if there are props set pre-upgrade or connect
for (const key of Object.keys(this)) {
if (key[0] !== '_') {
this._setProp(key, this[key as keyof this], true, false)
const isProps = propsVal.includes(key)
const isAttrs = attrs.includes(key)
isProps || isAttrs || rawKeys.push(key)
this._setProp(key, this[key as keyof this], isProps, false)
}
}

Expand All @@ -246,7 +251,7 @@ export class VueElement extends BaseClass {
return this._getProp(key)
},
set(val) {
this._setProp(key, val)
this._setProp(key, val, propsVal.includes(key))
}
})
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vue/examples/classic/markdown.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}),
computed: {
compiledMarkdown() {
return marked(this.input, { sanitize: true })
return marked.marked(this.input, { sanitize: true })
}
},
methods: {
Expand Down
2 changes: 1 addition & 1 deletion packages/vue/examples/composition/markdown.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Vue.createApp({
setup() {
const input = ref('# hello')
const output = computed(() => marked(input.value, { sanitize: true }))
const output = computed(() => marked.marked(input.value, { sanitize: true }))
const update = _.debounce(e => { input.value = e.target.value }, 50)

return {
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.