8.1 层功力 remnote 制卡,支持内置卡包制卡和移除

本贴最后更新于 472 天前,其中的信息可能已经时过境迁

2023.4.5 这些代码片段还能用,但假如不是你不愿意使用插件系统、或插件系统不能正常运行的话,我还是建议是使用插件版本的闪卡增强插件 v0.0.1 发布 基于社区插件系统 - 链滴


前情提要:8 层功力的 remnote 制卡 并多多一键制卡更新,支持列表项制卡,增加一键移除

在尝试了 v2.7.7 最新版本的文档树制卡后,我不得不真香了,我必须承认:

从文档树打开闪卡界面真是太酷了,很符合我对思源未来的想象,科技并带着趣味。

故紧急更新了一版,支持直接制卡到文档树的内置卡包。

不过,制卡到文档树的内置卡包和制卡到之前的自定义卡包是一个二选一的选项,因为这会造成重复制卡,也就是说一张卡片你会在复习的时候重复见两遍 :(

所以暂时只能做成开关了。默认为之前的自定义卡包制卡方式,想要使用新版本的文档树内置卡包,那就划到代码的最下方,把 false 改成 true 即可。

// 一键制卡 v0.7
// 使用匿名函数,防止污染空间
// 支持使用内建卡片
((useBulitIn = false) => {
    // 请求函数
    function request(url, data = null, method = "POST") {
        return new Promise((resolve, reject) => {
            if (method.toUpperCase() == "POST") {
                fetch(url, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(data),
                })
                    .then(
                        (data) => resolve(data.json()),
                        (error) => {
                            reject(error);
                        }
                    )
                    .catch((err) => {
                        console.error("请求失败:", err);
                    });
            }
        });
    }

    // 弹出提示信息
    async function showMessage(msg) {
        await request("/api/notification/pushMsg", { msg, timeout: 3000 });
    }

    // 获取当前文档id
    function getFileID() {
        //获取当前页面
        const currentPage = getCurrentPage();
        //获取当前页面id
        const currentPageID = currentPage.querySelector(
            "span.protyle-breadcrumb__item--active"
        ).getAttribute("data-node-id");

        return currentPageID;
    }

    function getCurrentPage() {
        var currentScreen;
        var currentPage;
        try {
            //获取当前屏幕
            currentScreen = document.querySelector(".layout__wnd--active");
            //获取当前页面
            currentPage = currentScreen.querySelector(
                ".fn__flex-1.protyle:not(.fn__none)"
            );
            return currentPage;
        }
        catch (e) {
            showMessage(`未能获取到页面焦点!`)
        }
        throw new Error("未能获取到页面焦点!");
    }

    //传入 node节点列表 arr和数据抽取函数 fliter。
    // fliter 传入一个节点,返回一个字典
    // 字典格式:
    // {status,data}
    // status 为 ok,则代表为抽取成功,将data压入返回列表anwer,并最后返回。
    function iterArr(arr, fliter) {

        if (!arr && arr.length <= 0) {
            return [];
        }

        length = arr.length;
        anwer = [];
        for (let node of arr) {
            data = fliter(node);
            if (data["status"] === "ok" && data["data"]) {
                anwer.push(data["data"]);
            }
        }
        return anwer;
    }

    // 传入列表下的段落,返回被标记段落的父节点:列表项的id
    function fliterListCard(node) {
        var re = /(>>|(》》))\s?\u200b?$/;
        if (re.test(node.innerText)) {
            return {
                status: "ok",
                data: node.parentElement.getAttribute("data-node-id")
            }
        }
        return { status: "no", data: null };
    }

    function fliterMarkCard(node) {
        return {
            status: "ok",
            data: node.parentElement.parentElement.getAttribute("data-node-id")
        }
    }

    function fliterSuperBlockCard(node) {
        return {
            status: "ok",
            data: node.getAttribute("data-node-id")
        }
    }

    // 获取当前文档路径
    async function getHpath() {
        currentPageID = getFileID()
        body = { "id": currentPageID }
        let res = await request("/api/filetree/getHPathByID", body);

        //检测返回值
        if (res.code === 0) {
            let currentPagePath = res.data;
            return currentPagePath;
        }
        return null;
    }

    // 添加多个卡片
    async function addCards() {
        //获取全部deck
        let res = await request("/api/riff/getRiffDecks", {});

        //获取当前打开文档的路径
        focusItemPath = await getHpath()
        if (focusItemPath === null) {
            showMessage(`未能当前文档读取路径`);
            return;
        }


        let customDeckIdArr = [];
        // 导入默认卡包
        if (useBulitIn === true) {
            customDeckIdArr.push('20230218211946-2kw8jgx');
        }
        else {

            //获取deck.id, 其deck.name 包含在 focusItemPath 中
            Array.from(res.data).forEach((item) => {
                let name = item.name;
                if (focusItemPath.search(name) != -1) {
                    customDeckIdArr.push(item.id);
                }
            })
        }



        if (customDeckIdArr.length === 0) {
            showMessage(`未能获取到:${focusItemPath} 的卡包`);
            return;
        }

        // 获取需要被制成卡片的块的ID
        const currentPage = getCurrentPage()
        //标记块
        const markList = currentPage.querySelectorAll("span[data-type='mark']")
        //超级块
        const superBlockList = currentPage.querySelectorAll("div[class='sb']")
        //列表块
        const listList = currentPage.querySelectorAll("div[class='li']:has(div[class='li'])  > div[class = 'p']")

        let arr = [];
        var markCardList = iterArr(markList, fliterMarkCard)
        var superBlockCardList = iterArr(superBlockList, fliterSuperBlockCard)
        var listCardList = iterArr(listList, fliterListCard)
        arr = arr.concat(markCardList, superBlockCardList, listCardList)


        if (arr.length === 0) {
            showMessage(`未能获取到:${focusItemPath} 页面的卡片`);
            return;
        }

        // 去重
        arr = [... new Set(arr)];

        for (deckIndex in customDeckIdArr) {

            // 把标记所在块全部制成卡片
            let body = {
                deckID: customDeckIdArr[deckIndex],
                blockIDs: arr,
            };
            let res = await request("/api/riff/addRiffCards", body);
            if (res.code === 0) {
                showMessage(`${res.data.name}卡包的总卡片数:${res.data.size}`);
            }
        }

    }

    // 添加一个按钮
    const barMode = document.getElementById("barMode");
    barMode.insertAdjacentHTML(
        "beforebegin",
        '<div id="addCards" class="toolbar__item b3-tooltips b3-tooltips__se" aria-label="制作卡片" ></div>'
    );
    const addCardsBtn = document.getElementById("addCards");
    addCardsBtn.style.width = "auto";
    const addCardsBtnIcon = `<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4526" width="200" height="200"><path d="M928 160H96a32 32 0 0 0-32 32v672a32 32 0 0 0 32 32h832a32 32 0 0 0 32-32V192a32 32 0 0 0-32-32z m-32 672H128V224h768v608z" p-id="4527" fill="#9aa0a6"></path><path d="M230.592 448.096H544a32 32 0 1 0 0-64H230.592a32 32 0 0 0 0 64zM230.592 640.096H544a32 32 0 1 0 0-64H230.592a32 32 0 1 0 0 64zM768 704a32 32 0 0 0 32-32V350.016a32 32 0 1 0-64 0V672a32 32 0 0 0 32 32z" p-id="4528" fill="#9aa0a6"></path></svg>`;
    addCardsBtn.innerHTML = addCardsBtnIcon;
    addCardsBtn.addEventListener(
        "click",
        (e) => {
            addCards();
            e.stopPropagation();
            e.preventDefault();
        },
        true
    );

})(false);
// 一键移除卡片 v0.2
// 使用匿名函数,防止污染空间
(() => {
    // 请求函数
    function request(url, data = null, method = "POST") {
        return new Promise((resolve, reject) => {
            if (method.toUpperCase() == "POST") {
                fetch(url, {
                    method: "POST",
                    headers: { "Content-Type": "application/json" },
                    body: JSON.stringify(data),
                })
                    .then(
                        (data) => resolve(data.json()),
                        (error) => {
                            reject(error);
                        }
                    )
                    .catch((err) => {
                        console.error("请求失败:", err);
                    });
            }
        });
    }

    // 弹出提示信息
    async function showMessage(msg) {
        await request("/api/notification/pushMsg", { msg, timeout: 3000 });
    }


    function getCurrentPage() {
        var currentScreen;
        var currentPage;
        try {
            //获取当前屏幕
            currentScreen = document.querySelector(".layout__wnd--active");
            //获取当前页面
            currentPage = currentScreen.querySelector(
                ".fn__flex-1.protyle:not(.fn__none)"
            );
            return currentPage;
        }
        catch (e) {
            showMessage(`未能获取到页面焦点!`)
        }
        throw new Error("未能获取到页面焦点!");
    }

    //传入 node节点列表 arr和数据抽取函数 fliter。
    // fliter 传入一个节点,返回一个字典
    // 字典格式:
    // {status,data}
    // status 为 ok,则代表为抽取成功,将data压入返回列表anwer,并最后返回。
    function iterArr(arr, fliter) {

        if (!arr && arr.length <= 0) {
            return [];
        }

        length = arr.length;
        anwer = [];
        for (let node of arr) {
            data = fliter(node);
            if (data["status"] === "ok" && data["data"]) {
                anwer.push(data["data"]);
            }
        }
        return anwer;
    }

    function fliterBlockCard(node) {
        return {
            status: "ok",
            data: node.getAttribute("data-node-id")
        }
    }

    async function removeCards() {


        // 获取需要被制成卡片的块的ID
        const currentPage = getCurrentPage()
        //已制卡块
        const cardedBlockList = currentPage.querySelectorAll(".protyle-wysiwyg [data-node-id][custom-riff-decks]")


        let arr = [];
        var cardedBlockIdList = iterArr(cardedBlockList, fliterBlockCard)

        arr = arr.concat(cardedBlockIdList)


        if (arr.length === 0) {
            showMessage(`未能获取到页面的卡片`);
            return;
        }

        // 去重
        arr = [... new Set(arr)];


        let body = {
            deckID: '',
            blockIDs: arr,
        };
        let res = await request("/api/riff/removeRiffCards", body);
        if (res.code === 0) {
            showMessage(`All卡包的移除卡片数:${arr.length}`);
        }

    }

    const barMode = document.getElementById("barMode");
    barMode.insertAdjacentHTML(
        "beforebegin",
        '<div id="removeCards" class="toolbar__item b3-tooltips b3-tooltips__se" aria-label="移除卡片" ></div>'
    );
    const removeCardsBtn = document.getElementById("removeCards");
    removeCardsBtn.style.width = "auto";
    const removeCardsBtnIcon = `<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M600 826v-60h145v60H600Zm0-368v-60h280v60H600Zm0 184v-60h235v60H600ZM125 381H80v-60h170v-45h135v45h170v60h-45v415q0 24-18 42t-42 18H185q-24 0-42-18t-18-42V381Zm60 0v415h265V381H185Zm0 0v415-415Z"/></svg>`;
    removeCardsBtn.innerHTML = removeCardsBtnIcon;
    removeCardsBtn.addEventListener(
        "click",
        (e) => {
            removeCards();
            e.stopPropagation();
            e.preventDefault();
        },
        true
    );

})();

