From 120354266915b003bc979b836b34cfc56b6189d4 Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Wed, 15 Dec 2021 13:30:09 +0800 Subject: [PATCH] fix(mp): support mini program components (#3071) --- .../uni-cli-shared/src/json/mp/jsonFile.ts | 66 ++++++++++++++++++- packages/uni-cli-shared/src/mp/template.ts | 4 ++ .../uni-mp-alipay/__tests__/component.spec.ts | 19 ++++++ packages/uni-mp-alipay/dist/uni.compiler.js | 4 +- packages/uni-mp-alipay/dist/uni.mp.esm.js | 21 +++--- .../uni-mp-alipay/src/compiler/options.ts | 5 +- .../uni-mp-baidu/__tests__/component.spec.ts | 19 ++++++ packages/uni-mp-baidu/dist/uni.compiler.js | 6 +- packages/uni-mp-baidu/dist/uni.mp.esm.js | 46 +++++++++---- packages/uni-mp-baidu/src/compiler/options.ts | 6 +- .../__tests__/component.spec.ts | 20 ++++++ packages/uni-mp-compiler/__tests__/test.ts | 38 ++++++++--- .../uni-mp-compiler/__tests__/testUtils.ts | 2 + packages/uni-mp-compiler/src/options.ts | 1 + packages/uni-mp-compiler/src/transform.ts | 18 ++++- .../src/transforms/transformComponent.ts | 30 +++++++-- .../uni-mp-compiler/src/transforms/utils.ts | 5 ++ .../src/runtime/componentOptions.ts | 36 +++++++--- .../uni-mp-core/src/runtime/componentProps.ts | 19 +++--- packages/uni-mp-core/src/runtime/polyfill.ts | 10 ++- .../__tests__/component.spec.ts | 19 ++++++ packages/uni-mp-kuaishou/dist/uni.compiler.js | 6 +- packages/uni-mp-kuaishou/dist/uni.mp.esm.js | 46 +++++++++---- .../uni-mp-kuaishou/src/compiler/options.ts | 7 +- packages/uni-mp-lark/dist/uni.compiler.js | 4 +- packages/uni-mp-lark/dist/uni.mp.esm.js | 46 +++++++++---- packages/uni-mp-qq/dist/uni.compiler.js | 4 +- packages/uni-mp-qq/dist/uni.mp.esm.js | 53 +++++++++++---- packages/uni-mp-qq/src/compiler/options.ts | 4 +- .../__tests__/component.spec.ts | 19 ++++++ packages/uni-mp-toutiao/dist/uni.compiler.js | 4 +- packages/uni-mp-toutiao/dist/uni.mp.esm.js | 46 +++++++++---- .../uni-mp-toutiao/src/compiler/options.ts | 4 +- packages/uni-mp-vite/src/plugin/uni/index.ts | 1 + packages/uni-mp-weixin/dist/uni.compiler.js | 4 +- packages/uni-mp-weixin/dist/uni.mp.esm.js | 53 +++++++++++---- .../uni-mp-weixin/src/compiler/options.ts | 5 +- .../uni-quickapp-webview/dist/uni.mp.esm.js | 46 +++++++++---- packages/vite-plugin-uni/src/vue/options.ts | 2 + 39 files changed, 606 insertions(+), 142 deletions(-) diff --git a/packages/uni-cli-shared/src/json/mp/jsonFile.ts b/packages/uni-cli-shared/src/json/mp/jsonFile.ts index f2dc2e5e9..a0a78b9f0 100644 --- a/packages/uni-cli-shared/src/json/mp/jsonFile.ts +++ b/packages/uni-cli-shared/src/json/mp/jsonFile.ts @@ -1,7 +1,12 @@ 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, + componentsDir: string +) { + return Object.keys(usingComponents).filter((name) => { + const path = usingComponents[name] + return path.includes(componentsDir + '/') + }) +} diff --git a/packages/uni-cli-shared/src/mp/template.ts b/packages/uni-cli-shared/src/mp/template.ts index 555b858f3..b7ddb4e9c 100644 --- a/packages/uni-cli-shared/src/mp/template.ts +++ b/packages/uni-cli-shared/src/mp/template.ts @@ -40,6 +40,10 @@ export interface MiniProgramCompilerOptions { lang: string } component?: { + /** + * 平台自定义组件目录,如 wxcomponents + */ + dir?: string /** * 自定义组件自定义 hidden 属性用于实现 v-show */ diff --git a/packages/uni-mp-alipay/__tests__/component.spec.ts b/packages/uni-mp-alipay/__tests__/component.spec.ts index 644e201a3..12ac063f0 100644 --- a/packages/uni-mp-alipay/__tests__/component.spec.ts +++ b/packages/uni-mp-alipay/__tests__/component.spec.ts @@ -1,3 +1,4 @@ +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( + ``, + ``, + `(_ctx, _cache) => { + return {} +}`, + { + filename, + } + ) + }) }) diff --git a/packages/uni-mp-alipay/dist/uni.compiler.js b/packages/uni-mp-alipay/dist/uni.compiler.js index f2c6d63cc..4ce9ebd54 100644 --- a/packages/uni-mp-alipay/dist/uni.compiler.js +++ b/packages/uni-mp-alipay/dist/uni.compiler.js @@ -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] : [], }, }, diff --git a/packages/uni-mp-alipay/dist/uni.mp.esm.js b/packages/uni-mp-alipay/dist/uni.mp.esm.js index de57b4fdb..a9b084eb7 100644 --- a/packages/uni-mp-alipay/dist/uni.mp.esm.js +++ b/packages/uni-mp-alipay/dist/uni.mp.esm.js @@ -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 diff --git a/packages/uni-mp-alipay/src/compiler/options.ts b/packages/uni-mp-alipay/src/compiler/options.ts index e7a449490..ed69f0ab8 100644 --- a/packages/uni-mp-alipay/src/compiler/options.ts +++ b/packages/uni-mp-alipay/src/compiler/options.ts @@ -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] : [], }, }, diff --git a/packages/uni-mp-baidu/__tests__/component.spec.ts b/packages/uni-mp-baidu/__tests__/component.spec.ts index e31bf7e22..2ae2c9f36 100644 --- a/packages/uni-mp-baidu/__tests__/component.spec.ts +++ b/packages/uni-mp-baidu/__tests__/component.spec.ts @@ -1,5 +1,6 @@ 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( + ``, + ``, + `(_ctx, _cache) => { + return {} +}`, + { + filename, + } + ) + }) }) diff --git a/packages/uni-mp-baidu/dist/uni.compiler.js b/packages/uni-mp-baidu/dist/uni.compiler.js index 96702ecac..c2137cd6e 100644 --- a/packages/uni-mp-baidu/dist/uni.compiler.js +++ b/packages/uni-mp-baidu/dist/uni.compiler.js @@ -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', diff --git a/packages/uni-mp-baidu/dist/uni.mp.esm.js b/packages/uni-mp-baidu/dist/uni.mp.esm.js index 8f301d13b..8cc71a307 100644 --- a/packages/uni-mp-baidu/dist/uni.mp.esm.js +++ b/packages/uni-mp-baidu/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/uni-mp-baidu/src/compiler/options.ts b/packages/uni-mp-baidu/src/compiler/options.ts index a579db6f7..a8e96b9e9 100644 --- a/packages/uni-mp-baidu/src/compiler/options.ts +++ b/packages/uni-mp-baidu/src/compiler/options.ts @@ -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', diff --git a/packages/uni-mp-compiler/__tests__/component.spec.ts b/packages/uni-mp-compiler/__tests__/component.spec.ts index c8deb0856..a3970b6b2 100644 --- a/packages/uni-mp-compiler/__tests__/component.spec.ts +++ b/packages/uni-mp-compiler/__tests__/component.spec.ts @@ -1,4 +1,5 @@ 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( + ``, + ``, + `(_ctx, _cache) => { + return {} +}`, + { + filename, + nodeTransforms, + } + ) + }) }) diff --git a/packages/uni-mp-compiler/__tests__/test.ts b/packages/uni-mp-compiler/__tests__/test.ts index 2ebbf6be0..8c1578e71 100644 --- a/packages/uni-mp-compiler/__tests__/test.ts +++ b/packages/uni-mp-compiler/__tests__/test.ts @@ -1,6 +1,9 @@ // 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( +// ``, +// ``, +// `(_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( - ``, - ``, + ``, + ``, `(_ctx, _cache) => { -return { a: _f(_ctx.items, (item, index, i0) => { return { a: _r(\"default\", { key: index }) }; }) } +return {} }`, { - inline: false, - nodeTransforms: [transformRef], + filename, + nodeTransforms: [transformComponentLink], } ) diff --git a/packages/uni-mp-compiler/__tests__/testUtils.ts b/packages/uni-mp-compiler/__tests__/testUtils.ts index d7ba93956..a6ad646fc 100644 --- a/packages/uni-mp-compiler/__tests__/testUtils.ts +++ b/packages/uni-mp-compiler/__tests__/testUtils.ts @@ -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, diff --git a/packages/uni-mp-compiler/src/options.ts b/packages/uni-mp-compiler/src/options.ts index e56bbf69a..d6acb7c4b 100644 --- a/packages/uni-mp-compiler/src/options.ts +++ b/packages/uni-mp-compiler/src/options.ts @@ -45,6 +45,7 @@ interface ParserOptions { interface SharedTransformCodegenOptions { inline?: boolean isTS?: boolean + root?: string filename?: string bindingMetadata?: BindingMetadata prefixIdentifiers?: boolean diff --git a/packages/uni-mp-compiler/src/transform.ts b/packages/uni-mp-compiler/src/transform.ts index 1b6882fd7..58c10ed86 100644 --- a/packages/uni-mp-compiler/src/transform.ts +++ b/packages/uni-mp-compiler/src/transform.ts @@ -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> { + extends Required> { 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(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) { diff --git a/packages/uni-mp-compiler/src/transforms/transformComponent.ts b/packages/uni-mp-compiler/src/transforms/transformComponent.ts index 97eeb1372..4dbcee187 100644 --- a/packages/uni-mp-compiler/src/transforms/transformComponent.ts +++ b/packages/uni-mp-compiler/src/transforms/transformComponent.ts @@ -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', diff --git a/packages/uni-mp-compiler/src/transforms/utils.ts b/packages/uni-mp-compiler/src/transforms/utils.ts index d38664712..43b7b9c71 100644 --- a/packages/uni-mp-compiler/src/transforms/utils.ts +++ b/packages/uni-mp-compiler/src/transforms/utils.ts @@ -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( diff --git a/packages/uni-mp-core/src/runtime/componentOptions.ts b/packages/uni-mp-core/src/runtime/componentOptions.ts index f47d2088b..3bb7cc94f 100644 --- a/packages/uni-mp-core/src/runtime/componentOptions.ts +++ b/packages/uni-mp-core/src/runtime/componentOptions.ts @@ -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) } }) diff --git a/packages/uni-mp-core/src/runtime/componentProps.ts b/packages/uni-mp-core/src/runtime/componentProps.ts index f69ce9310..c6e173336 100644 --- a/packages/uni-mp-core/src/runtime/componentProps.ts +++ b/packages/uni-mp-core/src/runtime/componentProps.ts @@ -1,4 +1,4 @@ -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()) } diff --git a/packages/uni-mp-core/src/runtime/polyfill.ts b/packages/uni-mp-core/src/runtime/polyfill.ts index 3503ef5dd..df4b63734 100644 --- a/packages/uni-mp-core/src/runtime/polyfill.ts +++ b/packages/uni-mp-core/src/runtime/polyfill.ts @@ -1,6 +1,8 @@ -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) } diff --git a/packages/uni-mp-kuaishou/__tests__/component.spec.ts b/packages/uni-mp-kuaishou/__tests__/component.spec.ts index c34f5d579..6b3f8eec1 100644 --- a/packages/uni-mp-kuaishou/__tests__/component.spec.ts +++ b/packages/uni-mp-kuaishou/__tests__/component.spec.ts @@ -1,3 +1,4 @@ +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( + ``, + ``, + `(_ctx, _cache) => { + return {} +}`, + { + filename, + } + ) + }) }) diff --git a/packages/uni-mp-kuaishou/dist/uni.compiler.js b/packages/uni-mp-kuaishou/dist/uni.compiler.js index 29c87a483..769f711c6 100644 --- a/packages/uni-mp-kuaishou/dist/uni.compiler.js +++ b/packages/uni-mp-kuaishou/dist/uni.compiler.js @@ -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', diff --git a/packages/uni-mp-kuaishou/dist/uni.mp.esm.js b/packages/uni-mp-kuaishou/dist/uni.mp.esm.js index 36b361529..17576d292 100644 --- a/packages/uni-mp-kuaishou/dist/uni.mp.esm.js +++ b/packages/uni-mp-kuaishou/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/uni-mp-kuaishou/src/compiler/options.ts b/packages/uni-mp-kuaishou/src/compiler/options.ts index 9b115ad36..1013ac86b 100644 --- a/packages/uni-mp-kuaishou/src/compiler/options.ts +++ b/packages/uni-mp-kuaishou/src/compiler/options.ts @@ -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', diff --git a/packages/uni-mp-lark/dist/uni.compiler.js b/packages/uni-mp-lark/dist/uni.compiler.js index 9f2ed4d61..2a687fed2 100644 --- a/packages/uni-mp-lark/dist/uni.compiler.js +++ b/packages/uni-mp-lark/dist/uni.compiler.js @@ -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', diff --git a/packages/uni-mp-lark/dist/uni.mp.esm.js b/packages/uni-mp-lark/dist/uni.mp.esm.js index 6f0cd1d90..ae424fdee 100644 --- a/packages/uni-mp-lark/dist/uni.mp.esm.js +++ b/packages/uni-mp-lark/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/uni-mp-qq/dist/uni.compiler.js b/packages/uni-mp-qq/dist/uni.compiler.js index 8f7e33726..73199331e 100644 --- a/packages/uni-mp-qq/dist/uni.compiler.js +++ b/packages/uni-mp-qq/dist/uni.compiler.js @@ -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'], diff --git a/packages/uni-mp-qq/dist/uni.mp.esm.js b/packages/uni-mp-qq/dist/uni.mp.esm.js index c5849b4de..19e71113a 100644 --- a/packages/uni-mp-qq/dist/uni.mp.esm.js +++ b/packages/uni-mp-qq/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/uni-mp-qq/src/compiler/options.ts b/packages/uni-mp-qq/src/compiler/options.ts index 85cc9d3a4..15e81b1aa 100644 --- a/packages/uni-mp-qq/src/compiler/options.ts +++ b/packages/uni-mp-qq/src/compiler/options.ts @@ -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'], diff --git a/packages/uni-mp-toutiao/__tests__/component.spec.ts b/packages/uni-mp-toutiao/__tests__/component.spec.ts index 8b3025341..678c9bbd9 100644 --- a/packages/uni-mp-toutiao/__tests__/component.spec.ts +++ b/packages/uni-mp-toutiao/__tests__/component.spec.ts @@ -1,3 +1,4 @@ +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( + ``, + ``, + `(_ctx, _cache) => { + return {} +}`, + { + filename, + } + ) + }) }) diff --git a/packages/uni-mp-toutiao/dist/uni.compiler.js b/packages/uni-mp-toutiao/dist/uni.compiler.js index f2dca6d06..eb86af9f3 100644 --- a/packages/uni-mp-toutiao/dist/uni.compiler.js +++ b/packages/uni-mp-toutiao/dist/uni.compiler.js @@ -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', diff --git a/packages/uni-mp-toutiao/dist/uni.mp.esm.js b/packages/uni-mp-toutiao/dist/uni.mp.esm.js index 8a78f0dfb..a703c43ec 100644 --- a/packages/uni-mp-toutiao/dist/uni.mp.esm.js +++ b/packages/uni-mp-toutiao/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/uni-mp-toutiao/src/compiler/options.ts b/packages/uni-mp-toutiao/src/compiler/options.ts index 674f5f831..ed320402a 100644 --- a/packages/uni-mp-toutiao/src/compiler/options.ts +++ b/packages/uni-mp-toutiao/src/compiler/options.ts @@ -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', diff --git a/packages/uni-mp-vite/src/plugin/uni/index.ts b/packages/uni-mp-vite/src/plugin/uni/index.ts index 76b4c36a1..2f5c464cf 100644 --- a/packages/uni-mp-vite/src/plugin/uni/index.ts +++ b/packages/uni-mp-vite/src/plugin/uni/index.ts @@ -28,6 +28,7 @@ export function uniOptions({ copyOptions, compiler: compiler as TemplateCompiler, compilerOptions: { + root: process.env.UNI_INPUT_DIR, miniProgram, isNativeTag, isCustomElement: createIsCustomElement(customElements), diff --git a/packages/uni-mp-weixin/dist/uni.compiler.js b/packages/uni-mp-weixin/dist/uni.compiler.js index 5552b8e0c..05ae6103e 100644 --- a/packages/uni-mp-weixin/dist/uni.compiler.js +++ b/packages/uni-mp-weixin/dist/uni.compiler.js @@ -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] : []), { diff --git a/packages/uni-mp-weixin/dist/uni.mp.esm.js b/packages/uni-mp-weixin/dist/uni.mp.esm.js index 5d997ec3c..82d1b0e9e 100644 --- a/packages/uni-mp-weixin/dist/uni.mp.esm.js +++ b/packages/uni-mp-weixin/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/uni-mp-weixin/src/compiler/options.ts b/packages/uni-mp-weixin/src/compiler/options.ts index b220c44ff..c03792b64 100644 --- a/packages/uni-mp-weixin/src/compiler/options.ts +++ b/packages/uni-mp-weixin/src/compiler/options.ts @@ -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] : []), { diff --git a/packages/uni-quickapp-webview/dist/uni.mp.esm.js b/packages/uni-quickapp-webview/dist/uni.mp.esm.js index 4e80dac82..55d8e2b95 100644 --- a/packages/uni-quickapp-webview/dist/uni.mp.esm.js +++ b/packages/uni-quickapp-webview/dist/uni.mp.esm.js @@ -1,5 +1,5 @@ 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); }; diff --git a/packages/vite-plugin-uni/src/vue/options.ts b/packages/vite-plugin-uni/src/vue/options.ts index 146cd4c5f..18b1c4d1b 100644 --- a/packages/vite-plugin-uni/src/vue/options.ts +++ b/packages/vite-plugin-uni/src/vue/options.ts @@ -97,6 +97,8 @@ export function initPluginVueOptions( // App,MP 平台不支持使用静态节点 compilerOptions.hoistStatic = false + // 小程序使用了 + ;(compilerOptions as any).root = process.env.UNI_INPUT_DIR return vueOptions } -- GitLab