UNPKG

@tarojs/taro-quickapp

Version:

Taro quickapp framework

222 lines (199 loc) 6.71 kB
import PropTypes from 'prop-types' import { internal_safe_get as safeGet, internal_safe_set as safeSet, Current, invokeEffects } from '@tarojs/taro' import { componentTrigger } from './create-component' import { enqueueRender } from './render-queue' import { shakeFnFromObject, isEmptyObject, isFunction, isUndefined, isArray } from './util' const isDEV = typeof process === 'undefined' || !process.env || process.env.NODE_ENV !== 'production' function hasNewLifecycle (component) { const { constructor: { getDerivedStateFromProps }, getSnapshotBeforeUpdate } = component return isFunction(getDerivedStateFromProps) || isFunction(getSnapshotBeforeUpdate) } function callGetDerivedStateFromProps (component, props, state) { const { getDerivedStateFromProps } = component.constructor let newState if (isFunction(getDerivedStateFromProps)) { const partialState = getDerivedStateFromProps(props, state) if (!isUndefined(partialState)) { newState = Object.assign({}, state, partialState) } else { console.warn('getDerivedStateFromProps 没有返回任何内容,这个生命周期必须返回 null 或一个新对象。') } } return newState } function callGetSnapshotBeforeUpdate (component, props, state) { const { getSnapshotBeforeUpdate } = component let snapshot if (isFunction(getSnapshotBeforeUpdate)) { snapshot = getSnapshotBeforeUpdate.call(component, props, state) } return snapshot } export function mountComponent (component) { const { props } = component // 在willMount前执行构造函数的副本 if (!component.__componentWillMountTriggered) { component._constructor && component._constructor(props) } const newState = callGetDerivedStateFromProps(component, props, component.state) if (!isUndefined(newState)) { component.state = newState } component._dirty = false component._disable = false component._isForceUpdate = false if (!component.__componentWillMountTriggered) { component.__componentWillMountTriggered = true if (!hasNewLifecycle(component)) { componentTrigger(component, 'componentWillMount') } } doUpdate(component, props, component.state) component.prevProps = component.props component.prevState = component.state } export function updateComponent (component) { const { props, __propTypes } = component if (isDEV && __propTypes) { let componentName = component.constructor.name if (isUndefined(componentName)) { const names = component.constructor.toString().match(/^function\s*([^\s(]+)/) componentName = isArray(names) ? names[0] : 'Component' } PropTypes.checkPropTypes(__propTypes, props, 'prop', componentName) } const prevProps = component.prevProps || props component.props = prevProps if (component.__mounted && component._unsafeCallUpdate === true && !hasNewLifecycle(component) && component.componentWillReceiveProps) { component._disable = true component.componentWillReceiveProps(props) component._disable = false } // 在willMount前执行构造函数的副本 let state = component.getState() const prevState = component.prevState || state const stateFromProps = callGetDerivedStateFromProps(component, props, state) if (!isUndefined(stateFromProps)) { state = stateFromProps } let skip = false if (component.__mounted) { if (isFunction(component.shouldComponentUpdate) && !component._isForceUpdate && component.shouldComponentUpdate(props, state) === false) { skip = true } else if (!hasNewLifecycle(component) && isFunction(component.componentWillUpdate)) { component.componentWillUpdate(props, state) } } component.props = props component.state = state component._dirty = false component._isForceUpdate = false if (!skip) { doUpdate(component, prevProps, prevState) } component.prevProps = component.props component.prevState = component.state } function injectContextType (component) { const ctxType = component.constructor.contextType if (ctxType) { const context = ctxType.context const emitter = context.emitter if (emitter === null) { component.context = context._defaultValue return } if (!component._hasContext) { component._hasContext = true emitter.on(_ => enqueueRender(component)) } component.context = emitter.value } } function doUpdate (component, prevProps, prevState) { const { state, props = {} } = component let data = state || {} if (component._createData) { // 返回null或undefined则保持不变 if (component.__isReady) { injectContextType(component) Current.current = component Current.index = 0 invokeEffects(component, true) } data = component._createData(state, props) || data if (component.__isReady) { Current.current = null } } data = Object.assign({}, props, data) if (component.$usedState && component.$usedState.length) { const _data = {} component.$usedState.forEach(key => { let val = safeGet(data, key) if (typeof val === 'undefined') { return } if (typeof val === 'object') { if (isEmptyObject(val)) return safeSet(_data, key, val) val = shakeFnFromObject(val) // 避免筛选完 Fn 后产生了空对象还去渲染 if (!isEmptyObject(val)) safeSet(_data, key, val) } else { safeSet(_data, key, val) } }) data = _data } data['priTaroCompReady'] = true const __mounted = component.__mounted let snapshot if (__mounted) { snapshot = callGetSnapshotBeforeUpdate(component, prevProps, prevState) } // 每次 setData 都独立生成一个 callback 数组 let cbs = [] if (component._pendingCallbacks && component._pendingCallbacks.length) { cbs = component._pendingCallbacks component._pendingCallbacks = [] } Object.keys(data).map(item => { if (!(item in component.$scope)) { component.$scope.$set(item, data[item]) } else { component.$scope[item] = data[item] } }) if (__mounted) { invokeEffects(component) if (component['$$hasLoopRef']) { Current.current = component Current.index = 0 component._disableEffect = true component._createData(component.state, component.props, true) component._disableEffect = false Current.current = null } if (isFunction(component.componentDidUpdate)) { component.componentDidUpdate(prevProps, prevState, snapshot) } } if (cbs.length) { let i = cbs.length while (--i >= 0) { isFunction(cbs[i]) && cbs[i].call(component) } } }