diff --git a/packages/uni-app/dist/uni-app.cjs.js b/packages/uni-app/dist/uni-app.cjs.js index 0f5e41198c5b180f754c2d330bebc3d4009582d1..38d586e886b293d60524303303fbe73e257acb4f 100644 --- a/packages/uni-app/dist/uni-app.cjs.js +++ b/packages/uni-app/dist/uni-app.cjs.js @@ -33,7 +33,7 @@ function proxy(target, track, trigger) { const globalData = {}; const ssrServerRef = (value, key, shallow = false) => { assertKey(key, shallow); - const ctx = vue.useSSRContext(); + const ctx = vue.getCurrentInstance() && vue.useSSRContext(); let state; if (ctx) { const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {}); diff --git a/packages/uni-app/src/ssr.ts b/packages/uni-app/src/ssr.ts index d9b937ccd8ec535e46cc62243654e1e89be3d968..903d570b2ece6d7bdc3ee4f9728a3970b695e0a4 100644 --- a/packages/uni-app/src/ssr.ts +++ b/packages/uni-app/src/ssr.ts @@ -76,7 +76,7 @@ const globalData: Record = {} const ssrServerRef: SSRRef = (value, key, shallow = false) => { assertKey(key, shallow) - const ctx = useSSRContext() + const ctx = getCurrentInstance() && useSSRContext() let state: Record if (ctx) { const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {}) diff --git a/packages/uni-cli-shared/src/json/manifest.ts b/packages/uni-cli-shared/src/json/manifest.ts index b01ada0259322c1a97cc8ee63d8802e1da58f387..9259c422d0676ae84404f435518ba856ce30dc64 100644 --- a/packages/uni-cli-shared/src/json/manifest.ts +++ b/packages/uni-cli-shared/src/json/manifest.ts @@ -1,7 +1,8 @@ import fs from 'fs' import path from 'path' -import { once } from '@dcloudio/uni-shared' +import { extend } from '@vue/shared' +import { once, defaultRpx2Unit } from '@dcloudio/uni-shared' import { parseJson } from './json' @@ -12,3 +13,8 @@ export const parseManifestJson = (inputDir: string) => { } export const parseManifestJsonOnce = once(parseManifestJson) + +export const parseRpx2UnitOnce = once((inputDir: string) => { + const { h5 } = parseManifestJsonOnce(inputDir) + return extend({}, defaultRpx2Unit, (h5 && h5.rpx) || {}) +}) diff --git a/packages/uni-shared/__tests__/dom/style.spec.ts b/packages/uni-shared/__tests__/dom/style.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6f855be0bd8ec191f4c6f3adb94acd073934e1e --- /dev/null +++ b/packages/uni-shared/__tests__/dom/style.spec.ts @@ -0,0 +1,19 @@ +import { createRpx2Unit } from '../../src/dom/style' + +const defaultRpx2Unit = { + unit: 'rem', + unitRatio: 10 / 320, + unitPrecision: 5, +} +const { unit, unitRatio, unitPrecision } = defaultRpx2Unit +describe('Rpx2Unit', () => { + test('rem', () => { + const rpx2unit = createRpx2Unit(unit, unitRatio, unitPrecision) + expect(rpx2unit('10')).toBe('10') + expect(rpx2unit('10rpx')).toBe('0.3125rem') + expect(rpx2unit('10upx')).toBe('0.3125rem') + expect(rpx2unit('color:red;height:10rpx;width:100px')).toBe( + 'color:red;height:0.3125rem;width:100px' + ) + }) +}) diff --git a/packages/uni-shared/dist/uni-shared.cjs.js b/packages/uni-shared/dist/uni-shared.cjs.js index 86477c2d9d4457a86cfaa9238979eb959755cfa5..75b50e7028dd18e2166bf8b627cf75f327c8d1e7 100644 --- a/packages/uni-shared/dist/uni-shared.cjs.js +++ b/packages/uni-shared/dist/uni-shared.cjs.js @@ -4,6 +4,27 @@ Object.defineProperty(exports, '__esModule', { value: true }); var shared = require('@vue/shared'); +const unitRE = new RegExp(`"[^"]+"|'[^']+'|url\\([^)]+\\)|(\\d*\\.?\\d+)[r|u]px`, 'g'); +function toFixed(number, precision) { + const multiplier = Math.pow(10, precision + 1); + const wholeNumber = Math.floor(number * multiplier); + return (Math.round(wholeNumber / 10) * 10) / multiplier; +} +const defaultRpx2Unit = { + unit: 'rem', + unitRatio: 10 / 320, + unitPrecision: 5, +}; +function createRpx2Unit(unit, unitRatio, unitPrecision) { + return (val) => val.replace(unitRE, (m, $1) => { + if (!$1) { + return m; + } + const value = toFixed(parseFloat($1) * unitRatio, unitPrecision); + return value === 0 ? '0' : `${value}${unit}`; + }); +} + function passive(passive) { return { passive }; } @@ -324,9 +345,11 @@ exports.UNI_SSR_DATA = UNI_SSR_DATA; exports.UNI_SSR_GLOBAL_DATA = UNI_SSR_GLOBAL_DATA; exports.UNI_SSR_STORE = UNI_SSR_STORE; exports.addFont = addFont; +exports.createRpx2Unit = createRpx2Unit; exports.debounce = debounce; exports.decode = decode; exports.decodedQuery = decodedQuery; +exports.defaultRpx2Unit = defaultRpx2Unit; exports.getEnvLocale = getEnvLocale; exports.getLen = getLen; exports.invokeArrayFns = invokeArrayFns; diff --git a/packages/uni-shared/dist/uni-shared.d.ts b/packages/uni-shared/dist/uni-shared.d.ts index 9ce9b81d5989da47da10424c4eb7b730da19dbe6..6a7b0d024659cee6aeab41643944b9fa34f5b903 100644 --- a/packages/uni-shared/dist/uni-shared.d.ts +++ b/packages/uni-shared/dist/uni-shared.d.ts @@ -10,6 +10,8 @@ export declare const COMPONENT_PREFIX: string; export declare const COMPONENT_SELECTOR_PREFIX = "uni-"; +export declare function createRpx2Unit(unit: string, unitRatio: number, unitPrecision: number): (val: string) => string; + export declare function debounce(fn: Function, delay: number): { (this: any): void; cancel(): void; @@ -26,6 +28,12 @@ export declare function decode(text: string | number): string; export declare function decodedQuery(query?: Record): Record; +export declare const defaultRpx2Unit: { + unit: string; + unitRatio: number; + unitPrecision: number; +}; + export declare function getEnvLocale(): string; export declare function getLen(str?: string): number; @@ -76,6 +84,8 @@ export declare function removeLeadingSlash(str: string): string; export declare const RESPONSIVE_MIN_WIDTH = 768; +export declare type Rpx2UnitOptions = typeof defaultRpx2Unit; + export declare const sanitise: (val: unknown) => any; declare function scrollTo_2(scrollTop: number | string, duration: number): void; diff --git a/packages/uni-shared/dist/uni-shared.es.js b/packages/uni-shared/dist/uni-shared.es.js index 2857a2f7549cb39aed0a194ad2d98773657279b5..bfdffedf2d4a734889ac9134ba82b5e803732460 100644 --- a/packages/uni-shared/dist/uni-shared.es.js +++ b/packages/uni-shared/dist/uni-shared.es.js @@ -1,5 +1,26 @@ import { isString, isHTMLTag, isSVGTag, isPlainObject, isArray } from '@vue/shared'; +const unitRE = new RegExp(`"[^"]+"|'[^']+'|url\\([^)]+\\)|(\\d*\\.?\\d+)[r|u]px`, 'g'); +function toFixed(number, precision) { + const multiplier = Math.pow(10, precision + 1); + const wholeNumber = Math.floor(number * multiplier); + return (Math.round(wholeNumber / 10) * 10) / multiplier; +} +const defaultRpx2Unit = { + unit: 'rem', + unitRatio: 10 / 320, + unitPrecision: 5, +}; +function createRpx2Unit(unit, unitRatio, unitPrecision) { + return (val) => val.replace(unitRE, (m, $1) => { + if (!$1) { + return m; + } + const value = toFixed(parseFloat($1) * unitRatio, unitPrecision); + return value === 0 ? '0' : `${value}${unit}`; + }); +} + function passive(passive) { return { passive }; } @@ -304,4 +325,4 @@ function getEnvLocale() { return (lang && lang.replace(/[.:].*/, '')) || 'en'; } -export { BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, NAVBAR_HEIGHT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, addFont, debounce, decode, decodedQuery, getEnvLocale, getLen, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle }; +export { BUILT_IN_TAGS, COMPONENT_NAME_PREFIX, COMPONENT_PREFIX, COMPONENT_SELECTOR_PREFIX, NAVBAR_HEIGHT, ON_REACH_BOTTOM_DISTANCE, PLUS_RE, PRIMARY_COLOR, RESPONSIVE_MIN_WIDTH, TABBAR_HEIGHT, TAGS, UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR_STORE, addFont, createRpx2Unit, debounce, decode, decodedQuery, defaultRpx2Unit, getEnvLocale, getLen, invokeArrayFns, isBuiltInComponent, isCustomElement, isNativeTag, normalizeDataset, normalizeTarget, once, parseQuery, passive, plusReady, removeLeadingSlash, sanitise, scrollTo, stringifyQuery, updateElementStyle }; diff --git a/packages/uni-shared/src/dom.ts b/packages/uni-shared/src/dom/index.ts similarity index 99% rename from packages/uni-shared/src/dom.ts rename to packages/uni-shared/src/dom/index.ts index a1b2be57d65e953b9f8e98a994c612f7215136bb..2809d1c4ea581f047be34bf212731a517c5a800c 100644 --- a/packages/uni-shared/src/dom.ts +++ b/packages/uni-shared/src/dom/index.ts @@ -1,6 +1,8 @@ import { FontFaceDescriptors } from 'css-font-loading-module' import { isString } from '@vue/shared' +export * from './style' + export function passive(passive: boolean) { return { passive } } diff --git a/packages/uni-shared/src/dom/style.ts b/packages/uni-shared/src/dom/style.ts new file mode 100644 index 0000000000000000000000000000000000000000..222e40d381664ed5735725c529c8d5916fdb2748 --- /dev/null +++ b/packages/uni-shared/src/dom/style.ts @@ -0,0 +1,33 @@ +const unitRE = new RegExp( + `"[^"]+"|'[^']+'|url\\([^)]+\\)|(\\d*\\.?\\d+)[r|u]px`, + 'g' +) + +function toFixed(number: number, precision: number) { + const multiplier = Math.pow(10, precision + 1) + const wholeNumber = Math.floor(number * multiplier) + return (Math.round(wholeNumber / 10) * 10) / multiplier +} + +export const defaultRpx2Unit = { + unit: 'rem', + unitRatio: 10 / 320, + unitPrecision: 5, +} + +export type Rpx2UnitOptions = typeof defaultRpx2Unit + +export function createRpx2Unit( + unit: string, + unitRatio: number, + unitPrecision: number +) { + return (val: string) => + val.replace(unitRE, (m, $1) => { + if (!$1) { + return m + } + const value = toFixed(parseFloat($1) * unitRatio, unitPrecision) + return value === 0 ? '0' : `${value}${unit}` + }) +} diff --git a/packages/vite-plugin-uni/lib/ssr/define.js b/packages/vite-plugin-uni/lib/ssr/define.js new file mode 100644 index 0000000000000000000000000000000000000000..213c54280ede3755ca98e0abe37faed9bc2ca653 --- /dev/null +++ b/packages/vite-plugin-uni/lib/ssr/define.js @@ -0,0 +1,24 @@ +const defines = __DEFINES__ +Object.keys(defines).forEach((key) => { + const segments = key.split('.') + let target = globalThis + for (let i = 0; i < segments.length; i++) { + const segment = segments[i] + if (i === segments.length - 1) { + target[segment] = defines[key] + } else { + target = target[segment] || (target[segment] = {}) + } + } +}) +const { createRpx2Unit } = require('@dcloudio/uni-shared') +const rpx2unit = createRpx2Unit(__UNIT__, __UNIT_RATIO__, __UNIT_PRECISION__) +const shared = require('@vue/shared') +const oldStringifyStyle = shared.stringifyStyle +shared.stringifyStyle = (styles) => rpx2unit(oldStringifyStyle(styles)) +const serverRender = require('@vue/server-renderer') +const oldSsrRenderStyle = serverRender.ssrRenderStyle +serverRender.ssrRenderStyle = (raw) => + shared.isString(raw) + ? rpx2unit(oldSsrRenderStyle(raw)) + : oldSsrRenderStyle(raw) diff --git a/packages/vite-plugin-uni/lib/ssr/env.js b/packages/vite-plugin-uni/lib/ssr/env.js deleted file mode 100644 index 5c9e9ea9e9cd7ff98438970ba58a56c0f653396e..0000000000000000000000000000000000000000 --- a/packages/vite-plugin-uni/lib/ssr/env.js +++ /dev/null @@ -1,25 +0,0 @@ -const context = (() => { - if (typeof globalThis !== 'undefined') { - return globalThis - } else if (typeof self !== 'undefined') { - return self - } else if (typeof window !== 'undefined') { - return window - } else { - return Function('return this')() - } -})() -// assign defines -const defines = __DEFINES__ -Object.keys(defines).forEach((key) => { - const segments = key.split('.') - let target = context - for (let i = 0; i < segments.length; i++) { - const segment = segments[i] - if (i === segments.length - 1) { - target[segment] = defines[key] - } else { - target = target[segment] || (target[segment] = {}) - } - } -}) diff --git a/packages/vite-plugin-uni/src/config/css.ts b/packages/vite-plugin-uni/src/config/css.ts index b6a14afb33f5d2045ccef7150e1c05c5a9d86184..7a007fea8f14cb2137f6bc7d5ffc957cd386bbbf 100644 --- a/packages/vite-plugin-uni/src/config/css.ts +++ b/packages/vite-plugin-uni/src/config/css.ts @@ -2,6 +2,8 @@ import path from 'path' import fs from 'fs-extra' import { UserConfig } from 'vite' import autoprefixer from 'autoprefixer' +import { extend } from '@vue/shared' +import { parseRpx2UnitOnce } from '@dcloudio/uni-cli-shared' import { VitePluginUniResolvedOptions } from '..' import { uniapp } from '../utils' @@ -19,7 +21,12 @@ export function createCss( return { postcss: { plugins: [ - uniapp({ page: options.platform === 'h5' ? 'uni-page-body' : 'body' }), + uniapp( + extend( + { page: options.platform === 'h5' ? 'uni-page-body' : 'body' }, + parseRpx2UnitOnce(options.inputDir) + ) + ), autoprefixer(), ], }, diff --git a/packages/vite-plugin-uni/src/config/define.ts b/packages/vite-plugin-uni/src/config/define.ts index 81ec41f11b5f4e7c34aa668087fc0f8062f43962..190f4c71a43d0b4fa856710ea7a874d2a8db1f4a 100644 --- a/packages/vite-plugin-uni/src/config/define.ts +++ b/packages/vite-plugin-uni/src/config/define.ts @@ -11,7 +11,7 @@ export function createDefine( { server }: UserConfig, { command }: ConfigEnv ): UserConfig['define'] { - const features = initFeatures({ + return initFeatures({ inputDir, command, platform, @@ -19,8 +19,4 @@ export function createDefine( manifestJson: parseManifestJsonOnce(inputDir), ssr: !!(server && server.middlewareMode), }) - if (server && server.middlewareMode) { - Object.assign(globalThis, features) - } - return features } diff --git a/packages/vite-plugin-uni/src/configResolved/config.ts b/packages/vite-plugin-uni/src/configResolved/config.ts index 8e5b9493c4d382d34bfcf42218ad1d88b490df7b..015fd4621919259b8665b827cb914a04c6c0c841 100644 --- a/packages/vite-plugin-uni/src/configResolved/config.ts +++ b/packages/vite-plugin-uni/src/configResolved/config.ts @@ -1,12 +1,14 @@ import { ResolvedConfig } from 'vite' -import { parserOptions } from '@vue/compiler-dom' -import { isNativeTag } from '@dcloudio/uni-shared' +import { rewriteSsrNativeTag, rewriteSsrRenderStyle } from '../utils' + // import alias from 'module-alias' export function initConfig(config: ResolvedConfig) { - if (config.server.middlewareMode) { - // TODO compiler-ssr时,传入的 isNativeTag 会被 @vue/compiler-dom 的 isNativeTag 覆盖 - // https://github.com/vuejs/vue-next/blob/master/packages/compiler-ssr/src/index.ts#L36 - parserOptions.isNativeTag = isNativeTag + if ( + (config.command === 'serve' && config.server.middlewareMode) || + (config.command === 'build' && config.build.ssr) + ) { + rewriteSsrNativeTag() + rewriteSsrRenderStyle(process.env.UNI_INPUT_DIR) } // let ssr = (config as any).ssr as SSROptions // if (!ssr) { diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/index.ts b/packages/vite-plugin-uni/src/configResolved/plugins/index.ts index 77a3542dfd1bcd2cdc31e979ee08198a63e85419..08269e71ca0d9996681299a97a5ff272d07dc16c 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/index.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/index.ts @@ -140,7 +140,7 @@ export function initPlugins( addPlugin( plugins, - uniSSRPlugin(extend({ exclude: [...COMMON_EXCLUDE] }, options)), + uniSSRPlugin(config, extend({ exclude: [...COMMON_EXCLUDE] }, options)), 'vite:vue' ) diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts b/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts index c5157e061ff67054c3001c61ab96f199c900241d..5d06c5d555fe5f4cd66c17b3bcc3336c8bff600c 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/mainJs.ts @@ -11,7 +11,12 @@ export function uniMainJsPlugin( const mainJsPath = mainPath + '.js' const mainTsPath = mainPath + '.ts' const pagesJsonJsPath = slash(path.resolve(options.inputDir, 'pages.json.js')) - const isSSR = config.server.middlewareMode + const isSSR = + config.command === 'serve' + ? !!config.server.middlewareMode + : config.command === 'build' + ? !!(config.build.ssr || config.build.ssrManifest) + : false return { name: 'vite:uni-main-js', transform(code, id, ssr) { diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/pagesJson.ts b/packages/vite-plugin-uni/src/configResolved/plugins/pagesJson.ts index adb396105ae549402c503be25a5bfa70dc0ae4b6..a58902d87026a2b8342918514a938186ec2b1c4f 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/pagesJson.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/pagesJson.ts @@ -33,13 +33,9 @@ export function uniPagesJsonPlugin( if (id.endsWith(PAGES_JSON_JS)) { return { code: - (config.define!.__UNI_FEATURE_RPX__ - ? registerGlobalCode(ssr) - : '') + - (options.command === 'serve' - ? registerDevServerGlobalCode(ssr) - : '') + - generatePagesJsonCode(ssr, code, config, options), + (options.command === 'serve' || (options.command === 'build' && ssr) + ? registerGlobalCode(config, ssr) + : '') + generatePagesJsonCode(ssr, code, config, options), map: { mappings: '' }, } } @@ -100,15 +96,15 @@ function getGlobal(ssr?: boolean) { return ssr ? 'global' : 'window' } -function registerGlobalCode(ssr?: boolean) { +function registerGlobalCode(config: ResolvedConfig, ssr?: boolean) { const name = getGlobal(ssr) - return `import {upx2px} from '@dcloudio/uni-h5' -${name}.rpx2px = upx2px + const rpx2pxCode = config.define!.__UNI_FEATURE_RPX__ + ? `import {upx2px} from '@dcloudio/uni-h5' + ${name}.rpx2px = upx2px ` -} -function registerDevServerGlobalCode(ssr?: boolean) { - const name = getGlobal(ssr) - return `import {uni,getCurrentPages,getApp,UniServiceJSBridge,UniViewJSBridge} from '@dcloudio/uni-h5' + : '' + return `${rpx2pxCode} +import {uni,getCurrentPages,getApp,UniServiceJSBridge,UniViewJSBridge} from '@dcloudio/uni-h5' ${name}.getApp = getApp ${name}.getCurrentPages = getCurrentPages ${name}.uni = uni diff --git a/packages/vite-plugin-uni/src/configResolved/plugins/ssr.ts b/packages/vite-plugin-uni/src/configResolved/plugins/ssr.ts index 65c5dfdae53dfb18ecf235d851a3ba1b51502205..f799a6fcbd18e0b8bc774013af2edf9e15f9805f 100644 --- a/packages/vite-plugin-uni/src/configResolved/plugins/ssr.ts +++ b/packages/vite-plugin-uni/src/configResolved/plugins/ssr.ts @@ -1,17 +1,22 @@ import path from 'path' import debug from 'debug' import crypto from 'crypto' -import { Plugin } from 'vite' +import { Plugin, ResolvedConfig } from 'vite' import { walk } from 'estree-walker' import { CallExpression } from 'estree' +import { OutputChunk } from 'rollup' import { createFilter } from '@rollup/pluginutils' import { MagicString } from '@vue/compiler-sfc' + +import { parseRpx2UnitOnce } from '@dcloudio/uni-cli-shared' + import { UniPluginFilterOptions } from '.' import { isIdentifier, isCallExpression, isMemberExpression, + generateSSRDefineCode, generateSSREntryServerCode, } from '../../utils' @@ -21,7 +26,10 @@ const KEYED_FUNC_RE = /(ssrRef|shallowSsrRef)/ const ENTRY_SERVER_JS = 'entry-server.js' -export function uniSSRPlugin(options: UniPluginFilterOptions): Plugin { +export function uniSSRPlugin( + config: ResolvedConfig, + options: UniPluginFilterOptions +): Plugin { const filter = createFilter(options.include, options.exclude) const entryServerJs = path.join(options.inputDir, ENTRY_SERVER_JS) const entryServerJsCode = generateSSREntryServerCode() @@ -73,6 +81,18 @@ export function uniSSRPlugin(options: UniPluginFilterOptions): Plugin { map: s.generateMap().toString(), } }, + generateBundle(_options, bundle) { + const chunk = bundle['entry-server.js'] as OutputChunk + if (chunk) { + chunk.code = + generateSSRDefineCode( + config.define!, + parseRpx2UnitOnce(options.inputDir) + ) + + '\n' + + chunk.code + } + }, } } diff --git a/packages/vite-plugin-uni/src/utils/features.ts b/packages/vite-plugin-uni/src/utils/features.ts index 2231f9456ab98e203b004dd19330191c6bc34712..71cda343a07d2394177dfdd9d1f83b1e35bf39de 100644 --- a/packages/vite-plugin-uni/src/utils/features.ts +++ b/packages/vite-plugin-uni/src/utils/features.ts @@ -1,7 +1,7 @@ import fs from 'fs' import path from 'path' import { ConfigEnv } from 'vite' -import { isArray } from '@vue/shared' +import { extend, isArray } from '@vue/shared' interface ProjectFeatures {} interface PagesFeatures { @@ -245,7 +245,7 @@ export function initFeatures(options: InitFeaturesOptions) { initPagesFeature(options), initProjectFeature(options) ) - return { + const features = { __UNI_FEATURE_WX__: wx, // 是否启用小程序的组件实例 API,如:selectComponent 等(uni-core/src/service/plugin/appConfig) __UNI_FEATURE_WXS__: wxs, // 是否启用 wxs 支持,如:getComponentDescriptor 等(uni-core/src/view/plugin/appConfig) __UNI_FEATURE_RPX__: rpx, // 是否启用运行时 rpx 支持 @@ -272,4 +272,9 @@ export function initFeatures(options: InitFeaturesOptions) { __UNI_FEATURE_NAVIGATIONBAR_SEARCHINPUT__: navigationBarSearchInput, // 是否启用标题栏搜索框 __UNI_FEATURE_NAVIGATIONBAR_TRANSPARENT__: navigationBarTransparent, // 是否启用透明标题栏 } + // ssr nodejs features + if (options.ssr) { + extend(globalThis, features) + } + return features } diff --git a/packages/vite-plugin-uni/src/utils/postcss.ts b/packages/vite-plugin-uni/src/utils/postcss.ts index cb5416b8b06d20b89b5b450bca6c44a15f6766f0..a04f516b4f0e4ccc894a398d37dd52cefc5ca980 100644 --- a/packages/vite-plugin-uni/src/utils/postcss.ts +++ b/packages/vite-plugin-uni/src/utils/postcss.ts @@ -2,6 +2,8 @@ import { extend } from '@vue/shared' import { rule, Rule, Declaration, Plugin } from 'postcss' import selectorParser from 'postcss-selector-parser' import { + createRpx2Unit, + defaultRpx2Unit, isBuiltInComponent, COMPONENT_SELECTOR_PREFIX, } from '@dcloudio/uni-shared' @@ -13,12 +15,12 @@ interface UniAppCssProcessorOptions { unitPrecision?: number // 单位精度,默认5 } -const defaultUniAppCssProcessorOptions = { - page: 'body', - unit: 'rem', - unitRatio: 10 / 320, - unitPrecision: 5, -} +const defaultUniAppCssProcessorOptions = extend( + { + page: 'body', + }, + defaultRpx2Unit +) const BG_PROPS = [ 'background', @@ -34,7 +36,7 @@ const BG_PROPS = [ function transform( selector: selectorParser.Node, - opts: UniAppCssProcessorOptions, + page: string, state: { bg: boolean } ) { if (selector.type !== 'tag') { @@ -44,7 +46,6 @@ function transform( if (isBuiltInComponent(value)) { selector.value = COMPONENT_SELECTOR_PREFIX + value } else if (value === 'page') { - const { page } = opts if (!page) { return } @@ -67,52 +68,40 @@ function createBodyBackgroundRule(origRule: Rule) { } } -function walkRules(opts: UniAppCssProcessorOptions) { +function walkRules(page: string) { return (rule: Rule) => { const state = { bg: false } rule.selector = selectorParser((selectors) => - selectors.walk((selector) => transform(selector, opts, state)) + selectors.walk((selector) => transform(selector, page, state)) ).processSync(rule.selector) state.bg && createBodyBackgroundRule(rule) } } -const unitRE = new RegExp( - `"[^"]+"|'[^']+'|url\\([^)]+\\)|(\\d*\\.?\\d+)[r|u]px`, - 'g' -) - -function toFixed(number: number, precision: number) { - const multiplier = Math.pow(10, precision + 1) - const wholeNumber = Math.floor(number * multiplier) - return (Math.round(wholeNumber / 10) * 10) / multiplier -} - -function walkDecls(opts: Required) { +function walkDecls(rpx2unit: ReturnType) { return (decl: Declaration) => { const { value } = decl if (value.indexOf('rpx') === -1 && value.indexOf('upx') === -1) { return } - decl.value = decl.value.replace(unitRE, (m, $1) => { - if (!$1) { - return m - } - const value = toFixed(parseFloat($1) * opts.unitRatio, opts.unitPrecision) - return value === 0 ? '0' : `${value}${opts.unit}` - }) + decl.value = rpx2unit(decl.value) } } export const uniapp = (opts?: UniAppCssProcessorOptions) => { - const options = extend({}, defaultUniAppCssProcessorOptions, opts || {}) + const { page, unit, unitRatio, unitPrecision } = extend( + {}, + defaultUniAppCssProcessorOptions, + opts || {} + ) + const rpx2unit = createRpx2Unit(unit, unitRatio, unitPrecision) return { postcssPlugin: 'uni-app', prepare() { return { OnceExit(root) { - root.walkDecls(walkDecls(options)) - root.walkRules(walkRules(options)) + root.walkDecls(walkDecls(rpx2unit)) + root.walkRules(walkRules(page)) }, } }, diff --git a/packages/vite-plugin-uni/src/utils/ssr.ts b/packages/vite-plugin-uni/src/utils/ssr.ts index c9d64681005885bc895b01f2c58dc428b3f83e9b..bc52d4249c47aad15c4a09f1754abf0ee8cfc75d 100644 --- a/packages/vite-plugin-uni/src/utils/ssr.ts +++ b/packages/vite-plugin-uni/src/utils/ssr.ts @@ -1,21 +1,36 @@ import path from 'path' import fs from 'fs-extra' +import { isString, NormalizedStyle } from '@vue/shared' +import { + isNativeTag, + createRpx2Unit, + Rpx2UnitOptions, +} from '@dcloudio/uni-shared' +import { parseRpx2UnitOnce } from '@dcloudio/uni-cli-shared' function serializeDefine(define: Record): string { let res = `{` for (const key in define) { const val = define[key] res += `${JSON.stringify(key)}: ${ - typeof val === 'string' ? `(${val})` : JSON.stringify(val) + typeof val === 'string' && !key.startsWith('process.env.') // process.env.* 必须序列化为字符串 + ? `(${val})` + : JSON.stringify(val) }, ` } return res + `}` } -export function generateSSREnvCode(define: Record): string { +export function generateSSRDefineCode( + define: Record, + { unit, unitRatio, unitPrecision }: Rpx2UnitOptions +): string { return fs - .readFileSync(path.join(__dirname, '../../lib/ssr/env.js'), 'utf8') + .readFileSync(path.join(__dirname, '../../lib/ssr/define.js'), 'utf8') .replace('__DEFINES__', serializeDefine(define)) + .replace('__UNIT__', JSON.stringify(unit)) + .replace('__UNIT_RATIO__', JSON.stringify(unitRatio)) + .replace('__UNIT_PRECISION__', JSON.stringify(unitPrecision)) } export function generateSSREntryServerCode() { @@ -24,3 +39,24 @@ export function generateSSREntryServerCode() { 'utf8' ) } + +export function rewriteSsrNativeTag() { + const { parserOptions } = require('@vue/compiler-dom') + // TODO compiler-ssr时,传入的 isNativeTag 会被 @vue/compiler-dom 的 isNativeTag 覆盖 + // https://github.com/vuejs/vue-next/blob/master/packages/compiler-ssr/src/index.ts#L36 + parserOptions.isNativeTag = isNativeTag +} + +export function rewriteSsrRenderStyle(inputDir: string) { + const { unit, unitRatio, unitPrecision } = parseRpx2UnitOnce(inputDir) + const rpx2unit = createRpx2Unit(unit, unitRatio, unitPrecision) + const shared = require('@vue/shared') + const oldStringifyStyle = shared.stringifyStyle + shared.stringifyStyle = (styles: NormalizedStyle | undefined) => + rpx2unit(oldStringifyStyle(styles)) + const serverRender = require('@vue/server-renderer') + const oldSsrRenderStyle = serverRender.ssrRenderStyle + // 仅对字符串类型做转换,非字符串类型,通过 stringifyStyle 转换 + serverRender.ssrRenderStyle = (raw: unknown) => + isString(raw) ? rpx2unit(oldSsrRenderStyle(raw)) : oldSsrRenderStyle(raw) +}