Skip to content

Commit

Permalink
[feat-composition-api]
Browse files Browse the repository at this point in the history
  • Loading branch information
CommanderXL committed Apr 28, 2022
1 parent 01586fc commit d8b270d
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 15 deletions.
36 changes: 36 additions & 0 deletions packages/core/src/core/composition-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import VueCompositionAPI from '@vue/composition-api'
import { LIFECYCLES_HOOKS, mergeWebHook } from './shim'

function install(mpx) {
if (__mpx_mode__ === 'web') {
mpx.__vue.use(VueCompositionAPI)
} else {
// proxy 能力增强
mpx.__proxy.mixin = function (mixins) {
mpx.mixin(mixins)
}
mpx.__proxy.observable = function (...args) {
return mpx.observable(...args)
}

// web -> 小程序 LifeCycle
const mpxProxyConfig = {
optionMergeStrategies: {}
}
LIFECYCLES_HOOKS.forEach((hook) => {
mpxProxyConfig.optionMergeStrategies[hook] = mergeWebHook
})
mpx.__proxy.config = mpxProxyConfig

const rawInstall = VueCompositionAPI.install
VueCompositionAPI.install = function (proxy1, options, proxy2) {
mpx.__proxy.version = '2.7.30'
rawInstall(mpx.__proxy, options)
}
VueCompositionAPI.install(mpx)
}
}

