| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- import { Component } from 'types/component'
- import { PropOptions } from 'types/options'
- import { popTarget, pushTarget } from '../core/observer/dep'
- import { def, invokeWithErrorHandling, isReserved, warn } from '../core/util'
- import VNode from '../core/vdom/vnode'
- import {
- bind,
- emptyObject,
- isArray,
- isFunction,
- isObject
- } from '../shared/util'
- import { currentInstance, setCurrentInstance } from './currentInstance'
- import { shallowReactive } from './reactivity/reactive'
- import { isRef } from './reactivity/ref'
- /**
- * @internal
- */
- export interface SetupContext {
- attrs: Record<string, any>
- slots: Record<string, () => VNode[]>
- emit: (event: string, ...args: any[]) => any
- expose: (exposed: Record<string, any>) => void
- }
- export function initSetup(vm: Component) {
- const options = vm.$options
- const setup = options.setup
- if (setup) {
- const ctx = (vm._setupContext = createSetupContext(vm))
- setCurrentInstance(vm)
- pushTarget()
- const setupResult = invokeWithErrorHandling(
- setup,
- null,
- [vm._props || shallowReactive({}), ctx],
- vm,
- `setup`
- )
- popTarget()
- setCurrentInstance()
- if (isFunction(setupResult)) {
- // render function
- // @ts-ignore
- options.render = setupResult
- } else if (isObject(setupResult)) {
- // bindings
- if (__DEV__ && setupResult instanceof VNode) {
- warn(
- `setup() should not return VNodes directly - ` +
- `return a render function instead.`
- )
- }
- vm._setupState = setupResult
- // __sfc indicates compiled bindings from <script setup>
- if (!setupResult.__sfc) {
- for (const key in setupResult) {
- if (!isReserved(key)) {
- proxyWithRefUnwrap(vm, setupResult, key)
- } else if (__DEV__) {
- warn(`Avoid using variables that start with _ or $ in setup().`)
- }
- }
- } else {
- // exposed for compiled render fn
- const proxy = (vm._setupProxy = {})
- for (const key in setupResult) {
- proxyWithRefUnwrap(proxy, setupResult, key)
- }
- }
- } else if (__DEV__ && setupResult !== undefined) {
- warn(
- `setup() should return an object. Received: ${
- setupResult === null ? 'null' : typeof setupResult
- }`
- )
- }
- }
- }
- export function proxyWithRefUnwrap(
- target: any,
- source: Record<string, any>,
- key: string
- ) {
- Object.defineProperty(target, key, {
- enumerable: true,
- configurable: true,
- get: () => {
- const raw = source[key]
- return isRef(raw) ? raw.value : raw
- },
- set: newVal => {
- const raw = source[key]
- isRef(raw) ? (raw.value = newVal) : (source[key] = newVal)
- }
- })
- }
- function createSetupContext(vm: Component): SetupContext {
- let exposeCalled = false
- return {
- get attrs() {
- return initAttrsProxy(vm)
- },
- get slots() {
- return initSlotsProxy(vm)
- },
- emit: bind(vm.$emit, vm) as any,
- expose(exposed?: Record<string, any>) {
- if (__DEV__) {
- if (exposeCalled) {
- warn(`expose() should be called only once per setup().`, vm)
- }
- exposeCalled = true
- }
- if (exposed) {
- Object.keys(exposed).forEach(key =>
- proxyWithRefUnwrap(vm, exposed, key)
- )
- }
- }
- }
- }
- function initAttrsProxy(vm: Component) {
- if (!vm._attrsProxy) {
- const proxy = (vm._attrsProxy = {})
- def(proxy, '_v_attr_proxy', true)
- syncSetupAttrs(proxy, vm.$attrs, emptyObject, vm)
- }
- return vm._attrsProxy
- }
- export function syncSetupAttrs(
- to: any,
- from: any,
- prev: any,
- instance: Component
- ) {
- let changed = false
- for (const key in from) {
- if (!(key in to)) {
- changed = true
- defineProxyAttr(to, key, instance)
- } else if (from[key] !== prev[key]) {
- changed = true
- }
- }
- for (const key in to) {
- if (!(key in from)) {
- changed = true
- delete to[key]
- }
- }
- return changed
- }
- function defineProxyAttr(proxy: any, key: string, instance: Component) {
- Object.defineProperty(proxy, key, {
- enumerable: true,
- configurable: true,
- get() {
- return instance.$attrs[key]
- }
- })
- }
- function initSlotsProxy(vm: Component) {
- if (!vm._slotsProxy) {
- syncSetupSlots((vm._slotsProxy = {}), vm.$scopedSlots)
- }
- return vm._slotsProxy
- }
- export function syncSetupSlots(to: any, from: any) {
- for (const key in from) {
- to[key] = from[key]
- }
- for (const key in to) {
- if (!(key in from)) {
- delete to[key]
- }
- }
- }
- /**
- * @internal use manual type def
- */
- export function useSlots(): SetupContext['slots'] {
- return getContext().slots
- }
- /**
- * @internal use manual type def
- */
- export function useAttrs(): SetupContext['attrs'] {
- return getContext().attrs
- }
- function getContext(): SetupContext {
- if (__DEV__ && !currentInstance) {
- warn(`useContext() called without active instance.`)
- }
- const vm = currentInstance!
- return vm._setupContext || (vm._setupContext = createSetupContext(vm))
- }
- /**
- * Runtime helper for merging default declarations. Imported by compiled code
- * only.
- * @internal
- */
- export function mergeDefaults(
- raw: string[] | Record<string, PropOptions>,
- defaults: Record<string, any>
- ): Record<string, PropOptions> {
- const props = isArray(raw)
- ? raw.reduce(
- (normalized, p) => ((normalized[p] = {}), normalized),
- {} as Record<string, PropOptions>
- )
- : raw
- for (const key in defaults) {
- const opt = props[key]
- if (opt) {
- if (isArray(opt) || isFunction(opt)) {
- props[key] = { type: opt, default: defaults[key] }
- } else {
- opt.default = defaults[key]
- }
- } else if (opt === null) {
- props[key] = { default: defaults[key] }
- } else if (__DEV__) {
- warn(`props default key "${key}" has no corresponding declaration.`)
- }
- }
- return props
- }
|