feat(ssr): experimental ssr

This commit is contained in:
07akioni 2021-06-01 22:01:21 +08:00
parent c761844aed
commit 787f624a11
7 changed files with 339 additions and 5 deletions

View File

@ -77,6 +77,7 @@
"@vue/compiler-sfc": "^3.0.10",
"@vue/eslint-config-standard": "^6.0.0",
"@vue/eslint-config-typescript": "^7.0.0",
"@vue/server-renderer": "^3.0.11",
"@vue/test-utils": "^2.0.0-rc.4",
"autoprefixer": "^9.8.6",
"babel-eslint": "^10.1.0",
@ -103,9 +104,10 @@
"vite": "^2.1.3"
},
"dependencies": {
"@css-render/plugin-bem": "^0.15.0",
"@css-render/plugin-bem": "^0.15.1",
"@css-render/vue3-ssr": "^0.15.1",
"async-validator": "^3.5.1",
"css-render": "^0.15.0",
"css-render": "^0.15.1",
"date-fns": "^2.19.0",
"evtd": "^0.2.0",
"highlight.js": "^10.7.1",

View File

@ -13,6 +13,7 @@ import { CNode } from 'css-render'
import type { GlobalTheme } from '../config-provider'
import { configProviderInjectionKey } from '../config-provider/src/ConfigProvider'
import type { ThemeCommonVars } from '../_styles/common'
import { ssrInjectionKey } from '../ssr/context'
globalStyle.mount({
id: 'naive-ui-global',
@ -95,17 +96,24 @@ function useTheme<N, T, R> (
props: UseThemeProps<Theme<N, T, R>>,
clsPrefixRef?: Ref<string | undefined>
): ComputedRef<MergedTheme<Theme<N, T, R>>> {
const ssrAdapter = inject(ssrInjectionKey, undefined)
if (style) {
onBeforeMount(() => {
const mountStyle = (): void => {
const clsPrefix = clsPrefixRef?.value
style.mount({
id: clsPrefix === undefined ? mountId : clsPrefix + mountId,
head: true,
props: {
bPrefix: clsPrefix ? `.${clsPrefix}-` : undefined
}
},
ssr: ssrAdapter
})
})
}
if (ssrAdapter) {
mountStyle()
} else {
onBeforeMount(mountStyle)
}
}
const NConfigProvider = inject(configProviderInjectionKey, null)
const mergedThemeRef = computed(() => {

17
src/ssr/SsrProvider.tsx Normal file
View File

@ -0,0 +1,17 @@
import { defineComponent, provide } from 'vue'
import { ssrAdapter } from '@css-render/vue3-ssr'
import { ssrInjectionKey } from './context'
export default defineComponent({
name: 'SsrProvider',
props: {
ssr: {
type: Boolean,
default: true
}
},
setup ({ ssr }, { slots }) {
ssr && provide(ssrInjectionKey, ssrAdapter)
return () => slots.default?.()
}
})

4
src/ssr/context.ts Normal file
View File

@ -0,0 +1,4 @@
import { InjectionKey } from 'vue'
import { ssrAdapter } from '@css-render/vue3-ssr'
export const ssrInjectionKey: InjectionKey<typeof ssrAdapter> = Symbol('ssr')

1
src/ssr/index.ts Normal file
View File

@ -0,0 +1 @@
export { default as NSsrProvider } from './SsrProvider'

View File

@ -0,0 +1,25 @@
import { h, createSSRApp } from 'vue'
import { renderToString } from '@vue/server-renderer'
import { NSsrProvider } from '../index'
import { NButton } from '../../index'
import { setup } from '@css-render/vue3-ssr'
describe('n-ssr-provider', () => {
it('should work with import on demand', (done) => {
const app = createSSRApp({
render () {
return h(NSsrProvider, null, {
default: () =>
h(NButton, null, {
default: () => 'btn'
})
})
}
})
const { collect } = setup(app)
renderToString(app).then((v) => {
expect(collect() + '\n' + v).toMatchSnapshot()
done()
})
})
})

View File

@ -0,0 +1,277 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`n-ssr-provider should work with import on demand 1`] = `
"<style cssr-id=\\"nButton\\">
.n-button {
font-weight: var(--font-weight);
line-height: 1;
font-family: inherit;
padding: var(--padding);
height: var(--height);
font-size: var(--font-size);
border-radius: var(--border-radius);
color: var(--text-color);
background-color: var(--color);
width: var(--width);
white-space: nowrap;
outline: none;
position: relative;
z-index: auto;
border: none;
display: inline-flex;
flex-wrap: nowrap;
align-items: center;
justify-content: center;
user-select: none;
text-align: center;
cursor: pointer;
text-decoration: none;
transition:
color .3s var(--bezier),
background-color .3s var(--bezier),
opacity .3s var(--bezier),
border-color .3s var(--bezier);
}
.n-button.n-button--color .n-button__border {
border-color: var(--border-color);
}
.n-button.n-button--color.n-button--disabled .n-button__border {
border-color: var(--border-color-disabled);
}
.n-button.n-button--color:not(.n-button--disabled):focus .n-button__state-border {
border-color: var(--border-color-focus);
}
.n-button.n-button--color:not(.n-button--disabled):hover .n-button__state-border {
border-color: var(--border-color-hover);
}
.n-button.n-button--color:not(.n-button--disabled):active .n-button__state-border {
border-color: var(--border-color-pressed);
}
.n-button.n-button--color:not(.n-button--disabled).n-button--pressed .n-button__state-border {
border-color: var(--border-color-pressed);
}
.n-button.n-button--disabled {
background-color: var(--color-disabled);
color: var(--text-color-disabled);
}
.n-button.n-button--disabled .n-button__border {
border: var(--border-disabled);
}
.n-button:not(.n-button--disabled):focus {
background-color: var(--color-focus);
color: var(--text-color-focus);
}
.n-button:not(.n-button--disabled):focus .n-button__state-border {
border: var(--border-focus);
}
.n-button:not(.n-button--disabled):hover {
background-color: var(--color-hover);
color: var(--text-color-hover);
}
.n-button:not(.n-button--disabled):hover .n-button__state-border {
border: var(--border-hover);
}
.n-button:not(.n-button--disabled):active {
background-color: var(--color-pressed);
color: var(--text-color-pressed);
}
.n-button:not(.n-button--disabled):active .n-button__state-border {
border: var(--border-pressed);
}
.n-button:not(.n-button--disabled).n-button--pressed {
background-color: var(--color-pressed);
color: var(--text-color-pressed);
}
.n-button:not(.n-button--disabled).n-button--pressed .n-button__state-border {
border: var(--border-pressed);
}
.n-button .n-base-wave {
pointer-events: none;
top: 0;
right: 0;
bottom: 0;
left: 0;
animation-iteration-count: 1;
animation-duration: var(--ripple-duration);
animation-timing-function: var(--bezier-ease-out), var(--bezier-ease-out);
}
.n-button .n-base-wave.n-base-wave--active {
z-index: 1;
animation-name: button-wave-spread, button-wave-opacity;
}
.n-button .n-button__border, .n-button .n-button__state-border {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: inherit;
transition: border-color .3s var(--bezier);
pointer-events: none;
}
.n-button .n-button__border {
border: var(--border);
}
.n-button .n-button__state-border {
border: var(--border);
border-color: #0000;
z-index: 1;
}
.n-button .n-button__icon {
margin: var(--icon-margin);
margin-left: 0;
height: var(--icon-size);
width: var(--icon-size);
max-width: var(--icon-size);
font-size: var(--icon-size);
position: relative;
flex-shrink: 0;
}
.n-button .n-button__icon .n-icon-slot {
height: var(--icon-size);
width: var(--icon-size);
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
display: flex;
}
.n-button .n-button__icon .n-icon-slot.icon-switch-transition-enter-from, .n-button .n-button__icon .n-icon-slot.icon-switch-transition-leave-to {
transform: translateY(-50%) scale(0.75);
left: 0;
top: 50%;
opacity: 0;
}
.n-button .n-button__icon .n-icon-slot.icon-switch-transition-enter-to, .n-button .n-button__icon .n-icon-slot.icon-switch-transition-leave-from {
transform: scale(1) translateY(-50%);
left: 0;
top: 50%;
opacity: 1;
}
.n-button .n-button__icon .n-icon-slot.icon-switch-transition-enter-active, .n-button .n-button__icon .n-icon-slot.icon-switch-transition-leave-active {
transform-origin: center;
position: absolute;
left: 0;
top: 50%;
transition: all .3s cubic-bezier(.4, 0, .2, 1) !important;
}
.n-button .n-button__icon.fade-in-width-expand-transition-leave-from, .n-button .n-button__icon.fade-in-width-expand-transition-enter-to {
opacity: 1;
}
.n-button .n-button__icon.fade-in-width-expand-transition-leave-to, .n-button .n-button__icon.fade-in-width-expand-transition-enter-from {
opacity: 0!important;
margin-left: 0!important;
margin-right: 0!important;
}
.n-button .n-button__icon.fade-in-width-expand-transition-leave-active {
overflow: hidden;
transition:
opacity .2s cubic-bezier(.4, 0, .2, 1),
max-width .2s cubic-bezier(.4, 0, .2, 1) .1s,
margin-left .2s cubic-bezier(.4, 0, .2, 1) .1s,
margin-right .2s cubic-bezier(.4, 0, .2, 1) .1s;
}
.n-button .n-button__icon.fade-in-width-expand-transition-enter-active {
overflow: hidden;
transition:
opacity .2s cubic-bezier(.4, 0, .2, 1) .1s,
max-width .2s cubic-bezier(.4, 0, .2, 1),
margin-left .2s cubic-bezier(.4, 0, .2, 1),
margin-right .2s cubic-bezier(.4, 0, .2, 1);
}
.n-button .n-button__content {
display: flex;
align-items: center;
flex-wrap: nowrap;
}
.n-button .n-button__content ~ .n-button__icon {
margin: var(--icon-margin);
margin-right: 0;
}
.n-button.n-button--block {
display: flex;
width: 100%;
}
.n-button.n-button--dashed .n-button__border, .n-button.n-button--dashed .n-button__state-border {
border-style: dashed !important;
}
.n-button.n-button--disabled {
cursor: not-allowed;
opacity: var(--opacity-disabled);
}
@keyframes button-wave-spread {
from {
box-shadow: 0 0 0.5px 0 var(--ripple-color);
}
to {
box-shadow: 0 0 0.5px 4.5px var(--ripple-color);
}
}
@keyframes button-wave-opacity {
from {
opacity: var(--wave-opacity);
}
to {
opacity: 0;
}
}
</style>
<!--[--><button class=\\"n-button n-button--default-type\\" tabindex=\\"0\\" type=\\"button\\" style=\\"--bezier:cubic-bezier(.4, 0, .2, 1);--bezier-ease-out:cubic-bezier(0, 0, .2, 1);--ripple-duration:.6s;--opacity-disabled:0.5;--wave-opacity:0.6;font-weight:400;--color:#0000;--color-hover:#0000;--color-pressed:#0000;--color-focus:#0000;--color-disabled:#0000;--ripple-color:#18a058;--text-color:rgb(51, 54, 57);--text-color-hover:#36ad6a;--text-color-pressed:#0c7a43;--text-color-focus:#36ad6a;--text-color-disabled:rgb(51, 54, 57);--border:1px solid rgb(224, 224, 230);--border-hover:1px solid #36ad6a;--border-pressed:1px solid #0c7a43;--border-focus:1px solid #36ad6a;--border-disabled:1px solid rgb(224, 224, 230);--width:initial;--height:34px;--font-size:14px;--padding:0 14px;--icon-size:18px;--icon-margin:6px;--border-radius:3px;\\"><!----><!----><span class=\\"n-button__content\\">btn</span><div class=\\"n-base-wave\\"></div><div class=\\"n-button__border\\" style=\\"\\"></div><div class=\\"n-button__state-border\\" style=\\"\\"></div></button><!--]-->"
`;