提交 12035426 编写于 作者: fxy060608's avatar fxy060608

fix(mp): support mini program components (#3071)

上级 f7a637f0
import path from 'path'
import { extend } from '@vue/shared'
import { ComponentJson, PageWindowOptions, UsingComponents } from './types'
import { removeExt, normalizePath, normalizeNodeModules } from '../../utils'
import {
removeExt,
normalizePath,
normalizeNodeModules,
normalizeMiniProgramFilename,
} from '../../utils'
import { relativeFile } from '../../resolve'
import { isVueSfcFile } from '../../vue/utils'
......@@ -30,6 +35,13 @@ export function hasJsonFile(filename: string) {
)
}
export function findJsonFile(filename: string) {
if (filename === 'app') {
return appJsonCache
}
return jsonPagesCache.get(filename) || jsonComponentsCache.get(filename)
}
export function normalizeJsonFilename(filename: string) {
return normalizeNodeModules(filename)
}
......@@ -95,3 +107,55 @@ export function addMiniProgramUsingComponents(
) {
jsonUsingComponentsCache.set(filename, json)
}
export function isMiniProgramUsingComponent(
name: string,
options: {
filename: string
inputDir: string
componentsDir?: string
}
) {
return findMiniProgramUsingComponents(options).includes(name)
}
export function findMiniProgramUsingComponents({
filename,
inputDir,
componentsDir,
}: {
filename: string
inputDir: string
componentsDir?: string
}) {
if (!componentsDir) {
return []
}
const globalUsingComponents = appJsonCache && appJsonCache.usingComponents
const miniProgramComponents: string[] = []
if (globalUsingComponents) {
miniProgramComponents.push(
...findMiniProgramUsingComponent(globalUsingComponents, componentsDir)
)
}
const jsonFile = findJsonFile(
removeExt(normalizeMiniProgramFilename(filename, inputDir))
)
if (jsonFile?.usingComponents) {
miniProgramComponents.push(
...findMiniProgramUsingComponent(jsonFile.usingComponents, componentsDir)
)
}
return miniProgramComponents
}
function findMiniProgramUsingComponent(
usingComponents: Record<string, string>,
componentsDir: string
) {
return Object.keys(usingComponents).filter((name) => {
const path = usingComponents[name]
return path.includes(componentsDir + '/')
})
}
......@@ -40,6 +40,10 @@ export interface MiniProgramCompilerOptions {
lang: string
}
component?: {
/**
* 平台自定义组件目录,如 wxcomponents
*/
dir?: string
/**
* 自定义组件自定义 hidden 属性用于实现 v-show
*/
......
import { addMiniProgramPageJson } from '@dcloudio/uni-cli-shared'
import { customElements } from '../src/compiler/options'
import { assert } from './testUtils'
......@@ -21,4 +22,22 @@ describe('mp-alipay: transform component', () => {
}`
)
})
test(`mini program component`, () => {
const filename = 'pages/vant/vant'
addMiniProgramPageJson(filename, {
usingComponents: {
'van-button': 'mycomponents/button/index',
},
})
assert(
`<van-button/>`,
`<van-button u-t="m" u-i="dc555fe4-0" onVI="__l"/>`,
`(_ctx, _cache) => {
return {}
}`,
{
filename,
}
)
})
})
......@@ -125,6 +125,7 @@ function transformOpenType(node) {
}
const projectConfigFilename = 'mini.project.json';
const COMPONENTS_DIR = 'mycomponents';
const miniProgram = {
event,
class: {
......@@ -138,6 +139,7 @@ const miniProgram = {
},
directive: 'a:',
component: {
dir: COMPONENTS_DIR,
getPropertySync: true,
},
};
......@@ -173,7 +175,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['mycomponents'],
assets: [COMPONENTS_DIR],
targets: process.env.UNI_MP_PLUGIN ? [uniCliShared.copyMiniProgramPluginJson] : [],
},
},
......
......@@ -456,6 +456,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -482,11 +487,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -501,9 +508,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -546,14 +553,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -864,7 +869,7 @@ function initComponentProps(rawProps) {
const propertiesOptions = {
properties: {},
};
initProps(propertiesOptions, rawProps, false);
initProps(propertiesOptions);
const properties = propertiesOptions.properties;
const props = {
// onVueInit
......
......@@ -14,7 +14,7 @@ import { event } from './event'
import { transformOpenType } from './transforms/transformOpenType'
const projectConfigFilename = 'mini.project.json'
const COMPONENTS_DIR = 'mycomponents'
export const miniProgram: MiniProgramCompilerOptions = {
event,
class: {
......@@ -28,6 +28,7 @@ export const miniProgram: MiniProgramCompilerOptions = {
},
directive: 'a:',
component: {
dir: COMPONENTS_DIR,
getPropertySync: true,
},
}
......@@ -65,7 +66,7 @@ export const options: UniMiniProgramPluginOptions = {
'uni-mp-runtime': path.resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['mycomponents'],
assets: [COMPONENTS_DIR],
targets: process.env.UNI_MP_PLUGIN ? [copyMiniProgramPluginJson] : [],
},
},
......
import { assert } from './testUtils'
import { customElements } from '../src/compiler/options'
import { addMiniProgramPageJson } from '@dcloudio/uni-cli-shared'
describe('mp-baidu: transform component', () => {
test(`built-in component`, () => {
const code = customElements.map((tag) => `<${tag}/>`).join('')
......@@ -20,4 +21,22 @@ describe('mp-baidu: transform component', () => {
}`
)
})
test(`mini program component`, () => {
const filename = 'pages/vant/vant'
addMiniProgramPageJson(filename, {
usingComponents: {
'van-button': 'swancomponents/button/index',
},
})
assert(
`<van-button/>`,
`<van-button u-t="m" u-i="dc555fe4-0"/>`,
`(_ctx, _cache) => {
return {}
}`,
{
filename,
}
)
})
})
......@@ -70,6 +70,7 @@ const directiveTransforms = {
on: transformOn,
model: transformModel,
};
const COMPONENTS_DIR = 'swancomponents';
const miniProgram = {
class: {
array: true,
......@@ -80,6 +81,9 @@ const miniProgram = {
dynamicSlotNames: false,
},
directive: 's-',
component: {
dir: COMPONENTS_DIR,
},
};
const compilerOptions = {
nodeTransforms,
......@@ -96,7 +100,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['swancomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'swan',
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize } from '@vue/shared';
import { injectHook, ref, nextTick, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, nextTick, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
// lifecycle
// App and Page
......@@ -599,6 +599,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -625,11 +630,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -638,15 +645,28 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.properties.uP.observer = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
const prevProps = toRaw(instance.props);
const nextProps = findComponentPropsData(up) || {};
......@@ -656,9 +676,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -701,14 +721,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -751,7 +769,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -843,6 +861,12 @@ Page = function (options) {
}
Component = function (options) {
initHook('created', options);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -18,6 +18,7 @@ const directiveTransforms = {
on: transformOn,
model: transformModel,
}
const COMPONENTS_DIR = 'swancomponents'
export const miniProgram: MiniProgramCompilerOptions = {
class: {
array: true,
......@@ -28,6 +29,9 @@ export const miniProgram: MiniProgramCompilerOptions = {
dynamicSlotNames: false,
},
directive: 's-',
component: {
dir: COMPONENTS_DIR,
},
}
export const compilerOptions: CompilerOptions = {
......@@ -47,7 +51,7 @@ export const options: UniMiniProgramPluginOptions = {
'uni-mp-runtime': path.resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['swancomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'swan',
......
import {
addMiniProgramPageJson,
COMPONENT_BIND_LINK,
createTransformComponentLink,
} from '@dcloudio/uni-cli-shared'
......@@ -106,4 +107,23 @@ describe('compiler: transform component', () => {
}`
)
})
test(`mini program component`, () => {
const filename = 'pages/vant/vant'
addMiniProgramPageJson(filename, {
usingComponents: {
'van-button': 'wxcomponents/button/index',
},
})
assert(
`<van-button/>`,
`<van-button u-t="m" u-i="dc555fe4-0" bind:__l="__l"/>`,
`(_ctx, _cache) => {
return {}
}`,
{
filename,
nodeTransforms,
}
)
})
})
// import { inspect } from './testUtils'
import { transformRef } from '@dcloudio/uni-cli-shared'
import {
addMiniProgramPageJson,
transformComponentLink,
} from '@dcloudio/uni-cli-shared'
import { compile } from '../src/index'
import { CompilerOptions } from '../src/options'
import { miniProgram } from './testUtils'
......@@ -36,17 +39,34 @@ function assert(
}
}
// assert(
// `<custom class="a"
// dd
// a-b="b" :class="c"
// :a-c="c" b="bb" :c="cc" :data-d="dd" @click="d" :b="d"/>`,
// `<slot wx:for="{{a}}" wx:for-item="item"></slot>`,
// `(_ctx, _cache) => {
// return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) }
// }`,
// {
// inline: false,
// nodeTransforms: [transformRef],
// }
// )
const filename = 'pages/vant/vant'
addMiniProgramPageJson(filename, {
usingComponents: {
'van-button': 'wxcomponents/button/index',
},
})
assert(
`<custom class="a"
dd
a-b="b" :class="c"
:a-c="c" b="bb" :c="cc" :data-d="dd" @click="d" :b="d"/>`,
`<slot wx:for="{{a}}" wx:for-item="item"></slot>`,
`<van-button/>`,
`<van-button u-i="dc555fe4-0"/>`,
`(_ctx, _cache) => {
return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) }
return {}
}`,
{
inline: false,
nodeTransforms: [transformRef],
filename,
nodeTransforms: [transformComponentLink],
}
)
......@@ -16,6 +16,7 @@ export const miniProgram: MiniProgramCompilerOptions = {
},
directive: 'wx:',
component: {
dir: 'wxcomponents',
getPropertySync: true,
},
} as const
......@@ -31,6 +32,7 @@ export function assert(
options: CompilerOptions = {}
) {
const res = compile(template, {
root: '',
mode: 'module',
filename: 'foo.vue',
prefixIdentifiers: true,
......
......@@ -45,6 +45,7 @@ interface ParserOptions {
interface SharedTransformCodegenOptions {
inline?: boolean
isTS?: boolean
root?: string
filename?: string
bindingMetadata?: BindingMetadata
prefixIdentifiers?: boolean
......
......@@ -35,6 +35,7 @@ import {
CacheExpression,
locStub,
} from '@vue/compiler-core'
import { findMiniProgramUsingComponents } from '@dcloudio/uni-cli-shared'
import IdentifierGenerator from './identifier'
import {
CodegenRootNode,
......@@ -83,7 +84,7 @@ export const enum BindingComponentTypes {
UNKNOWN = 'unknown',
}
export interface TransformContext
extends Required<Omit<TransformOptions, 'filename'>> {
extends Required<Omit<TransformOptions, 'filename' | 'root'>> {
selfName: string | null
currentNode: RootNode | TemplateChildNode | null
parent: ParentNode | null
......@@ -119,6 +120,7 @@ export interface TransformContext
addVIfScope(initScope: CodegenVIfScopeInit): CodegenVIfScope
addVForScope(initScope: CodegenVForScopeInit): CodegenVForScope
cache<T extends JSChildNode>(exp: T, isVNode?: boolean): CacheExpression | T
isMiniProgramComponent(name: string): boolean
}
export function isRootScope(scope: CodegenScope): scope is CodegenRootScope {
......@@ -240,8 +242,9 @@ function defaultOnWarn(msg: CompilerError) {
}
export function createTransformContext(
root: RootNode,
rootNode: RootNode,
{
root = '',
filename = '',
isTS = false,
inline = false,
......@@ -310,6 +313,12 @@ export function createTransformContext(
const vueIds: string[] = []
const identifiers = Object.create(null)
const scopes: CodegenScope[] = [rootScope]
const miniProgramComponents = findMiniProgramUsingComponents({
filename,
componentsDir: miniProgram.component?.dir,
inputDir: root,
})
// const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
const context: TransformContext = {
// options
......@@ -351,7 +360,7 @@ export function createTransformContext(
get currentScope() {
return scopes[scopes.length - 1]
},
currentNode: root,
currentNode: rootNode,
vueIds,
get currentVueId() {
return vueIds[vueIds.length - 1]
......@@ -458,6 +467,9 @@ export function createTransformContext(
cache(exp, isVNode = false) {
return createCacheExpression(context.cached++, exp, isVNode)
},
isMiniProgramComponent(name) {
return miniProgramComponents.includes(name)
},
}
function addId(id: string) {
......
......@@ -12,7 +12,15 @@ import {
isUserComponent,
} from '@dcloudio/uni-cli-shared'
import { isVForScope, NodeTransform, TransformContext } from '../transform'
import { ATTR_VUE_ID, ATTR_VUE_PROPS, rewirteWithHelper } from './utils'
import {
ATTR_COM_TYPE,
ATTR_VUE_ID,
ATTR_VUE_PROPS,
ATTR_VUE_REF,
ATTR_VUE_REF_IN_FOR,
ATTR_VUE_SLOTS,
rewirteWithHelper,
} from './utils'
import { genExpr, genBabelExpr } from '../codegen'
import {
identifier,
......@@ -29,6 +37,9 @@ export const transformComponent: NodeTransform = (node, context) => {
if (!isUserComponent(node, context as any)) {
return
}
addComponentType(node, context)
addVueId(node, context)
processBooleanAttr(node)
return function postTransformComponent() {
......@@ -36,6 +47,13 @@ export const transformComponent: NodeTransform = (node, context) => {
}
}
function addComponentType(node: ComponentNode, context: TransformContext) {
if (!context.isMiniProgramComponent(node.tag)) {
return
}
node.props.push(createAttributeNode(ATTR_COM_TYPE, 'm'))
}
function addVueId(node: ComponentNode, context: TransformContext) {
let { hashId, scopes, currentScope, currentVueId } = context
if (!hashId) {
......@@ -94,10 +112,12 @@ function isComponentProp(name: string) {
[
'class',
'style',
'u-i',
'u-r',
'u-r-i-f',
'u-s',
ATTR_VUE_ID,
ATTR_VUE_PROPS,
ATTR_VUE_SLOTS,
ATTR_VUE_REF,
ATTR_VUE_REF_IN_FOR,
ATTR_COM_TYPE,
'eO',
'e-o',
'onVI',
......
......@@ -13,6 +13,7 @@ import {
SpreadElement,
stringLiteral,
} from '@babel/types'
import { VUE_REF, VUE_REF_IN_FOR } from '@dcloudio/uni-cli-shared'
import {
createSimpleExpression,
ExpressionNode,
......@@ -30,6 +31,10 @@ import { isVForScope, isVIfScope, TransformContext } from '../transform'
export const ATTR_VUE_ID = 'u-i'
export const ATTR_VUE_SLOTS = 'u-s'
export const ATTR_VUE_PROPS = 'u-p'
export const ATTR_VUE_REF = 'u-' + VUE_REF
export const ATTR_VUE_REF_IN_FOR = 'u-' + VUE_REF_IN_FOR
export const ATTR_COM_TYPE = 'u-t'
export const SCOPED_SLOT_IDENTIFIER = '__SCOPED_SLOT__'
export function rewriteSpreadElement(
......
......@@ -13,7 +13,6 @@ import {
import { MPComponentInstance } from '..'
import { MPComponentOptions } from './component'
import { initProps } from './componentProps'
export function initData(_: ComponentOptions) {
return {}
......@@ -22,20 +21,37 @@ export function initData(_: ComponentOptions) {
export function initPropsObserver(componentOptions: MPComponentOptions) {
const observe = function observe(this: MPComponentInstance) {
const up = this.properties.uP
if (!up || !this.$vm) {
if (!up) {
return
}
updateComponentProps(up, this.$vm.$)
if (this.$vm) {
updateComponentProps(up, this.$vm.$)
} else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this)
}
}
if (__PLATFORM__ === 'mp-weixin' || __PLATFORM__ === 'mp-qq') {
componentOptions.observers = {
uP: observe,
if (!componentOptions.observers) {
componentOptions.observers = {}
}
componentOptions.observers.uP = observe
} else {
;(componentOptions.properties as any).uP.observer = observe
}
}
function updateMiniProgramComponentProperties(
up: string,
mpInstance: MPComponentInstance
) {
const prevProps = mpInstance.properties
const nextProps = findComponentPropsData(up) || {}
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps)
}
}
export function updateComponentProps(
up: string,
instance: ComponentInternalInstance
......@@ -49,9 +65,13 @@ export function updateComponentProps(
}
}
function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
function hasPropsChanged(
prevProps: Data,
nextProps: Data,
checkLen: boolean = true
): boolean {
const nextKeys = Object.keys(nextProps)
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -100,14 +120,12 @@ export function initBehaviors(
}
if (vueExtends && vueExtends.props) {
const behavior = {}
initProps(behavior, vueExtends.props, true)
behaviors.push(initBehavior(behavior) as string)
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {}
initProps(behavior, vueMixin.props, true)
behaviors.push(initBehavior(behavior) as string)
}
})
......
import type { ComponentPropsOptions } from 'vue'
import { extend } from '@vue/shared'
import type { MPComponentOptions, MPComponentInstance } from './component'
import Component = WechatMiniprogram.Component
......@@ -30,6 +30,11 @@ function initDefaultProps(isBehavior: boolean = false) {
type: null,
value: '',
}
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
}
// 组件 props
properties.uP = {
type: null,
......@@ -57,13 +62,11 @@ function initDefaultProps(isBehavior: boolean = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
export function initProps(
mpComponentOptions: MPComponentOptions,
_rawProps: ComponentPropsOptions | null,
isBehavior: boolean = false
) {
mpComponentOptions.properties = initDefaultProps(isBehavior)
export function initProps(mpComponentOptions: MPComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {}
}
extend(mpComponentOptions.properties, initDefaultProps())
}
import { ON_LOAD } from '@dcloudio/uni-shared'
import { camelize } from '@vue/shared'
import { ON_LOAD } from '@dcloudio/uni-shared'
import { MPComponentInstance } from './component'
import { initPropsObserver } from './componentOptions'
import { initProps } from './componentProps'
const MPPage = Page
const MPComponent = Component
......@@ -54,5 +56,11 @@ if (__PLATFORM__ === 'mp-baidu') {
}
Component = function (options) {
initHook('created', options, true)
// 小程序组件
const isVueComponent = options.properties && options.properties.uP
if (!isVueComponent) {
initProps(options)
initPropsObserver(options)
}
return MPComponent(options)
}
import { addMiniProgramPageJson } from '@dcloudio/uni-cli-shared'
import { assert } from './testUtils'
describe('mp-kuaishou: transform component', () => {
......@@ -17,4 +18,22 @@ describe('mp-kuaishou: transform component', () => {
}`
)
})
test(`mini program component`, () => {
const filename = 'pages/vant/vant'
addMiniProgramPageJson(filename, {
usingComponents: {
'van-button': 'kscomponents/button/index',
},
})
assert(
`<van-button/>`,
`<van-button u-t="m" u-i="dc555fe4-0" bind:__l="__l"/>`,
`(_ctx, _cache) => {
return {}
}`,
{
filename,
}
)
})
})
......@@ -137,6 +137,7 @@ const compilerOptions = {
nodeTransforms,
directiveTransforms,
};
const COMPONENTS_DIR = 'kscomponents';
const miniProgram = {
class: {
array: false,
......@@ -149,6 +150,9 @@ const miniProgram = {
lazyElement: {
switch: [{ name: 'on', arg: ['change'] }],
},
component: {
dir: COMPONENTS_DIR,
},
};
const projectConfigFilename = 'project.config.json';
const options = {
......@@ -161,7 +165,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['kscomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'ks',
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize } from '@vue/shared';
import { injectHook, ref, nextTick, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, nextTick, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
const ON_READY$1 = 'onReady';
......@@ -594,6 +594,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -620,11 +625,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -633,15 +640,28 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.properties.uP.observer = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
const prevProps = toRaw(instance.props);
const nextProps = findComponentPropsData(up) || {};
......@@ -651,9 +671,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -696,14 +716,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -746,7 +764,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -835,6 +853,12 @@ Page = function (options) {
};
Component = function (options) {
initHook('created', options);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -21,7 +21,7 @@ export const compilerOptions: CompilerOptions = {
nodeTransforms,
directiveTransforms,
}
const COMPONENTS_DIR = 'kscomponents'
export const miniProgram: MiniProgramCompilerOptions = {
class: {
array: false,
......@@ -34,6 +34,9 @@ export const miniProgram: MiniProgramCompilerOptions = {
lazyElement: {
switch: [{ name: 'on', arg: ['change'] }],
},
component: {
dir: COMPONENTS_DIR,
},
}
const projectConfigFilename = 'project.config.json'
......@@ -47,7 +50,7 @@ export const options: UniMiniProgramPluginOptions = {
'uni-mp-runtime': path.resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['kscomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'ks',
......
......@@ -72,6 +72,7 @@ const nodeTransforms = [
const compilerOptions = {
nodeTransforms,
};
const COMPONENTS_DIR = 'ttcomponents';
const miniProgram = {
class: {
array: false,
......@@ -82,6 +83,7 @@ const miniProgram = {
},
directive: 'tt:',
component: {
dir: COMPONENTS_DIR,
vShow: uniCliShared.COMPONENT_CUSTOM_HIDDEN_BIND,
},
};
......@@ -95,7 +97,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['ttcomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'tt',
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize, isObject } from '@vue/shared';
import { injectHook, ref, nextTick, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, nextTick, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
const ON_READY$1 = 'onReady';
......@@ -559,6 +559,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -585,11 +590,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -598,15 +605,28 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.properties.uP.observer = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
const prevProps = toRaw(instance.props);
const nextProps = findComponentPropsData(up) || {};
......@@ -616,9 +636,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -661,14 +681,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -711,7 +729,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -804,6 +822,12 @@ Page = function (options) {
};
Component = function (options) {
initHook('created', options, true);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -90,6 +90,7 @@ const nodeTransforms = [
const compilerOptions = {
nodeTransforms,
};
const COMPONENTS_DIR = 'wxcomponents';
const miniProgram = {
class: {
array: true,
......@@ -100,6 +101,7 @@ const miniProgram = {
},
directive: 'qq:',
component: {
dir: COMPONENTS_DIR,
vShow: uniCliShared.COMPONENT_CUSTOM_HIDDEN,
getPropertySync: false, // 为了避免 Setting data field "uP" to undefined is invalid 警告
},
......@@ -114,7 +116,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['wxcomponents'],
assets: [COMPONENTS_DIR],
targets: [
{
src: ['custom-tab-bar'],
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize } from '@vue/shared';
import { injectHook, ref, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
const ON_READY$1 = 'onReady';
......@@ -545,6 +545,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -571,11 +576,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -584,15 +591,29 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.observers = {
uP: observe,
};
if (!componentOptions.observers) {
componentOptions.observers = {};
}
componentOptions.observers.uP = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
......@@ -604,9 +625,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -649,14 +670,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -699,7 +718,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -794,6 +813,12 @@ Page = function (options) {
};
Component = function (options) {
initHook('created', options);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -20,6 +20,7 @@ const nodeTransforms = [
export const compilerOptions: CompilerOptions = {
nodeTransforms,
}
const COMPONENTS_DIR = 'wxcomponents'
export const miniProgram: MiniProgramCompilerOptions = {
class: {
......@@ -31,6 +32,7 @@ export const miniProgram: MiniProgramCompilerOptions = {
},
directive: 'qq:',
component: {
dir: COMPONENTS_DIR,
vShow: COMPONENT_CUSTOM_HIDDEN,
getPropertySync: false, // 为了避免 Setting data field "uP" to undefined is invalid 警告
},
......@@ -46,7 +48,7 @@ export const options: UniMiniProgramPluginOptions = {
'uni-mp-runtime': path.resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['wxcomponents'],
assets: [COMPONENTS_DIR],
targets: [
{
src: ['custom-tab-bar'],
......
import { addMiniProgramPageJson } from '@dcloudio/uni-cli-shared'
import { assert } from './testUtils'
describe('mp-baidu: transform component', () => {
......@@ -19,4 +20,22 @@ describe('mp-baidu: transform component', () => {
}`
)
})
test(`mini program component`, () => {
const filename = 'pages/vant/vant'
addMiniProgramPageJson(filename, {
usingComponents: {
'van-button': 'ttcomponents/button/index',
},
})
assert(
`<van-button/>`,
`<van-button u-t="m" u-i="dc555fe4-0" bind:__l="__l"/>`,
`(_ctx, _cache) => {
return {}
}`,
{
filename,
}
)
})
})
......@@ -72,6 +72,7 @@ const nodeTransforms = [
const compilerOptions = {
nodeTransforms,
};
const COMPONENTS_DIR = 'ttcomponents';
const miniProgram = {
class: {
array: false,
......@@ -82,6 +83,7 @@ const miniProgram = {
},
directive: 'tt:',
component: {
dir: COMPONENTS_DIR,
vShow: uniCliShared.COMPONENT_CUSTOM_HIDDEN_BIND,
},
};
......@@ -95,7 +97,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['ttcomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'tt',
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize, isObject } from '@vue/shared';
import { injectHook, ref, nextTick, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, nextTick, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
const ON_READY$1 = 'onReady';
......@@ -559,6 +559,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -585,11 +590,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -598,15 +605,28 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.properties.uP.observer = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
const prevProps = toRaw(instance.props);
const nextProps = findComponentPropsData(up) || {};
......@@ -616,9 +636,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -661,14 +681,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -711,7 +729,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -804,6 +822,12 @@ Page = function (options) {
};
Component = function (options) {
initHook('created', options, true);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -24,6 +24,7 @@ const nodeTransforms = [
export const compilerOptions: CompilerOptions = {
nodeTransforms,
}
const COMPONENTS_DIR = 'ttcomponents'
export const miniProgram: MiniProgramCompilerOptions = {
class: {
array: false,
......@@ -34,6 +35,7 @@ export const miniProgram: MiniProgramCompilerOptions = {
},
directive: 'tt:',
component: {
dir: COMPONENTS_DIR,
vShow: COMPONENT_CUSTOM_HIDDEN_BIND,
},
}
......@@ -48,7 +50,7 @@ export const options: UniMiniProgramPluginOptions = {
'uni-mp-runtime': path.resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['ttcomponents'],
assets: [COMPONENTS_DIR],
},
},
global: 'tt',
......
......@@ -28,6 +28,7 @@ export function uniOptions({
copyOptions,
compiler: compiler as TemplateCompiler,
compilerOptions: {
root: process.env.UNI_INPUT_DIR,
miniProgram,
isNativeTag,
isCustomElement: createIsCustomElement(customElements),
......
......@@ -62,6 +62,7 @@ const customElements = ['page-meta', 'navigation-bar', 'match-media'];
const compilerOptions = {
nodeTransforms: [uniCliShared.transformRef, uniCliShared.transformComponentLink],
};
const COMPONENTS_DIR = 'wxcomponents';
const miniProgram = {
class: {
array: true,
......@@ -76,6 +77,7 @@ const miniProgram = {
editor: [{ name: 'on', arg: ['ready'] }],
},
component: {
dir: COMPONENTS_DIR,
vShow: uniCliShared.COMPONENT_CUSTOM_HIDDEN,
getPropertySync: false, // 为了避免 Setting data field "uP" to undefined is invalid 警告
},
......@@ -91,7 +93,7 @@ const options = {
'uni-mp-runtime': path__default["default"].resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['wxcomponents'],
assets: [COMPONENTS_DIR],
targets: [
...(process.env.UNI_MP_PLUGIN ? [uniCliShared.copyMiniProgramPluginJson] : []),
{
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize } from '@vue/shared';
import { injectHook, ref, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
// quickapp-webview 不能使用 default 作为插槽名称
const SLOT_DEFAULT_NAME = 'd';
......@@ -417,6 +417,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -443,11 +448,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -456,15 +463,29 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.observers = {
uP: observe,
};
if (!componentOptions.observers) {
componentOptions.observers = {};
}
componentOptions.observers.uP = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
......@@ -476,9 +497,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -521,14 +542,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -571,7 +590,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -668,6 +687,12 @@ Page = function (options) {
};
Component = function (options) {
initHook('created', options);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -17,6 +17,8 @@ export const compilerOptions: CompilerOptions = {
nodeTransforms: [transformRef, transformComponentLink],
}
const COMPONENTS_DIR = 'wxcomponents'
export const miniProgram: MiniProgramCompilerOptions = {
class: {
array: true,
......@@ -31,6 +33,7 @@ export const miniProgram: MiniProgramCompilerOptions = {
editor: [{ name: 'on', arg: ['ready'] }],
},
component: {
dir: COMPONENTS_DIR,
vShow: COMPONENT_CUSTOM_HIDDEN,
getPropertySync: false, // 为了避免 Setting data field "uP" to undefined is invalid 警告
},
......@@ -47,7 +50,7 @@ export const options: UniMiniProgramPluginOptions = {
'uni-mp-runtime': path.resolve(__dirname, 'uni.mp.esm.js'),
},
copyOptions: {
assets: ['wxcomponents'],
assets: [COMPONENTS_DIR],
targets: [
...(process.env.UNI_MP_PLUGIN ? [copyMiniProgramPluginJson] : []),
{
......
import { isPlainObject, isArray, hasOwn, isFunction, extend, camelize, isObject } from '@vue/shared';
import { injectHook, ref, nextTick, toRaw, findComponentPropsData, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
import { injectHook, ref, nextTick, findComponentPropsData, toRaw, updateProps, invalidateJob, pruneComponentPropsCache } from 'vue';
const ON_READY$1 = 'onReady';
......@@ -537,6 +537,11 @@ function initDefaultProps(isBehavior = false) {
type: null,
value: '',
};
// 组件类型 m: 小程序组件
properties.uT = {
type: null,
value: '',
};
// 组件 props
properties.uP = {
type: null,
......@@ -563,11 +568,13 @@ function initDefaultProps(isBehavior = false) {
/**
*
* @param mpComponentOptions
* @param rawProps
* @param isBehavior
*/
function initProps(mpComponentOptions, _rawProps, isBehavior = false) {
mpComponentOptions.properties = initDefaultProps(isBehavior);
function initProps(mpComponentOptions) {
if (!mpComponentOptions.properties) {
mpComponentOptions.properties = {};
}
extend(mpComponentOptions.properties, initDefaultProps());
}
function initData(_) {
......@@ -576,15 +583,28 @@ function initData(_) {
function initPropsObserver(componentOptions) {
const observe = function observe() {
const up = this.properties.uP;
if (!up || !this.$vm) {
if (!up) {
return;
}
updateComponentProps(up, this.$vm.$);
if (this.$vm) {
updateComponentProps(up, this.$vm.$);
}
else if (this.properties.uT === 'm') {
// 小程序组件
updateMiniProgramComponentProperties(up, this);
}
};
{
componentOptions.properties.uP.observer = observe;
}
}
function updateMiniProgramComponentProperties(up, mpInstance) {
const prevProps = mpInstance.properties;
const nextProps = findComponentPropsData(up) || {};
if (hasPropsChanged(prevProps, nextProps, false)) {
mpInstance.setData(nextProps);
}
}
function updateComponentProps(up, instance) {
const prevProps = toRaw(instance.props);
const nextProps = findComponentPropsData(up) || {};
......@@ -594,9 +614,9 @@ function updateComponentProps(up, instance) {
instance.update();
}
}
function hasPropsChanged(prevProps, nextProps) {
function hasPropsChanged(prevProps, nextProps, checkLen = true) {
const nextKeys = Object.keys(nextProps);
if (nextKeys.length !== Object.keys(prevProps).length) {
if (checkLen && nextKeys.length !== Object.keys(prevProps).length) {
return true;
}
for (let i = 0; i < nextKeys.length; i++) {
......@@ -639,14 +659,12 @@ function initBehaviors(vueOptions, initBehavior) {
}
if (vueExtends && vueExtends.props) {
const behavior = {};
initProps(behavior, vueExtends.props, true);
behaviors.push(initBehavior(behavior));
}
if (isArray(vueMixins)) {
vueMixins.forEach((vueMixin) => {
if (vueMixin.props) {
const behavior = {};
initProps(behavior, vueMixin.props, true);
behaviors.push(initBehavior(behavior));
}
});
......@@ -689,7 +707,7 @@ function parseComponent(vueOptions, { parse, mocks, isPage, initRelation, handle
if (__VUE_OPTIONS_API__) {
applyOptions(mpComponentOptions, vueOptions, initBehavior);
}
initProps(mpComponentOptions, vueOptions.props, false);
initProps(mpComponentOptions, vueOptions.props);
initPropsObserver(mpComponentOptions);
initExtraOptions(mpComponentOptions, vueOptions);
initWxsCallMethods(mpComponentOptions.methods, vueOptions.wxsCallMethods);
......@@ -778,6 +796,12 @@ Page = function (options) {
};
Component = function (options) {
initHook('created', options);
// 小程序组件
const isVueComponent = options.properties && options.properties.uP;
if (!isVueComponent) {
initProps(options);
initPropsObserver(options);
}
return MPComponent(options);
};
......
......@@ -97,6 +97,8 @@ export function initPluginVueOptions(
// App,MP 平台不支持使用静态节点
compilerOptions.hoistStatic = false
// 小程序使用了
;(compilerOptions as any).root = process.env.UNI_INPUT_DIR
return vueOptions
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册