misskey-dev / misskey

🌎 An interplanetary microblogging platform 🚀
https://misskey-hub.net/
GNU Affero General Public License v3.0
9.84k stars 1.33k forks source link

CSS Modulesを使用している個所をminifyしたい #10923

Closed syuilo closed 1 year ago

syuilo commented 1 year ago

現在CSS Modulesは以下のようにコンパイルされている image

ランタイムでCSS Modulesの元の名前とビルド後のクラス文字列のマッピングを持っておく(かつそのマッピングを元に変換処理をランタイムでやる)必要はないと考えられるので、直接クラス名文字列として埋め込みたい

acid-chicken commented 1 year ago

調査中

syuilo commented 1 year ago

https://github.com/misskey-dev/misskey/blob/a4a171781b336f85c841846fe78b10715ce97f29/packages/frontend/src/components/MkModal.vue#L4 みたいに動的に使ってる箇所はまあそういう感じにコンパイルされるのも分かるけどそうじゃなく完全に静的に定義されている個所はもっと効率よくビルドできるはずなんだよな

acid-chicken commented 1 year ago

調査結果

Vue のランタイムを目 grep したところ useCssModule のためだけにマップを持っていることがわかった 一切の使用を禁止すれば一応マップを取り除くことは可能

acid-chicken commented 1 year ago

https://github.com/misskey-dev/misskey/blob/a4a171781b336f85c841846fe78b10715ce97f29/packages/frontend/src/components/MkModal.vue#L4

みたいに動的に使ってる箇所はまあそういう感じにコンパイルされるのも分かるけどそうじゃなく完全に静的に定義されている個所はもっと効率よくビルドできるはずなんだよな

最適化するなら動的に使うのもやめたいところではある

syuilo commented 1 year ago

最適化するなら動的に使うのもやめたいところではある

yes 他にもこんな感じに使ってるとこいくつかあるけど、どれもやりようによっては静的に定義できるからやっていきたい

syuilo commented 1 year ago

調査結果

Vue のランタイムを目 grep したところ useCssModule のためだけにマップを持っていることがわかった 一切の使用を禁止すれば一応マップを取り除くことは可能

useCssModuleを使っている個所をゼロにすればいい感じにコンパイルされるようになるってこと?

acid-chicken commented 1 year ago

調査結果

Vue のランタイムを目 grep したところ useCssModule のためだけにマップを持っていることがわかった 一切の使用を禁止すれば一応マップを取り除くことは可能

useCssModuleを使っている個所をゼロにすればいい感じにコンパイルされるようになるってこと?

No それはそれとしてコンパイラに手を加える必要がある(ただ useCssModule を使っていると壊れる)

acid-chicken commented 1 year ago

コンパイラに手を加えるというか、ポストプロセッシングする必要があるという方が正しいか

acid-chicken commented 1 year ago

Options API のときは

https://github.com/misskey-dev/misskey/blob/9384f5399da39e53855beb8e7f8ded1aa56bf72e/packages/frontend/src/components/MkMarquee.vue

から

import { a as c, w as u, e as d, o as l, a0 as r } from "./vue-561933f7.js";
import { _ as i } from "./app-c70a3e7b.js";
const p = {
    name: "MarqueeText",
    props: {
      duration: { type: Number, default: 15 },
      repeat: { type: Number, default: 2 },
      paused: { type: Boolean, default: !1 },
      reverse: { type: Boolean, default: !1 },
    },
    setup(t) {
      const e = c();
      function a() {
        const n = e.value.offsetWidth / t.repeat,
          o = 3e3,
          s = t.duration / ((1 / n) * o);
        e.value.style.animationDuration = `${s}s`;
      }
      return (
        u(() => t.duration, a),
        d(() => {
          a();
        }),
        l(() => {}),
        { contentEl: e }
      );
    },
    render({
      $slots: t,
      $style: e,
      $props: { duration: a, repeat: n, paused: o, reverse: s },
    }) {
      return r("div", { class: [e.wrap] }, [
        r(
          "span",
          { ref: "contentEl", class: [o ? e.paused : void 0, e.content] },
          Array(n).fill(
            r(
              "span",
              {
                class: e.text,
                style: { animationDirection: s ? "reverse" : void 0 },
              },
              t.default()
            )
          )
        ),
      ]);
    },
  },
  f = "xhtbz",
  m = "xatdo",
  x = "xgYHc",
  v = "x8M5E",
  M = "xclje",
  _ = { wrap: f, content: m, text: x, marquee: v, paused: M },
  h = { $style: _ },
  q = i(p, [["__cssModules", h]]);
export { q as M };

みたいなコードが吐かれるので

import { a as c, w as u, e as d, o as l, a0 as r } from "./vue-561933f7.js";
const p = {
    name: "MarqueeText",
    props: {
      duration: { type: Number, default: 15 },
      repeat: { type: Number, default: 2 },
      paused: { type: Boolean, default: !1 },
      reverse: { type: Boolean, default: !1 },
    },
    setup(t) {
      const e = c();
      function a() {
        const n = e.value.offsetWidth / t.repeat,
          o = 3e3,
          s = t.duration / ((1 / n) * o);
        e.value.style.animationDuration = `${s}s`;
      }
      return (
        u(() => t.duration, a),
        d(() => {
          a();
        }),
        l(() => {}),
        { contentEl: e }
      );
    },
    render({
      $slots: t,
      $style: e,
      $props: { duration: a, repeat: n, paused: o, reverse: s },
    }) {
      return r("div", { class: "xhtbz" }, [
        r(
          "span",
          { ref: "contentEl", class: [o ? "xclje" : void 0, "xatdo"] },
          Array(n).fill(
            r(
              "span",
              {
                class: "xgYHc",
                style: { animationDirection: s ? "reverse" : void 0 },
              },
              t.default()
            )
          )
        ),
      ]);
    },
  };
