feat: provide/inject
This commit is contained in:
parent
1e447d021b
commit
7484b4d2e6
@ -14,7 +14,7 @@ import { ErrorTypes } from './errorHandling'
|
|||||||
|
|
||||||
type Flatten<T> = { [K in keyof T]: T[K] }
|
type Flatten<T> = { [K in keyof T]: T[K] }
|
||||||
|
|
||||||
export interface ComponentClass extends Flatten<typeof Component> {
|
export interface ComponentClass extends Flatten<typeof InternalComponent> {
|
||||||
new <D = Data, P = Data>(): MountedComponent<D, P> & D & P
|
new <D = Data, P = Data>(): MountedComponent<D, P> & D & P
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,8 @@ export interface FunctionalComponent<P = Data> extends RenderFunction<P> {
|
|||||||
|
|
||||||
// this interface is merged with the class type
|
// this interface is merged with the class type
|
||||||
// to represent a mounted component
|
// to represent a mounted component
|
||||||
export interface MountedComponent<D = Data, P = Data> extends Component {
|
export interface MountedComponent<D = Data, P = Data>
|
||||||
|
extends InternalComponent {
|
||||||
$vnode: VNode
|
$vnode: VNode
|
||||||
$data: D
|
$data: D
|
||||||
$props: P
|
$props: P
|
||||||
@ -60,7 +61,7 @@ export interface MountedComponent<D = Data, P = Data> extends Component {
|
|||||||
_self: MountedComponent<D, P> // on proxies only
|
_self: MountedComponent<D, P> // on proxies only
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Component {
|
class InternalComponent {
|
||||||
public static options?: ComponentOptions
|
public static options?: ComponentOptions
|
||||||
|
|
||||||
public get $el(): RenderNode | RenderFragment | null {
|
public get $el(): RenderNode | RenderFragment | null {
|
||||||
@ -108,14 +109,14 @@ export class Component {
|
|||||||
$watch(
|
$watch(
|
||||||
this: MountedComponent,
|
this: MountedComponent,
|
||||||
keyOrFn: string | (() => any),
|
keyOrFn: string | (() => any),
|
||||||
cb: () => void,
|
cb: (newValue: any, oldValue: any) => void,
|
||||||
options?: WatchOptions
|
options?: WatchOptions
|
||||||
) {
|
) {
|
||||||
return setupWatcher(this, keyOrFn, cb, options)
|
return setupWatcher(this, keyOrFn, cb, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eventEmitter interface
|
// eventEmitter interface
|
||||||
$on(event: string, fn: Function): Component {
|
$on(this: MountedComponent, event: string, fn: Function): MountedComponent {
|
||||||
if (Array.isArray(event)) {
|
if (Array.isArray(event)) {
|
||||||
for (let i = 0; i < event.length; i++) {
|
for (let i = 0; i < event.length; i++) {
|
||||||
this.$on(event[i], fn)
|
this.$on(event[i], fn)
|
||||||
@ -127,7 +128,7 @@ export class Component {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
$once(event: string, fn: Function): Component {
|
$once(this: MountedComponent, event: string, fn: Function): MountedComponent {
|
||||||
const onceFn = (...args: any[]) => {
|
const onceFn = (...args: any[]) => {
|
||||||
this.$off(event, onceFn)
|
this.$off(event, onceFn)
|
||||||
fn.apply(this, args)
|
fn.apply(this, args)
|
||||||
@ -136,7 +137,11 @@ export class Component {
|
|||||||
return this.$on(event, onceFn)
|
return this.$on(event, onceFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
$off(event?: string, fn?: Function) {
|
$off(
|
||||||
|
this: MountedComponent,
|
||||||
|
event?: string,
|
||||||
|
fn?: Function
|
||||||
|
): MountedComponent {
|
||||||
if (this._events) {
|
if (this._events) {
|
||||||
if (!event && !fn) {
|
if (!event && !fn) {
|
||||||
this._events = null
|
this._events = null
|
||||||
@ -162,7 +167,11 @@ export class Component {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
$emit(this: MountedComponent, name: string, ...payload: any[]) {
|
$emit(
|
||||||
|
this: MountedComponent,
|
||||||
|
name: string,
|
||||||
|
...payload: any[]
|
||||||
|
): MountedComponent {
|
||||||
const parentListener =
|
const parentListener =
|
||||||
this.$props['on' + name] || this.$props['on' + name.toLowerCase()]
|
this.$props['on' + name] || this.$props['on' + name.toLowerCase()]
|
||||||
if (parentListener) {
|
if (parentListener) {
|
||||||
@ -188,3 +197,7 @@ function invokeListeners(value: Function | Function[], payload: any[]) {
|
|||||||
value(...payload)
|
value(...payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the exported Component has the implementation details of the actual
|
||||||
|
// InternalComponent class but with proper type inference of ComponentClass.
|
||||||
|
export const Component = InternalComponent as ComponentClass
|
||||||
|
@ -103,7 +103,16 @@ export function normalizeComponentRoot(
|
|||||||
} else if (typeof vnode !== 'object') {
|
} else if (typeof vnode !== 'object') {
|
||||||
vnode = createTextVNode(vnode + '')
|
vnode = createTextVNode(vnode + '')
|
||||||
} else if (Array.isArray(vnode)) {
|
} else if (Array.isArray(vnode)) {
|
||||||
vnode = createFragment(vnode)
|
if (vnode.length === 1) {
|
||||||
|
vnode = normalizeComponentRoot(
|
||||||
|
vnode[0],
|
||||||
|
componentVNode,
|
||||||
|
attrs,
|
||||||
|
inheritAttrs
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vnode = createFragment(vnode)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const { flags } = vnode
|
const { flags } = vnode
|
||||||
if (
|
if (
|
||||||
|
57
packages/core/src/context.ts
Normal file
57
packages/core/src/context.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { observable } from '@vue/observer'
|
||||||
|
import { Component } from './component'
|
||||||
|
import { Slots } from './vdom'
|
||||||
|
|
||||||
|
const contextStore = observable() as Record<string, any>
|
||||||
|
|
||||||
|
export class Provide extends Component {
|
||||||
|
updateValue() {
|
||||||
|
contextStore[this.$props.id] = this.$props.value
|
||||||
|
}
|
||||||
|
created() {
|
||||||
|
if (__DEV__) {
|
||||||
|
if (contextStore.hasOwnProperty(this.$props.id)) {
|
||||||
|
console.warn(
|
||||||
|
`A context provider with id ${this.$props.id} already exists.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.$watch(
|
||||||
|
() => this.$props.id,
|
||||||
|
(id: string, oldId: string) => {
|
||||||
|
console.warn(
|
||||||
|
`Context provider id change detected (from "${oldId}" to "${id}"). ` +
|
||||||
|
`This is not supported and should be avoided.`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ sync: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.updateValue()
|
||||||
|
}
|
||||||
|
beforeUpdate() {
|
||||||
|
this.updateValue()
|
||||||
|
}
|
||||||
|
render(_: any, slots: Slots) {
|
||||||
|
return slots.default && slots.default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__DEV__) {
|
||||||
|
Provide.options = {
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Inject extends Component {
|
||||||
|
render(props: any, slots: Slots) {
|
||||||
|
return slots.default && slots.default(contextStore[props.id])
|
||||||
|
}
|
||||||
|
}
|
@ -32,7 +32,10 @@ export interface createElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
|
||||||
if (Array.isArray(data) || (data !== void 0 && typeof data !== 'object')) {
|
if (
|
||||||
|
Array.isArray(data) ||
|
||||||
|
(data != null && (typeof data !== 'object' || data._isVNode))
|
||||||
|
) {
|
||||||
children = data
|
children = data
|
||||||
data = null
|
data = null
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,7 @@
|
|||||||
export { h, Fragment, Portal } from './h'
|
export { h, Fragment, Portal } from './h'
|
||||||
export { cloneVNode, createPortal, createFragment } from './vdom'
|
export { cloneVNode, createPortal, createFragment } from './vdom'
|
||||||
export { createRenderer } from './createRenderer'
|
export { createRenderer } from './createRenderer'
|
||||||
|
export { Component } from './component'
|
||||||
import { Component as InternalComponent, ComponentClass } from './component'
|
|
||||||
// the public component constructor with proper type inference.
|
|
||||||
export const Component = InternalComponent as ComponentClass
|
|
||||||
|
|
||||||
// observer api
|
// observer api
|
||||||
export * from '@vue/observer'
|
export * from '@vue/observer'
|
||||||
@ -15,7 +12,10 @@ export { nextTick } from '@vue/scheduler'
|
|||||||
|
|
||||||
// internal api
|
// internal api
|
||||||
export { createComponentInstance } from './componentUtils'
|
export { createComponentInstance } from './componentUtils'
|
||||||
|
|
||||||
|
// import-on-demand apis
|
||||||
export { applyDirective } from './directive'
|
export { applyDirective } from './directive'
|
||||||
|
export { Provide, Inject } from './context'
|
||||||
|
|
||||||
// flags & types
|
// flags & types
|
||||||
export { ComponentClass, FunctionalComponent } from './component'
|
export { ComponentClass, FunctionalComponent } from './component'
|
||||||
|
@ -169,7 +169,7 @@ export function createComponentVNode(
|
|||||||
|
|
||||||
// slots
|
// slots
|
||||||
let slots: any
|
let slots: any
|
||||||
if (childFlags == null) {
|
if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
|
||||||
childFlags = children
|
childFlags = children
|
||||||
? ChildrenFlags.DYNAMIC_SLOTS
|
? ChildrenFlags.DYNAMIC_SLOTS
|
||||||
: ChildrenFlags.NO_CHILDREN
|
: ChildrenFlags.NO_CHILDREN
|
||||||
@ -178,11 +178,12 @@ export function createComponentVNode(
|
|||||||
if (childrenType === 'function') {
|
if (childrenType === 'function') {
|
||||||
// function as children
|
// function as children
|
||||||
slots = { default: children }
|
slots = { default: children }
|
||||||
} else if (childrenType === 'object' && !(children as VNode)._isVNode) {
|
} else if (Array.isArray(children) || (children as VNode)._isVNode) {
|
||||||
|
// direct vnode children
|
||||||
|
slots = { default: () => children }
|
||||||
|
} else if (typeof children === 'object') {
|
||||||
// slot object as children
|
// slot object as children
|
||||||
slots = children
|
slots = children
|
||||||
} else {
|
|
||||||
slots = { default: () => children }
|
|
||||||
}
|
}
|
||||||
slots = normalizeSlots(slots)
|
slots = normalizeSlots(slots)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user