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

feat(ssr): globalData

上级 46efbd97
dist
lib
\ No newline at end of file
dist
\ No newline at end of file
......@@ -7,12 +7,8 @@ var shared = require('@vue/shared');
const sanitise = (val) => (val && JSON.parse(JSON.stringify(val))) || val;
const UNI_SSR = '__uniSSR';
const UNI_SSR_DATA = 'data';
const UNI_SSR_GLOBAL_DATA = 'globalData';
const UNI_SSR_DATA = 'data';
function getSSRDataType() {
return vue.getCurrentInstance() ? UNI_SSR_DATA : UNI_SSR_GLOBAL_DATA;
}
function assertKey(key, shallow = false) {
if (!key) {
throw new Error(`${shallow ? 'shallowSsrRef' : 'ssrRef'}: You must provide a key.`);
......@@ -34,12 +30,18 @@ function proxy(target, track, trigger) {
},
});
}
const globalData = {};
const ssrServerRef = (value, key, shallow = false) => {
const type = getSSRDataType();
assertKey(key, shallow);
const ctx = vue.useSSRContext();
const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {});
const state = __uniSSR[type] || (__uniSSR[type] = {});
let state;
if (ctx) {
const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {});
state = __uniSSR[UNI_SSR_DATA] || (__uniSSR[UNI_SSR_DATA] = {});
}
else {
state = globalData;
}
// SSR 模式下 watchEffect 不生效 https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/apiWatch.ts#L253
// 故自定义ref
return vue.customRef((track, trigger) => {
......@@ -68,7 +70,10 @@ const shallowSsrRef = (value, key) => {
{
return ssrServerRef(value, key, true);
}
};
};
function getSsrGlobalData() {
return sanitise(globalData);
}
// @ts-ignore
// App and Page
......@@ -125,6 +130,7 @@ const onNavigationBarSearchInputClicked = /*#__PURE__*/ createHook(ON_NAVIGATION
const onNavigationBarSearchInputConfirmed = /*#__PURE__*/ createHook(ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED);
const onNavigationBarSearchInputFocusChanged = /*#__PURE__*/ createHook(ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED);
exports.getSsrGlobalData = getSsrGlobalData;
exports.onAddToFavorites = onAddToFavorites;
exports.onBackPress = onBackPress;
exports.onError = onError;
......
......@@ -2,6 +2,8 @@ import { ComponentInternalInstance } from 'vue';
import { ref } from 'vue';
import { shallowRef } from 'vue';
export declare function getSsrGlobalData(): any;
export declare const onAddToFavorites: (hook: () => any, target?: ComponentInternalInstance | null) => any;
export declare const onBackPress: (hook: () => any, target?: ComponentInternalInstance | null) => any;
......
import { shallowRef, ref, getCurrentInstance, isInSSRComponentSetup, injectHook } from 'vue';
import { hasOwn } from '@vue/shared';
const sanitise = (val) => (val && JSON.parse(JSON.stringify(val))) || val;
const UNI_SSR = '__uniSSR';
const UNI_SSR_DATA = 'data';
const UNI_SSR_GLOBAL_DATA = 'globalData';
......@@ -20,15 +22,24 @@ const ssrClientRef = (value, key, shallow = false) => {
}
const type = getSSRDataType();
assertKey(key, shallow);
valRef.value = __uniSSR[type][key];
if (hasOwn(__uniSSR[type], key)) {
valRef.value = __uniSSR[type][key];
if (type === UNI_SSR_DATA) {
delete __uniSSR[type][key]; // TODO 非全局数据仅使用一次?否则下次还会再次使用该数据
}
}
return valRef;
};
const globalData = {};
const ssrRef = (value, key) => {
return ssrClientRef(value, key);
};
const shallowSsrRef = (value, key) => {
return ssrClientRef(value, key, true);
};
};
function getSsrGlobalData() {
return sanitise(globalData);
}
// @ts-ignore
// App and Page
......@@ -85,4 +96,4 @@ const onNavigationBarSearchInputClicked = /*#__PURE__*/ createHook(ON_NAVIGATION
const onNavigationBarSearchInputConfirmed = /*#__PURE__*/ createHook(ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED);
const onNavigationBarSearchInputFocusChanged = /*#__PURE__*/ createHook(ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED);
export { onAddToFavorites, onBackPress, onError, onHide, onLaunch, onNavigationBarButtonTap, onNavigationBarSearchInputChanged, onNavigationBarSearchInputClicked, onNavigationBarSearchInputConfirmed, onNavigationBarSearchInputFocusChanged, onPageNotFound, onPageScroll, onPullDownRefresh, onReachBottom, onReady, onResize, onShareAppMessage, onShareTimeline, onShow, onTabItemTap, onThemeChange, onUnhandledRejection, onUnload, shallowSsrRef, ssrRef };
export { getSsrGlobalData, onAddToFavorites, onBackPress, onError, onHide, onLaunch, onNavigationBarButtonTap, onNavigationBarSearchInputChanged, onNavigationBarSearchInputClicked, onNavigationBarSearchInputConfirmed, onNavigationBarSearchInputFocusChanged, onPageNotFound, onPageScroll, onPullDownRefresh, onReachBottom, onReady, onResize, onShareAppMessage, onShareTimeline, onShow, onTabItemTap, onThemeChange, onUnhandledRejection, onUnload, shallowSsrRef, ssrRef };
......@@ -5,7 +5,7 @@ import {
useSSRContext,
getCurrentInstance,
} from 'vue'
import { isObject } from '@vue/shared'
import { hasOwn, isObject } from '@vue/shared'
import {
sanitise,
UNI_SSR,
......@@ -42,7 +42,12 @@ const ssrClientRef: SSRRef = (value, key, shallow = false) => {
}
const type = getSSRDataType()
assertKey(key, shallow)
valRef.value = (__uniSSR[type] as any)[key!]
if (hasOwn(__uniSSR[type], key!)) {
valRef.value = __uniSSR[type][key!]
if (type === UNI_SSR_DATA) {
delete __uniSSR[type][key!] // TODO 非全局数据仅使用一次?否则下次还会再次使用该数据
}
}
return valRef
}
......@@ -67,12 +72,18 @@ function proxy(
})
}
const globalData: Record<string, any> = {}
const ssrServerRef: SSRRef = (value, key, shallow = false) => {
const type = getSSRDataType()
assertKey(key, shallow)
const ctx = useSSRContext()!
const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {})
const state = __uniSSR[type] || (__uniSSR[type] = {})
const ctx = useSSRContext()
let state: Record<string, any>
if (ctx) {
const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {})
state = __uniSSR[UNI_SSR_DATA] || (__uniSSR[UNI_SSR_DATA] = {})
} else {
state = globalData
}
// SSR 模式下 watchEffect 不生效 https://github.com/vuejs/vue-next/blob/master/packages/runtime-core/src/apiWatch.ts#L253
// 故自定义ref
return customRef((track, trigger) => {
......@@ -106,3 +117,7 @@ export const shallowSsrRef: SSRRef = (value, key) => {
}
return ssrClientRef(value, key, true)
}
export function getSsrGlobalData() {
return sanitise(globalData)
}
......@@ -3457,7 +3457,7 @@ const KeepAliveImpl = {
// if the internal renderer is not registered, it indicates that this is server-side rendering,
// for KeepAlive, we just need to render its children
if (!sharedContext.renderer) {
return slots.default;
return () => slots.default && slots.default()[0]; // fixed by xxxxxx ssr
}
if ((process.env.NODE_ENV !== 'production') && props.cache && hasOwn(props, 'max')) {
warn('The `max` prop will be ignored if you provide a custom caching strategy');
......@@ -9434,6 +9434,111 @@ function initDev() {
}
}
function createVueAppContext() {
return {
app: null,
config: {
performance: false,
globalProperties: {},
optionMergeStrategies: {},
errorHandler: undefined,
warnHandler: undefined
},
mixins: [],
components: {},
directives: {},
provides: Object.create(null)
};
}
let currentApp;
let currentPlugins;
function createVueSSRApp(rootComponent, rootProps = null) {
if (rootProps != null && !isObject(rootProps)) {
rootProps = null;
}
currentPlugins = [];
const context = createVueAppContext();
const app = (context.app = currentApp = {
_uid: -1,
_component: rootComponent,
_props: rootProps,
_container: null,
_context: context,
version: "3.0.9",
get config() {
return context.config;
},
set config(_v) { },
use(plugin, ...options) {
currentPlugins.push([plugin, ...options]);
return app;
},
mixin(mixin) {
context.mixins.push(mixin);
return app;
},
component(name, component) {
if (!component) {
return context.components[name];
}
context.components[name] = component;
return app;
},
directive(name, directive) {
if (!directive) {
return context.directives[name];
}
context.directives[name] = directive;
return app;
},
mount() { },
unmount() { },
provide(key, value) {
context.provides[key] = value;
return app;
}
});
return app;
}
function createVueSSRAppInstance() {
const app = createSSRApp(currentApp._component, currentApp._props);
const { config, mixins, components, directives, provides } = currentApp._context;
initAppConfig(app, config);
initAppPlugins(app, currentPlugins);
initAppMixins(app, mixins);
initAppComponents(app, components);
initAppDirectives(app, directives);
initAppProvides(app, provides);
return app;
}
function initAppConfig(app, { performance, globalProperties, optionMergeStrategies, errorHandler, warnHandler }) {
const { config } = app;
extend(config, { performance, errorHandler, warnHandler });
extend(config.globalProperties, globalProperties);
extend(config.optionMergeStrategies, optionMergeStrategies);
return app;
}
function initAppMixins(app, mixins) {
mixins.forEach(mixin => app.mixin(mixin));
return app;
}
function initAppComponents(app, components) {
Object.keys(components).forEach(name => app.component(name, components[name]));
return app;
}
function initAppDirectives(app, directives) {
Object.keys(directives).forEach(name => app.directive(name, directives[name]));
return app;
}
function initAppProvides(app, provides) {
Object.keys(provides).forEach(name => app.provide(name, provides[name]));
return app;
}
function initAppPlugins(app, plugins) {
plugins.forEach(plugin => app.use.apply(app, plugin));
return app;
}
// This entry exports the runtime only, and is built as
if ((process.env.NODE_ENV !== 'production')) {
initDev();
......@@ -9446,4 +9551,4 @@ const compile$1 = () => {
}
};
export { BaseTransition, Comment, Fragment, KeepAlive, Static, Suspense, Teleport, Text, Transition, TransitionGroup, callWithAsyncErrorHandling, callWithErrorHandling, cloneVNode, compile$1 as compile, computed$1 as computed, createApp, createBlock, createCommentVNode, createHydrationRenderer, createRenderer, createSSRApp, createSlots, createStaticVNode, createTextVNode, createVNode, createApp as createVueApp, createSSRApp as createVueSSRApp, customRef, defineAsyncComponent, defineComponent, defineEmit, defineProps, devtools, getCurrentInstance, getTransitionRawChildren, h, handleError, hydrate, initCustomFormatter, inject, injectHook, isInSSRComponentSetup, isProxy, isReactive, isReadonly, isRef, isRuntimeOnly, isVNode, markRaw, mergeProps, nextTick, onActivated, onBeforeActivate, onBeforeDeactivate, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onUnmounted, onUpdated, openBlock, popScopeId, provide, proxyRefs, pushScopeId, queuePostFlushCb, reactive, readonly, ref, registerRuntimeCompiler, render, renderList, renderSlot, resolveComponent, resolveDirective, resolveDynamicComponent, resolveTransitionHooks, setBlockTracking, setDevtoolsHook, setTransitionHooks, shallowReactive, shallowReadonly, shallowRef, ssrContextKey, ssrUtils, toHandlers, toRaw, toRef, toRefs, transformVNodeArgs, triggerRef, unref, useContext, useCssModule, useCssVars, useSSRContext, useTransitionState, vModelCheckbox, vModelDynamic, vModelRadio, vModelSelect, vModelText, vShow, version, warn, watch, watchEffect, withCtx, withDirectives, withKeys, withModifiers, withScopeId };
export { BaseTransition, Comment, Fragment, KeepAlive, Static, Suspense, Teleport, Text, Transition, TransitionGroup, callWithAsyncErrorHandling, callWithErrorHandling, cloneVNode, compile$1 as compile, computed$1 as computed, createApp, createBlock, createCommentVNode, createHydrationRenderer, createRenderer, createSSRApp, createSlots, createStaticVNode, createTextVNode, createVNode, createApp as createVueApp, createVueSSRApp, createVueSSRAppInstance, customRef, defineAsyncComponent, defineComponent, defineEmit, defineProps, devtools, getCurrentInstance, getTransitionRawChildren, h, handleError, hydrate, initCustomFormatter, inject, injectHook, isInSSRComponentSetup, isProxy, isReactive, isReadonly, isRef, isRuntimeOnly, isVNode, markRaw, mergeProps, nextTick, onActivated, onBeforeActivate, onBeforeDeactivate, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onUnmounted, onUpdated, openBlock, popScopeId, provide, proxyRefs, pushScopeId, queuePostFlushCb, reactive, readonly, ref, registerRuntimeCompiler, render, renderList, renderSlot, resolveComponent, resolveDirective, resolveDynamicComponent, resolveTransitionHooks, setBlockTracking, setDevtoolsHook, setTransitionHooks, shallowReactive, shallowReadonly, shallowRef, ssrContextKey, ssrUtils, toHandlers, toRaw, toRef, toRefs, transformVNodeArgs, triggerRef, unref, useContext, useCssModule, useCssVars, useSSRContext, useTransitionState, vModelCheckbox, vModelDynamic, vModelRadio, vModelSelect, vModelText, vShow, version, warn, watch, watchEffect, withCtx, withDirectives, withKeys, withModifiers, withScopeId };
import { UNI_SSR, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA } from '@dcloudio/uni-shared'
import { createVueSSRAppInstance } from 'vue'
import { renderToString } from '@vue/server-renderer'
import {
UNI_SSR,
UNI_SSR_DATA,
UNI_SSR_GLOBAL_DATA,
} from '@dcloudio/uni-shared'
let AppInstance
function createApp(App) {
AppInstance = createVueSSRApp(App).use(plugin)
AppInstance.mount = () => {}
return AppInstance
}
import { getSsrGlobalData } from '@dcloudio/uni-app'
export async function render(url, manifest = {}) {
const app = AppInstance
const app = createVueSSRAppInstance()
const router = app.router
// set the router to the desired URL before rendering
......@@ -30,12 +29,10 @@ export async function render(url, manifest = {}) {
const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
// the SSR context
const __uniSSR = ctx[UNI_SSR] || (ctx[UNI_SSR] = {})
if(!__uniSSR[UNI_SSR_DATA]){
if (!__uniSSR[UNI_SSR_DATA]) {
__uniSSR[UNI_SSR_DATA] = {}
}
if(!__uniSSR[UNI_SSR_GLOBAL_DATA]){
__uniSSR[UNI_SSR_GLOBAL_DATA] = {}
}
__uniSSR[UNI_SSR_GLOBAL_DATA] = getSsrGlobalData()
const appContext = renderAppContext(ctx)
return [html, preloadLinks, appContext]
}
......@@ -68,6 +65,6 @@ function renderPreloadLink(file) {
}
}
function renderAppContext(ctx){
function renderAppContext(ctx) {
return `<script>window.__uniSSR = ${JSON.stringify(ctx[UNI_SSR])}</script>`
}
......@@ -39,14 +39,14 @@ function createApp(code: string) {
}
function createSSRClientApp(code: string) {
return `function createApp(rootComponent, rootProps) {const app = createVueSSRApp(rootComponent, rootProps).use(plugin);const oldMount = app.mount;app.mount = (selector) => app.router.isReady().then(() => oldMount.call(app, selector));return app;};${code.replace(
return `function createApp(rootComponent, rootProps) {const app = createSSRApp(rootComponent, rootProps).use(plugin);const oldMount = app.mount;app.mount = (selector) => app.router.isReady().then(() => oldMount.call(app, selector));return app;};${code.replace(
'createApp',
'createVueSSRApp'
'createSSRApp'
)}`
}
function createSSRServerApp(code: string) {
return `${generateSSRRenderCode()};${code.replace(
return `function createApp(App) {return createVueSSRApp(App).use(plugin)};${generateSSRRenderCode()};${code.replace(
'createApp',
'createVueSSRApp'
)}`
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册