# # 书签 APIs 升级指南

本指南旨在帮助开发者从旧版的书签 APIs 升级到最新版本。从 `10.0.0` 版本开始，我们重构了书签 API，提供了更强大的功能和性能改进。接下来，我们将介绍哪些旧的书签 APIs 将被弃用，以及如何将其替换为新的 APIs。

## # 确认当前使用的 API 版本

如果你的应用程序是基于 `10.0.0` 之前的版本开发的，当升级到 `10.0.0` 之后的版本时，请注意您需要在代码中搜索相关的 API 调用，或者查看组件模板中是否使用了旧的书签组件。旧的 API 和组件的名称将随后列出。

## # 被废弃的 APIs

下表列出了被删除的书签 APIs 以及替代方案：

旧版 API | 描述 | 替代 API
--- | --- | ---
PDFDoc.getRootBookmark | 获取文档的书签根节点，该方法将一次性加载文档中的所有书签节点 | BookmarkDataService.getFirstLevelBookmarks (opens new window)
PDFDoc.createRootBookmark | 创建书签根节点，新版本会自动创建书签根节点，不需要通过接口创建 | 无
PDFBookmark.insertBookmark | 向指定位置插入新节点 | BookmarkDataService.addBookmark (opens new window), BookmarkUIService.addBookmark (opens new window)
PDFBookmark.setProperty | 修改书签节点属性 | BookmarkDataService.renameBookmark (opens new window), BookmarkDataService.setColor (opens new window), BookmarkDataService.setFontStyle (opens new window), BookmarkDataService.setDestination (opens new window), BookmarkUIService.renameBookmark (opens new window), BookmarkUIService.setColor (opens new window), BookmarkUIService.setFontStyle (opens new window), BookmarkUIService.setDestination (opens new window)
PDFBookmark.remove | 删除特定的书签节点 | BookmarkDataService.deleteBookmark (opens new window), BookmarkUIService.deleteBookmark (opens new window)
PDF.constant.DataEvents.bookmarkAdded | 当添加书签节点时触发 | BookmarkDataService.onBookmarkAdded (opens new window)
PDF.constant.DataEvents.bookmarkUpdated | 当更新书签节点时触发 | BookmarkDataService.onBookmarkPropertiesUpdated (opens new window)
PDF.constant.DataEvents.bookmarkRemoved | Triggered when bookmark 当删除书签节点时触发 | BookmarkDataService.onBookmarkDeleted (opens new window)

下面是被废弃的书签 UIExtension 组件以及替代方案：

旧版组件 | 描述 | 替代组件
--- | --- | ---
`<bookmark-sidebar-panel>` | 侧边栏书签面板组件 | `<bookmark-v2:sidebar-panel>`
`<bookmark-contextmenu>` | 书签右键菜单组件 | `<bookmark-v2:bookmark-contextmenu>`

当这些旧的组件被替换成新的组件后，先前注册的书签事件将不会被触发。此时，需要替换这些事件的注册方法。以下将提供示例进行说明。

如果您的应用程序需要自定义书签UI，请参考 自定义书签。

## # 应用示例

以下将通过示例来展示如何使用新的书签 APIs 来替换旧版 APIs，以及对比两个版本之间的差异。

### # 示例 1 - 监听书签节点添加事件

此示例展示了如何监听书签节点添加事件，并通过 BookmarkDataService
(opens new window) 实例修改书签节点的属性。

    <html>
        <div id="pdf-ui"></div>
    </html>
    <style>
    html{
            overflow:hidden;
        }
        body {
            height: 100vh;
        }
        #pdf-ui {
            position: relative;
            top: 50px;
        }
    </style>

    <script>
    const libPath = window.top.location.origin + '/lib';
    const pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: libPath,
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui',
        appearance: UIExtension.appearances.adaptive,
        addons: []
    });

    pdfui.getBookmarkDataService().then(bookmarkDataService => {
        bookmarkDataService.onBookmarkAdded((event) => {
            bookmarkDataService.setColor(event.bookmarkId, '#FF0000');
            bookmarkDataService.setFontStyle(event.bookmarkId, {
                bold: true,
                italic: true
            });
        })
    })
    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

运行上述示例并打开一个文档后，然后通过书签面板添加一个新的书签，您会发现新书签的字体颜色被设置为红色，字体样式被设置为粗体和斜体。

对于 `9.*` 及更早版本，书签事件的注册方式如下：

    pdfui.addViewerEventListener(PDFViewCtrl.ViewerEvents.bookmarkAdded, bookmark => {
        // Here operates the bookmark object
    })

