首页
/ 3个脚本解放90%笔记管理时间:Trilium自动化指南

3个脚本解放90%笔记管理时间:Trilium自动化指南

2026-04-04 09:37:29作者:尤峻淳Whitney

作为知识工作者,你是否每天都在重复这些操作:手动分类新收集的资料、定期整理分散的笔记片段、在不同设备间同步重要内容?这些机械劳动不仅占用大量时间,还容易出错和遗漏。Trilium Notes(以下简称Trilium)的脚本编程功能就像一位不知疲倦的助手,能帮你将这些重复工作自动化,让你专注于真正有价值的思考和创作。

本文将通过三个实战案例,带你掌握Trilium脚本开发的核心技术,从痛点分析到实际应用,全方位提升你的笔记管理效率。无论你是Trilium新手还是有一定经验的用户,都能从中找到适合自己的自动化方案。

一、痛点分析:笔记管理中的效率陷阱

在知识管理过程中,我们常常面临以下效率瓶颈:

1.1 信息收集的碎片化困境

每天从网页、电子书、会议记录中收集大量信息,但这些内容往往分散在不同笔记中,缺乏统一的组织方式。据统计,知识工作者平均每天要花费20%的时间寻找和整理信息,相当于每周损失近一整天的有效工作时间。

💡 技术提示:信息碎片化 - 指知识内容分散存储在多个位置,缺乏关联性和结构化的状态

1.2 重复性操作的时间消耗

格式化笔记、添加标签、创建索引等重复性工作占用大量时间。以每周整理10篇技术文章为例,手动添加标签、分类和关联至少需要2小时,一年下来就是超过100小时的无效劳动。

1.3 跨设备同步的复杂性

在电脑、平板和手机间同步笔记时,往往需要手动选择和传输,不仅效率低下,还可能导致版本混乱和数据丢失。特别是包含大量图片或附件的笔记,同步过程更加繁琐。

二、核心功能:Trilium脚本API详解

Trilium提供了强大的前后端脚本API,就像为你配备了两个专业助手:后端API负责数据处理和系统操作,前端API专注于用户界面和交互体验。

2.1 前后端API对比表

特性 后端API 前端API
运行环境 服务器/本地应用 浏览器
主要功能 数据操作、文件处理、定时任务 UI交互、用户输入、界面展示
权限级别 高(可访问所有笔记数据) 中(主要访问当前用户数据)
典型应用 批量处理、数据导入导出、定时任务 界面组件、快捷键操作、交互工具
入口文件 src/services/backend_script_api.js src/public/app/services/frontend_script_api.js

💡 技术提示:API(应用程序编程接口) - 允许不同软件组件之间通信的规则和协议,就像餐厅菜单,定义了你可以点的菜品(功能)和如何下单(参数)

2.2 核心API功能介绍

后端核心功能

  1. 笔记操作:创建、读取、更新和删除笔记,是自动化的基础功能

    // 创建文本笔记
    const {note, branch} = api.createTextNote(
      parentNoteId,  // 父笔记ID
      "新笔记标题",   // 笔记标题
      "笔记内容"     // 笔记内容
    );
    
  2. 搜索功能:使用Trilium强大的查询语法筛选笔记

    // 搜索最近7天修改的未完成任务
    const tasks = api.searchForNotes("#type:task AND #status:incomplete AND #dateModified:>now-7d");
    
  3. 事务处理:确保一系列操作要么全部成功,要么全部失败,保证数据一致性

    api.transactional(() => {
      // 在此执行多个数据库操作
      note1.save();
      note2.save();
    });
    

前端核心功能

  1. 界面交互:添加按钮、菜单和自定义组件

    // 添加工具栏按钮
    api.addButtonToToolbar({
      id: "quick-note",
      title: "快速笔记",
      icon: "plus",
      action: () => createQuickNote()
    });
    
  2. 消息提示:向用户展示操作结果

    // 显示成功消息
    api.showMessage("笔记创建成功", "success");
    
    // 显示错误消息
    api.showMessage("保存失败,请重试", "error");
    
  3. 上下文感知:获取当前活动笔记和用户操作

    // 获取当前正在编辑的笔记
    const currentNote = await api.getActiveNote();
    console.log("当前编辑笔记:", currentNote.title);
    

📚 参考文档:BackendScriptApi

📚 参考文档:FrontendScriptApi

