2019-09-07 00:58:31 +08:00
|
|
|
import {
|
|
|
|
ComponentInternalInstance,
|
|
|
|
FunctionalComponent,
|
|
|
|
Data
|
|
|
|
} from './component'
|
2019-10-26 00:12:17 +08:00
|
|
|
import {
|
|
|
|
VNode,
|
|
|
|
normalizeVNode,
|
|
|
|
createVNode,
|
|
|
|
Comment,
|
|
|
|
cloneVNode
|
|
|
|
} from './vnode'
|
2019-09-06 23:25:11 +08:00
|
|
|
import { ShapeFlags } from './shapeFlags'
|
2019-09-07 00:58:31 +08:00
|
|
|
import { handleError, ErrorCodes } from './errorHandling'
|
2019-10-26 00:12:17 +08:00
|
|
|
import { PatchFlags, EMPTY_OBJ } from '@vue/shared'
|
|
|
|
import { warn } from './warning'
|
2019-09-06 23:25:11 +08:00
|
|
|
|
|
|
|
// mark the current rendering instance for asset resolution (e.g.
|
|
|
|
// resolveComponent, resolveDirective) during render
|
2019-09-07 00:58:31 +08:00
|
|
|
export let currentRenderingInstance: ComponentInternalInstance | null = null
|
2019-09-06 23:25:11 +08:00
|
|
|
|
2020-02-06 12:07:23 +08:00
|
|
|
// exposed for server-renderer only
|
|
|
|
export function setCurrentRenderingInstance(
|
|
|
|
instance: ComponentInternalInstance | null
|
|
|
|
) {
|
|
|
|
currentRenderingInstance = instance
|
|
|
|
}
|
|
|
|
|
2019-10-26 00:12:17 +08:00
|
|
|
// dev only flag to track whether $attrs was used during render.
|
|
|
|
// If $attrs was used during render then the warning for failed attrs
|
|
|
|
// fallthrough can be suppressed.
|
|
|
|
let accessedAttrs: boolean = false
|
|
|
|
|
|
|
|
export function markAttrsAccessed() {
|
|
|
|
accessedAttrs = true
|
|
|
|
}
|
|
|
|
|
2019-09-07 00:58:31 +08:00
|
|
|
export function renderComponentRoot(
|
|
|
|
instance: ComponentInternalInstance
|
|
|
|
): VNode {
|
2019-09-06 23:25:11 +08:00
|
|
|
const {
|
|
|
|
type: Component,
|
|
|
|
vnode,
|
2019-12-11 00:14:29 +08:00
|
|
|
proxy,
|
|
|
|
withProxy,
|
2019-09-06 23:25:11 +08:00
|
|
|
props,
|
|
|
|
slots,
|
|
|
|
attrs,
|
2020-02-11 02:15:36 +08:00
|
|
|
vnodeHooks,
|
2020-02-11 06:29:12 +08:00
|
|
|
emit,
|
|
|
|
renderCache
|
2019-09-06 23:25:11 +08:00
|
|
|
} = instance
|
|
|
|
|
|
|
|
let result
|
|
|
|
currentRenderingInstance = instance
|
2019-10-26 00:12:17 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
accessedAttrs = false
|
|
|
|
}
|
2019-09-06 23:25:11 +08:00
|
|
|
try {
|
|
|
|
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
2020-02-11 06:29:12 +08:00
|
|
|
result = normalizeVNode(
|
|
|
|
instance.render!.call(withProxy || proxy, proxy, renderCache)
|
|
|
|
)
|
2019-09-06 23:25:11 +08:00
|
|
|
} else {
|
|
|
|
// functional
|
|
|
|
const render = Component as FunctionalComponent
|
|
|
|
result = normalizeVNode(
|
|
|
|
render.length > 1
|
2019-12-03 03:11:12 +08:00
|
|
|
? render(props, {
|
2019-09-06 23:25:11 +08:00
|
|
|
attrs,
|
|
|
|
slots,
|
|
|
|
emit
|
|
|
|
})
|
2019-12-03 03:11:12 +08:00
|
|
|
: render(props, null as any /* we know it doesn't need it */)
|
2019-09-06 23:25:11 +08:00
|
|
|
)
|
|
|
|
}
|
2019-10-26 00:12:17 +08:00
|
|
|
|
|
|
|
// attr merging
|
|
|
|
if (
|
|
|
|
Component.props != null &&
|
|
|
|
Component.inheritAttrs !== false &&
|
|
|
|
attrs !== EMPTY_OBJ &&
|
|
|
|
Object.keys(attrs).length
|
|
|
|
) {
|
|
|
|
if (
|
|
|
|
result.shapeFlag & ShapeFlags.ELEMENT ||
|
|
|
|
result.shapeFlag & ShapeFlags.COMPONENT
|
|
|
|
) {
|
|
|
|
result = cloneVNode(result, attrs)
|
2019-12-03 04:17:30 +08:00
|
|
|
} else if (__DEV__ && !accessedAttrs && result.type !== Comment) {
|
2019-10-26 00:12:17 +08:00
|
|
|
warn(
|
|
|
|
`Extraneous non-props attributes (${Object.keys(attrs).join(',')}) ` +
|
2019-11-26 16:27:51 +08:00
|
|
|
`were passed to component but could not be automatically inherited ` +
|
2019-10-26 00:12:17 +08:00
|
|
|
`because component renders fragment or text root nodes.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2019-11-21 10:56:17 +08:00
|
|
|
|
2020-02-11 02:15:36 +08:00
|
|
|
// inherit vnode hooks
|
|
|
|
if (vnodeHooks !== EMPTY_OBJ) {
|
|
|
|
result = cloneVNode(result, vnodeHooks)
|
|
|
|
}
|
|
|
|
// inherit directives
|
|
|
|
if (vnode.dirs != null) {
|
|
|
|
if (__DEV__ && !isElementRoot(result)) {
|
|
|
|
warn(
|
|
|
|
`Runtime directive used on component with non-element root node. ` +
|
|
|
|
`The directives will not function as intended.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
result.dirs = vnode.dirs
|
|
|
|
}
|
2019-11-21 10:56:17 +08:00
|
|
|
// inherit transition data
|
|
|
|
if (vnode.transition != null) {
|
2020-02-11 02:15:36 +08:00
|
|
|
if (__DEV__ && !isElementRoot(result)) {
|
2019-11-25 23:04:00 +08:00
|
|
|
warn(
|
|
|
|
`Component inside <Transition> renders non-element root node ` +
|
|
|
|
`that cannot be animated.`
|
|
|
|
)
|
|
|
|
}
|
2019-11-21 10:56:17 +08:00
|
|
|
result.transition = vnode.transition
|
|
|
|
}
|
2019-09-06 23:25:11 +08:00
|
|
|
} catch (err) {
|
2019-09-07 00:58:31 +08:00
|
|
|
handleError(err, instance, ErrorCodes.RENDER_FUNCTION)
|
2019-09-25 02:37:14 +08:00
|
|
|
result = createVNode(Comment)
|
2019-09-06 23:25:11 +08:00
|
|
|
}
|
|
|
|
currentRenderingInstance = null
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-02-11 02:15:36 +08:00
|
|
|
function isElementRoot(vnode: VNode) {
|
|
|
|
return (
|
|
|
|
vnode.shapeFlag & ShapeFlags.COMPONENT ||
|
|
|
|
vnode.shapeFlag & ShapeFlags.ELEMENT ||
|
|
|
|
vnode.type === Comment // potential v-if branch switch
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-09-06 23:25:11 +08:00
|
|
|
export function shouldUpdateComponent(
|
|
|
|
prevVNode: VNode,
|
|
|
|
nextVNode: VNode,
|
2019-12-13 07:13:59 +08:00
|
|
|
parentComponent: ComponentInternalInstance | null,
|
2019-09-06 23:25:11 +08:00
|
|
|
optimized?: boolean
|
|
|
|
): boolean {
|
|
|
|
const { props: prevProps, children: prevChildren } = prevVNode
|
|
|
|
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
|
2019-12-13 07:13:59 +08:00
|
|
|
|
|
|
|
// Parent component's render function was hot-updated. Since this may have
|
|
|
|
// caused the child component's slots content to have changed, we need to
|
|
|
|
// force the child to update as well.
|
|
|
|
if (
|
|
|
|
__BUNDLER__ &&
|
|
|
|
__DEV__ &&
|
|
|
|
(prevChildren || nextChildren) &&
|
|
|
|
parentComponent &&
|
|
|
|
parentComponent.renderUpdated
|
|
|
|
) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-02-11 02:15:36 +08:00
|
|
|
// force child update on runtime directive usage on component vnode.
|
|
|
|
if (nextVNode.dirs != null) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-10-04 03:09:09 +08:00
|
|
|
if (patchFlag > 0) {
|
2019-09-06 23:25:11 +08:00
|
|
|
if (patchFlag & PatchFlags.DYNAMIC_SLOTS) {
|
|
|
|
// slot content that references values that might have changed,
|
|
|
|
// e.g. in a v-for
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (patchFlag & PatchFlags.FULL_PROPS) {
|
|
|
|
// presence of this flag indicates props are always non-null
|
2019-10-05 22:09:34 +08:00
|
|
|
return hasPropsChanged(prevProps!, nextProps!)
|
2020-02-14 06:27:52 +08:00
|
|
|
} else {
|
|
|
|
if (patchFlag & PatchFlags.CLASS) {
|
|
|
|
return prevProps!.class === nextProps!.class
|
|
|
|
}
|
|
|
|
if (patchFlag & PatchFlags.STYLE) {
|
|
|
|
return hasPropsChanged(prevProps!.style, nextProps!.style)
|
|
|
|
}
|
|
|
|
if (patchFlag & PatchFlags.PROPS) {
|
|
|
|
const dynamicProps = nextVNode.dynamicProps!
|
|
|
|
for (let i = 0; i < dynamicProps.length; i++) {
|
|
|
|
const key = dynamicProps[i]
|
|
|
|
if (nextProps![key] !== prevProps![key]) {
|
|
|
|
return true
|
|
|
|
}
|
2019-09-06 23:25:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!optimized) {
|
|
|
|
// this path is only taken by manually written render functions
|
|
|
|
// so presence of any children leads to a forced update
|
|
|
|
if (prevChildren != null || nextChildren != null) {
|
2019-11-26 23:03:36 +08:00
|
|
|
if (nextChildren == null || !(nextChildren as any).$stable) {
|
|
|
|
return true
|
|
|
|
}
|
2019-09-06 23:25:11 +08:00
|
|
|
}
|
|
|
|
if (prevProps === nextProps) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if (prevProps === null) {
|
|
|
|
return nextProps !== null
|
|
|
|
}
|
|
|
|
if (nextProps === null) {
|
2019-10-12 23:00:29 +08:00
|
|
|
return true
|
2019-09-06 23:25:11 +08:00
|
|
|
}
|
|
|
|
return hasPropsChanged(prevProps, nextProps)
|
|
|
|
}
|
2020-02-11 02:15:36 +08:00
|
|
|
|
2019-09-06 23:25:11 +08:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
|
|
|
|
const nextKeys = Object.keys(nextProps)
|
|
|
|
if (nextKeys.length !== Object.keys(prevProps).length) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for (let i = 0; i < nextKeys.length; i++) {
|
|
|
|
const key = nextKeys[i]
|
|
|
|
if (nextProps[key] !== prevProps[key]) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2019-10-30 00:30:09 +08:00
|
|
|
|
|
|
|
export function updateHOCHostEl(
|
|
|
|
{ vnode, parent }: ComponentInternalInstance,
|
|
|
|
el: object // HostNode
|
|
|
|
) {
|
|
|
|
while (parent && parent.subTree === vnode) {
|
|
|
|
;(vnode = parent.vnode).el = el
|
|
|
|
parent = parent.parent
|
|
|
|
}
|
|
|
|
}
|