refactor: option merging + extract helper functions
This commit is contained in:
parent
7bc28a6e61
commit
149d82d618
@ -3,7 +3,6 @@ import {
|
||||
Component,
|
||||
render,
|
||||
nodeOps,
|
||||
ComponentInstance,
|
||||
observable,
|
||||
nextTick
|
||||
} from '@vue/renderer-test'
|
||||
@ -41,7 +40,7 @@ describe('Parent chain management', () => {
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
const parent = render(h(Parent), root) as ComponentInstance
|
||||
const parent = render(h(Parent), root) as Component
|
||||
|
||||
expect(child.$parent).toBe(parent)
|
||||
expect(child.$root).toBe(parent)
|
||||
@ -100,7 +99,7 @@ describe('Parent chain management', () => {
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
const parent = render(h(Parent), root) as ComponentInstance
|
||||
const parent = render(h(Parent), root) as Component
|
||||
|
||||
expect(child.$parent).toBe(parent)
|
||||
expect(child.$root).toBe(parent)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EMPTY_OBJ, NOOP } from './utils'
|
||||
import { EMPTY_OBJ, NOOP, isArray } from '@vue/shared'
|
||||
import { VNode, Slots, RenderNode, MountedVNode } from './vdom'
|
||||
import {
|
||||
Data,
|
||||
@ -44,30 +44,30 @@ interface PublicInstanceMethods {
|
||||
$emit(name: string, ...payload: any[]): this
|
||||
}
|
||||
|
||||
interface APIMethods<P, D> {
|
||||
data?(): Partial<D>
|
||||
export interface APIMethods<P = {}, D = {}> {
|
||||
data(): Partial<D>
|
||||
render(props: Readonly<P>, slots: Slots, attrs: Data, parentVNode: VNode): any
|
||||
}
|
||||
|
||||
interface LifecycleMethods {
|
||||
beforeCreate?(): void
|
||||
created?(): void
|
||||
beforeMount?(): void
|
||||
mounted?(): void
|
||||
beforeUpdate?(vnode: VNode): void
|
||||
updated?(vnode: VNode): void
|
||||
beforeUnmount?(): void
|
||||
unmounted?(): void
|
||||
errorCaptured?(): (
|
||||
export interface LifecycleMethods {
|
||||
beforeCreate(): void
|
||||
created(): void
|
||||
beforeMount(): void
|
||||
mounted(): void
|
||||
beforeUpdate(vnode: VNode): void
|
||||
updated(vnode: VNode): void
|
||||
beforeUnmount(): void
|
||||
unmounted(): void
|
||||
errorCaptured(): (
|
||||
err: Error,
|
||||
type: ErrorTypes,
|
||||
instance: ComponentInstance | null,
|
||||
vnode: VNode
|
||||
) => boolean | void
|
||||
activated?(): void
|
||||
deactivated?(): void
|
||||
renderTracked?(e: DebuggerEvent): void
|
||||
renderTriggered?(e: DebuggerEvent): void
|
||||
activated(): void
|
||||
deactivated(): void
|
||||
renderTracked(e: DebuggerEvent): void
|
||||
renderTriggered(e: DebuggerEvent): void
|
||||
}
|
||||
|
||||
export interface ComponentClass extends ComponentClassOptions {
|
||||
@ -88,9 +88,10 @@ export type ComponentType = ComponentClass | FunctionalComponent
|
||||
// It extends InternalComponent with mounted instance properties.
|
||||
export interface ComponentInstance<P = {}, D = {}>
|
||||
extends InternalComponent,
|
||||
APIMethods<P, D>,
|
||||
LifecycleMethods {
|
||||
Partial<APIMethods<P, D>>,
|
||||
Partial<LifecycleMethods> {
|
||||
constructor: ComponentClass
|
||||
render: APIMethods<P, D>['render']
|
||||
|
||||
$vnode: MountedVNode
|
||||
$data: D
|
||||
@ -157,7 +158,7 @@ class InternalComponent implements PublicInstanceMethods {
|
||||
|
||||
// eventEmitter interface
|
||||
$on(event: string, fn: Function): this {
|
||||
if (Array.isArray(event)) {
|
||||
if (isArray(event)) {
|
||||
for (let i = 0; i < event.length; i++) {
|
||||
this.$on(event[i], fn)
|
||||
}
|
||||
@ -181,7 +182,7 @@ class InternalComponent implements PublicInstanceMethods {
|
||||
if (this._events) {
|
||||
if (!event && !fn) {
|
||||
this._events = null
|
||||
} else if (Array.isArray(event)) {
|
||||
} else if (isArray(event)) {
|
||||
for (let i = 0; i < event.length; i++) {
|
||||
this.$off(event[i], fn)
|
||||
}
|
||||
@ -223,7 +224,7 @@ class InternalComponent implements PublicInstanceMethods {
|
||||
|
||||
function invokeListeners(value: Function | Function[], payload: any[]) {
|
||||
// TODO handle error
|
||||
if (Array.isArray(value)) {
|
||||
if (isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
value[i](...payload)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NOOP } from './utils'
|
||||
import { NOOP, isFunction } from '@vue/shared'
|
||||
import { computed, stop, ComputedGetter } from '@vue/observer'
|
||||
import { ComponentInstance } from './component'
|
||||
import { ComponentComputedOptions } from './componentOptions'
|
||||
@ -17,7 +17,7 @@ export function initializeComputed(
|
||||
const proxy = instance.$proxy
|
||||
for (const key in computedOptions) {
|
||||
const option = computedOptions[key]
|
||||
const getter = typeof option === 'function' ? option : option.get || NOOP
|
||||
const getter = isFunction(option) ? option : option.get || NOOP
|
||||
handles[key] = computed(getter, proxy)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,12 @@
|
||||
import { ComponentInstance } from './component'
|
||||
import {
|
||||
ComponentInstance,
|
||||
ComponentClass,
|
||||
APIMethods,
|
||||
LifecycleMethods
|
||||
} from './component'
|
||||
import { Slots } from './vdom'
|
||||
import { isArray, isObject, isFunction } from '@vue/shared'
|
||||
import { normalizePropsOptions } from './componentProps'
|
||||
|
||||
export type Data = Record<string, any>
|
||||
|
||||
@ -73,3 +80,100 @@ export interface WatchOptions {
|
||||
deep?: boolean
|
||||
immediate?: boolean
|
||||
}
|
||||
|
||||
type ReservedKeys = { [K in keyof (APIMethods & LifecycleMethods)]: 1 }
|
||||
|
||||
export const reservedMethods: ReservedKeys = {
|
||||
data: 1,
|
||||
render: 1,
|
||||
beforeCreate: 1,
|
||||
created: 1,
|
||||
beforeMount: 1,
|
||||
mounted: 1,
|
||||
beforeUpdate: 1,
|
||||
updated: 1,
|
||||
beforeUnmount: 1,
|
||||
unmounted: 1,
|
||||
errorCaptured: 1,
|
||||
activated: 1,
|
||||
deactivated: 1,
|
||||
renderTracked: 1,
|
||||
renderTriggered: 1
|
||||
}
|
||||
|
||||
// This is called in the base component constructor and the return value is
|
||||
// set on the instance as $options.
|
||||
export function resolveComponentOptionsFromClass(
|
||||
Component: ComponentClass
|
||||
): ComponentOptions {
|
||||
if (Component.options) {
|
||||
return Component.options
|
||||
}
|
||||
const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
|
||||
const options = {} as any
|
||||
for (const key in staticDescriptors) {
|
||||
const { enumerable, get, value } = staticDescriptors[key]
|
||||
if (enumerable || get) {
|
||||
options[key] = get ? get() : value
|
||||
}
|
||||
}
|
||||
const instanceDescriptors = Object.getOwnPropertyDescriptors(
|
||||
Component.prototype
|
||||
)
|
||||
for (const key in instanceDescriptors) {
|
||||
const { get, value } = instanceDescriptors[key]
|
||||
if (get) {
|
||||
// computed properties
|
||||
;(options.computed || (options.computed = {}))[key] = get
|
||||
// there's no need to do anything for the setter
|
||||
// as it's already defined on the prototype
|
||||
} else if (isFunction(value)) {
|
||||
if (key in reservedMethods) {
|
||||
options[key] = value
|
||||
} else {
|
||||
;(options.methods || (options.methods = {}))[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
options.props = normalizePropsOptions(options.props)
|
||||
Component.options = options
|
||||
return options
|
||||
}
|
||||
|
||||
export function mergeComponentOptions(to: any, from: any): ComponentOptions {
|
||||
const res: any = Object.assign({}, to)
|
||||
if (from.mixins) {
|
||||
from.mixins.forEach((mixin: any) => {
|
||||
from = mergeComponentOptions(from, mixin)
|
||||
})
|
||||
}
|
||||
for (const key in from) {
|
||||
const value = from[key]
|
||||
const existing = res[key]
|
||||
if (isFunction(value) && isFunction(existing)) {
|
||||
if (key === 'data') {
|
||||
// for data we need to merge the returned value
|
||||
res[key] = function() {
|
||||
return Object.assign(existing(), value())
|
||||
}
|
||||
} else if (/^render|^errorCaptured/.test(key)) {
|
||||
// render, renderTracked, renderTriggered & errorCaptured
|
||||
// are never merged
|
||||
res[key] = value
|
||||
} else {
|
||||
// merge lifecycle hooks
|
||||
res[key] = function(...args: any[]) {
|
||||
existing.call(this, ...args)
|
||||
value.call(this, ...args)
|
||||
}
|
||||
}
|
||||
} else if (isArray(value) && isArray(existing)) {
|
||||
res[key] = existing.concat(value)
|
||||
} else if (isObject(value) && isObject(existing)) {
|
||||
res[key] = Object.assign({}, existing, value)
|
||||
} else {
|
||||
res[key] = value
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -2,19 +2,40 @@ import { immutable, unwrap, lock, unlock } from '@vue/observer'
|
||||
import { ComponentInstance } from './component'
|
||||
import {
|
||||
Data,
|
||||
ComponentPropsOptions,
|
||||
PropOptions,
|
||||
Prop,
|
||||
PropType
|
||||
PropType,
|
||||
ComponentPropsOptions
|
||||
} from './componentOptions'
|
||||
import { EMPTY_OBJ, camelize, hyphenate, capitalize } from './utils'
|
||||
import {
|
||||
EMPTY_OBJ,
|
||||
camelize,
|
||||
hyphenate,
|
||||
capitalize,
|
||||
isString,
|
||||
isFunction,
|
||||
isArray,
|
||||
isObject
|
||||
} from '@vue/shared'
|
||||
import { warn } from './warning'
|
||||
|
||||
const EMPTY_PROPS = { props: EMPTY_OBJ }
|
||||
|
||||
const enum BooleanFlags {
|
||||
shouldCast = '1',
|
||||
shouldCastTrue = '2'
|
||||
}
|
||||
|
||||
type NormalizedProp = PropOptions & {
|
||||
[BooleanFlags.shouldCast]?: boolean
|
||||
[BooleanFlags.shouldCastTrue]?: boolean
|
||||
}
|
||||
|
||||
type NormalizedPropsOptions = Record<string, NormalizedProp>
|
||||
|
||||
export function initializeProps(
|
||||
instance: ComponentInstance,
|
||||
options: ComponentPropsOptions | undefined,
|
||||
options: NormalizedPropsOptions | undefined,
|
||||
data: Data | null
|
||||
) {
|
||||
const { props, attrs } = resolveProps(data, options)
|
||||
@ -31,13 +52,13 @@ export function initializeProps(
|
||||
// - else: everything goes in `props`.
|
||||
export function resolveProps(
|
||||
rawData: any,
|
||||
rawOptions: ComponentPropsOptions | void
|
||||
_options: NormalizedPropsOptions | void
|
||||
): { props: Data; attrs?: Data } {
|
||||
const hasDeclaredProps = rawOptions !== void 0
|
||||
const hasDeclaredProps = _options !== void 0
|
||||
const options = _options as NormalizedPropsOptions
|
||||
if (!rawData && !hasDeclaredProps) {
|
||||
return EMPTY_PROPS
|
||||
}
|
||||
const options = normalizePropsOptions(rawOptions) as NormalizedPropsOptions
|
||||
const props: any = {}
|
||||
let attrs: any = void 0
|
||||
if (rawData != null) {
|
||||
@ -66,8 +87,7 @@ export function resolveProps(
|
||||
// default values
|
||||
if (hasDefault && currentValue === void 0) {
|
||||
const defaultValue = opt.default
|
||||
props[key] =
|
||||
typeof defaultValue === 'function' ? defaultValue() : defaultValue
|
||||
props[key] = isFunction(defaultValue) ? defaultValue() : defaultValue
|
||||
}
|
||||
// boolean casting
|
||||
if (opt[BooleanFlags.shouldCast]) {
|
||||
@ -129,58 +149,34 @@ export function updateProps(instance: ComponentInstance, nextData: Data) {
|
||||
}
|
||||
}
|
||||
|
||||
const enum BooleanFlags {
|
||||
shouldCast = '1',
|
||||
shouldCastTrue = '2'
|
||||
}
|
||||
|
||||
type NormalizedProp = PropOptions & {
|
||||
[BooleanFlags.shouldCast]?: boolean
|
||||
[BooleanFlags.shouldCastTrue]?: boolean
|
||||
}
|
||||
|
||||
type NormalizedPropsOptions = Record<string, NormalizedProp>
|
||||
|
||||
const normalizationCache = new WeakMap<
|
||||
ComponentPropsOptions,
|
||||
NormalizedPropsOptions
|
||||
>()
|
||||
|
||||
function normalizePropsOptions(
|
||||
export function normalizePropsOptions(
|
||||
raw: ComponentPropsOptions | void
|
||||
): NormalizedPropsOptions {
|
||||
): NormalizedPropsOptions | void {
|
||||
if (!raw) {
|
||||
return EMPTY_OBJ
|
||||
}
|
||||
const hit = normalizationCache.get(raw)
|
||||
if (hit) {
|
||||
return hit
|
||||
return
|
||||
}
|
||||
const normalized: NormalizedPropsOptions = {}
|
||||
if (Array.isArray(raw)) {
|
||||
if (isArray(raw)) {
|
||||
for (let i = 0; i < raw.length; i++) {
|
||||
if (__DEV__ && typeof raw !== 'string') {
|
||||
warn(`props must be strings when using array syntax.`)
|
||||
if (__DEV__ && !isString(raw[i])) {
|
||||
warn(`props must be strings when using array syntax.`, raw[i])
|
||||
}
|
||||
normalized[camelize(raw[i])] = EMPTY_OBJ
|
||||
}
|
||||
} else {
|
||||
if (__DEV__ && typeof raw !== 'object') {
|
||||
if (__DEV__ && !isObject(raw)) {
|
||||
warn(`invalid props options`, raw)
|
||||
}
|
||||
for (const key in raw) {
|
||||
const opt = raw[key]
|
||||
const prop = (normalized[camelize(key)] =
|
||||
Array.isArray(opt) || typeof opt === 'function'
|
||||
? { type: opt }
|
||||
: opt) as NormalizedProp
|
||||
isArray(opt) || isFunction(opt) ? { type: opt } : opt) as NormalizedProp
|
||||
const booleanIndex = getTypeIndex(Boolean, prop.type)
|
||||
const stringIndex = getTypeIndex(String, prop.type)
|
||||
prop[BooleanFlags.shouldCast] = booleanIndex > -1
|
||||
prop[BooleanFlags.shouldCastTrue] = booleanIndex < stringIndex
|
||||
}
|
||||
}
|
||||
normalizationCache.set(raw, normalized)
|
||||
return normalized
|
||||
}
|
||||
|
||||
@ -199,13 +195,13 @@ function getTypeIndex(
|
||||
type: Prop<any>,
|
||||
expectedTypes: PropType<any> | void | null | true
|
||||
): number {
|
||||
if (Array.isArray(expectedTypes)) {
|
||||
if (isArray(expectedTypes)) {
|
||||
for (let i = 0, len = expectedTypes.length; i < len; i++) {
|
||||
if (isSameType(expectedTypes[i], type)) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
} else if (expectedTypes != null && typeof expectedTypes === 'object') {
|
||||
} else if (isObject(expectedTypes)) {
|
||||
return isSameType(expectedTypes, type) ? 0 : -1
|
||||
}
|
||||
return -1
|
||||
@ -235,7 +231,7 @@ function validateProp(
|
||||
// type check
|
||||
if (type != null && type !== true) {
|
||||
let isValid = false
|
||||
const types = Array.isArray(type) ? type : [type]
|
||||
const types = isArray(type) ? type : [type]
|
||||
const expectedTypes = []
|
||||
// value is valid as long as one of the specified types match
|
||||
for (let i = 0; i < types.length && !isValid; i++) {
|
||||
@ -269,7 +265,7 @@ function assertType(value: any, type: Prop<any>): AssertionResult {
|
||||
} else if (expectedType === 'Object') {
|
||||
valid = toRawType(value) === 'Object'
|
||||
} else if (expectedType === 'Array') {
|
||||
valid = Array.isArray(value)
|
||||
valid = isArray(value)
|
||||
} else {
|
||||
valid = value instanceof type
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ComponentInstance } from './component'
|
||||
import { isString, isFunction } from '@vue/shared'
|
||||
|
||||
const bindCache = new WeakMap()
|
||||
|
||||
@ -41,7 +42,7 @@ const renderProxyHandlers = {
|
||||
// TODO warn non-present property
|
||||
}
|
||||
const value = Reflect.get(target, key, receiver)
|
||||
if (typeof value === 'function') {
|
||||
if (isFunction(value)) {
|
||||
// auto bind
|
||||
return getBoundMethod(value, target, receiver)
|
||||
} else {
|
||||
@ -56,7 +57,7 @@ const renderProxyHandlers = {
|
||||
receiver: any
|
||||
): boolean {
|
||||
if (__DEV__) {
|
||||
if (typeof key === 'string' && key[0] === '$') {
|
||||
if (isString(key) && key[0] === '$') {
|
||||
// TODO warn setting immutable properties
|
||||
return false
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
// import { EMPTY_OBJ } from './utils'
|
||||
import { ComponentInstance } from './component'
|
||||
import { observable } from '@vue/observer'
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { VNodeFlags } from './flags'
|
||||
import { EMPTY_OBJ } from './utils'
|
||||
import { EMPTY_OBJ, isArray, isFunction, isObject } from '@vue/shared'
|
||||
import { h } from './h'
|
||||
import { VNode, MountedVNode, createFragment } from './vdom'
|
||||
import {
|
||||
@ -13,7 +13,10 @@ import { initializeState } from './componentState'
|
||||
import { initializeProps, resolveProps } from './componentProps'
|
||||
import { initializeComputed, teardownComputed } from './componentComputed'
|
||||
import { initializeWatch, teardownWatch } from './componentWatch'
|
||||
import { ComponentOptions } from './componentOptions'
|
||||
import {
|
||||
ComponentOptions,
|
||||
resolveComponentOptionsFromClass
|
||||
} from './componentOptions'
|
||||
import { createRenderProxy } from './componentProxy'
|
||||
import { handleError, ErrorTypes } from './errorHandling'
|
||||
import { warn } from './warning'
|
||||
@ -58,7 +61,7 @@ export function initializeComponentInstance(instance: ComponentInstance) {
|
||||
)
|
||||
}
|
||||
|
||||
instance.$options = resolveComponentOptions(instance.constructor)
|
||||
instance.$options = resolveComponentOptionsFromClass(instance.constructor)
|
||||
instance.$parentVNode = currentVNode as MountedVNode
|
||||
|
||||
// renderProxy
|
||||
@ -143,9 +146,9 @@ function normalizeComponentRoot(
|
||||
): VNode {
|
||||
if (vnode == null) {
|
||||
vnode = createTextVNode('')
|
||||
} else if (typeof vnode !== 'object') {
|
||||
} else if (!isObject(vnode)) {
|
||||
vnode = createTextVNode(vnode + '')
|
||||
} else if (Array.isArray(vnode)) {
|
||||
} else if (isArray(vnode)) {
|
||||
if (vnode.length === 1) {
|
||||
vnode = normalizeComponentRoot(vnode[0], componentVNode)
|
||||
} else {
|
||||
@ -158,13 +161,13 @@ function normalizeComponentRoot(
|
||||
(flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
|
||||
) {
|
||||
if (el) {
|
||||
vnode = cloneVNode(vnode)
|
||||
vnode = cloneVNode(vnode as VNode)
|
||||
}
|
||||
if (flags & VNodeFlags.COMPONENT) {
|
||||
vnode.parentVNode = componentVNode
|
||||
}
|
||||
} else if (el) {
|
||||
vnode = cloneVNode(vnode)
|
||||
vnode = cloneVNode(vnode as VNode)
|
||||
}
|
||||
}
|
||||
return vnode
|
||||
@ -209,7 +212,7 @@ export function createComponentClassFromOptions(
|
||||
// name -> displayName
|
||||
if (key === 'name') {
|
||||
options.displayName = options.name
|
||||
} else if (typeof value === 'function') {
|
||||
} else if (isFunction(value)) {
|
||||
// lifecycle hook / data / render
|
||||
if (__COMPAT__) {
|
||||
if (key === 'render') {
|
||||
@ -229,7 +232,7 @@ export function createComponentClassFromOptions(
|
||||
} else if (key === 'computed') {
|
||||
for (const computedKey in value) {
|
||||
const computed = value[computedKey]
|
||||
const isGet = typeof computed === 'function'
|
||||
const isGet = isFunction(computed)
|
||||
Object.defineProperty(proto, computedKey, {
|
||||
configurable: true,
|
||||
get: isGet ? computed : computed.get,
|
||||
@ -250,37 +253,3 @@ export function createComponentClassFromOptions(
|
||||
}
|
||||
return AnonymousComponent as ComponentClass
|
||||
}
|
||||
|
||||
// This is called in the base component constructor and the return value is
|
||||
// set on the instance as $options.
|
||||
export function resolveComponentOptions(
|
||||
Component: ComponentClass
|
||||
): ComponentOptions {
|
||||
if (Component.options) {
|
||||
return Component.options
|
||||
}
|
||||
const staticDescriptors = Object.getOwnPropertyDescriptors(Component)
|
||||
const options = {} as any
|
||||
for (const key in staticDescriptors) {
|
||||
const { enumerable, get, value } = staticDescriptors[key]
|
||||
if (enumerable || get) {
|
||||
options[key] = get ? get() : value
|
||||
}
|
||||
}
|
||||
const instanceDescriptors = Object.getOwnPropertyDescriptors(
|
||||
Component.prototype
|
||||
)
|
||||
for (const key in instanceDescriptors) {
|
||||
const { get, value } = instanceDescriptors[key]
|
||||
if (get) {
|
||||
// computed properties
|
||||
;(options.computed || (options.computed = {}))[key] = get
|
||||
// there's no need to do anything for the setter
|
||||
// as it's already defined on the prototype
|
||||
} else if (typeof value === 'function') {
|
||||
;(options.methods || (options.methods = {}))[key] = value
|
||||
}
|
||||
}
|
||||
Component.options = options
|
||||
return options
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { EMPTY_OBJ, NOOP } from './utils'
|
||||
import {
|
||||
EMPTY_OBJ,
|
||||
NOOP,
|
||||
isFunction,
|
||||
isArray,
|
||||
isString,
|
||||
isObject
|
||||
} from '@vue/shared'
|
||||
import { ComponentInstance } from './component'
|
||||
import { ComponentWatchOptions, WatchOptions } from './componentOptions'
|
||||
import { autorun, stop } from '@vue/observer'
|
||||
@ -13,11 +20,11 @@ export function initializeWatch(
|
||||
if (options !== void 0) {
|
||||
for (const key in options) {
|
||||
const opt = options[key]
|
||||
if (Array.isArray(opt)) {
|
||||
if (isArray(opt)) {
|
||||
opt.forEach(o => setupWatcher(instance, key, o))
|
||||
} else if (typeof opt === 'function') {
|
||||
} else if (isFunction(opt)) {
|
||||
setupWatcher(instance, key, opt)
|
||||
} else if (typeof opt === 'string') {
|
||||
} else if (isString(opt)) {
|
||||
setupWatcher(instance, key, (instance as any)[opt])
|
||||
} else if (opt.handler) {
|
||||
setupWatcher(instance, key, opt.handler, opt)
|
||||
@ -35,8 +42,7 @@ export function setupWatcher(
|
||||
const handles = instance._watchHandles || (instance._watchHandles = new Set())
|
||||
const proxy = instance.$proxy
|
||||
|
||||
const rawGetter =
|
||||
typeof keyOrFn === 'string'
|
||||
const rawGetter = isString(keyOrFn)
|
||||
? parseDotPath(keyOrFn, proxy)
|
||||
: () => keyOrFn.call(proxy)
|
||||
|
||||
@ -116,11 +122,11 @@ function parseDotPath(path: string, ctx: any): Function {
|
||||
}
|
||||
|
||||
function traverse(value: any, seen: Set<any> = new Set()) {
|
||||
if (value === null || typeof value !== 'object' || seen.has(value)) {
|
||||
if (!isObject(value) || seen.has(value)) {
|
||||
return
|
||||
}
|
||||
seen.add(value)
|
||||
if (Array.isArray(value)) {
|
||||
if (isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
traverse(value[i], seen)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { autorun, stop } from '@vue/observer'
|
||||
import { queueJob } from '@vue/scheduler'
|
||||
import { VNodeFlags, ChildrenFlags } from './flags'
|
||||
import { EMPTY_OBJ, reservedPropRE, lis } from './utils'
|
||||
import { EMPTY_OBJ, reservedPropRE, isString } from '@vue/shared'
|
||||
import {
|
||||
VNode,
|
||||
MountedVNode,
|
||||
@ -297,7 +297,7 @@ export function createRenderer(options: RendererOptions) {
|
||||
contextVNode: MountedVNode | null
|
||||
) {
|
||||
const { tag, children, childFlags, ref } = vnode
|
||||
const target = typeof tag === 'string' ? platformQuerySelector(tag) : tag
|
||||
const target = isString(tag) ? platformQuerySelector(tag) : tag
|
||||
|
||||
if (__DEV__ && !target) {
|
||||
// TODO warn poartal target not found
|
||||
@ -1410,3 +1410,49 @@ export function createRenderer(options: RendererOptions) {
|
||||
|
||||
return { render }
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
|
||||
export function lis(arr: number[]): number[] {
|
||||
const p = arr.slice()
|
||||
const result = [0]
|
||||
let i
|
||||
let j
|
||||
let u
|
||||
let v
|
||||
let c
|
||||
const len = arr.length
|
||||
for (i = 0; i < len; i++) {
|
||||
const arrI = arr[i]
|
||||
if (arrI !== 0) {
|
||||
j = result[result.length - 1]
|
||||
if (arr[j] < arrI) {
|
||||
p[i] = j
|
||||
result.push(i)
|
||||
continue
|
||||
}
|
||||
u = 0
|
||||
v = result.length - 1
|
||||
while (u < v) {
|
||||
c = ((u + v) / 2) | 0
|
||||
if (arr[result[c]] < arrI) {
|
||||
u = c + 1
|
||||
} else {
|
||||
v = c
|
||||
}
|
||||
}
|
||||
if (arrI < arr[result[u]]) {
|
||||
if (u > 0) {
|
||||
p[i] = result[u - 1]
|
||||
}
|
||||
result[u] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
u = result.length
|
||||
v = result[u - 1]
|
||||
while (u-- > 0) {
|
||||
result[u] = v
|
||||
v = p[v]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
} from './vdom'
|
||||
import { isObservable } from '@vue/observer'
|
||||
import { warn } from './warning'
|
||||
import { isString, isArray, isFunction, isObject } from '@vue/shared'
|
||||
|
||||
export const Fragment = Symbol()
|
||||
export const Portal = Symbol()
|
||||
@ -98,10 +99,7 @@ interface createElement extends VNodeFactories {
|
||||
}
|
||||
|
||||
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||
if (
|
||||
Array.isArray(data) ||
|
||||
(data != null && (typeof data !== 'object' || data._isVNode))
|
||||
) {
|
||||
if (isArray(data) || !isObject(data) || data._isVNode) {
|
||||
children = data
|
||||
data = null
|
||||
}
|
||||
@ -133,7 +131,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof tag === 'string') {
|
||||
if (isString(tag)) {
|
||||
// element
|
||||
return createElementVNode(
|
||||
tag,
|
||||
@ -160,10 +158,7 @@ export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||
ref
|
||||
)
|
||||
} else {
|
||||
if (
|
||||
__DEV__ &&
|
||||
(!tag || (typeof tag !== 'function' && typeof tag !== 'object'))
|
||||
) {
|
||||
if (__DEV__ && !isFunction(tag) && !isObject(tag)) {
|
||||
warn('Invalid component passed to h(): ', tag)
|
||||
}
|
||||
// component
|
||||
|
@ -10,21 +10,26 @@ export * from '@vue/observer'
|
||||
// Scheduler API
|
||||
export { nextTick } from '@vue/scheduler'
|
||||
|
||||
// Internal API
|
||||
export {
|
||||
createComponentInstance,
|
||||
createComponentClassFromOptions
|
||||
} from './componentUtils'
|
||||
|
||||
// Optional APIs
|
||||
// these are imported on-demand and can be tree-shaken
|
||||
export { applyDirectives } from './optional/directive'
|
||||
export { Provide, Inject } from './optional/context'
|
||||
export { createAsyncComponent } from './optional/asyncComponent'
|
||||
export { KeepAlive } from './optional/keepAlive'
|
||||
export { mixins } from './optional/mixin'
|
||||
|
||||
// flags & types
|
||||
export { ComponentType, ComponentClass, FunctionalComponent } from './component'
|
||||
export * from './componentOptions'
|
||||
export { VNodeFlags, ChildrenFlags } from './flags'
|
||||
export { VNode, Slots } from './vdom'
|
||||
|
||||
// Internal API, for libraries or renderers that need to perform low level work
|
||||
export {
|
||||
reservedMethods,
|
||||
resolveComponentOptionsFromClass,
|
||||
mergeComponentOptions
|
||||
} from './componentOptions'
|
||||
export {
|
||||
createComponentInstance,
|
||||
createComponentClassFromOptions
|
||||
} from './componentUtils'
|
||||
|
@ -2,6 +2,7 @@ import { ChildrenFlags } from '../flags'
|
||||
import { createComponentVNode, Slots } from '../vdom'
|
||||
import { Component, ComponentType, ComponentClass } from '../component'
|
||||
import { unwrap } from '@vue/observer'
|
||||
import { isFunction } from '@vue/shared'
|
||||
|
||||
interface AsyncComponentFactory {
|
||||
(): Promise<ComponentType>
|
||||
@ -21,7 +22,7 @@ type AsyncComponentOptions = AsyncComponentFactory | AsyncComponentFullOptions
|
||||
export function createAsyncComponent(
|
||||
options: AsyncComponentOptions
|
||||
): ComponentClass {
|
||||
if (typeof options === 'function') {
|
||||
if (isFunction(options)) {
|
||||
options = { factory: options }
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ return applyDirectives(
|
||||
|
||||
import { VNode, cloneVNode, VNodeData } from '../vdom'
|
||||
import { ComponentInstance } from '../component'
|
||||
import { EMPTY_OBJ } from '../utils'
|
||||
import { EMPTY_OBJ } from '@vue/shared'
|
||||
|
||||
interface DirectiveBinding {
|
||||
instance: ComponentInstance
|
||||
|
@ -2,6 +2,7 @@ import { Component, ComponentClass, ComponentInstance } from '../component'
|
||||
import { VNode, Slots, cloneVNode } from '../vdom'
|
||||
import { VNodeFlags } from '../flags'
|
||||
import { warn } from '../warning'
|
||||
import { isString, isArray } from '@vue/shared'
|
||||
|
||||
type MatchPattern = string | RegExp | string[] | RegExp[]
|
||||
|
||||
@ -119,9 +120,9 @@ function getName(comp: ComponentClass): string | void {
|
||||
}
|
||||
|
||||
function matches(pattern: MatchPattern, name: string): boolean {
|
||||
if (Array.isArray(pattern)) {
|
||||
if (isArray(pattern)) {
|
||||
return (pattern as any).some((p: string | RegExp) => matches(p, name))
|
||||
} else if (typeof pattern === 'string') {
|
||||
} else if (isString(pattern)) {
|
||||
return pattern.split(',').indexOf(name) > -1
|
||||
} else if (pattern.test) {
|
||||
return pattern.test(name)
|
||||
|
@ -1,4 +1,12 @@
|
||||
import { Component } from '../component'
|
||||
import { createComponentClassFromOptions } from '../componentUtils'
|
||||
import {
|
||||
ComponentOptions,
|
||||
resolveComponentOptionsFromClass,
|
||||
mergeComponentOptions
|
||||
} from '../componentOptions'
|
||||
import { normalizePropsOptions } from '../componentProps'
|
||||
import { isFunction } from '@vue/shared'
|
||||
|
||||
interface ComponentConstructor<This = Component> {
|
||||
new (): This
|
||||
@ -25,7 +33,19 @@ export function mixins<
|
||||
V = ExtractInstance<T>
|
||||
>(...args: T): ComponentConstructorWithMixins<V>
|
||||
export function mixins(...args: any[]): any {
|
||||
// TODO
|
||||
let options: ComponentOptions = {}
|
||||
args.forEach(mixin => {
|
||||
if (isFunction(mixin)) {
|
||||
options = mergeComponentOptions(
|
||||
options,
|
||||
resolveComponentOptionsFromClass(mixin)
|
||||
)
|
||||
} else {
|
||||
mixin.props = normalizePropsOptions(mixin.props)
|
||||
options = mergeComponentOptions(options, mixin)
|
||||
}
|
||||
})
|
||||
return createComponentClassFromOptions(options)
|
||||
}
|
||||
|
||||
/* Example usage
|
||||
|
@ -1,105 +0,0 @@
|
||||
export const EMPTY_OBJ: { readonly [key: string]: any } = Object.freeze({})
|
||||
|
||||
export const NOOP = () => {}
|
||||
|
||||
export const onRE = /^on/
|
||||
export const vnodeHookRE = /^vnode/
|
||||
export const handlersRE = /^on|^vnode/
|
||||
export const reservedPropRE = /^(?:key|ref|slots)$|^vnode/
|
||||
|
||||
export function normalizeStyle(
|
||||
value: any
|
||||
): Record<string, string | number> | void {
|
||||
if (Array.isArray(value)) {
|
||||
const res: Record<string, string | number> = {}
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const normalized = normalizeStyle(value[i])
|
||||
if (normalized) {
|
||||
for (const key in normalized) {
|
||||
res[key] = normalized[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
} else if (value && typeof value === 'object') {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeClass(value: any): string {
|
||||
let res = ''
|
||||
if (typeof value === 'string') {
|
||||
res = value
|
||||
} else if (Array.isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
res += normalizeClass(value[i]) + ' '
|
||||
}
|
||||
} else if (typeof value === 'object') {
|
||||
for (const name in value) {
|
||||
if (value[name]) {
|
||||
res += name + ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.trim()
|
||||
}
|
||||
|
||||
const camelizeRE = /-(\w)/g
|
||||
export const camelize = (str: string): string => {
|
||||
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
|
||||
}
|
||||
|
||||
const hyphenateRE = /\B([A-Z])/g
|
||||
export const hyphenate = (str: string): string => {
|
||||
return str.replace(hyphenateRE, '-$1').toLowerCase()
|
||||
}
|
||||
|
||||
export const capitalize = (str: string): string => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Longest_increasing_subsequence
|
||||
export function lis(arr: number[]): number[] {
|
||||
const p = arr.slice()
|
||||
const result = [0]
|
||||
let i
|
||||
let j
|
||||
let u
|
||||
let v
|
||||
let c
|
||||
const len = arr.length
|
||||
for (i = 0; i < len; i++) {
|
||||
const arrI = arr[i]
|
||||
if (arrI !== 0) {
|
||||
j = result[result.length - 1]
|
||||
if (arr[j] < arrI) {
|
||||
p[i] = j
|
||||
result.push(i)
|
||||
continue
|
||||
}
|
||||
u = 0
|
||||
v = result.length - 1
|
||||
while (u < v) {
|
||||
c = ((u + v) / 2) | 0
|
||||
if (arr[result[c]] < arrI) {
|
||||
u = c + 1
|
||||
} else {
|
||||
v = c
|
||||
}
|
||||
}
|
||||
if (arrI < arr[result[u]]) {
|
||||
if (u > 0) {
|
||||
p[i] = result[u - 1]
|
||||
}
|
||||
result[u] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
u = result.length
|
||||
v = result[u - 1]
|
||||
while (u-- > 0) {
|
||||
result[u] = v
|
||||
v = p[v]
|
||||
}
|
||||
return result
|
||||
}
|
@ -5,7 +5,14 @@ import {
|
||||
} from './component'
|
||||
import { VNodeFlags, ChildrenFlags } from './flags'
|
||||
import { createComponentClassFromOptions } from './componentUtils'
|
||||
import { normalizeClass, normalizeStyle, handlersRE, EMPTY_OBJ } from './utils'
|
||||
import {
|
||||
handlersRE,
|
||||
EMPTY_OBJ,
|
||||
isObject,
|
||||
isArray,
|
||||
isFunction,
|
||||
isString
|
||||
} from '@vue/shared'
|
||||
import { RawChildrenType, RawSlots } from './h'
|
||||
|
||||
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
|
||||
@ -98,15 +105,6 @@ export function createVNode(
|
||||
return vnode
|
||||
}
|
||||
|
||||
function normalizeClassAndStyle(data: VNodeData) {
|
||||
if (data.class != null) {
|
||||
data.class = normalizeClass(data.class)
|
||||
}
|
||||
if (data.style != null) {
|
||||
data.style = normalizeStyle(data.style)
|
||||
}
|
||||
}
|
||||
|
||||
export function createElementVNode(
|
||||
tag: string,
|
||||
data: VNodeData | null,
|
||||
@ -122,6 +120,50 @@ export function createElementVNode(
|
||||
return createVNode(flags, tag, data, children, childFlags, key, ref, null)
|
||||
}
|
||||
|
||||
function normalizeClassAndStyle(data: VNodeData) {
|
||||
if (data.class != null) {
|
||||
data.class = normalizeClass(data.class)
|
||||
}
|
||||
if (data.style != null) {
|
||||
data.style = normalizeStyle(data.style)
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeStyle(value: any): Record<string, string | number> | void {
|
||||
if (isArray(value)) {
|
||||
const res: Record<string, string | number> = {}
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const normalized = normalizeStyle(value[i])
|
||||
if (normalized) {
|
||||
for (const key in normalized) {
|
||||
res[key] = normalized[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
} else if (isObject(value)) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeClass(value: any): string {
|
||||
let res = ''
|
||||
if (isString(value)) {
|
||||
res = value
|
||||
} else if (isArray(value)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
res += normalizeClass(value[i]) + ' '
|
||||
}
|
||||
} else if (isObject(value)) {
|
||||
for (const name in value) {
|
||||
if (value[name]) {
|
||||
res += name + ' '
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.trim()
|
||||
}
|
||||
|
||||
export function createComponentVNode(
|
||||
comp: any,
|
||||
data: VNodeData | null,
|
||||
@ -134,8 +176,7 @@ export function createComponentVNode(
|
||||
let flags: VNodeFlags
|
||||
|
||||
// flags
|
||||
const compType = typeof comp
|
||||
if (compType === 'object') {
|
||||
if (isObject(comp)) {
|
||||
if (comp.functional) {
|
||||
// object literal functional
|
||||
flags = VNodeFlags.COMPONENT_FUNCTIONAL
|
||||
@ -155,7 +196,7 @@ export function createComponentVNode(
|
||||
}
|
||||
} else {
|
||||
// assumes comp is function here now
|
||||
if (__DEV__ && compType !== 'function') {
|
||||
if (__DEV__ && !isFunction(comp)) {
|
||||
// TODO warn invalid comp value in dev
|
||||
}
|
||||
if (comp.prototype && comp.prototype.render) {
|
||||
@ -178,14 +219,13 @@ export function createComponentVNode(
|
||||
? ChildrenFlags.DYNAMIC_SLOTS
|
||||
: ChildrenFlags.NO_CHILDREN
|
||||
if (children != null) {
|
||||
const childrenType = typeof children
|
||||
if (childrenType === 'function') {
|
||||
if (isFunction(children)) {
|
||||
// function as children
|
||||
slots = { default: children }
|
||||
} else if (Array.isArray(children) || (children as any)._isVNode) {
|
||||
} else if (isArray(children) || (children as any)._isVNode) {
|
||||
// direct vnode children
|
||||
slots = { default: () => children }
|
||||
} else if (typeof children === 'object') {
|
||||
} else if (isObject(children)) {
|
||||
// slot object as children
|
||||
slots = children
|
||||
}
|
||||
@ -313,7 +353,7 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
|
||||
|
||||
function normalizeChildren(vnode: VNode, children: any) {
|
||||
let childFlags
|
||||
if (Array.isArray(children)) {
|
||||
if (isArray(children)) {
|
||||
const { length } = children
|
||||
if (length === 0) {
|
||||
childFlags = ChildrenFlags.NO_CHILDREN
|
||||
@ -356,7 +396,7 @@ export function normalizeVNodes(
|
||||
newChild = createTextVNode('')
|
||||
} else if (child._isVNode) {
|
||||
newChild = child.el ? cloneVNode(child) : child
|
||||
} else if (Array.isArray(child)) {
|
||||
} else if (isArray(child)) {
|
||||
normalizeVNodes(child, newChildren, currentPrefix + i + '|')
|
||||
} else {
|
||||
newChild = createTextVNode(child + '')
|
||||
@ -386,7 +426,7 @@ function normalizeSlots(slots: { [name: string]: any }): Slots {
|
||||
function normalizeSlot(value: any): VNode[] {
|
||||
if (value == null) {
|
||||
return [createTextVNode('')]
|
||||
} else if (Array.isArray(value)) {
|
||||
} else if (isArray(value)) {
|
||||
return normalizeVNodes(value)
|
||||
} else if (value._isVNode) {
|
||||
return [value]
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ComponentType, ComponentClass, FunctionalComponent } from './component'
|
||||
import { EMPTY_OBJ } from './utils'
|
||||
import { EMPTY_OBJ, isString } from '@vue/shared'
|
||||
import { VNode } from './vdom'
|
||||
import { Data } from './componentOptions'
|
||||
|
||||
@ -119,7 +119,7 @@ function formatProps(props: Data) {
|
||||
const res = []
|
||||
for (const key in props) {
|
||||
const value = props[key]
|
||||
if (typeof value === 'string') {
|
||||
if (isString(value)) {
|
||||
res.push(`${key}=${JSON.stringify(value)}`)
|
||||
} else {
|
||||
res.push(`${key}=`, value)
|
||||
|
@ -2,6 +2,7 @@ import { observable, immutable, unwrap } from './index'
|
||||
import { OperationTypes } from './operations'
|
||||
import { track, trigger } from './autorun'
|
||||
import { LOCKED } from './lock'
|
||||
import { isObject } from '@vue/shared'
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
|
||||
@ -18,7 +19,7 @@ function createGetter(isImmutable: boolean) {
|
||||
return res
|
||||
}
|
||||
track(target, OperationTypes.GET, key)
|
||||
return res !== null && typeof res === 'object'
|
||||
return isObject(res)
|
||||
? isImmutable
|
||||
? // need to lazy access immutable and observable here to avoid
|
||||
// circular dependency
|
||||
|
@ -2,8 +2,8 @@ import { unwrap, observable, immutable } from './index'
|
||||
import { track, trigger } from './autorun'
|
||||
import { OperationTypes } from './operations'
|
||||
import { LOCKED } from './lock'
|
||||
import { isObject } from '@vue/shared'
|
||||
|
||||
const isObject = (value: any) => value !== null && typeof value === 'object'
|
||||
const toObservable = (value: any) =>
|
||||
isObject(value) ? observable(value) : value
|
||||
const toImmutable = (value: any) => (isObject(value) ? immutable(value) : value)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { isObject, EMPTY_OBJ } from '@vue/shared'
|
||||
import { mutableHandlers, immutableHandlers } from './baseHandlers'
|
||||
|
||||
import {
|
||||
@ -28,7 +29,6 @@ export { OperationTypes } from './operations'
|
||||
export { computed, ComputedGetter } from './computed'
|
||||
export { lock, unlock } from './lock'
|
||||
|
||||
const EMPTY_OBJ = {}
|
||||
const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
|
||||
const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
|
||||
|
||||
@ -83,7 +83,7 @@ function createObservable(
|
||||
baseHandlers: ProxyHandler<any>,
|
||||
collectionHandlers: ProxyHandler<any>
|
||||
) {
|
||||
if (target === null || typeof target !== 'object') {
|
||||
if (!isObject(target)) {
|
||||
if (__DEV__) {
|
||||
console.warn(`value is not observable: ${String(target)}`)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createRenderer, VNode, ComponentInstance } from '@vue/core'
|
||||
import { createRenderer, VNode, Component } from '@vue/core'
|
||||
import { nodeOps } from './nodeOps'
|
||||
import { patchData } from './patchData'
|
||||
import { teardownVNode } from './teardownVNode'
|
||||
@ -12,7 +12,7 @@ const { render: _render } = createRenderer({
|
||||
type publicRender = (
|
||||
node: VNode | null,
|
||||
container: HTMLElement
|
||||
) => ComponentInstance | null
|
||||
) => Component | null
|
||||
export const render = _render as publicRender
|
||||
|
||||
// re-export everything from core
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { isString } from '@vue/shared'
|
||||
|
||||
// style properties that should NOT have "px" added when numeric
|
||||
const nonNumericRE = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i
|
||||
|
||||
@ -5,7 +7,7 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
|
||||
const { style } = el
|
||||
if (!next) {
|
||||
el.removeAttribute('style')
|
||||
} else if (typeof next === 'string') {
|
||||
} else if (isString(next)) {
|
||||
style.cssText = next
|
||||
} else {
|
||||
for (const key in next) {
|
||||
@ -15,7 +17,7 @@ export function patchStyle(el: any, prev: any, next: any, data: any) {
|
||||
}
|
||||
style[key] = value
|
||||
}
|
||||
if (prev && typeof prev !== 'string') {
|
||||
if (prev && !isString(prev)) {
|
||||
for (const key in prev) {
|
||||
if (!next[key]) {
|
||||
style[key] = ''
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createRenderer, VNode, ComponentInstance } from '@vue/core'
|
||||
import { createRenderer, VNode, Component } from '@vue/core'
|
||||
import { nodeOps, TestElement } from './nodeOps'
|
||||
import { patchData } from './patchData'
|
||||
|
||||
@ -10,7 +10,7 @@ const { render: _render } = createRenderer({
|
||||
type publicRender = (
|
||||
node: VNode | null,
|
||||
container: TestElement
|
||||
) => ComponentInstance | null
|
||||
) => Component | null
|
||||
export const render = _render as publicRender
|
||||
|
||||
export { serialize } from './serialize'
|
||||
|
1
packages/shared/README.md
Normal file
1
packages/shared/README.md
Normal file
@ -0,0 +1 @@
|
||||
# @vue/shared
|
4
packages/shared/package.json
Normal file
4
packages/shared/package.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "@vue/shared",
|
||||
"private": true
|
||||
}
|
29
packages/shared/src/index.ts
Normal file
29
packages/shared/src/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export const EMPTY_OBJ: { readonly [key: string]: any } = Object.freeze({})
|
||||
|
||||
export const NOOP = () => {}
|
||||
|
||||
export const onRE = /^on/
|
||||
export const vnodeHookRE = /^vnode/
|
||||
export const handlersRE = /^on|^vnode/
|
||||
export const reservedPropRE = /^(?:key|ref|slots)$|^vnode/
|
||||
|
||||
export const isArray = Array.isArray
|
||||
export const isFunction = (val: any): val is Function =>
|
||||
typeof val === 'function'
|
||||
export const isString = (val: any): val is string => typeof val === 'string'
|
||||
export const isObject = (val: any): val is Record<any, any> =>
|
||||
val !== null && typeof val === 'object'
|
||||
|
||||
const camelizeRE = /-(\w)/g
|
||||
export const camelize = (str: string): string => {
|
||||
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
|
||||
}
|
||||
|
||||
const hyphenateRE = /\B([A-Z])/g
|
||||
export const hyphenate = (str: string): string => {
|
||||
return str.replace(hyphenateRE, '-$1').toLowerCase()
|
||||
}
|
||||
|
||||
export const capitalize = (str: string): string => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
@ -16,7 +16,7 @@ class Vue {
|
||||
// convert it to a class
|
||||
const Component = createComponentClassFromOptions(options || {})
|
||||
const vnode = h(Component)
|
||||
const instance = createComponentInstance(vnode, Component, null)
|
||||
const instance = createComponentInstance(vnode, Component)
|
||||
|
||||
function mount(el: any) {
|
||||
const dom = typeof el === 'string' ? document.querySelector(el) : el
|
||||
|
@ -95,12 +95,13 @@ function createConfig(output, plugins = []) {
|
||||
// during a single build.
|
||||
hasTSChecked = true
|
||||
|
||||
const externals = Object.keys(aliasOptions).filter(p => p !== '@vue/shared')
|
||||
|
||||
return {
|
||||
input: resolve(`src/index.ts`),
|
||||
// Global and Browser ESM builds inlines everything so that they can be
|
||||
// used alone.
|
||||
external:
|
||||
isGlobalBuild || isBrowserESMBuild ? [] : Object.keys(aliasOptions),
|
||||
external: isGlobalBuild || isBrowserESMBuild ? [] : externals,
|
||||
plugins: [
|
||||
tsPlugin,
|
||||
aliasPlugin,
|
||||
|
@ -1,8 +1,8 @@
|
||||
const fs = require('fs')
|
||||
|
||||
const targets = exports.targets = fs.readdirSync('packages').filter(f => {
|
||||
return fs.statSync(`packages/${f}`).isDirectory()
|
||||
})
|
||||
const targets = (exports.targets = fs.readdirSync('packages').filter(f => {
|
||||
return f !== 'shared' && fs.statSync(`packages/${f}`).isDirectory()
|
||||
}))
|
||||
|
||||
exports.fuzzyMatchTarget = partialTarget => {
|
||||
const matched = []
|
||||
|
@ -19,6 +19,7 @@
|
||||
],
|
||||
"rootDir": ".",
|
||||
"paths": {
|
||||
"@vue/shared": ["packages/shared/src"],
|
||||
"@vue/core": ["packages/core/src"],
|
||||
"@vue/observer": ["packages/observer/src"],
|
||||
"@vue/scheduler": ["packages/scheduler/src"],
|
||||
|
Loading…
Reference in New Issue
Block a user