三、实战应用:三个高效自动化脚本

3.1 构建:网页内容自动抓取与格式化

问题引入:每天阅读多篇技术文章,手动复制粘贴到笔记中并格式化需要大量时间。

解决方案:开发一个后端脚本,监控剪贴板内容,自动识别网页链接,抓取内容并格式化为统一笔记格式。

代码实现

// == 网页内容自动抓取器 ==
// 每30秒检查一次剪贴板
api.setInterval(async () => {
  try {
    // ① 获取剪贴板内容
    const clipboardContent = await api.getClipboardText();
    
    // ② 检查是否为URL且未处理过
    if (isValidUrl(clipboardContent) && !await isUrlProcessed(clipboardContent)) {
      api.log(`发现新URL: ${clipboardContent}`);
      
      // ③ 使用事务确保操作完整性
      api.transactional(async () => {
        // ④ 抓取网页内容
        const {title, content, author, publishDate} = await fetchWebPage(clipboardContent);
        
        // ⑤ 创建格式化笔记
        const {note} = api.createTextNote(
          "web-clippings",  // 父笔记ID
          title,            // 网页标题
          generateFormattedContent(title, content, author, publishDate, clipboardContent)
        );
        
        // ⑥ 添加标签和属性
        note.setLabel("source", "web");
        note.setLabel("author", author || "unknown");
        note.setLabel("url", clipboardContent);
        note.setLabel("processed", "true");
        
        // ⑦ 保存笔记
        await note.save();
        
        // ⑧ 前端通知
        api.showMessage(`已自动保存: ${title}`, "success");
      });
    }
  } catch (e) {
    api.log(`抓取失败: ${e.message}`, "error");
    api.showMessage(`网页抓取失败: ${e.message}`, "error");
  }
}, 30000);  // 30秒检查一次

// 辅助函数:验证URL
function isValidUrl(url) {
  try {
    new URL(url);
    return true;
  } catch (e) {
    return false;
  }
}

// 辅助函数:检查URL是否已处理
async function isUrlProcessed(url) {
  const existingNotes = await api.searchForNotes(`#url:"${url}"`);
  return existingNotes.length > 0;
}

// 辅助函数:生成格式化内容
function generateFormattedContent(title, content, author, date, url) {
  return `# ${title}\n\n` +
         (author ? `**作者**: ${author}\n\n` : "") +
         (date ? `**发布日期**: ${date}\n\n` : "") +
         `**来源**: 原文链接\n\n` +
         "## 内容摘要\n\n" +
         content + "\n\n" +
         "## 个人笔记\n\n" +
         "- [ ] 需要深入研究的点\n" +
         "- [ ] 相关联的已有笔记";
}

效果验证

  1. 复制任意网页链接到剪贴板
  2. 等待30秒或手动触发脚本
  3. 检查"web-clippings"笔记下是否出现新创建的格式化笔记
  4. 验证笔记是否包含标题、作者、来源链接和内容摘要

应用场景扩展

  • 添加自动分类功能,根据内容主题自动分配到不同父笔记
  • 实现摘要生成,使用API自动提取文章关键点
  • 添加图片处理,自动下载并保存网页中的图片到笔记附件

3.2 构建:笔记自动关联与知识图谱

问题引入:随着笔记数量增加,手动建立笔记间的关联变得越来越困难,导致知识孤岛。

解决方案:开发一个前端小部件,分析当前笔记内容,自动推荐相关笔记并提供一键关联功能。

代码实现

// == 智能笔记关联助手 ==
class NoteRelationAssistant extends api.NoteContextAwareWidget {
  constructor() {
    super();
    this.relatedNotes = [];
    this.currentNoteId = null;
  }
  
  // 当笔记上下文变化时调用
  async onNoteContextChanged({noteId}) {
    this.currentNoteId = noteId;
    await this.updateRecommendations();
  }
  
