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

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

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