其他 null

ccmywish · 2023年11月04日 · 最后由 ratazzi 回复于 2023年11月13日 · 1315 次阅读

.

ccmywish 大家一般用 Ruby 都来做什么?(除了 Rails) 提及了此话题。 11月04日 09:37

简单小应用,turbo 的确可以基本上不写 js,但目前的 turbo 处理 redirect_to,flash 等等,感觉不是很优雅,希望 turbo8 出来后能处理的更优雅。我的熊猫监控 https://jiankong.xmtui.com 就用 turbo frame 了,redirect_to 的时候还是加了几行 JS。

turbo8 用到 morphdom 理念,在 turbo:before-render,turbo:before-frame-render,turbo:load 等 turbo 事件中做事情,

import "flowbite/dist/flowbite.turbo.js";
import "@hotwired/turbo-rails"
import "./controllers"
import Trix from "trix";
import "@rails/actiontext"
import idiomorph from "./utils/idiomorph";
import { StreamActions } from "@hotwired/turbo";
import {
  shouldPerformTransition,
  performTransition,
} from "turbo-view-transitions";

let prevPath = window.location.pathname;

const morphRender = (prevEl, newEl) => {
  return idiomorph.morph(prevEl, newEl, {
    callbacks: {
      beforeNodeMorphed: (fromEl, toEl) => {
        console.log("🚀 ~ file: application.js:20 ~ morphRender ~ fromEl, toEl:", fromEl, toEl)
        if (typeof fromEl !== "object" || !fromEl.hasAttribute) return true;
        if (fromEl.isEqualNode(toEl)) return false;

        if (
          fromEl.hasAttribute("data-morph-permanent") &&
          toEl.hasAttribute("data-morph-permanent")
        ) {
          return false;
        }

        return true;
      },
    },
  });
};

document.addEventListener("turbo:before-render", (event) => {
    Turbo.navigator.currentVisit.scrolled = prevPath === window.location.pathname;
    prevPath = window.location.pathname;

    event.detail.render = async (prevEl, newEl) => {
      await new Promise((resolve) => setTimeout(() => resolve(), 0));
      await morphRender(prevEl, newEl);
    };

    if (shouldPerformTransition()) {
      // Make sure rendering is synchronous in this case
      event.detail.render = (prevEl, newEl) => {
        morphRender(prevEl, newEl);
      };

      event.preventDefault();

      performTransition(document.body, event.detail.newBody, async () => {
        await event.detail.resume();
      });
    }
  });

  document.addEventListener("turbo:before-frame-render", (event) => {
    event.detail.render = (prevEl, newEl) => {
      Idiomorph.morph(prevEl, newEl.children, { morphStyle: "innerHTML" });
    };
  });

  document.addEventListener("turbo:before-stream-render", (event) => {
    if (shouldPerformTransition()) {
      const fallbackToDefaultActions = event.detail.render;

      event.detail.render = (streamEl) => {
        if (streamEl.action == "update" || streamEl.action == "replace") {
          const [target] = streamEl.targetElements;

          if (target) {
            return performTransition(
              target,
              streamEl.templateElement.content,
              async () => {
                await fallbackToDefaultActions(streamEl);
              },
              { transitionAttr: "data-turbo-stream-transition" }
            );
          }
        }
        return fallbackToDefaultActions(streamEl);
      };
    }
  });

  document.addEventListener("turbo:load", () => {
    if (shouldPerformTransition()) Turbo.cache.exemptPageFromCache();
  });

以上是最近在折腾时看到的引到我的项目里用,这里用到https://github.com/bigskysoftware/idiomorph,BaseCamp 引用这个 idiomorph 去封装 turbo8

turbo 和 Stimulus 结合 rails 的 route 和 controller 在 view 层上做到单页面应用的效果,让开发者不去走前后端分离开发也能实现 JavaScript 框架实现的,One-Person-FrameWork

想要优雅,你自己拓展 turbo action, 可以解决你跳转和 flash 的问题

turbo stream 可以访问你页面任何 DOM,

但是你只是单独的 turbo frame, flash 的结构要变,得跟着 frame 走,确实不够优雅

turbo stream 确实可以让你尽可能的不写 js, 但是会让你尽可能多的和服务端做交互,也就是你的 Req 会变多, 并且所有客户端逻辑都散落在你服务端的 turbo stream 的代码中,你要维护这些,以及耦合的 dom_id 所以你需要合理的遵循 rails 的方式来组织你的 view 虽然有这些缺点,但是我觉得体验还是挺好的,因为统一了客户端的行为

jisuanjixue 回复

DOM Merge 算法解决不了楼主的问题了,

虽然官方在推idiomorph 但是他大部分场景性能还不如 morphdom

替换一下 Turbo.PageRenderer 就行了