  // 更新关联推荐
  async updateRecommendations() {
    if (!this.currentNoteId) return;
    
    try {
      // ① 获取当前笔记
      const note = await api.getNote(this.currentNoteId);
      if (!note) return;
      
      // ② 提取关键词
      const keywords = this.extractKeywords(note.content);
      if (keywords.length === 0) {
        this.relatedNotes = [];
        this.refresh();
        return;
      }
      
      // ③ 搜索相关笔记
      const query = this.buildSearchQuery(keywords);
      const results = await api.searchForNotes(query);
      
      // ④ 排除当前笔记并排序
      this.relatedNotes = results
        .filter(n => n.noteId !== this.currentNoteId)
        .sort((a, b) => (b.score || 0) - (a.score || 0))
        .slice(0, 5); // 取前5个最相关的笔记
      
      // ⑤ 刷新界面
      this.refresh();
    } catch (e) {
      api.log(`关联推荐错误: ${e.message}`, "error");
      this.relatedNotes = [];
      this.refresh();
    }
  }
  
  // 提取关键词
  extractKeywords(content) {
    // 简单实现:提取长度>4的独特单词
    const words = content.toLowerCase()
      .replace(/[^\w\s]/g, ' ')
      .split(/\s+/)
      .filter(word => word.length > 4);
    
    // 去重并返回前10个关键词
    return [...new Set(words)].slice(0, 10);
  }
  
  // 构建搜索查询
  buildSearchQuery(keywords) {
    // 构建OR查询,匹配任意关键词
    return keywords.map(k => `text:${k}`).join(" OR ");
  }
  
  // 渲染小部件
  render() {
    if (this.relatedNotes.length === 0) {
      return `
        <div class="relation-assistant">
          <h3>笔记关联助手</h3>
          <p>没有找到相关笔记</p>
        </div>
      `;
    }
    
    return `
      <div class="relation-assistant">
        <h3>推荐关联笔记</h3>
        <ul>
          ${this.relatedNotes.map(note => `
            <li>
              <div class="note-item">
                <a href="#" onclick="api.activateNote('${note.noteId}')">${note.title}</a>
                <button onclick="api.getWidget('relation-assistant').createRelation('${note.noteId}')">
                  关联
                </button>
              </div>
            </li>
          `).join('')}
        </ul>
      </div>
    `;
  }
  
  // 创建笔记关联
  async createRelation(targetNoteId) {
    if (!this.currentNoteId || !targetNoteId) return;
    
    try {
      // ① 获取当前笔记和目标笔记
      const currentNote = await api.getNote(this.currentNoteId);
      const targetNote = await api.getNote(targetNoteId);
      
      // ② 添加双向关联属性
      currentNote.addAttribute("relation", targetNote.title, {
        noteId: targetNoteId
      });
      
      targetNote.addAttribute("relation", currentNote.title, {
        noteId: this.currentNoteId
      });
      
      // ③ 保存更改
      await currentNote.save();
      await targetNote.save();
      
      // ④ 显示成功消息
      api.showMessage(`已关联笔记: ${targetNote.title}`, "success");
      
      // ⑤ 更新推荐列表
      await this.updateRecommendations();
    } catch (e) {
      api.log(`创建关联失败: ${e.message}`, "error");
      api.showMessage(`关联失败: ${e.message}`, "error");
    }
  }
}

// 注册小部件到右侧面板
api.addWidgetToRightPanel({
  widget: NoteRelationAssistant,
  title: "笔记关联助手",
  id: "relation-assistant"
});

效果验证

  1. 创建或打开任意笔记
  2. 在右侧面板找到"笔记关联助手"小部件
  3. 观察是否显示相关笔记推荐
  4. 点击"关联"按钮测试关联功能
  5. 检查相关笔记是否添加了relation属性

应用场景扩展

  • 基于笔记内容相似度自动创建关联,无需手动触发
  • 添加关联强度评分,帮助识别最相关的笔记
  • 实现关联可视化,以图形方式展示笔记间的关系网络

3.3 构建:基于标签的自动化工作流

问题引入:项目管理中需要跟踪任务进度,手动更新任务状态和截止日期提醒效率低下。

解决方案:开发一个状态机脚本,基于标签自动管理任务状态流转和截止日期提醒。

代码实现

// == 任务状态自动化管理器 ==
// 每天9:00运行
api.scheduleDailyJob("0 9 * * *", async () => {
  await processOverdueTasks();
  await processInProgressTasks();
  await processCompletedTasks();
});