export default {
install
}
2 changes: 1 addition & 1 deletion packages/core/src/core/mergeOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ function transformHOOKS (options) {
const componentHooksMap = makeMap(convertRule.lifecycle.component)
for (const key in options) {
// 使用Component创建page实例,页面专属生命周期&自定义方法需写在methods内部
if (typeof options[key] === 'function' && key !== 'dataFn' && !componentHooksMap[key]) {
if (typeof options[key] === 'function' && key !== 'dataFn' && key !== 'setup' && !componentHooksMap[key]) {
if (!options.methods) options.methods = {}
options.methods[key] = options[key]
delete options[key]
Expand Down
77 changes: 67 additions & 10 deletions packages/core/src/core/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
aIsSubPathOfB,
getFirstKey,
makeMap,
hasOwn
hasOwn,
def
} from '../helper/utils'
import _getByPath from '../helper/getByPath'
import { getRenderCallBack } from '../platform/patch'
Expand All @@ -29,14 +30,22 @@ import {
BEFOREMOUNT,
MOUNTED,
UPDATED,
DESTROYED
DESTROYED,
INNER_LIFECYCLES
} from './innerLifecycle'
import { warn, error } from '../helper/log'

let uid = 0

export default class MPXProxy {
constructor (options, target) {
constructor (
options,
target = {
__getInitialData: () => {},
__render: () => {}
},
params = []
) {
this.target = target
this.uid = uid++
this.name = options.name || ''
Expand All @@ -57,6 +66,8 @@ export default class MPXProxy {
this.forceUpdateAll = false // 下次是否需要强制更新全部渲染数据
this.curRenderTask = null
}

this.created(params)
}

created (params) {
Expand Down Expand Up @@ -142,13 +153,27 @@ export default class MPXProxy {

initApi () {
// 挂载扩展属性到实例上
proxy(this.target, this.options.proto, Object.keys(this.options.proto), true, (key) => {
if (this.ignoreProxyMap[key]) {
error(`The key [${key}] of mpx.prototype is a reserved keyword of miniprogram, please check and rename it!`, this.options.mpxFileResource)
return false
}
error(`The key [${key}] of mpx.prototype exist in the component/page instance already, please check your plugins!`, this.options.mpxFileResource)
})
if (__mpx_mode__ !== 'web') {
this.target.__mpxProxy = this
def(this.target, '$attrs', {})
def(this.target, '$options', this.options)
def(this.target, '_watchers', this._watchers)
def(this, '$destroy', this.destroyed)
def(this.target, '$emit', (...args) => {
this.target.triggEvent(...args)
})
def(this.target, '$on', () => {})
def(this.target, '_vnode', [])
}
if (this.options.proto) {
proxy(this.target, this.options.proto, Object.keys(this.options.proto), true, (key) => {
if (this.ignoreProxyMap[key]) {
error(`The key [${key}] of mpx.prototype is a reserved keyword of miniprogram, please check and rename it!`, this.options.mpxFileResource)
return false
}
error(`The key [${key}] of mpx.prototype exist in the component/page instance already, please check your plugins!`, this.options.mpxFileResource)
})
}
// 挂载混合模式下createPage中的自定义属性,模拟原生Page构造器的表现
if (this.options.__type__ === 'page' && !this.options.__pageCtor__) {
proxy(this.target, this.options, this.options.mpxCustomKeysForBlend, undefined, (key) => {
Expand Down Expand Up @@ -207,6 +232,26 @@ export default class MPXProxy {
let proxyedKeys = []
// 获取包含data/props在内的初始数据,包含初始原生微信转换支付宝时合并props进入data的逻辑
const initialData = this.target.__getInitialData(this.options) || {}
if (typeof data === 'function') {
data = data()

INNER_LIFECYCLES.forEach(lifecycle => {
const webLifecycle = lifecycle.replace(/_/g, '')
if (this.options[webLifecycle]) {
this.options[lifecycle] = this.options[webLifecycle]
delete this.options[webLifecycle]
}
})
// 收集 setup 返回的响应式数据,作为小程序渲染逻辑
if (this.target.__composition_api_state__) {
const { rawBindings = {} } = this.target.__composition_api_state__
Object.keys(rawBindings).forEach(name => {
if (hasOwn(this.target, name)) {
this.localKeysMap[name] = true
}
})
}
}
// 之所以没有直接使用initialData,而是通过对原始dataOpt进行深clone获取初始数据对象,主要是为了避免小程序自身序列化时错误地转换数据对象,比如将promise转为普通object
this.data = diffAndCloneA(data || {}).clone
if (dataFn) {
Expand Down Expand Up @@ -234,6 +279,11 @@ export default class MPXProxy {
this.data.mpxCid = this.uid
this.localKeysMap.mpxCid = true
observe(this.data, true)
if (__mpx_mode__ !== 'web') {
def(this.target, '$props', initialData)
def(this.target, '_data', this.data)
def(this, '_data', this.data)
}
return proxyedKeys
}

Expand Down Expand Up @@ -270,6 +320,13 @@ export default class MPXProxy {

callUserHook (hookName, params) {
const hook = this.options[hookName] || this.target[hookName]
if (Array.isArray(hook)) {
hook.forEach(item => {
if (typeof item === 'function') {
item.apply(this.target, params)
}
})
}
if (typeof hook === 'function') {
try {
hook.apply(this.target, params)
Expand Down
29 changes: 29 additions & 0 deletions packages/core/src/core/shim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function dedupeHooks(hooks) {
const res = []
for (let i = 0; i < hooks.length; i++) {
if (res.indexOf(hooks[i]) === -1) {
res.push(hooks[i])
}
}
return res
}

export function mergeWebHook(parentVal, childVal) {
const res = childVal
? parentVal
? Array.isArray(parentVal)
? parentVal.concat(childVal)
: [parentVal, childVal]
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
return res ? dedupeHooks(res) : res
}

export const LIFECYCLES_HOOKS = [
'beforeMount',
'mounted',
'updated',
'destroyed'
]
2 changes: 2 additions & 0 deletions packages/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Vue from './vue'
import { observe, set, del } from './observer/index'
import { watch as watchWithVm } from './observer/watch'
import implement from './core/implement'
import MpxProxy from './core/proxy'

export function createApp (config, ...rest) {
const mpx = new EXPORT_MPX()
Expand Down Expand Up @@ -164,6 +165,7 @@ function factory () {
this.proto = extend({}, this)
}

MPX.__proxy = MpxProxy
Object.assign(MPX, APIs)
Object.assign(MPX.prototype, InstanceAPIs)
// 输出web时在mpx上挂载Vue对象
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/platform/patch/web/getDefaultOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ function initProxy (context, rawOptions, params) {
// 缓存options
context.$rawOptions = rawOptions
// 创建proxy对象
context.__mpxProxy = new MPXProxy(rawOptions, context)
context.__mpxProxy.created(params)
context.__mpxProxy = new MPXProxy(rawOptions, context, params)
} else if (context.__mpxProxy.isDestroyed()) {
context.__mpxProxy.reCreated(params)
}
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/platform/patch/wx/getDefaultOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ export function initProxy (context, rawOptions, currentInject, params) {
// 缓存options
context.$rawOptions = rawOptions
// 创建proxy对象
context.__mpxProxy = new MPXProxy(rawOptions, context)
context.__mpxProxy.created(params)
context.__mpxProxy = new MPXProxy(rawOptions, context, params)
} else if (context.__mpxProxy.isDestroyed()) {
context.__mpxProxy.reCreated(params)
}
Expand Down

0 comments on commit d8b270d

Please sign in to comment.