export { p as M };

といった感じにポストプロセスできれば良い

syuilo commented 1 year ago

(あーMkMarqueeまだoptions apiだったか)

acid-chicken commented 1 year ago

Composition API のときは

https://github.com/misskey-dev/misskey/blob/develop/packages/frontend/src/components/MkUserCardMini.vue

から

import { _ as p } from "./MkMiniChart.vue_vue_type_script_setup_true_lang-c405dd11.js";
import { an as _, br as m, _ as h } from "./app-c70a3e7b.js";
import {
  b as f,
  a as v,
  e as M,
  z as r,
  f as k,
  j as y,
  g as n,
  k as C,
  A as c,
  l as e,
  E as b,
  u as t,
  h as B,
  B as g,
  q as N,
} from "./vue-561933f7.js";
const w = { class: "body" },
  U = { class: "name" },
  V = { class: "sub" },
  A = { class: "acct _monospace" },
  D = f({
    __name: "MkUserCardMini",
    props: { user: {}, withChart: { type: Boolean, default: !0 } },
    setup(i) {
      const o = i;
      let a = v(null);
      return (
        M(() => {
          o.withChart &&
            _("charts/user/notes", {
              userId: o.user.id,
              limit: 16 + 1,
              span: "day",
            }).then((s) => {
              s.inc.splice(0, 1), (a.value = s.inc);
            });
        }),
        (s, z) => {
          const l = r("MkAvatar"),
            u = r("MkUserName"),
            d = k("adaptive-bg");
          return y(
            (n(),
            C(
              "div",
              {
                class: N([
                  s.$style.root,
                  {
                    yellow: s.user.isSilenced,
                    red: s.user.isSuspended,
                    gray: !1,
                  },
                ]),
              },
              [
                c(
                  l,
                  { class: "avatar", user: s.user, indicator: "" },
                  null,
                  8,
                  ["user"]
                ),
                e("div", w, [
                  e("span", U, [
                    c(u, { class: "name", user: s.user }, null, 8, ["user"]),
                  ]),
                  e("span", V, [e("span", A, "@" + b(t(m)(s.user)), 1)]),
                ]),
                t(a)
                  ? (n(),
                    B(p, { key: 0, class: "chart", src: t(a) }, null, 8, [
                      "src",
                    ]))
                  : g("", !0),
              ],
              2
            )),
            [[d]]
          );
        }
      );
    },
  }),
  S = "xqX2u",
  $ = { root: S },
  q = { $style: $ },
  I = h(D, [["__cssModules", q]]);
export { I as M };

みたいなコードが吐かれるので

import { _ as p } from "./MkMiniChart.vue_vue_type_script_setup_true_lang-c405dd11.js";
import { an as _, br as m, _ as h } from "./app-c70a3e7b.js";
import {
  b as f,
  a as v,
  e as M,
  z as r,
  f as k,
  j as y,
  g as n,
  k as C,
  A as c,
  l as e,
  E as b,
  u as t,
  h as B,
  B as g,
  q as N,
} from "./vue-561933f7.js";
const w = { class: "body" },
  U = { class: "name" },
  V = { class: "sub" },
  A = { class: "acct _monospace" },
  D = f({
    __name: "MkUserCardMini",
    props: { user: {}, withChart: { type: Boolean, default: !0 } },
    setup(i) {
      const o = i;
      let a = v(null);
      return (
        M(() => {
          o.withChart &&
            _("charts/user/notes", {
              userId: o.user.id,
              limit: 16 + 1,
              span: "day",
            }).then((s) => {
              s.inc.splice(0, 1), (a.value = s.inc);
            });
        }),
        (s, z) => {
          const l = r("MkAvatar"),
            u = r("MkUserName"),
            d = k("adaptive-bg");
          return y(
            (n(),
            C(
              "div",
              {
                class: N([
                  "xqX2u",
                  {
                    yellow: s.user.isSilenced,
                    red: s.user.isSuspended,
                    gray: !1,
                  },
                ]),
              },
              [
                c(
                  l,
                  { class: "avatar", user: s.user, indicator: "" },
                  null,
                  8,
                  ["user"]
                ),
                e("div", w, [
                  e("span", U, [
                    c(u, { class: "name", user: s.user }, null, 8, ["user"]),
                  ]),
                  e("span", V, [e("span", A, "@" + b(t(m)(s.user)), 1)]),
                ]),
                t(a)
                  ? (n(),
                    B(p, { key: 0, class: "chart", src: t(a) }, null, 8, [
                      "src",
                    ]))
                  : g("", !0),
              ],
              2
            )),
            [[d]]
          );
        }
      );
    },
  });
export { D as M };

といった感じにポストプロセスできれば良い

acid-chicken commented 1 year ago

ちょくちょくおるな

https://github.com/misskey-dev/misskey/blob/6addf9002cbdcd09e2deb66f0278946db990f014/packages/frontend/src/components/MkDateSeparatedList.vue#L39

acid-chicken commented 1 year ago

ベタ書きだけどできた https://github.com/misskey-dev/misskey/compare/perf/10923

acid-chicken commented 1 year ago

現時点の制約 / 問題点