// 处理逾期任务
async function processOverdueTasks() {
  // ① 搜索所有逾期且未完成的任务
  const overdueTasks = await api.searchForNotes(`
    #type:task 
    AND #status:!completed 
    AND #dueDate:<today
  `);
  
  if (overdueTasks.length === 0) return;
  
  api.log(`发现${overdueTasks.length}个逾期任务`);
  
  // ② 处理每个逾期任务
  api.transactional(async () => {
    for (const task of overdueTasks) {
      // 设置逾期标签
      task.setLabel("status", "overdue");
      
      // 添加逾期提醒评论
      const dueDate = task.getLabelValue("dueDate");
      task.addComment(`任务已逾期 (原截止日期: ${dueDate})`);
      
      // 保存更改
      await task.save();
      
      // 发送通知
      api.sendNotification({
        title: "任务逾期提醒",
        message: `任务 "${task.title}" 已逾期,请及时处理`,
        noteId: task.noteId
      });
    }
  });
}

// 处理进行中任务
async function processInProgressTasks() {
  // ① 搜索进行中的任务
  const inProgressTasks = await api.searchForNotes(`
    #type:task 
    AND #status:in-progress 
    AND #dueDate:>today 
    AND #dueDate:<now+3d
  `);
  
  if (inProgressTasks.length === 0) return;
  
  api.log(`发现${inProgressTasks.length}个即将到期的任务`);
  
  // ② 发送即将到期提醒
  for (const task of inProgressTasks) {
    const dueDate = task.getLabelValue("dueDate");
    api.sendNotification({
      title: "任务即将到期",
      message: `任务 "${task.title}" 将在${dueDate}到期`,
      noteId: task.noteId
    });
  }
}

// 处理已完成任务
async function processCompletedTasks() {
  // ① 搜索最近完成的任务
  const completedTasks = await api.searchForNotes(`
    #type:task 
    AND #status:completed 
    AND #dateModified:>now-1d
  `);
  
  if (completedTasks.length === 0) return;
  
  api.log(`发现${completedTasks.length}个最近完成的任务`);
  
  // ② 自动归档完成任务
  api.transactional(async () => {
    const archiveNoteId = await getOrCreateArchiveNote();
    
    for (const task of completedTasks) {
      // 创建到归档文件夹的分支
      await api.createBranch(archiveNoteId, task.noteId, {
        prefix: "[完成] "
      });
      
      // 添加完成日期标签
      task.setLabel("completedDate", new Date().toISOString().split('T')[0]);
      
      await task.save();
    }
  });
}

// 获取或创建归档笔记
async function getOrCreateArchiveNote() {
  const archiveNotes = await api.searchForNotes("#archive AND #type:folder");
  
  if (archiveNotes.length > 0) {
    return archiveNotes[0].noteId;
  }
  
  // 创建归档笔记
  const {note} = await api.createTextNote(
    api.rootNoteId,  // 根笔记下
    "任务归档",       // 标题
    "自动归档已完成的任务" // 描述
  );
  
  note.setLabel("type", "folder");
  note.setLabel("archive", "true");
  await note.save();
  
  return note.noteId;
}

// 手动触发处理函数(用于测试)
global.processTasks = async () => {
  await processOverdueTasks();
  await processInProgressTasks();
  await processCompletedTasks();
  api.showMessage("任务处理完成");
};

效果验证

  1. 创建带有#type:task、#status:in-progress和#dueDate标签的测试任务
  2. 将dueDate设置为昨天(逾期任务)和明天(即将到期任务)
  3. 运行global.processTasks()手动触发处理
  4. 检查逾期任务是否被标记为overdue并收到通知
  5. 检查即将到期任务是否收到提醒
  6. 将任务状态改为completed,检查是否被自动归档

应用场景扩展

  • 添加任务优先级机制,高优先级任务提前提醒
  • 实现任务依赖关系,前置任务完成后自动激活后续任务
  • 生成任务完成情况报告,统计周期内任务完成率和延期率

四、常见问题排查:脚本开发与运行故障解决

在Trilium脚本开发过程中,你可能会遇到各种问题。以下是几个常见错误案例及解决方法:

4.1 问题:脚本执行时提示"api未定义"

错误表现

ReferenceError: api is not defined

原因分析

  • 脚本未正确设置运行环境属性
  • 脚本在不支持的环境中运行(如前端API在后端环境调用)

解决方法

  1. 确保笔记已添加正确的运行环境属性:

    • 后端脚本:添加#run=backend属性
    • 前端脚本:添加#run=frontend属性
  2. 验证API是否适用于当前环境:

    • 后端API(如文件操作、数据库事务)只能在后端脚本中使用
    • 前端API(如UI组件、用户交互)只能在前端脚本中使用

