惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
B
Blog
Stack Overflow Blog
Stack Overflow Blog
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
小众软件
小众软件
博客园 - 三生石上(FineUI控件)
Recent Announcements
Recent Announcements
U
Unit 42
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
腾讯CDC
D
DataBreaches.Net
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
M
MIT News - Artificial intelligence
P
Proofpoint News Feed
罗磊的独立博客
L
LangChain Blog
V
Visual Studio Blog
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
宝玉的分享
宝玉的分享

WAYJAM

告别 NPM Token:迁移到 Trusted Publishing 全记录 Sing-box:IPv6 优先出站与 Netflix 强制 IPv6 分流 [All In One] HomeLab 虚拟化 iKuai + Mihomo - IPv6 支持 [All In One] HomeLab 虚拟化 iKuai + Mihomo LiteLLM 多模型 API 中转 MacOS Bootstrp with Nix Fluency Support Full Rss [All In One] PVE 中 OpenWrt LXC 重启问题 Tabby 终端配置以及配置全平台同步 Cloudflare Tunnel + Nginx Proxy Manager:服务穿透 PVE 7 升级 8 笔记 PVE 安装 Android x86 & 安装 Magisk Fly.io 部署 Artalk 评论系统 [All In One] HomeLab 2023 搭建总结 [All In One] 轻量的文件 & 媒体服务器配置 [All In One] R71s 主机散热改造 [All In One] PVE LXC 安装 OpenWrt 软路由以及网络规划 [All In One] 安装 Proxmox VE 7.4 从零开始配置Rime - 2023 Self Hosted Bitwarden + Cloudfare HTTPS Debian NFS Server 以及 MacOS Client AdguardHome 和 Clash 透明代理配合使用 使用 Kind 部署 Prow 手记 BackBlaze B2 + Cloudfare Worker 图床 PicGo Amazon S3 插件 自动给模板生成 Inline-Svg Hugo Theme WeUI 发布! Hugo Theme Fluency 发布! Docker Clash 和 透明代理 有限状态机与分布式熔断器
无可奈何的页面增强方案
2018-07-14 · via WAYJAM

如果有一个大型的 Web 程序,需要对其进行一些使用上的改进,但有时候又需要跟踪上游的更新;需要保持其稳定性,不能频繁地部署;更重要的是,没有足够的时间、人员资源去进行二次开发。 如果说只是更改页面的样式或者体验性增强,可以通过浏览器插件来做到类似客户端的效果,而且有更大的权限去做更多的事情,但是可能没有办法去令到每个人安装这样的插件,特别是Chrome收紧了安全策略之后。 该怎么做呢?

我是这样做的:找到切入点,使这个程序加载一个外部的 JavaScript 脚本,通过脚本去修改页面的 DOM,数据来源是页面上的 DOM 结构以及藏在 window 对象下的的信息。当然了,单纯地改变页面并不足以满足需求,还要通过各式各样的API(包括这个 Web 程序的,或者我们自己开发的后端)去获取数据、去触发钩子操作。

仔细分析,这样的操作就是 userscript,greasyfork.org 上有大量这样的脚本,唯一不同的只是脚本的位置,我将这样的操作叫做:运行在服务端的UserScript。

首先第一个问题来了,为了部署方便,最后我们只会利用 Webpack 打包出一个脚本,整站都会加载,那么不同的页面自然有不同的功能需求,且不同功能之间不能够互相影响。我希望有一个功能(规则)表,能够根据规则来开启不同的功能。

import FeatureA from 'components/feature-a';
import FeatureB from 'components/feature-b';
import FeatureC from 'components/feature-c';
rules = {
    "controller1": {
        "page1": FeatureA
    },
    "controller2": {
        "page2": [FeatureB, FeatureC],
        "controller5": {
            components: FeatureB,
            children: [{"page4": FeatureC}]
        }
    },
    "controller3": {
        "controller4": {
            "page3": [FeatureA]
        }
    }
}

第二个问题,如何编写功能组件。首先设计一个抽象基类,所有功能组件都继承自它,并且它保持了一些基本的元信息,并且可以进行一些初始化操作和开启关闭某些操作。

abstract class Base {
    feature={}
    constructor(parent: Base | undefined) {
        this.parent = perent;
    }
    abstract init(): void;
}
class FeatureA extends Base {
    feature={action1: true, action2: false}
    init() {
        // do something
    }
    action1() {}
    action2() {}
}

第三个问题,就是解决根据规则表加载功能组件。

class App {
    constructor () {
        go(rules, path);
    }
    load (component, args) {
        let c = new component(..args);
        c.init();
        if (c.hasOwnProperty('feature')) {
            Object.keys(c.feature).map((action)=> {
                if (!c.feature.action) return;
                if (c.hasOwnProperty(action)) c.action();
            })
        }
    }
    go (rules: IRules, path, parent) {
        // if is component
        // or if is component list
        // then load it directly
        // if has components key, load rules[components] = parent
        // then go rules[children], and set parent to parent
    }
}
new App()

通过脚本来更改 DOM 免不了需要编写 HTML 片段,个人认为 JSX 可能是比较好的解决方案,我会选择轻量的 Preact 去渲染 HTML 片段,处理内部事件,但其生命周期由上面所述功能组件决定。例如,我需要在页面上增加一个按钮,这个按钮会触发一些事件,进行某些操作,按钮的状态与外部交互通过事件通信,或者说由外部重新渲染(依靠内部 diff )。

class ReactButtonDemo extends PreactComponent {}
class Feature extends Base {
    init () {
        render(<ReactButtonDemo {...args} click={()=>this.click()} />)
    }
}