mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
ui: 优化资源加载
This commit is contained in:
1 parent
fffacdc63c
commit
c8a0d48a41
7 files changed
+111
-143
No files matched your search
@@ -1,5 +1,6 @@
|
|||||||
window.Hydro = {
|
window.Hydro = {
|
||||||
extraPages: [],
|
extraPages: [],
|
||||||
|
preload: [],
|
||||||
components: {},
|
components: {},
|
||||||
utils: {},
|
utils: {},
|
||||||
node_modules: {},
|
node_modules: {},
|
||||||
@@ -19,9 +20,28 @@ console.log(
|
|||||||
`,
|
`,
|
||||||
);
|
);
|
||||||
|
|
||||||
window.UiContext = JSON.parse(window.UiContext);
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
window.UiContext = JSON.parse(window.UiContext);
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
try { __webpack_public_path__ = UiContext.cdn_prefix } catch (e) { }
|
try { __webpack_public_path__ = UiContext.cdn_prefix } catch (e) { }
|
||||||
|
|
||||||
import('./hydro');
|
// Locale & langs
|
||||||
|
const { version, payload } = JSON.parse(localStorage.getItem('hydro-constant') || '{}');
|
||||||
|
if (version === UiContext.constantVersion) {
|
||||||
|
eval(payload[0]); // eslint-disable-line no-eval
|
||||||
|
payload.shift();
|
||||||
|
window.Hydro.preload = payload;
|
||||||
|
import('./hydro');
|
||||||
|
} else {
|
||||||
|
fetch(`/constant?version=${UiContext.constantVersion}`)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
eval(data.payload[0]); // eslint-disable-line no-eval
|
||||||
|
localStorage.setItem('hydro-constant', JSON.stringify(data));
|
||||||
|
data.payload.shift();
|
||||||
|
window.Hydro.preload = data.payload;
|
||||||
|
import('./hydro');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
@@ -1,24 +1,82 @@
|
|||||||
/* eslint-disable no-return-await */
|
/* eslint-disable no-return-await */
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
const { readdirSync, readFileSync } = require('fs');
|
import crypto from 'crypto';
|
||||||
const { join } = require('path');
|
import { readdir, readFile } from 'fs-extra';
|
||||||
const crypto = require('crypto');
|
import { join } from 'path';
|
||||||
const { tmpdir } = require('os');
|
import { tmpdir } from 'os';
|
||||||
const { ObjectID } = require('mongodb');
|
import { ObjectID } from 'mongodb';
|
||||||
const bus = require('hydrooj/src/service/bus');
|
import * as bus from 'hydrooj/src/service/bus';
|
||||||
const { PERM } = require('hydrooj/src/model/builtin');
|
import { Route, Handler } from 'hydrooj/src/service/server';
|
||||||
const markdown = require('./backendlib/markdown');
|
import { PERM } from 'hydrooj/src/model/builtin';
|
||||||
|
import markdown from './backendlib/markdown';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
system, domain, user, setting, problem, contest,
|
system, domain, user, setting, problem, contest,
|
||||||
} = global.Hydro.model;
|
} = global.Hydro.model;
|
||||||
const { Route, Handler, UiContextBase } = global.Hydro.service.server;
|
|
||||||
|
interface ConstantArgs {
|
||||||
|
lang: string;
|
||||||
|
domainId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cache = {};
|
||||||
|
|
||||||
|
const basedir = join(tmpdir(), 'hydro', 'public');
|
||||||
|
async function constant(args: ConstantArgs) {
|
||||||
|
// CompileLangs
|
||||||
|
const payload = [`window.LANGS=${JSON.stringify(setting.langs)};`];
|
||||||
|
|
||||||
|
// Locale
|
||||||
|
let { lang } = args;
|
||||||
|
if (!global.Hydro.locales[lang]) lang = system.get('server.language');
|
||||||
|
payload[0] += `window.LOCALES=${JSON.stringify(global.Hydro.locales[lang])};`;
|
||||||
|
|
||||||
|
// Extra style
|
||||||
|
let [nav_logo_dark, nav_logo_dark_2x] = system.getMany([
|
||||||
|
'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x',
|
||||||
|
]);
|
||||||
|
const ddoc = await domain.get(args.domainId);
|
||||||
|
nav_logo_dark = ddoc.ui?.nav_logo_dark || nav_logo_dark;
|
||||||
|
nav_logo_dark_2x = ddoc.ui?.nav_logo_dark_2x || nav_logo_dark_2x;
|
||||||
|
payload[0] += `\
|
||||||
|
const e = document.createElement('style');
|
||||||
|
e.innerHTML = \`\
|
||||||
|
${nav_logo_dark ? `.nav__logo { background-image: url(${nav_logo_dark}) !important }` : ''}
|
||||||
|
${nav_logo_dark_2x ? `\
|
||||||
|
@media
|
||||||
|
only screen and (-webkit-min-device-pixel-ratio: 1.5),
|
||||||
|
only screen and (min-resolution: 1.5dppx),
|
||||||
|
only screen and (min-resolution: 144dpi) {
|
||||||
|
.nav__logo, .header--mobile__domain {
|
||||||
|
background-image: url(${nav_logo_dark_2x}) !important
|
||||||
|
}
|
||||||
|
}` : ''}\`;
|
||||||
|
document.body.appendChild(e);`;
|
||||||
|
|
||||||
|
const files = await readdir(basedir);
|
||||||
|
const pages = files
|
||||||
|
.filter((file) => file.endsWith('.page.js'))
|
||||||
|
.map((i) => readFile(join(basedir, i), 'utf-8'));
|
||||||
|
payload.push(...await Promise.all(pages));
|
||||||
|
|
||||||
|
const c = crypto.createHash('sha1');
|
||||||
|
c.update(JSON.stringify(payload));
|
||||||
|
const version = c.digest('hex');
|
||||||
|
cache[version] = { version, payload };
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.on('handler/after', async (that) => {
|
||||||
|
if (that.response.template) {
|
||||||
|
that.UiContext.constantVersion = await constant({
|
||||||
|
domainId: that.domainId,
|
||||||
|
lang: that.session.viewLang || that.user.viewLang,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
class WikiHelpHandler extends Handler {
|
class WikiHelpHandler extends Handler {
|
||||||
constructor(args) {
|
noCheckPermView = true;
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get({ domainId }) {
|
async get({ domainId }) {
|
||||||
const LANGS = setting.langs;
|
const LANGS = setting.langs;
|
||||||
@@ -34,72 +92,15 @@ class WikiHelpHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WikiAboutHandler extends Handler {
|
class WikiAboutHandler extends Handler {
|
||||||
constructor(args) {
|
noCheckPermView = true;
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get() {
|
async get() {
|
||||||
this.response.template = 'about.html';
|
this.response.template = 'about.html';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UiConstantsHandler extends Handler {
|
|
||||||
constructor(args) {
|
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get() {
|
|
||||||
this.response.body = `window.LANGS=${JSON.stringify(setting.langs)}`;
|
|
||||||
this.response.type = 'text/javascript';
|
|
||||||
this.ctx.set('nolog', '1');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UiSettingsHandler extends Handler {
|
|
||||||
constructor(args) {
|
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get({ domainId }) {
|
|
||||||
const [nav_logo_dark, nav_logo_dark_2x] = system.getMany([
|
|
||||||
'ui-default.nav_logo_dark', 'ui-default.nav_logo_dark_2x',
|
|
||||||
]);
|
|
||||||
const ddoc = await domain.get(domainId);
|
|
||||||
this.response.body = await this.renderHTML('extra.css', {
|
|
||||||
nav_logo_dark,
|
|
||||||
nav_logo_dark_2x,
|
|
||||||
...(ddoc.ui || {}),
|
|
||||||
});
|
|
||||||
this.response.type = 'text/css';
|
|
||||||
this.ctx.set('nolog', '1');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LocaleHandler extends Handler {
|
|
||||||
constructor(args) {
|
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get({ id }) {
|
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
id = id.split('.')[0];
|
|
||||||
// TODO use language_default setting
|
|
||||||
if (!global.Hydro.locales[id]) id = system.get('server.language');
|
|
||||||
this.response.body = `window.LOCALES=${JSON.stringify(global.Hydro.locales[id])}`;
|
|
||||||
this.response.type = 'text/javascript';
|
|
||||||
this.ctx.set('nolog', '1');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SetThemeHandler extends Handler {
|
class SetThemeHandler extends Handler {
|
||||||
constructor(args) {
|
noCheckPermView = true
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get({ theme }) {
|
async get({ theme }) {
|
||||||
await user.setById(this.user._id, { theme });
|
await user.setById(this.user._id, { theme });
|
||||||
@@ -108,10 +109,7 @@ class SetThemeHandler extends Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MarkdownHandler extends Handler {
|
class MarkdownHandler extends Handler {
|
||||||
constructor(args) {
|
noCheckPermView = true;
|
||||||
super(args);
|
|
||||||
this.noCheckPermView = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async post({ text, html = false, inline = false }) {
|
async post({ text, html = false, inline = false }) {
|
||||||
this.response.body = inline
|
this.response.body = inline
|
||||||
@@ -122,6 +120,14 @@ class MarkdownHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UiConstantsHandler extends Handler {
|
||||||
|
noCheckPermView = true;
|
||||||
|
|
||||||
|
async get({ version }) {
|
||||||
|
this.response.body = cache[version];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class RichMediaHandler extends Handler {
|
class RichMediaHandler extends Handler {
|
||||||
async renderUser(domainId, payload) {
|
async renderUser(domainId, payload) {
|
||||||
let d = payload.domainId || domainId;
|
let d = payload.domainId || domainId;
|
||||||
@@ -162,35 +168,11 @@ class RichMediaHandler extends Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getHash = (i) => {
|
|
||||||
const shasum = crypto.createHash('sha1');
|
|
||||||
const file = readFileSync(join(tmpdir(), 'hydro', 'public', i));
|
|
||||||
shasum.update(file);
|
|
||||||
return shasum.digest('hex').substr(0, 10);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getUrl = (files) => files.map((i) => `/${i}?${getHash(i)}`);
|
|
||||||
|
|
||||||
bus.on('app/started', () => {
|
|
||||||
setTimeout(() => {
|
|
||||||
const files = readdirSync(join(tmpdir(), 'hydro', 'public'));
|
|
||||||
const pages = files.filter((file) => file.endsWith('.page.js'));
|
|
||||||
const themes = files.filter((file) => file.endsWith('.theme.js'));
|
|
||||||
UiContextBase.extraPages = getUrl(pages);
|
|
||||||
UiContextBase.themes = {};
|
|
||||||
for (const theme of themes) {
|
|
||||||
UiContextBase.themes[theme] = `/${theme}?${getHash(theme)}`;
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
global.Hydro.handler.ui = async () => {
|
global.Hydro.handler.ui = async () => {
|
||||||
Route('wiki_help', '/wiki/help', WikiHelpHandler);
|
Route('wiki_help', '/wiki/help', WikiHelpHandler);
|
||||||
Route('wiki_about', '/wiki/about', WikiAboutHandler);
|
Route('wiki_about', '/wiki/about', WikiAboutHandler);
|
||||||
Route('ui_constants', '/ui-constants.js', UiConstantsHandler);
|
|
||||||
Route('locale', '/locale/:id', LocaleHandler);
|
|
||||||
Route('set_theme', '/set_theme/:id', SetThemeHandler);
|
Route('set_theme', '/set_theme/:id', SetThemeHandler);
|
||||||
Route('ui_extracss', '/extra.css', UiSettingsHandler);
|
Route('constant', '/constant', UiConstantsHandler);
|
||||||
Route('markdown', '/markdown', MarkdownHandler);
|
Route('markdown', '/markdown', MarkdownHandler);
|
||||||
Route('media', '/media', RichMediaHandler);
|
Route('media', '/media', RichMediaHandler);
|
||||||
};
|
};
|
||||||
@@ -24,25 +24,7 @@ function buildSequence(pages, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
if (UiContext.extraPages) {
|
for (const page of window.Hydro.preload) await eval(page); // eslint-disable-line no-eval
|
||||||
const tasks = [];
|
|
||||||
const ts = new Date().getTime();
|
|
||||||
for (const page of UiContext.extraPages) {
|
|
||||||
const head = document.getElementsByTagName('head')[0];
|
|
||||||
const script = document.createElement('script');
|
|
||||||
if (page.includes('.module.')) script.type = 'module';
|
|
||||||
script.src = page;
|
|
||||||
head.appendChild(script);
|
|
||||||
tasks.push(new Promise((resolve) => {
|
|
||||||
script.onload = resolve;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
await Promise.all(tasks);
|
|
||||||
const time = new Date().getTime() - ts;
|
|
||||||
if ((process.env.NODE_ENV !== 'production' && time > 16) || time > 256) {
|
|
||||||
console.warn(`Extra pages loading took ${time}ms`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pageLoader = new PageLoader();
|
const pageLoader = new PageLoader();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.18.15",
|
"version": "4.19.0",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "hydro.js",
|
"main": "hydro.js",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% if udoc %}
|
{% if udoc %}
|
||||||
<span class="user-profile-link">
|
<span class="user-profile-link">
|
||||||
{% if avatar %}
|
{% if avatar %}
|
||||||
<img class="small user-profile-avatar v-center" src="{{ avatarUrl(udoc.avatar|default('')) }}" width="20" height="20">
|
<img class="small user-profile-avatar v-center" loading="lazy" src="{{ avatarUrl(udoc.avatar|default('')) }}" width="20" height="20">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="user-profile-name" href="{{ url('user_detail', uid=udoc._id) }}">
|
<a class="user-profile-name" href="{{ url('user_detail', uid=udoc._id) }}">
|
||||||
{% if udoc.displayName and udoc.displayName != udoc.uname %}
|
{% if udoc.displayName and udoc.displayName != udoc.uname %}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
/* {% if nav_logo_dark %} */
|
|
||||||
.nav__logo {
|
|
||||||
background-image: url({{nav_logo_dark}}) !important
|
|
||||||
}
|
|
||||||
/* {% endif %} */
|
|
||||||
|
|
||||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx), only screen and (min-resolution: 144dpi) {
|
|
||||||
/* {% if nav_logo_dark_2x %} */
|
|
||||||
.nav__logo, .header--mobile__domain {
|
|
||||||
background-image: url({{nav_logo_dark_2x}}) !important
|
|
||||||
}
|
|
||||||
/* {% endif %} */
|
|
||||||
}
|
|
||||||
@@ -26,9 +26,9 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% if not isIE(handler.request.headers['user-agent']) %}
|
{% if not isIE(handler.request.headers['user-agent']) %}
|
||||||
<script src="{{ static_url((handler.user|default({})).theme|default('default') + '.theme.js') }}"></script>
|
<script src="{{ static_url((handler.user|default({})).theme|default('default') + '.theme.js') }}"></script>
|
||||||
|
<script type="text/javascript" src="{{ static_url('hydro.js') }}"></script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<link rel="stylesheet" media="all" href="{{ static_url((handler.user|default({})).theme|default('default') + '.theme.css') }}">
|
<link rel="stylesheet" media="all" href="{{ static_url((handler.user|default({})).theme|default('default') + '.theme.css') }}">
|
||||||
<link rel="stylesheet" media="all" href="{{ url('ui_extracss') }}">
|
|
||||||
<title>{{ handler.renderTitle(page_name) }}</title>
|
<title>{{ handler.renderTitle(page_name) }}</title>
|
||||||
{% if not isIE(handler.request.headers['user-agent']) %}
|
{% if not isIE(handler.request.headers['user-agent']) %}
|
||||||
<script>
|
<script>
|
||||||
@@ -46,9 +46,6 @@
|
|||||||
UiContext = '{{ UiContext|json|jsesc|safe }}';
|
UiContext = '{{ UiContext|json|jsesc|safe }}';
|
||||||
UserContext = '{{ UserContext|json|jsesc|safe }}';
|
UserContext = '{{ UserContext|json|jsesc|safe }}';
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="{{ ('/locale/{0}.js'.format(handler.translate('__id'))) }}"></script>
|
|
||||||
<script type="text/javascript" src="/ui-constants.js"></script>
|
|
||||||
<script type="text/javascript" src="{{ static_url('hydro.js') }}"></script>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in new issue
Block a user