### # 示例 2 - 获取书签树节点

在 `9.*` 及以前的版本，由于书签是一次性加载到内存，并全部插入到 DOM 树中，所以当书签数量很多时，加载速度会很慢，甚至会出现卡顿。重构后，书签的加载和渲染是基于用户可见的书签数量，所以加载速度会更快。此外，书签通过 TreeComponent
(opens new window) 组件进行渲染，为用户自定义预留了一定的空间。以下示例展示了如何获取书签树的节点并修改节点样式：

    <html>
        <div id="pdf-ui"></div>
    </html>
    <style>
    html{
            overflow:hidden;
        }
        body {
            height: 100vh;
        }
        #pdf-ui {
            position: relative;
            top: 50px;
        }
    </style>

    <script>
    const libPath = window.top.location.origin + '/lib';
    const pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: libPath,
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui',
        appearance: UIExtension.appearances.adaptive,
        addons: []
    });

    (async () => {
        const rootComponent = await pdfui.getRootComponent();
        const bookmarkTreeComponent = rootComponent.querySelector('@bookmark-v2:bookmark-tree');
        const treeComponent = bookmarkTreeComponent.getTree()
        treeComponent.on('create-tree-node', treeNode => {
            const titleElement = treeNode.element.querySelector('.fv__ui-tree-node-title');
            titleElement.style.cssText += 'font-family: system-ui, --apple-system, BlinkMacSystemFont;';
        })
    })()

    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

与旧版本不同，旧版本的书签一旦加载，就可以通过 `querySelector` 获取所有的 DOM 节点。然而，新版本必须通过监听 `create-tree-node` 事件来获取节点。

### # 示例 3 - undo/redo 的支持

在 10.0.0 版本中，提供了一个 BookmarkUIService
(opens new window) 实例，该实例仅在应用层基于 UIExtension 的实现时可用。它提供了与 BookmarkDataService
(opens new window) 相同的 API，但支持 undo/redo 操作。

    <html>
        <div id="pdf-ui"></div>
    </html>
    <style>
    html{
            overflow:hidden;
        }
        body {
            height: 100vh;
        }
        #pdf-ui {
            position: relative;
            top: 50px;
        }
    </style>

    <script>
    const libPath = window.top.location.origin + '/lib';
    const pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: libPath,
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        },
        renderTo: '#pdf-ui',
        appearance: UIExtension.appearances.adaptive,
        addons: [
            libPath + '/uix-addons/undo-redo'
        ]
    });

    (async () => {
        // Create a custom menu item component.
        class HighlightBookmarkContextMenuItem extends UIExtension.SeniorComponentFactory.createSuperClass({
            // Listen to the click event through the @on.click directive.
            template: `<contextmenu-item @on.click="$component.onClick()">Highlight it</contextmenu-item>`
        }) {
            static getName() {
                return 'highlight-bookmark-contextmenu-item';
            }
            async onClick() {
                const treeNode = this.parent.getCurrentTarget();
                const nodeData = treeNode.getData();
                const bookmarkData = nodeData.data;
                const bookmarkId = bookmarkData.id;
                const bookmarkUIService = pdfui.getBookmarkUIService();
                await bookmarkUIService.setColor(bookmarkId, '#FF0000', bookmarkData.color);
            }
        }
        const module = UIExtension.modular.module('custom', []);
        module.registerComponent(HighlightBookmarkContextMenuItem);
        const rootComponent = await pdfui.getRootComponent();
        // Get the bookmark contextmenu instance.
        const bookmarkContextMenu = rootComponent.querySelector('@bookmark-v2:bookmark-contextmenu');
        // Insert a custom menu item at the end of the bookmark contextmenu.
        bookmarkContextMenu.append('<custom:highlight-bookmark-contextmenu-item>');
    })()

    </script>

    {
        "iframeOptions": {
            "style": "height: 500px"
        }
    }

该示例向书签右键菜单末尾插入了一个自定义菜单项。点击该菜单项，可以将当前选中的书签设置为红色。并且可以通过 `Ctrl+Z`/`Ctrl+Y` 来撤销/重做这个操作。需要注意的是，要启用 undo/redo 功能，必须在初始化 PDFUI 时，将 `addons` 配置项添加上 `libPath + '/uix-addons/undo-redo'`，或者直接配置 `addons: libPath + '/uix-addons/allInOne.js'` 加载所有插件。