refactor: move prop decorator into core, expose initial props to initialziers
This commit is contained in:
parent
2f165c1e87
commit
ff9cddd46f
@ -1,3 +0,0 @@
|
|||||||
__tests__/
|
|
||||||
__mocks__/
|
|
||||||
dist/packages
|
|
@ -1 +0,0 @@
|
|||||||
# @vue/decorators
|
|
@ -1,7 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
module.exports = require('./dist/decorators.cjs.prod.js')
|
|
||||||
} else {
|
|
||||||
module.exports = require('./dist/decorators.cjs.js')
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@vue/decorators",
|
|
||||||
"version": "3.0.0-alpha.1",
|
|
||||||
"description": "@vue/decorators",
|
|
||||||
"main": "index.js",
|
|
||||||
"module": "dist/decorators.esm-bundler.js",
|
|
||||||
"types": "dist/index.d.ts",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/vuejs/vue.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"vue"
|
|
||||||
],
|
|
||||||
"author": "Evan You",
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/vuejs/vue/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/vuejs/vue/tree/dev/packages/decorators#readme"
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import { prop } from '../src/index'
|
import { prop } from '../src/optional/propDecorator'
|
||||||
import { Component, createInstance } from '@vue/runtime-test'
|
import { Component, createInstance } from '@vue/runtime-test'
|
||||||
|
|
||||||
test('without options', () => {
|
test('without options', () => {
|
||||||
@ -32,16 +32,20 @@ test('without options', () => {
|
|||||||
test('with options', () => {
|
test('with options', () => {
|
||||||
let capturedThisValue
|
let capturedThisValue
|
||||||
let capturedPropsValue
|
let capturedPropsValue
|
||||||
|
let capturedDataValue
|
||||||
|
|
||||||
class Foo extends Component<{ p: number }> {
|
class Foo extends Component<{ p: number }> {
|
||||||
@prop({
|
@prop({
|
||||||
default: 1
|
default: 1
|
||||||
})
|
})
|
||||||
p: number
|
p: number
|
||||||
|
// data property should be able to make use of prop
|
||||||
|
d: number = this.p + 1
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
capturedThisValue = this.p
|
capturedThisValue = this.p
|
||||||
capturedPropsValue = this.$props.p
|
capturedPropsValue = this.$props.p
|
||||||
|
capturedDataValue = this.d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +53,7 @@ test('with options', () => {
|
|||||||
createInstance(Foo)
|
createInstance(Foo)
|
||||||
expect(capturedThisValue).toBe(1)
|
expect(capturedThisValue).toBe(1)
|
||||||
expect(capturedPropsValue).toBe(1)
|
expect(capturedPropsValue).toBe(1)
|
||||||
|
expect(capturedDataValue).toBe(2)
|
||||||
|
|
||||||
// explicit override
|
// explicit override
|
||||||
createInstance(Foo, {
|
createInstance(Foo, {
|
||||||
@ -56,4 +61,5 @@ test('with options', () => {
|
|||||||
})
|
})
|
||||||
expect(capturedThisValue).toBe(2)
|
expect(capturedThisValue).toBe(2)
|
||||||
expect(capturedPropsValue).toBe(2)
|
expect(capturedPropsValue).toBe(2)
|
||||||
|
expect(capturedDataValue).toBe(3)
|
||||||
})
|
})
|
@ -105,6 +105,10 @@ export const reservedMethods: ReservedKeys = {
|
|||||||
renderTriggered: 1
|
renderTriggered: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isReservedKey(key: string): boolean {
|
||||||
|
return key[0] === '_' || key[0] === '$' || reservedMethods.hasOwnProperty(key)
|
||||||
|
}
|
||||||
|
|
||||||
// This is a special marker from the @prop decorator.
|
// This is a special marker from the @prop decorator.
|
||||||
// The decorator stores prop options on the Class' prototype as __prop_xxx
|
// The decorator stores prop options on the Class' prototype as __prop_xxx
|
||||||
const propPrefixRE = /^__prop_/
|
const propPrefixRE = /^__prop_/
|
||||||
|
@ -5,7 +5,8 @@ import {
|
|||||||
PropOptions,
|
PropOptions,
|
||||||
Prop,
|
Prop,
|
||||||
PropType,
|
PropType,
|
||||||
ComponentPropsOptions
|
ComponentPropsOptions,
|
||||||
|
isReservedKey
|
||||||
} from './componentOptions'
|
} from './componentOptions'
|
||||||
import {
|
import {
|
||||||
EMPTY_OBJ,
|
EMPTY_OBJ,
|
||||||
@ -43,6 +44,15 @@ export function initializeProps(
|
|||||||
? immutable(attrs)
|
? immutable(attrs)
|
||||||
: attrs
|
: attrs
|
||||||
: instance.$props
|
: instance.$props
|
||||||
|
// expose initial props on the raw instance so that they can be accessed
|
||||||
|
// in the child class constructor by class field initializers.
|
||||||
|
if (options != null) {
|
||||||
|
for (const key in props) {
|
||||||
|
// it's okay to just set it here because props options are normalized
|
||||||
|
// and reserved keys should have been filtered away
|
||||||
|
;(instance as any)[key] = props[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve raw VNode data.
|
// resolve raw VNode data.
|
||||||
@ -59,7 +69,7 @@ export function resolveProps(
|
|||||||
rawData: any,
|
rawData: any,
|
||||||
_options: NormalizedPropsOptions | void
|
_options: NormalizedPropsOptions | void
|
||||||
): [Data, Data] {
|
): [Data, Data] {
|
||||||
const hasDeclaredProps = _options !== void 0
|
const hasDeclaredProps = _options != null
|
||||||
const options = _options as NormalizedPropsOptions
|
const options = _options as NormalizedPropsOptions
|
||||||
if (!rawData && !hasDeclaredProps) {
|
if (!rawData && !hasDeclaredProps) {
|
||||||
return EMPTY_PROPS
|
return EMPTY_PROPS
|
||||||
@ -129,22 +139,32 @@ export function normalizePropsOptions(
|
|||||||
if (__DEV__ && !isString(raw[i])) {
|
if (__DEV__ && !isString(raw[i])) {
|
||||||
warn(`props must be strings when using array syntax.`, raw[i])
|
warn(`props must be strings when using array syntax.`, raw[i])
|
||||||
}
|
}
|
||||||
normalized[camelize(raw[i])] = EMPTY_OBJ
|
const normalizedKey = camelize(raw[i])
|
||||||
|
if (!isReservedKey(normalizedKey)) {
|
||||||
|
normalized[normalizedKey] = EMPTY_OBJ
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (__DEV__ && !isObject(raw)) {
|
if (__DEV__ && !isObject(raw)) {
|
||||||
warn(`invalid props options`, raw)
|
warn(`invalid props options`, raw)
|
||||||
}
|
}
|
||||||
for (const key in raw) {
|
for (const key in raw) {
|
||||||
const opt = raw[key]
|
const normalizedKey = camelize(key)
|
||||||
const prop = (normalized[camelize(key)] =
|
if (!isReservedKey(normalizedKey)) {
|
||||||
isArray(opt) || isFunction(opt) ? { type: opt } : opt)
|
const opt = raw[key]
|
||||||
if (prop) {
|
const prop = (normalized[normalizedKey] =
|
||||||
const booleanIndex = getTypeIndex(Boolean, prop.type)
|
isArray(opt) || isFunction(opt) ? { type: opt } : opt)
|
||||||
const stringIndex = getTypeIndex(String, prop.type)
|
if (prop) {
|
||||||
;(prop as NormalizedProp)[BooleanFlags.shouldCast] = booleanIndex > -1
|
const booleanIndex = getTypeIndex(Boolean, prop.type)
|
||||||
;(prop as NormalizedProp)[BooleanFlags.shouldCastTrue] =
|
const stringIndex = getTypeIndex(String, prop.type)
|
||||||
booleanIndex < stringIndex
|
;(prop as NormalizedProp)[BooleanFlags.shouldCast] = booleanIndex > -1
|
||||||
|
;(prop as NormalizedProp)[BooleanFlags.shouldCastTrue] =
|
||||||
|
booleanIndex < stringIndex
|
||||||
|
}
|
||||||
|
} else if (__DEV__) {
|
||||||
|
warn(`Invalid prop name: "${normalizedKey}" is a reserved property.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ComponentInstance } from './component'
|
import { ComponentInstance } from './component'
|
||||||
import { isFunction, isReservedKey } from '@vue/shared'
|
import { isFunction } from '@vue/shared'
|
||||||
import { isRendering } from './componentRenderUtils'
|
import { isRendering } from './componentRenderUtils'
|
||||||
import { reservedMethods } from './componentOptions'
|
import { isReservedKey, reservedMethods } from './componentOptions'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
|
||||||
const bindCache = new WeakMap()
|
const bindCache = new WeakMap()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { ComponentInstance } from './component'
|
import { ComponentInstance } from './component'
|
||||||
import { observable } from '@vue/observer'
|
import { observable } from '@vue/observer'
|
||||||
import { isReservedKey } from '@vue/shared'
|
import { isReservedKey } from './componentOptions'
|
||||||
import { warn } from './warning'
|
|
||||||
|
|
||||||
export function initializeState(
|
export function initializeState(
|
||||||
instance: ComponentInstance,
|
instance: ComponentInstance,
|
||||||
@ -25,16 +24,8 @@ export function extractInitializers(
|
|||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const key = keys[i]
|
const key = keys[i]
|
||||||
if (!isReservedKey(key)) {
|
if (!isReservedKey(key)) {
|
||||||
// it's possible for a prop to be present here when it's declared with
|
// it's possible for a prop to be present here when it's declared
|
||||||
// decorators and has a default value.
|
if (!props || !props.hasOwnProperty(key)) {
|
||||||
if (props && props.hasOwnProperty(key)) {
|
|
||||||
__DEV__ &&
|
|
||||||
warn(
|
|
||||||
`Class property "${key}" is declared as a prop but also has an initializer. ` +
|
|
||||||
`If you are trying to provide a default value for the prop, use the ` +
|
|
||||||
`prop's "default" option instead.`
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
data[key] = (instance as any)[key]
|
data[key] = (instance as any)[key]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ export { KeepAlive } from './optional/keepAlive'
|
|||||||
export { mixins } from './optional/mixins'
|
export { mixins } from './optional/mixins'
|
||||||
export { EventEmitter } from './optional/eventEmitter'
|
export { EventEmitter } from './optional/eventEmitter'
|
||||||
export { memoize } from './optional/memoize'
|
export { memoize } from './optional/memoize'
|
||||||
|
export { prop } from './optional/propDecorator'
|
||||||
|
|
||||||
// flags & types
|
// flags & types
|
||||||
export { ComponentType, ComponentClass, FunctionalComponent } from './component'
|
export { ComponentType, ComponentClass, FunctionalComponent } from './component'
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { PropValidator, Component } from '@vue/runtime-core'
|
import { Component } from '../component'
|
||||||
|
import { PropValidator } from '../componentOptions'
|
||||||
|
import { camelize } from '@vue/shared'
|
||||||
|
|
||||||
export function prop(
|
export function prop(
|
||||||
target: Component | PropValidator<any>,
|
target: Component | PropValidator<any>,
|
||||||
@ -16,7 +18,7 @@ export function prop(
|
|||||||
|
|
||||||
function applyProp(target: any, key: string, options: PropValidator<any> = {}) {
|
function applyProp(target: any, key: string, options: PropValidator<any> = {}) {
|
||||||
// here `target` is the prototype of the component class
|
// here `target` is the prototype of the component class
|
||||||
Object.defineProperty(target, `__prop_${key}`, {
|
Object.defineProperty(target, `__prop_${camelize(key)}`, {
|
||||||
value: options
|
value: options
|
||||||
})
|
})
|
||||||
}
|
}
|
@ -5,7 +5,6 @@ export const NOOP = () => {}
|
|||||||
export const reservedPropRE = /^(?:key|ref|slots)$|^vnode/
|
export const reservedPropRE = /^(?:key|ref|slots)$|^vnode/
|
||||||
|
|
||||||
export const isOn = (key: string) => key[0] === 'o' && key[1] === 'n'
|
export const isOn = (key: string) => key[0] === 'o' && key[1] === 'n'
|
||||||
export const isReservedKey = (key: string) => key[0] === '_' || key[0] === '$'
|
|
||||||
|
|
||||||
export const isArray = Array.isArray
|
export const isArray = Array.isArray
|
||||||
export const isFunction = (val: any): val is Function =>
|
export const isFunction = (val: any): val is Function =>
|
||||||
|
Loading…
Reference in New Issue
Block a user