Skip to content

MVP: vue 3 support #109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@
"rollup": "^2.33.3",
"rollup-plugin-babel": "^4.4.0",
"serve": "^11.3.2",
"uglify-es": "^3.3.9",
"vue": "^2.6.12",
"vue-server-renderer": "^2.6.12",
"vue-template-compiler": "^2.6.12"
"uglify-es": "^3.3.9"
},
"main": "dist/LazyHydrate.js",
"module": "dist/LazyHydrate.esm.js",
Expand All @@ -72,5 +69,10 @@
"> 0.5%",
"not ie <= 10",
"not op_mini all"
]
],
"dependencies": {
"vue": "^3.1.5",
"@vue/server-renderer": "^3.1.5",
"@vue/compiler-sfc": "^3.1.5"
}
}
2 changes: 1 addition & 1 deletion src/LazyHydrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function hydrateOnInteraction(componentOrFactory, { event = `focus` } = {

const Placeholder = {
render() {
return this.$slots.default;
return this.$slots.default();
},
};

Expand Down
49 changes: 30 additions & 19 deletions src/utils/hydration-blocker.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
import { h, ref } from 'vue';
import { makeHydrationObserver } from './hydration-observer';
import { makeHydrationPromise } from './hydration-promise';
import { makeNonce } from './nonce';

export function makeHydrationBlocker(component, options) {
return Object.assign({
render() {
console.log('renderFn');
return h(this.Nonce, {
// attrs: this.$attrs,
// on: this.$listeners,
// scopedSlots: this.$scopedSlots,
}, this.$slots.default);
},
mixins: [{
beforeCreate() {
console.log('beforeCreate');
this.cleanupHandlers = [];
const { hydrate, hydrationPromise } = makeHydrationPromise();
this.Nonce = makeNonce({ component, hydrationPromise });
this.hydrate = hydrate;
this.hydrationPromise = hydrationPromise;
},
beforeDestroy() {
beforeUnmount() {
this.cleanup();
},
mounted() {
if (this.$el.nodeType === Node.COMMENT_NODE) {
const targetNode = this.$el.nextSibling;
console.log('mounted', this.$el.nextSibling, this);
if (this.$el.nodeType === Node.COMMENT_NODE && !targetNode) {
console.log('no-ssr link found', this.$el);
// No SSR rendered content, hydrate immediately.
this.hydrate();
return;
}

if (this.never) return;

if (this.whenVisible) {
if (targetNode && this.whenVisible) {
const observerOptions = this.whenVisible !== true ? this.whenVisible : undefined;
const observer = makeHydrationObserver(observerOptions);

Expand All @@ -34,15 +47,15 @@ export function makeHydrationBlocker(component, options) {
return;
}

this.$el.hydrate = this.hydrate;
const cleanup = () => observer.unobserve(this.$el);
targetNode.hydrate = this.hydrate;
const cleanup = () => observer.unobserve(targetNode);
this.cleanupHandlers.push(cleanup);
this.hydrationPromise.then(cleanup);
observer.observe(this.$el);
observer.observe(targetNode);
return;
}

if (this.whenIdle) {
if (targetNode && this.whenIdle) {
// If `requestIdleCallback()` or `requestAnimationFrame()`
// is not supported, hydrate immediately.
if (!(`requestIdleCallback` in window) || !(`requestAnimationFrame` in window)) {
Expand All @@ -60,17 +73,22 @@ export function makeHydrationBlocker(component, options) {
this.hydrationPromise.then(cleanup);
}

if (this.interactionEvents && this.interactionEvents.length) {
console.log('interactionEvents', this.interactionEvents);


const interactionEvents = ['click'] //this.interactionEvents;
if (interactionEvents && interactionEvents.length) {
console.log('interactionEvents', interactionEvents);
const eventListenerOptions = {
capture: true,
once: true,
passive: true,
};

this.interactionEvents.forEach((eventName) => {
this.$el.addEventListener(eventName, this.hydrate, eventListenerOptions);
interactionEvents.forEach((eventName) => {
targetNode.addEventListener(eventName, this.hydrate, eventListenerOptions);
const cleanup = () => {
this.$el.removeEventListener(eventName, this.hydrate, eventListenerOptions);
targetNode.removeEventListener(eventName, this.hydrate, eventListenerOptions);
};
this.cleanupHandlers.push(cleanup);
});
Expand All @@ -80,14 +98,7 @@ export function makeHydrationBlocker(component, options) {
cleanup() {
this.cleanupHandlers.forEach(handler => handler());
},
},
render(h) {
return h(this.Nonce, {
attrs: this.$attrs,
on: this.$listeners,
scopedSlots: this.$scopedSlots,
}, this.$slots.default);
},
}
}],
}, options);
}
9 changes: 8 additions & 1 deletion src/utils/nonce.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { defineAsyncComponent, h } from 'vue';

const isServer = typeof window === `undefined`;

function isAsyncComponentFactory(componentOrFactory) {
console.log({componentOrFactory});
return typeof componentOrFactory === `function`;
}

Expand All @@ -13,6 +16,10 @@ function resolveComponent(componentOrFactory) {

export function makeNonce({ component, hydrationPromise }) {
if (isServer) return component;


return () => hydrationPromise.then(() => resolveComponent(component));
return defineAsyncComponent(async function() {
await hydrationPromise;
return await resolveComponent(component);
})
}
8 changes: 4 additions & 4 deletions test/integration/components/IntegrationAsync.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
class="show-when-event"
/>
</div>
<div
slot="named"
<template
#named
class="named-slot"
/>
</DummyInteraction>
Expand All @@ -28,8 +28,8 @@
class="show-when-event"
/>
</div>
<div
slot="named"
<template
#named
class="named-slot"
/>
</DummyInteractionWrapper>
Expand Down
8 changes: 4 additions & 4 deletions test/integration/components/IntegrationSync.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
class="show-when-event"
/>
</div>
<div
slot="named"
<template
#named
class="named-slot"
/>
</DummyInteraction>
Expand All @@ -28,8 +28,8 @@
class="show-when-event"
/>
</div>
<div
slot="named"
<template
#named
class="named-slot"
/>
</DummyInteractionWrapper>
Expand Down
19 changes: 12 additions & 7 deletions test/integration/entry-integration.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import Vue from 'vue';
import { createSSRApp, createApp } from 'vue';

import IntegrationAsync from './components/IntegrationAsync.vue';
import IntegrationSync from './components/IntegrationSync.vue';

export const AppAsync = new Vue({
render: h => h(IntegrationAsync),
}).$mount(`#app-async`);
const isBrowser = typeof document !== `undefined`;
const newApp = isBrowser ? createSSRApp : createApp;

export const AppSync = new Vue({
render: h => h(IntegrationSync),
}).$mount(`#app-sync`);

export const AppAsync = newApp(IntegrationAsync);

export const AppSync = newApp(IntegrationSync);

if (isBrowser) {
AppAsync.mount(`#app-async`);
AppSync.mount(`#app-sync`);
}
22 changes: 8 additions & 14 deletions test/integration/render.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
const fs = require(`fs`);
const vueServerRenderer = require(`vue-server-renderer`);
const { renderToString } = require(`@vue/server-renderer`);

const entryIntegration = require(`./dist/entry-integration.common.js`);

function saveFile(name, contents) {
fs.writeFile(`${__dirname}/dist/${name}.html`, contents, (error) => {
const entry = fs.readFileSync(`${__dirname}/template.html`, `utf-8`);

fs.writeFile(`${__dirname}/dist/${name}.html`, entry.replace(`<!--vue-ssr-outlet-->`, contents), (error) => {
if (error) throw error;

// eslint-disable-next-line no-console
console.log(`${name} rendered.`);
});
}

const integrationRenderer = vueServerRenderer.createRenderer({
template: fs.readFileSync(`${__dirname}/template.html`, `utf-8`),
renderToString(entryIntegration.AppAsync).then((result) => {
saveFile(`integration-async`, result);
});

integrationRenderer.renderToString(entryIntegration.AppAsync, (error, html) => {
if (error) throw error;

saveFile(`integration-async`, html);
});

integrationRenderer.renderToString(entryIntegration.AppSync, (error, html) => {
if (error) throw error;

saveFile(`integration-sync`, html);
renderToString(entryIntegration.AppSync).then((result) => {
saveFile(`integration-sync`, result);
});
2 changes: 1 addition & 1 deletion test/integration/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>integration test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@next"></script>
</head>

<body>
Expand Down
2 changes: 1 addition & 1 deletion test/performance/render.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const vueServerRenderer = require(`vue-server-renderer`);
const { renderToString } = require(`@vue/server-renderer`);
const fs = require(`fs`);

const entryReference = require(`./dist/entry-reference.common.js`);
Expand Down
2 changes: 1 addition & 1 deletion test/performance/template-hydrate-never.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>performance test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@next"></script>
</head>

<body>
Expand Down
2 changes: 1 addition & 1 deletion test/performance/template-reference.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>performance test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@next"></script>
</head>

<body>
Expand Down
Loading