如果喜欢,请给我留言吧。

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    20156 引用 • 77726 回帖
1 操作
zxhd86 在 2023-04-07 22:25:46 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • 是吧,这可是我当初据理力争、力排众议、舌战群儒争取来的 😄

  • 支持,太棒了,上次想一键移除卡片,D 大说为了稳定性暂时不考虑一次性删除多个属性,没想到大佬这么快就安排了。等待大佬的列表项制卡,达到 9 层功力。

    感谢提供。

    1 回复
  • wz169645

    支持,很棒

  • renky

    所以现在的代码是还暂时不可以列表项制卡吗?

    2 回复
  • zxhd86

    @gladi 我不太理解这个意思,不过现在确实是支持列表项制卡的。

    有什么改进的想法也可以提一下,虽然我暂时想不到什么能改进而且我能实现的。

    比如我有一个想法是动态跟踪用户的标记,不用手动点击,但是想不到有什么比较好的办法追踪,如果定期执行感觉又太浪费性能,而且可能不符合用户的想法。

    1 回复
  • 感谢大佬回复,当时看到一键移除有点激动就立即评论了,没有注意到是否支持列表项制卡。我这里测试了一下,列表项制卡还是和之前一样是用的段落块制作的卡片,不过加入了一键移除后也没什么影响了,挺好用的,再次感谢提供。

    动态追踪那个我不清楚耶,我一个外行想到,是否能打开某个开关后,就可以通过,在标记或超级块生成时的按键或快捷键后就立即执行自动点一下制作卡片的按钮呢。(触发机制就是点了标记或超级块的按键就触发)

    没有也没事,现在已经很好了。

    之前用 anki 制作卡时有用到一些模版批量制卡,有在想思源能用什么方法达到这样的列表项制作问答卡片,但是无奈啥都不会,大佬可以看看,也许能有所启发。

    方法来源于学习骇客:模板制卡的网站,所用到的 anki 模板

    达到的效果image.png

    image.png

    image.png

    image.png

    image.png

    image.png

    image.png

    可以看到,书写笔记需要有一定的原则,比如第 1 层是书名,第 2 层是章名,第 3 层是节名,第 4 层是问题,第 5 层是答案。

    那么就会得到这么些卡片

    • 1、问题是:书名,答案是:所有章名(问题处的下标指示了《书名》)
    • 2、问题是:章名,答案是:所有节名(问题处的下标指示了《书名》)
    • 3、问题是:节名,答案是:所有的问题(问题处的下标指示了《书名-章名》)
    • 4、问题是:问题,答案是:问题的答案(问题处的下标指示了《书名-章名-节名》)

    前 3 种自动生成的卡片的好处是让我们对这本书有个大的框架的把握,最后一种卡片是细化到每一问题,然后又有下标指示让知识点不是那么碎片化。

    @88250

    • 对应到思源上面,闪卡上的面包屑相当于上面的下标这个功能,但是思源的列表项在闪卡中展开与否,看的是文档中是否展开了,就比较想要在闪卡中能自动判断是否该展开。
    • (比如我制作的是一张上面的第 3 类卡,那么就默认是不展开问题的答案而只展示所有问题,想看答案也可以全部展开)
    • 现在是文档中全部展开,闪卡显示的时候就展开的;文档中全部关闭的,闪卡显示的时候就只有最开始能看到的卡片正面,背面需要自己展开才显示。(这点估计得让 D 大或 V 姐看看能否支持)
    • 列表项多制卡片右边的标记用渐变色来表示是否多次制作卡片了,对了撤销键 D 大看看要不要换一个数字 5 或是 J,换成 5 就可以单手完成用闪卡时的所有操做了,换成 J 就是左手负责选择程度,右手负责撤销(毕竟大家的右手食指一般都是放在 J 上的,会比用小拇指按 P 更方便。

    @zxhd86 大佬的一键制作卡片加入制作列表项制作卡片就可以实现上面这样了(每一个层级都制作一张)

    谢谢

  • zxhd86

    怎么说呢,原来你说的支持列表项制卡是指:要是段落挖空在列表项里,就用该列表项制卡的意思啊。那这个确实还没有支持,而且在引入一键移除后,感觉也没有太大支持的必要。而且会与思源目前支持的列表项制卡混杂。

    我所说的支持列表项制卡其实就是目前 siyuan 提供的那种列表项卡片,也就是:

    • 问题 < 标记符号 >

      • 答案 1
      • 答案 2

    就能一键制成卡片的那种。目前支持的标记符号有》》和 >>,想要添加的话不是很有难度,但是在键盘上又没有其他用途的符号难找,不然像 remnote 一样支持箭头符号也可以。

    如果要通过标记或超级块的按键或快捷键执行时触发的话,那以我的认知来看,就需要修改思源的源码才能实现了……

    2 回复
  • 好嘞,谢谢解答哟,目前已经很好用了,很棒

  • 把大佬 < 标记符号 > 中的一个改成了?,然后就不用用》》做标记了,很不错。(我习惯在问题后面打上问号)就很 nice。

  • 可以的,是我之前没有注意到,真的香的一批。

    你将大佬一键制作卡片的代码中的 var re = /(>>|(》》))\s?\u200b?$/;中的 >> 换成?

    然后列表项目的问答题,你只要写问题的时候打了 ?(哈哈问号不就是天然的问答卡的标记符么)

    就可以一键用列表项生成问答卡

    至于列表项中的填空卡是用段落块生成的,也没什么影响。

    总结:太香了

  • 想问请教一下大佬,现在一键移除这么方便,也紧紧挨着一键制卡的。

    如果去点一键制卡的时候不小心点到了一键移除,怎么撤销呢?

    是只能用历史数据回滚么,还是有撤销的快捷键呢?

    如果有撤销的快捷键麻烦大佬告知一下呀,谢谢。

    如果没有,希望下一版的一键移除可以改进。(方式有:增加撤销键 或 增加一键移除的难度,比如多一步确认才能一键移除)

  • zxhd86 1 赞同

    因为我个人并不常用一键移除。事实上,我只有用到的时候才打开代码片段的……

    所以并没有撤销机制,这也并不是使用代码片段能做到的。只能历史数据回滚。

    增加撤销的确定可以考虑,不过不会是近期了。因为打算迁移到社区里的插件系统,打算那时候插件 api 完善了再把两个合并。

    1 回复
  • 好嘞,谢谢,那我暂时先将一键移除的代码片段关掉,用的时候再打开

  • yinyon

    支持!

    学习中!

请输入回帖内容 ...

推荐标签 标签

  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 1 关注
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 94 关注
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖 • 5 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 615 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    4 引用 • 91 回帖
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖 • 2 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 283 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    16 引用 • 7 回帖 • 2 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    7018 引用 • 31717 回帖 • 219 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 1 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    106 引用 • 152 回帖
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 321 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    70 引用 • 533 回帖 • 734 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 239 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 404 关注
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 110 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    165 引用 • 1474 回帖
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1427 引用 • 10046 回帖 • 473 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 196 关注
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 330 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    35 引用 • 35 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    86 引用 • 897 回帖 • 1 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    124 引用 • 580 回帖
  • LeetCode

    LeetCode(力扣)是一个全球极客挚爱的高质量技术成长平台,想要学习和提升专业能力从这里开始,充足技术干货等你来啃,轻松拿下 Dream Offer!

    209 引用 • 72 回帖
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 141 关注
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 3 关注