Skip to content

Commit

Permalink
feat(taro-swan): 百度小程序重构 props 系统
Browse files Browse the repository at this point in the history
  • Loading branch information
Chen-jj authored and luckyadam committed Apr 15, 2019
1 parent 29162b8 commit 1a6d244
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 84 deletions.
3 changes: 2 additions & 1 deletion packages/taro-swan/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rollup -c rollup.config.js"
"build": "rollup -c rollup.config.js",
"watch": "rollup -c rollup.config.js -w"
},
"repository": {
"type": "git",
Expand Down
82 changes: 27 additions & 55 deletions packages/taro-swan/src/create-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,19 @@ import { getCurrentPageUrl } from '@tarojs/utils'
import { isEmptyObject, noop } from './util'
import { updateComponent } from './lifecycle'
import { cacheDataGet, cacheDataHas } from './data-cache'
import propsManager from './propsManager'

const privatePropValName = 'privateTriggerObserer'
const anonymousFnNamePreffix = 'funPrivate'
const componentFnReg = /^__fn_/
const PRELOAD_DATA_KEY = 'preload'
const pageExtraFns = ['onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll', 'onTabItemTap']

function bindProperties (weappComponentConf, ComponentClass) {
weappComponentConf.properties = ComponentClass.properties || {}
const defaultProps = ComponentClass.defaultProps || {}
for (const key in defaultProps) {
if (defaultProps.hasOwnProperty(key)) {
weappComponentConf.properties[key] = {
type: null,
value: defaultProps[key]
}
}
}
// 拦截props的更新,插入生命周期
// 调用小程序setData或会造成性能消耗
weappComponentConf.properties[privatePropValName] = {
type: Boolean,
value: false,
observer: function () {
if (!this.$component || !this.$component.__isReady) return
const nextProps = filterProps(ComponentClass.properties, ComponentClass.defaultProps, this.$component.props, this.data)
this.$component.props = nextProps
this.$component._unsafeCallUpdate = true
updateComponent(this.$component)
this.$component._unsafeCallUpdate = false
function bindProperties (weappComponentConf, ComponentClass, isPage) {
weappComponentConf.properties = {}
weappComponentConf.properties.compid = {
type: null,
value: null,
observer () {
initComponent.apply(this, [ComponentClass, isPage])
}
}
}
Expand Down Expand Up @@ -156,28 +139,12 @@ function bindEvents (weappComponentConf, events, isPage) {
})
}

function filterProps (properties, defaultProps = {}, componentProps = {}, weappComponentData) {
let newProps = Object.assign({}, componentProps)
for (const propName in properties) {
if (propName === privatePropValName) {
continue
}
if (typeof componentProps[propName] === 'function') {
newProps[propName] = componentProps[propName]
} else if (propName in weappComponentData) {
newProps[propName] = weappComponentData[propName]
}
if (componentFnReg.test(propName)) {
if (weappComponentData[propName] === true) {
const fnName = propName.replace(componentFnReg, '')
newProps[fnName] = noop
}
delete newProps[propName]
}
}
export function filterProps (defaultProps = {}, propsFromPropsManager = {}, curAllProps = {}) {
let newProps = Object.assign({}, curAllProps, propsFromPropsManager)

if (!isEmptyObject(defaultProps)) {
for (const propName in defaultProps) {
if (newProps[propName] === undefined || newProps[propName] === null) {
if (newProps[propName] === undefined) {
newProps[propName] = defaultProps[propName]
}
}
Expand All @@ -186,6 +153,11 @@ function filterProps (properties, defaultProps = {}, componentProps = {}, weappC
}

export function componentTrigger (component, key, args) {
if (key === 'componentWillUnmount') {
const compid = component.$scope.data.compid
if (compid) propsManager.delete(compid)
}

args = args || []
component[key] && typeof component[key] === 'function' && component[key](...args)
if (key === 'componentWillUnmount') {
Expand All @@ -208,7 +180,7 @@ export function componentTrigger (component, key, args) {
let hasPageInited = false

function initComponent (ComponentClass, isPage) {
if (this.$component.__isReady) return
if (!this.$component || this.$component.__isReady) return
// ready之后才可以setData,
// ready之前,小程序组件初始化时仍然会触发observer,__isReady为否的时候放弃处理observer
this.$component.__isReady = true
Expand All @@ -220,7 +192,12 @@ function initComponent (ComponentClass, isPage) {
// 小程序组件ready,但是数据并没有ready,需要通过updateComponent来初始化数据,setData完成之后才是真正意义上的组件ready
// 动态组件执行改造函数副本的时,在初始化数据前计算好props
if (hasPageInited && !isPage) {
const nextProps = filterProps(ComponentClass.properties, ComponentClass.defaultProps, this.$component.props, this.data)
const compid = this.data.compid
propsManager.observers[compid] = {
component: this.$component,
ComponentClass
}
const nextProps = filterProps(ComponentClass.defaultProps, propsManager.map[compid], this.$component.props)
this.$component.props = nextProps
}
if (hasPageInited || isPage) {
Expand All @@ -229,10 +206,8 @@ function initComponent (ComponentClass, isPage) {
}

function createComponent (ComponentClass, isPage) {
let initData = {
_componentProps: 1
}
const componentProps = filterProps({}, ComponentClass.defaultProps)
let initData = {}
const componentProps = filterProps(ComponentClass.defaultProps)
const componentInstance = new ComponentClass(componentProps)
componentInstance._constructor && componentInstance._constructor(componentProps)
try {
Expand Down Expand Up @@ -269,9 +244,6 @@ function createComponent (ComponentClass, isPage) {
initComponent.apply(this, [ComponentClass, isPage])
},
ready () {
if (!isPage) {
initComponent.apply(this, [ComponentClass, isPage])
}
const component = this.$component
if (component['$$refs'] && component['$$refs'].length > 0) {
let refs = {}
Expand Down Expand Up @@ -334,7 +306,7 @@ function createComponent (ComponentClass, isPage) {
}
}

bindProperties(weappComponentConf, ComponentClass)
bindProperties(weappComponentConf, ComponentClass, isPage)
bindBehaviors(weappComponentConf, ComponentClass)
bindStaticFns(weappComponentConf, ComponentClass)
bindStaticOptions(weappComponentConf, ComponentClass)
Expand Down
8 changes: 6 additions & 2 deletions packages/taro-swan/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import PureComponent from './pure-component'
import createApp from './create-app'
import createComponent from './create-component'
import initNativeApi from './native-api'
import { getElementById } from './util'
import propsManager from './propsManager'
import { getElementById, genCompid, genLoopCompid } from './util'

export const Taro = {
Component,
Expand All @@ -35,7 +36,10 @@ export const Taro = {
createComponent,
internal_get_original,
interceptors,
getElementById
getElementById,
propsManager,
genCompid,
genLoopCompid
}

export default Taro
Expand Down
14 changes: 8 additions & 6 deletions packages/taro-swan/src/lifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import PropTypes from 'prop-types'
const isDEV = typeof process === 'undefined' ||
!process.env ||
process.env.NODE_ENV !== 'production'
const privatePropKeyName = '_triggerObserer'

export function updateComponent (component) {
const { props, __propTypes } = component
Expand Down Expand Up @@ -63,7 +62,6 @@ function doUpdate (component, prevProps, prevState) {
const isRunLoopRef = !component.__mounted
data = component._createData(state, props, isRunLoopRef) || data
}
let privatePropKeyVal = component.$scope.data[privatePropKeyName] || 0

data = Object.assign({}, props, data)
if (component.$usedState && component.$usedState.length) {
Expand All @@ -85,8 +83,7 @@ function doUpdate (component, prevProps, prevState) {
})
data = _data
}
// 改变这个私有的props用来触发(observer)子组件的更新
data[privatePropKeyName] = privatePropKeyVal + 1
data['$taroCompReady'] = true

const __mounted = component.__mounted
// 每次 setData 都独立生成一个 callback 数组
Expand All @@ -96,7 +93,7 @@ function doUpdate (component, prevProps, prevState) {
component._pendingCallbacks = []
}

component.$scope.setData(data, function () {
const cb = function () {
if (__mounted) {
if (component['$$refs'] && component['$$refs'].length > 0) {
component['$$refs'].forEach(ref => {
Expand Down Expand Up @@ -130,5 +127,10 @@ function doUpdate (component, prevProps, prevState) {
typeof cbs[i] === 'function' && cbs[i].call(component)
}
}
})
}
if (Object.keys(data).length === 0) {
cb()
} else {
component.$scope.setData(data, cb)
}
}
43 changes: 43 additions & 0 deletions packages/taro-swan/src/propsManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { updateComponent } from './lifecycle'
import { filterProps } from './create-component'

class Manager {
map = {}
observers = {}

set (props = {}, compid) {
if (!compid) return

const { observers } = this
if (!this.map[compid]) {
Object.defineProperty(this.map, compid, {
configurable: true,
get () {
return this[`__${compid}`]
},
set (props) {
this[`__${compid}`] = props

const component = observers[compid] && observers[compid].component
const ComponentClass = observers[compid] && observers[compid].ComponentClass
if (!component || !ComponentClass || !component.__isReady) return

const nextProps = filterProps(ComponentClass.defaultProps, props, component.props)
component.props = nextProps
component._unsafeCallUpdate = true
updateComponent(component)
component._unsafeCallUpdate = false
}
})
}
this.map[compid] = props
}

delete (compid) {
delete this.map[compid]
delete this.map[`__${compid}`]
delete this.observers[compid]
}
}

export default new Manager()
24 changes: 24 additions & 0 deletions packages/taro-swan/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,27 @@ export function getElementById (component, id, type) {

return null
}

let id = 0
export function genCompid () {
return String(id++)
}

export function genLoopCompid (scope, variableName, loops) {
if (scope && scope.data) {
let data = scope.data
for (let len = loops.length, i = 0; i < len; i++) {
const { indexId, name } = loops[i]
if (data[name] && data[name][indexId]) {
data = data[name][indexId]
} else {
return genCompid()
}
}
if (data[variableName]) {
return data[variableName]
} else {
return genCompid()
}
}
}
8 changes: 4 additions & 4 deletions packages/taro-transformer-wx/src/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function processThisPropsFnMemberProperties (
]
)
)
} else if (Adapters.weapp !== Adapter.type) {
} else if (Adapters.weapp !== Adapter.type && Adapters.swan !== Adapter.type) {
path.replaceWith(
t.callExpression(
t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')),
Expand Down Expand Up @@ -414,14 +414,14 @@ class Transformer {
t.isIdentifier(expr.callee.property, { name: 'bind' })
) {
if (
Adapter.type !== Adapters.weapp ||
(Adapter.type !== Adapters.weapp && Adapter.type !== Adapters.swan) ||
(t.isJSXIdentifier(jsx.node.name) && DEFAULT_Component_SET.has(jsx.node.name.name))
) {
self.buildPropsAnonymousFunc(attr, expr, true)
}
} else if (t.isMemberExpression(expr)) {
if (
Adapter.type !== Adapters.weapp ||
(Adapter.type !== Adapters.weapp && Adapter.type !== Adapters.swan) ||
(t.isJSXIdentifier(jsx.node.name) && DEFAULT_Component_SET.has(jsx.node.name.name))
) {
self.buildPropsAnonymousFunc(attr, expr as any, false)
Expand Down Expand Up @@ -608,7 +608,7 @@ class Transformer {
if (methodName.startsWith('on')) {
this.componentProperies.add(`__fn_${methodName}`)
}
const method = Adapters.weapp !== Adapter.type ?
const method = (Adapters.weapp !== Adapter.type && Adapters.swan !== Adapter.type) ?
t.classMethod('method', t.identifier(funcName), [], t.blockStatement([
t.expressionStatement(t.callExpression(
t.memberExpression(t.thisExpression(), t.identifier('__triggerPropsFn')),
Expand Down
6 changes: 3 additions & 3 deletions packages/taro-transformer-wx/src/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export function parseJSXElement (element: t.JSXElement): string {
if (attributes.length) {
attributesTrans = attributes.reduce((obj, attr) => {
if (t.isJSXSpreadAttribute(attr)) {
if (Adapter.type === Adapters.weapp) return {}
if (Adapter.type === Adapters.weapp || Adapter.type === Adapters.swan) return {}
throw codeFrameError(attr.loc, 'JSX 参数暂不支持 ...spread 表达式')
}
let name = attr.name.name
Expand Down Expand Up @@ -261,13 +261,13 @@ export function parseJSXElement (element: t.JSXElement): string {
obj[isDefaultComponent && !name.includes('-') && !name.includes(':') ? kebabCase(name) : name] = value
}
}
if (!isDefaultComponent && !specialComponentName.includes(componentName) && Adapter.type !== Adapters.weapp) {
if (!isDefaultComponent && !specialComponentName.includes(componentName) && Adapter.type !== Adapters.weapp && Adapter.type !== Adapters.swan) {
obj[TRIGGER_OBSERER] = '{{ _triggerObserer }}'
}
return obj
}, {})
} else if (!isDefaultComponent && !specialComponentName.includes(componentName)) {
if (Adapter.type !== Adapters.weapp) {
if (Adapter.type !== Adapters.weapp && Adapter.type !== Adapters.swan) {
attributesTrans[TRIGGER_OBSERER] = '{{ _triggerObserer }}'
}
}
Expand Down
Loading

0 comments on commit 1a6d244

Please sign in to comment.