diff --git a/packages/uni-core/src/view/plugin/appConfig.ts b/packages/uni-core/src/view/plugin/appConfig.ts index 05ceaf9b3cba773cd05944d4aabd5969399c7bb2..7b7c073948e1bc596cb9306a468d9a058973a873 100644 --- a/packages/uni-core/src/view/plugin/appConfig.ts +++ b/packages/uni-core/src/view/plugin/appConfig.ts @@ -9,12 +9,13 @@ export function initAppConfig(appConfig: AppConfig) { const globalProperties = appConfig.globalProperties extend(globalProperties, instance) if (__UNI_FEATURE_WXS__) { - globalProperties.getComponentDescriptor = getComponentDescriptor - Object.defineProperty(globalProperties, '$ownerInstance', { - get() { - return this.$getComponentDescriptor(this) - }, - }) + //$getComponentDescriptor + globalProperties.$gcd = getComponentDescriptor + // Object.defineProperty(globalProperties, '$ownerInstance', { + // get() { + // return getComponentDescriptor(this) + // }, + // }) // globalProperties.$handleWxsEvent = handleWxsEvent } } diff --git a/packages/uni-core/src/view/plugin/componentWxs.ts b/packages/uni-core/src/view/plugin/componentWxs.ts index 40d48d35960ccce88e13fc8bf69280eaa380d986..ab44ea3535c04158d7dc651b734c4f5c60b5f13c 100644 --- a/packages/uni-core/src/view/plugin/componentWxs.ts +++ b/packages/uni-core/src/view/plugin/componentWxs.ts @@ -1,5 +1,6 @@ import { ComponentPublicInstance } from 'vue' -import { isFunction, isPlainObject } from '@vue/shared' +import { hyphenate, isFunction, isPlainObject } from '@vue/shared' +import { isBuiltInComponent } from '@dcloudio/uni-shared' // import { normalizeEvent, findUniTarget } from './componentEvents' interface WxsElement extends HTMLElement { @@ -192,7 +193,7 @@ function createComponentDescriptor( isOwnerInstance && vm && vm.$options.name && - vm.$options.name.indexOf('VUni') === 0 + isBuiltInComponent(hyphenate(vm.$options.name)) ) { // ownerInstance 内置组件需要使用父 vm vm = vm.$parent! @@ -206,11 +207,10 @@ function createComponentDescriptor( } export function getComponentDescriptor( - this: ComponentPublicInstance, instance: ComponentPublicInstance, isOwnerInstance: boolean ) { - return createComponentDescriptor(instance || this, isOwnerInstance) + return createComponentDescriptor(instance, isOwnerInstance) } // export function handleWxsEvent(this: ComponentPublicInstance, $event: Event) { diff --git a/packages/uni-h5-vue/dist/vue.runtime.esm.js b/packages/uni-h5-vue/dist/vue.runtime.esm.js index bf0db7cbeb45f5dd9b52a862ec505b35493c8c0d..edcc810e7f453e2fa29f96c3e38a0a51d3c0f158 100644 --- a/packages/uni-h5-vue/dist/vue.runtime.esm.js +++ b/packages/uni-h5-vue/dist/vue.runtime.esm.js @@ -8424,9 +8424,32 @@ function patchStopImmediatePropagation(e, value) { } } +function patchWxs(el, rawName, nextValue, instance = null) { + if (!el.__wxsWatches) { + el.__wxsWatches = {}; + } + if (!nextValue) { + return el.__wxsWatches[rawName] && el.__wxsWatches[rawName](); + } + if (!el.__wxsWatches[rawName] && instance && instance.proxy) { + const proxy = instance.proxy; + const name = rawName.split(':')[1]; + el.__wxsWatches[rawName] = proxy.$watch(() => instance.attrs[name], (value, oldValue) => { + // TODO ownerInstance,instance + nextValue(value, oldValue, proxy.$gcd(proxy, true), proxy.$gcd(proxy, true)); + }, { + deep: true + }); + } +} + const nativeOnRE = /^on[a-z]/; const forcePatchProp = (_, key) => key === 'value'; const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => { + // @ts-expect-error fixed by xxxxxx + if (__UNI_FEATURE_WXS__ && key.indexOf('change:') === 0) { + patchWxs(el, key, nextValue, parentComponent); + } switch (key) { // special case 'class': diff --git a/packages/uni-h5-vue/lib/vue.runtime.esm.js b/packages/uni-h5-vue/lib/vue.runtime.esm.js index 4298b6cdaae38e4ce7c242d61ca56275c419437d..3b31283c3a18cf4a8f0000487bd9d70d78c7db8a 100644 --- a/packages/uni-h5-vue/lib/vue.runtime.esm.js +++ b/packages/uni-h5-vue/lib/vue.runtime.esm.js @@ -8424,9 +8424,32 @@ function patchStopImmediatePropagation(e, value) { } } +function patchWxs(el, rawName, nextValue, instance = null) { + if (!el.__wxsWatches) { + el.__wxsWatches = {}; + } + if (!nextValue) { + return el.__wxsWatches[rawName] && el.__wxsWatches[rawName](); + } + if (!el.__wxsWatches[rawName] && instance && instance.proxy) { + const proxy = instance.proxy; + const name = rawName.split(':')[1]; + el.__wxsWatches[rawName] = proxy.$watch(() => instance.attrs[name], (value, oldValue) => { + // TODO ownerInstance,instance + nextValue(value, oldValue, proxy.$gcd(proxy, true), proxy.$gcd(proxy, true)); + }, { + deep: true + }); + } +} + const nativeOnRE = /^on[a-z]/; const forcePatchProp = (_, key) => key === 'value'; const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => { + // @ts-expect-error fixed by xxxxxx + if (__UNI_FEATURE_WXS__ && key.indexOf('change:') === 0) { + patchWxs(el, key, nextValue, parentComponent); + } switch (key) { // special case 'class': diff --git a/packages/uni-h5/dist/uni-h5.esm.js b/packages/uni-h5/dist/uni-h5.esm.js index 06b8c4a2612f2151bc7cdb37f559b9660ae55b41..856f9f491d6781c9cb72546328adf991c5fdfa1e 100644 --- a/packages/uni-h5/dist/uni-h5.esm.js +++ b/packages/uni-h5/dist/uni-h5.esm.js @@ -1,6 +1,6 @@ -import {isFunction, extend, isPlainObject, isString, isArray, hasOwn as hasOwn$1, isObject as isObject$1, capitalize, toRawType, makeMap as makeMap$1, isPromise, invokeArrayFns as invokeArrayFns$1, hyphenate} from "@vue/shared"; +import {isFunction, extend, hyphenate, isPlainObject, isString, isArray, hasOwn as hasOwn$1, isObject as isObject$1, capitalize, toRawType, makeMap as makeMap$1, isPromise, invokeArrayFns as invokeArrayFns$1} from "@vue/shared"; import {injectHook, withModifiers, createVNode, inject, provide, reactive, computed, nextTick, getCurrentInstance, onBeforeMount, onMounted, onBeforeActivate, onBeforeDeactivate, openBlock, createBlock, mergeProps, toDisplayString, ref, defineComponent, resolveComponent, toHandlers, renderSlot, watch, onUnmounted, onBeforeUnmount, onActivated, withDirectives, vShow, createTextVNode, createCommentVNode, renderList, onDeactivated, createApp, watchEffect, Transition, withCtx, KeepAlive, resolveDynamicComponent, Fragment} from "vue"; -import {once, passive, normalizeTarget, invokeArrayFns, NAVBAR_HEIGHT, parseQuery, PRIMARY_COLOR, removeLeadingSlash, getLen, ON_REACH_BOTTOM_DISTANCE, decodedQuery, debounce, updateElementStyle, addFont, scrollTo} from "@dcloudio/uni-shared"; +import {once, passive, normalizeTarget, isBuiltInComponent, invokeArrayFns, NAVBAR_HEIGHT, parseQuery, PRIMARY_COLOR, removeLeadingSlash, getLen, ON_REACH_BOTTOM_DISTANCE, decodedQuery, debounce, updateElementStyle, addFont, scrollTo} from "@dcloudio/uni-shared"; import {useRoute, createRouter, createWebHistory, createWebHashHistory, useRouter, isNavigationFailure, RouterView} from "vue-router"; function applyOptions(options, instance2, publicThis) { Object.keys(options).forEach((name) => { @@ -1087,7 +1087,7 @@ class ComponentDescriptor { } } function createComponentDescriptor(vm, isOwnerInstance = true) { - if (isOwnerInstance && vm && vm.$options.name && vm.$options.name.indexOf("VUni") === 0) { + if (isOwnerInstance && vm && vm.$options.name && isBuiltInComponent(hyphenate(vm.$options.name))) { vm = vm.$parent; } if (vm && vm.$el) { @@ -1098,18 +1098,13 @@ function createComponentDescriptor(vm, isOwnerInstance = true) { } } function getComponentDescriptor(instance2, isOwnerInstance) { - return createComponentDescriptor(instance2 || this, isOwnerInstance); + return createComponentDescriptor(instance2, isOwnerInstance); } function initAppConfig$1(appConfig) { const globalProperties = appConfig.globalProperties; extend(globalProperties, instance); if (__UNI_FEATURE_WXS__) { - globalProperties.getComponentDescriptor = getComponentDescriptor; - Object.defineProperty(globalProperties, "$ownerInstance", { - get() { - return this.$getComponentDescriptor(this); - } - }); + globalProperties.$gcd = getComponentDescriptor; } } function initView(app) { @@ -13144,7 +13139,7 @@ function useToastIcon(props2) { let showToastState; let showType = ""; let timeoutId; -const onHidePopupOnce = once(() => { +const onHidePopupOnce = /* @__PURE__ */ once(() => { UniServiceJSBridge.on("onHidePopup", () => hidePopup("onHidePopup")); }); function createToast(args) { diff --git a/packages/uni-h5/src/service/api/ui/popup/showToast.ts b/packages/uni-h5/src/service/api/ui/popup/showToast.ts index 395a874153e84ac21cd717303be8111702634fe8..77d0b8f19fb85a6291ed5c3fe6254eb2a1b9080e 100644 --- a/packages/uni-h5/src/service/api/ui/popup/showToast.ts +++ b/packages/uni-h5/src/service/api/ui/popup/showToast.ts @@ -30,7 +30,7 @@ import { once } from '@dcloudio/uni-shared' let showToastState: ToastProps let showType: 'onShowToast' | 'onShowLoading' | '' = '' let timeoutId: number -const onHidePopupOnce = once(() => { +const onHidePopupOnce = /*#__PURE__*/ once(() => { UniServiceJSBridge.on('onHidePopup', () => hidePopup('onHidePopup')) }) diff --git a/packages/vite-plugin-uni/__tests__/__snapshots__/wxs.spec.ts.snap b/packages/vite-plugin-uni/__tests__/__snapshots__/wxs.spec.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..aa0141698860fd9b1c763970a600b3de331e4aea --- /dev/null +++ b/packages/vite-plugin-uni/__tests__/__snapshots__/wxs.spec.ts.snap @@ -0,0 +1,53 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`wxs normalizeWxsCode 1`] = ` +Array [ + Array [], + " + + + export default{ + mounted(){ + console.log('mounted') + } + } + + + ", +] +`; + +exports[`wxs normalizeWxsCode 2`] = ` +Array [ + Array [], + " + + + export default{ + mounted(){ + console.log('mounted') + } + } + + + ", +] +`; diff --git a/packages/vite-plugin-uni/__tests__/wxs.spec.ts b/packages/vite-plugin-uni/__tests__/wxs.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..0c18f8505dd8b42fe6268d5b66aac4f0783fde0e --- /dev/null +++ b/packages/vite-plugin-uni/__tests__/wxs.spec.ts @@ -0,0 +1,50 @@ +import { normalizeWxsCode } from '../src/configResolved/plugins/preVue' + +describe('wxs', () => { + test('normalizeWxsCode', () => { + expect( + normalizeWxsCode(` + + + + `) + ).toMatchSnapshot() + expect( + normalizeWxsCode(` + + + + `) + ).toMatchSnapshot() + }) +}) diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/easycom.ts b/packages/vite-plugin-uni/src/configResolved/plugins/easycom.ts index 060fcb85c9ba279ee29a8434c51c633f5b71e4b8..4cf7a2a2e29bb3971d7fd93314d67817c4d37277 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/easycom.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/easycom.ts @@ -68,7 +68,10 @@ export function uniEasycomPlugin(options: UniPluginFilterOptions): Plugin { return } const { filename, query } = parseVueRequest(id) - if (query.vue || !EXTNAME_VUE.includes(path.extname(filename))) { + if ( + query.type !== 'template' && + (query.vue || !EXTNAME_VUE.includes(path.extname(filename))) + ) { return } debugEasycom(id) diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/index.ts b/packages/vite-plugin-uni/src/configResolved/plugins/index.ts index 0662f4a384fed79c76644d6312b2a75ab13736b8..04692ad2b12f7d07a14024cfd57147a41ca32d34 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/index.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/index.ts @@ -15,6 +15,8 @@ import { uniPageVuePlugin } from './pageVue' import { uniCopyPlugin } from './copy' import { uniStaticPlugin } from './static' import { uniCssScopedPlugin } from './cssScoped' +import { uniRenderjsPlugin } from './renderjs' +import { uniPreVuePlugin } from './preVue' const debugPlugin = debug('uni:plugin') @@ -28,14 +30,13 @@ const UNI_H5_RE = /@dcloudio\/uni-h5/ const COMMON_EXCLUDE = [ /pages\.json\.js$/, /manifest\.json\.js$/, - /vue&type=/, /vite\//, /\/@vue\//, /\/vue-router\//, /\/vuex\//, /@dcloudio\/uni-h5-vue/, /@dcloudio\/uni-shared/, - /\.html$/, + /@dcloudio\/uni-components\/style/, ] const APP_VUE_RE = /App.vue$/ @@ -112,6 +113,9 @@ export function initPlugins( uniPreCssPlugin(Object.assign(uniPreCssPluginOptions, options)), 'vite:css' ) + addPlugin(plugins, uniPreVuePlugin(), 'vite:vue', 'pre') + addPlugin(plugins, uniRenderjsPlugin(), 'vite:vue') + if (command === 'build') { addPlugin( plugins, diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/pre.ts b/packages/vite-plugin-uni/src/configResolved/plugins/pre.ts index 174f924cfc4359999f3157403fced04c3ad25a48..6d9a4b8934c8a97bab5a786abb92d8aaea81ad8f 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/pre.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/pre.ts @@ -26,11 +26,12 @@ export function uniPrePlugin(options: UniPluginFilterOptions): Plugin { return code } const { filename, query } = parseVueRequest(id) - if (query.vue) { + if (query.vue && query.type !== 'template') { return code } const extname = path.extname(filename) - const isHtml = PRE_HTML_EXTNAME.includes(extname) + const isHtml = + query.type === 'template' || PRE_HTML_EXTNAME.includes(extname) const isJs = PRE_JS_EXTNAME.includes(extname) const isPre = isHtml || isJs if (isPre) { @@ -49,8 +50,8 @@ export function uniPrePlugin(options: UniPluginFilterOptions): Plugin { // 读取sourcemap时,需要移除?mpType=page等参数,否则读取不到提示文件不存在 const map = this.getCombinedSourcemap() if (map) { - map.sources = map.sources.map((source) => - source.replace('?mpType=page', '') + map.sources = map.sources.map( + (source) => parseVueRequest(source).filename ) } return { diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/preVue.ts b/packages/vite-plugin-uni/src/configResolved/plugins/preVue.ts new file mode 100644 index 0000000000000000000000000000000000000000..93a6a75e6de2329c968dddad57f452b13a4c3b4b --- /dev/null +++ b/packages/vite-plugin-uni/src/configResolved/plugins/preVue.ts @@ -0,0 +1,115 @@ +import path from 'path' +import debug from 'debug' +import { Plugin } from 'vite' +import { + TextModes, + NodeTypes, + ElementNode, + AttributeNode, +} from '@vue/compiler-core' +import { parse } from '@vue/compiler-dom' +import { MagicString } from '@vue/compiler-sfc' +import { EXTNAME_VUE, parseVueRequest } from '@dcloudio/uni-cli-shared' + +const debugPreVue = debug('uni:pre-vue') + +const WXS_LANG_RE = /lang=["|'](renderjs|wxs)["|']/ + +const WXS_ATTRS = ['wxs', 'renderjs'] + +const sourceToSFC = new Map() + +export function uniPreVuePlugin(): Plugin { + return { + name: 'vite:uni-pre-vue', + transform(code, id) { + const { filename, query } = parseVueRequest(id) + if (query.vue) { + return + } + if (!EXTNAME_VUE.includes(path.extname(filename))) { + return + } + if (!WXS_LANG_RE.test(code)) { + return + } + const sourceKey = code + filename + const cache = sourceToSFC.get(sourceKey) + if (cache) { + debugPreVue('cache', id) + return cache + } + debugPreVue(id) + const [errors, wxsCode] = normalizeWxsCode(code) + if (errors.length) { + this.error(errors.join('\n')) + } + sourceToSFC.set(sourceKey, wxsCode) + return wxsCode + }, + } +} + +export function normalizeWxsCode(code: string): [SyntaxError[], string] { + const errors: SyntaxError[] = [] + const ast = parse(code, { + isNativeTag: () => true, + isPreTag: () => true, + getTextMode: () => TextModes.DATA, + onError: (e) => { + errors.push(e) + }, + }) + const wxsNode = ast.children.find( + (node) => + node.type === NodeTypes.ELEMENT && + node.tag === 'script' && + node.props.find( + (prop) => + prop.name === 'lang' && + prop.type === NodeTypes.ATTRIBUTE && + prop.value && + WXS_ATTRS.includes(prop.value.content) + ) + ) + if (wxsNode) { + code = normalizeWxsNode(code, wxsNode as ElementNode) + } + return [errors, code] +} + +const SCRIPT_END_LEN = ''.length +const SCRIPT_START_LEN = ' prop.name === 'lang') as AttributeNode + const moduleAttr = props.find( + (prop) => prop.name === 'module' + ) as AttributeNode + const startOffset = loc.start.offset + const endOffset = loc.end.offset + const lang = langAttr.value!.content + const langStartOffset = langAttr.loc.start.offset + magicString.overwrite(startOffset, startOffset + SCRIPT_START_LEN, '<' + lang) // ' + ) // or + + if (moduleAttr) { + const moduleStartOffset = moduleAttr.loc.start.offset + magicString.overwrite( + moduleStartOffset, + moduleStartOffset + 'module'.length, + 'name' + ) // module="echarts" => name="echarts" + } + return magicString.toString() +} diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/renderjs.ts b/packages/vite-plugin-uni/src/configResolved/plugins/renderjs.ts new file mode 100644 index 0000000000000000000000000000000000000000..5129f58318b591a56ac962ef255ff1f3b77b2760 --- /dev/null +++ b/packages/vite-plugin-uni/src/configResolved/plugins/renderjs.ts @@ -0,0 +1,36 @@ +import debug from 'debug' +import { Plugin } from 'vite' +import { rewriteDefault } from '@vue/compiler-sfc' + +import { parseVueRequest } from '@dcloudio/uni-cli-shared' + +const debugRenderjs = debug('uni:renderjs') + +export function uniRenderjsPlugin(): Plugin { + return { + name: 'vite:uni-renderjs', + transform(code, id) { + const isWxs = /vue&type=wxs/.test(id) + const isRenderjs = /vue&type=renderjs/.test(id) + if (!isWxs && !isRenderjs) { + return + } + const type = isWxs ? 'wxs' : 'renderjs' + const { query } = parseVueRequest(id) + debugRenderjs(id) + if (!(query as any).name) { + this.error( + `` + ) + } + return `${rewriteDefault(code, '_sfc_' + type)} +export default Comp => { + if(!Comp.mixins){Comp.mixins = []} + Comp.mixins.push({beforeCreate(){ this['${(query as any).name}'] = this }}) + Comp.mixins.push(_sfc_${type}) +}` + }, + } +} diff --git a/packages/vite-plugin-uni/src/vue/compilerOptions.ts b/packages/vite-plugin-uni/src/vue/compilerOptions.ts index 4b91493562aca1782c2660f0376298cb271afdcf..e39aa0a422e11c3bff0c2c736b9dfe3ad0131ee3 100644 --- a/packages/vite-plugin-uni/src/vue/compilerOptions.ts +++ b/packages/vite-plugin-uni/src/vue/compilerOptions.ts @@ -1,21 +1,12 @@ -import { ElementNode, NodeTransform } from '@vue/compiler-core' - import { CompilerOptions, SFCTemplateCompileOptions } from '@vue/compiler-sfc' import { isNativeTag } from '@dcloudio/uni-shared' -const transform: NodeTransform = (node, ctx) => { - if ( - process.env.UNI_PLATFORM !== 'mp-weixin' && - (node as ElementNode).tag === 'match-media' - ) { - ;(node as ElementNode).tag = 'uni-match-media' - } -} +import { matchMedia } from './transforms/matchMedia' export const uniVueCompilerOptions: CompilerOptions = { isNativeTag, - nodeTransforms: [transform], + nodeTransforms: [matchMedia], } export const uniVueTransformAssetUrls: SFCTemplateCompileOptions['transformAssetUrls'] = { diff --git a/packages/vite-plugin-uni/src/vue/transforms/matchMedia.ts b/packages/vite-plugin-uni/src/vue/transforms/matchMedia.ts new file mode 100644 index 0000000000000000000000000000000000000000..99a9f872e0851604e7d438eae4c04ae13dad40dd --- /dev/null +++ b/packages/vite-plugin-uni/src/vue/transforms/matchMedia.ts @@ -0,0 +1,10 @@ +import { ElementNode, NodeTransform } from '@vue/compiler-core' + +export const matchMedia: NodeTransform = (node) => { + if ( + process.env.UNI_PLATFORM !== 'mp-weixin' && + (node as ElementNode).tag === 'match-media' + ) { + ;(node as ElementNode).tag = 'uni-match-media' + } +}