💡 技术提示:环境隔离 - Trilium严格分离前后端环境,以确保安全性和稳定性,就像餐厅的前厅和后厨,各有专门的工作区域和工具

4.2 问题:搜索结果与预期不符

错误表现

// 预期返回多个结果,实际返回空数组
const results = api.searchForNotes("#tag:important");
console.log(results.length); // 输出 0

原因分析

  • 搜索语法错误
  • 标签名称大小写不匹配
  • 笔记权限设置导致无法访问

解决方法

  1. 检查搜索语法是否正确:

    • 使用#tag:value而非#tag=value
    • 注意特殊字符需要转义,如空格使用%20
    • 日期格式需符合YYYY-MM-DD或相对格式(如now-7d)
  2. 验证标签实际存在:

    // 列出所有标签验证
    const tags = await api.searchForNotes("attrs:text=tag");
    console.log(tags.map(t => t.title));
    
  3. 检查笔记权限:

    // 检查当前用户是否有权限访问
    const note = await api.getNote(noteId);
    console.log(note.isProtected); // 如果为true,可能需要解锁
    

4.3 问题:脚本执行超时或内存溢出

错误表现

Script execution timed out after 30 seconds

RangeError: Maximum call stack size exceeded

原因分析

  • 处理大量笔记时没有分页
  • 递归调用没有终止条件
  • 循环中执行了耗时操作

解决方法

  1. 实现分页处理大量数据:

    // 使用offset和limit分页处理
    let offset = 0;
    const batchSize = 50;
    let hasMore = true;
    
    while (hasMore) {
      const notes = await api.searchForNotes("#type:note", {
        offset,
        limit: batchSize
      });
      
      if (notes.length < batchSize) hasMore = false;
      
      // 处理当前批次
      for (const note of notes) {
        // 处理逻辑
      }
      
      offset += batchSize;
      
      // 每批处理后短暂休息,避免资源占用过高
      await api.sleep(100);
    }
    
  2. 优化递归逻辑:

    • 添加明确的终止条件
    • 考虑将递归改为迭代
    • 增加递归深度检查
  3. 使用事务批量处理:

    // 将多个操作合并为一个事务
    api.transactional(() => {
      notes.forEach(note => {
        // 批量更新操作
        note.setLabel("processed", "true");
        note.save();
      });
    });
    

4.4 问题:前端小部件不显示或无法交互

错误表现

  • 自定义小部件未出现在右侧面板
  • 小部件显示但按钮点击无反应
  • 界面更新不及时

原因分析

  • 小部件注册代码有误
  • 事件处理函数作用域问题
  • 未调用刷新方法更新界面

解决方法

  1. 检查小部件注册代码:

    // 正确的注册方式
    api.addWidgetToRightPanel({
      widget: MyWidgetClass,  // 类本身,不是实例
      title: "我的小部件",
      id: "my-widget-unique-id"
    });
    
  2. 确保事件处理函数正确绑定:

    // 错误方式
    render() {
      return `<button onclick="handleClick()">点击</button>`;
    }
    
    // 正确方式
    render() {
      return `<button onclick="api.getWidget('my-widget-unique-id').handleClick()">点击</button>`;
    }
    
    handleClick() {
      // 处理逻辑
    }
    
  3. 数据更新后刷新界面:

    // 数据变化后调用refresh()
    this.data = newData;
    this.refresh(); // 触发重新渲染
    

4.5 问题:权限错误,无法执行操作

错误表现

PermissionError: Not allowed to modify protected note

原因分析

  • 尝试修改受保护的系统笔记
  • 当前用户权限不足
  • 操作需要管理员权限

