From 1784df6f8292623220470ded417486d921e8771e Mon Sep 17 00:00:00 2001 From: fxy060608 Date: Mon, 26 Apr 2021 21:10:25 +0800 Subject: [PATCH] feat: renderjs --- .../uni-core/src/view/plugin/appConfig.ts | 13 +- .../uni-core/src/view/plugin/componentWxs.ts | 8 +- packages/uni-h5-vue/dist/vue.runtime.esm.js | 23 ++++ packages/uni-h5-vue/lib/vue.runtime.esm.js | 23 ++++ packages/uni-h5/dist/uni-h5.esm.js | 17 +-- .../src/service/api/ui/popup/showToast.ts | 2 +- .../__tests__/__snapshots__/wxs.spec.ts.snap | 53 ++++++++ .../vite-plugin-uni/__tests__/wxs.spec.ts | 50 ++++++++ .../src/configResolved/plugins/easycom.ts | 5 +- .../src/configResolved/plugins/index.ts | 8 +- .../src/configResolved/plugins/pre.ts | 9 +- .../src/configResolved/plugins/preVue.ts | 115 ++++++++++++++++++ .../src/configResolved/plugins/renderjs.ts | 36 ++++++ .../src/vue/compilerOptions.ts | 13 +- .../src/vue/transforms/matchMedia.ts | 10 ++ 15 files changed, 345 insertions(+), 40 deletions(-) create mode 100644 packages/vite-plugin-uni/__tests__/__snapshots__/wxs.spec.ts.snap create mode 100644 packages/vite-plugin-uni/__tests__/wxs.spec.ts create mode 100644 packages/vite-plugin-uni/src/configResolved/plugins/preVue.ts create mode 100644 packages/vite-plugin-uni/src/configResolved/plugins/renderjs.ts create mode 100644 packages/vite-plugin-uni/src/vue/transforms/matchMedia.ts diff --git a/packages/uni-core/src/view/plugin/appConfig.ts b/packages/uni-core/src/view/plugin/appConfig.ts index 05ceaf9b3c..7b7c073948 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 40d48d3596..ab44ea3535 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 bf0db7cbeb..edcc810e7f 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 4298b6cdaa..3b31283c3a 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 06b8c4a261..856f9f491d 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 395a874153..77d0b8f19f 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 0000000000..aa01416988 --- /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 0000000000..0c18f8505d --- /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 060fcb85c9..4cf7a2a2e2 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 0662f4a384..04692ad2b1 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 174f924cfc..6d9a4b8934 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 0000000000..93a6a75e6d --- /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 0000000000..5129f58318 --- /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 4b91493562..e39aa0a422 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 0000000000..99a9f872e0 --- /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' + } +} -- GitLab