Turbo.PageRenderer.prototype.assignNewBody = function () {
    if (document.body) {
        morphdom(document.body, this.newElement, {
            onBeforeElUpdated: (fromEl, toEl) => !fromEl.isEqualNode(toEl),
        });
    } else document.documentElement.appendChild(this.newElement);
};

opal 理論上是能用的,但用到 js 庫都要寫個 binding,性能也會比正常 js 慢,有不少人嘗試過做 opal 的組件框架,如 hyperstack 和 clearwater,不過目前看都是棄坑的狀態,現在前端都 ts 和 vite 了,opal 做點簡單的可能還行,不然開發體驗跟不上

我有个理论就是寄生性语言都会死掉。

从 CoffeScript 开始,即使是今天热度很高的 TypeScript。他们都会因为 JavaScript 不断完善而被淘汰掉。

归根结底还是因为浏览器执行的就是 JavaScript,绑定 UI 的语言使用的是单线程的模型,不同于其他语言,思维方式不同。其他语言即使去封装,比如用 Ruby 语法写 JavaScript,最后结果就是:你不仅要关心 Ruby 还要关心 JavaScript 还要关心他们之间的差异和版本区别,痛苦翻倍。

如果你已经理解 JavaScript,还不如去写 JavaScript。最后就会发现这样的换写毫无意义。

除非 Ruby 被浏览器支持。但是不太可能。浏览器是比操作系统甚至还复杂的软件,他的更新换代周期很慢,牵扯很多,往往是厂商们博弈后的结果。最后,你没什么选择。市场现状也说明了这个问题。

9 楼 已删除

回答一下第三个问题。wasm 现在在前端运用的其实比较少。

前端说白了其实就是操作状态、渲染 UI。

什么是状态?例如一个 Switch 开关是开还是关;一个 Button 是被 hover、被 click、loading 中、disable 中;你的用户列表展示哪些用户,他们的名字、头像都是什么……这是前端最常用到的状态。

怎么操作 UI?通过 DOM 操作,比如最常用的操作,做一次网络请求,修改按钮的状态:

function submit() {
  const button = 找到 Button
  button.设置为 loading
  button.设置为 不可点击
  request API
  button.设置为 正常
  button.设置为 可以点击
  // ...
}

WASM 的优势是在计算上比 JS 更快,但是他的局限在与无法操作 DOM,目前只有 JS 可以操作 DOM,上面的函数无法用 wasm 写出来。

那我们在关键计算上可以用 wasm 吗?可以,但是 99.9999% 的情况下都没有必要。

第一个问题是,JavaScript 目前的主流引擎都内置强大的 JIT 系统,尤其是 chrome 的 v8,它可以让 JS 的热点代码拥有强大的性能,而 wasm 是没有的。绝大部分前端执行的计算都是短暂的,在 JIT 的加持下,二者的性能差距其实非常小。即使真的有耗费时间的计算,我们也可以利用 WebWorker 来启动工作线程处理,让计算不阻塞 UI。

第二个问题,传递参数给 wasm 的时候有转化成本,你传入的数据越大,转化成本就越高,并且 wasm 不能进行网络请求,你只能从外部传入数据。到最后会发现,这个性能损耗,还不如直接写 JS。

wasm 现在在前端有哪些应用呢?(服务器端其实也有应用,但我不太了解)

  1. 大文件处理,例如处理一个上 G 的文件,计算 md5。
  2. 底层有非常成熟且复杂的其它语言的库,例如 PS、PR,这些软件底层有大量 C++ 写的库,用 JS 全部重写成本太高了,目前很多 Web 版本的 PS、PR 都使用了 wasm 来迁移这些底层库。

turbo 不是为了去 js 化,而是把精力用去真正需要 js 的地方。

js 是 DHH 最喜欢的第二语言。

Rei 回复

这个解释到位,只有 turbo 实现不了的时候,才用 js

优先级是 turbo frame -> turbo stream -> javascript

@willx wasm 可以请求网络,这里有个端口扫描的 https://github.com/avilum/portsscan

我们还真在客户端用,不过是内部系统,为了在 https 的页面访问本地 http 服务用于打印,另一个方式是写个浏览器扩展

ratazzi 回复

客户端可以,浏览器不行的

willx 回复

怪我用了“客户端”这个词,浏览器亲测可以的,服务器允许 CORS 就行,上面那个 portscan 就是在浏览器里跑的

ratazzi 回复

不是的哦,wasm 本身没有网络能力,他只能调用 JavaScript 的 API。

在这里打断点就可以看到它实际上的调用是 Go 语言帮你做了一层编译,类似把 net/http 包编译成了 Call("fetch"),本质上还是调用 JS、然后把网络请求的结果变成 uint8 数组进行传递。

willx 回复

受教了

ccmywish 关闭了讨论。 01月22日 00:15
需要 登录 后方可回复, 如果你还没有账号请 注册新账号