解决方法

  1. 检查笔记是否受保护:

    const note = await api.getNote(noteId);
    console.log(note.isProtected); // true表示受保护
    
  2. 确保操作的是用户自己的笔记:

    • 避免修改系统内置笔记(如#root、#templates等)
    • 创建自己的笔记进行操作和测试
  3. 必要时修改保护状态(谨慎操作):

    // 仅在测试环境使用
    note.setProtected(false);
    await note.save();
    // 操作完成后恢复保护状态
    note.setProtected(true);
    await note.save();
    

五、进阶技巧:提升脚本质量与性能

5.1 模块化开发与代码复用

将通用功能封装为模块,提高代码复用性和可维护性。

// 模块笔记: #module=note-utils
// 保存为"笔记工具库"笔记

/**
 * 格式化日期为YYYY-MM-DD格式
 * @param {Date} date - 日期对象
 * @returns {string} 格式化后的日期字符串
 */
exports.formatDate = (date) => {
  return date.toISOString().split('T')[0];
};

/**
 * 获取笔记的所有标签
 * @param {Note} note - 笔记对象
 * @returns {Object} 标签键值对
 */
exports.getNoteTags = (note) => {
  const tags = {};
  note.getAttributes().forEach(attr => {
    if (attr.type === 'label') {
      tags[attr.name] = attr.value;
    }
  });
  return tags;
};

// 在其他脚本中使用
const noteUtils = require("note-utils");
const formattedDate = noteUtils.formatDate(new Date());
const tags = noteUtils.getNoteTags(note);

💡 技术提示:模块化 - 将复杂系统分解为独立、可重用的组件,就像乐高积木,不同模块可以组合成各种复杂结构

5.2 性能优化策略

针对处理大量笔记的脚本,采用以下优化策略:

  1. 批量操作代替循环单个操作

    // 低效方式
    for (const note of notes) {
      await note.save(); // 每次保存都触发数据库操作
    }
    
    // 高效方式
    api.transactional(() => {
      notes.forEach(note => {
        note.save(); // 事务内批量处理
      });
    });
    
  2. 使用缓存减少重复计算

    // 使用api.cache缓存计算结果
    function getKeywordIndex() {
      if (!api.cache.keywordIndex) {
        // 耗时的计算过程
        api.cache.keywordIndex = buildKeywordIndex();
        
        // 设置缓存过期时间(1小时)
        api.setTimeout(() => {
          api.cache.keywordIndex = null;
        }, 3600000);
      }
      return api.cache.keywordIndex;
    }
    
  3. 延迟加载非关键数据

    // 只加载必要数据,其他数据按需加载
    async function getNotesSummary() {
      // 先获取基本信息
      const notes = await api.searchForNotes("#type:article", {
        fields: ["noteId", "title", "dateModified"] // 只获取需要的字段
      });
      
      // 需要时才加载详细内容
      for (const note of notes) {
        note.getContent = async () => {
          if (!note._content) {
            const fullNote = await api.getNote(note.noteId);
            note._content = fullNote.content;
          }
          return note._content;
        };
      }
      
      return notes;
    }
    

5.3 错误处理与日志记录

完善的错误处理和日志记录是生产级脚本的必备要素:

/**
 * 安全执行函数并记录错误
 * @param {Function} func - 要执行的函数
 * @param {string} operation - 操作名称,用于日志
 * @param {boolean} showMessage - 是否向用户显示消息
 * @returns {*} 函数执行结果
 */
async function safeExecute(func, operation, showMessage = true) {
  try {
    const result = await func();
    api.log(`[成功] ${operation}`);
    if (showMessage) {
      api.showMessage(`${operation}成功`, "success");
    }
    return result;
  } catch (e) {
    // 详细日志记录
    const errorMsg = `[失败] ${operation}: ${e.message}\n${e.stack}`;
    api.log(errorMsg, "error");
    
    // 向用户显示简化消息
    if (showMessage) {
      api.showMessage(`${operation}失败: ${e.message}`, "error");
    }
    
    // 重新抛出错误,允许上层处理
    throw e;
  }
}

// 使用示例
const result = await safeExecute(
  () => api.createTextNote(parentId, title, content),
  `创建笔记 "${title}"`
);

六、技术成长路径图

初级阶段:脚本使用与简单修改(1-2周)

目标:能够使用现有脚本并进行简单修改

学习内容

  • 理解Trilium脚本基本概念
  • 学会创建和运行简单脚本
  • 修改现有脚本参数适应个人需求
  • 使用基础API(createTextNote, searchForNotes)

实践项目

  • 修改自动抓取脚本,调整笔记保存位置
  • 自定义搜索条件,筛选特定类型笔记

中级阶段:独立开发实用脚本(1-2个月)

目标:能够独立开发解决实际问题的脚本

学习内容

  • 掌握前后端API主要功能
  • 学习事务处理和错误处理
  • 理解脚本调度和事件系统
  • 开发简单的前端小部件

实践项目

  • 开发个人知识分类系统
  • 创建项目管理工作流脚本
  • 实现自定义数据导入工具

高级阶段:构建复杂自动化系统(3-6个月)

目标:能够构建跨功能的复杂自动化系统

学习内容

  • 深入理解Trilium内部架构
  • 掌握模块化和代码组织
  • 学习性能优化和安全最佳实践
  • 开发高级UI组件和交互系统

实践项目

  • 构建个人知识图谱系统
  • 开发与外部应用集成的接口
  • 创建自定义编辑器扩展

七、脚本模板库

以下是几个常用的脚本模板,可作为你开发自己脚本的起点:

  1. 定时任务模板

    // #run=backend
    // 定时任务模板:每天执行一次
    
    // 配置定时表达式
    const cronExpression = "0 0 * * *"; // 每天午夜执行
    
    // 注册定时任务
    api.scheduleJob(cronExpression, async () => {
      try {
        api.log("定时任务开始执行");
        
        // 任务逻辑
        await performTask();
        
        api.log("定时任务执行完成");
      } catch (e) {
        api.log(`定时任务失败: ${e.message}`, "error");
      }
    });
    
    // 任务实现
    async function performTask() {
      // TODO: 实现具体任务逻辑
    }
    
    // 手动触发函数
    global.runTask = performTask;
    
  2. 前端小部件模板

    // #run=frontend
    // 前端小部件模板
    
    class MyCustomWidget extends api.NoteContextAwareWidget {
      constructor() {
        super();
        this.data = [];
      }
      
      // 当笔记上下文变化时调用
      async onNoteContextChanged({noteId}) {
        this.data = await loadData(noteId);
        this.refresh();
      }
      
      // 加载数据
      async loadData(noteId) {
        // TODO: 实现数据加载逻辑
        return [];
      }
      
      // 渲染界面
      render() {
        return `
          <div class="my-widget">
            <h3>我的小部件</h3>
            <div class="widget-content">
              <!-- TODO: 实现界面渲染 -->
            </div>
          </div>
        `;
      }
    }
    
    // 注册小部件
    api.addWidgetToRightPanel({
      widget: MyCustomWidget,
      title: "我的小部件",
      id: "my-custom-widget"
    });
    
  3. 批量处理模板

    // #run=backend
    // 批量处理模板
    
    // 配置
    const SEARCH_QUERY = "#type:note AND #needsProcessing:true";
    const BATCH_SIZE = 50;
    
    // 主函数
    async function batchProcess() {
      let offset = 0;
      let processedCount = 0;
      
      api.log(`开始批量处理,查询: ${SEARCH_QUERY}`);
      
      while (true) {
        // 获取一批笔记
        const notes = await api.searchForNotes(SEARCH_QUERY, {
          offset,
          limit: BATCH_SIZE
        });
        
        if (notes.length === 0) break;
        
        // 处理当前批次
        await api.transactional(async () => {
          for (const note of notes) {
            await processNote(note);
            processedCount++;
          }
        });
        
        api.log(`已处理 ${processedCount} 个笔记`);
        offset += BATCH_SIZE;
      }
      
      api.log(`批量处理完成,共处理 ${processedCount} 个笔记`);
      return processedCount;
    }
    
    // 处理单个笔记
    async function processNote(note) {
      // TODO: 实现单个笔记处理逻辑
      note.setLabel("needsProcessing", "false");
      await note.save();
    }
    
    // 执行批量处理
    global.batchProcess = batchProcess;
    batchProcess().then(count => {
      api.showMessage(`批量处理完成,共处理 ${count} 个笔记`);
    });
    

八、结语

通过本文介绍的Trilium脚本编程技术,你已经掌握了自动化笔记管理的核心能力。从简单的网页抓取到复杂的工作流自动化,这些工具能帮你将知识管理效率提升数倍。

记住,最好的自动化方案是那些为你个人工作流程量身定制的方案。不要满足于使用别人的脚本,而是思考自己日常工作中最耗时的环节,尝试用脚本将其自动化。

你最想自动化的笔记场景是什么?是文献管理、项目跟踪还是内容创作?尝试用今天学到的API实现它,并在Trilium社区分享你的成果。自动化的旅程没有终点,每一个小的改进都会累积成显著的效率提升。

现在就打开Trilium,开始你的自动化之旅吧!

登录后查看全文
热门项目推荐
相关项目推荐