Ms word Word插件CustomXMLParts数据建模和/或性能优化

Ms word Word插件CustomXMLParts数据建模和/或性能优化,ms-word,office-js,office-addins,officedev,Ms Word,Office Js,Office Addins,Officedev,环境:Mac 10.12.6,Word 2016(16.11.1),@microsoft/office js“^1.1.4” 我想知道在如何使用CustomXMLParts以最大限度地提高读/写性能方面是否有任何指导原则或最佳实践,或者是否有一种“理想”的方法来为XML部件中的数据建模以达到相同的目的 我正在编写一个外接程序,需要在可见文档之外,但在docx文件内部保存一些数据 例如,我正在存储一个发票列表(可能是100-200张发票),每个发票都有典型的结构化数据(名称、id、日期、工作项列表

环境:Mac 10.12.6,Word 2016(16.11.1),@microsoft/office js“^1.1.4”

我想知道在如何使用CustomXMLParts以最大限度地提高读/写性能方面是否有任何指导原则或最佳实践,或者是否有一种“理想”的方法来为XML部件中的数据建模以达到相同的目的

我正在编写一个外接程序,需要在可见文档之外,但在docx文件内部保存一些数据

例如,我正在存储一个发票列表(可能是100-200张发票),每个发票都有典型的结构化数据(名称、id、日期、工作项列表)和一个freestyle notes部分,其中最多可以包含5-10kb的文本、说明等

我获取这些发票,在Word文档中呈现其中一些发票,然后在任务窗格中对其余发票的数据进行可视化分析,用户可以在其中将注释写(并保存)回自定义XML部分(他们正在查看的发票)

现在。。。这是我有点困惑的地方。。。我不确定是否最好将每张发票作为单独的CustomXMLPart存储在文件中(例如,每张发票一个XML文件),或者是否最好将所有发票存储在一个大的CustomXMLPart中,或者是否有一个中间点(例如,每个XML部件10张发票)。如前所述,用例读取所有发票,然后偶尔更新发票中10-20%的数据

现在,我正在为每个XML部件存储一张发票,当我加载我的加载项并进行批量读取以将所有内容都存储到内存中时,需要每个发票平均250-500毫秒才能并行地将它们读回(因此,250-500毫秒*100-200张发票)。按顺序,它需要更长的时间(2-3倍)。使用performance进行测试。现在()使用挂钟计时进行验证

这似乎是一个非常长的时间,所以我不知道我是否做了一些不正确的事情-或者这只是打开并从这些文件中提取数据所需的时间

// Sequential example - excluding error handling and type-safety
// Parallel equivalent is essentially a Promise.all with a .map

// Approx 50ms
let result = await this.xmlPartsHelper.getByNamespaceAsync(...);

for (const item of result.value) {
    // Approx 150-200ms
    result = await this.xmlPartsHelper.getByIdAsync(item.id);

    // Approx 150-200ms
    result = await this.xmlPartsHelper.getXmlAsync(result.value);

    // Approx 5ms
    const invoice = this.mapper.reverseMap(result.value);
    invoices.push(invoice)
}
我用Promissions手动包装了Office JS回调,但我用async/await、then/catch和Office JS回调测试了这个示例,结果几乎相同

public getByNamespaceAsync(namespace: string): Promise<Office.AsyncResult> {
    return new Promise<Office.AsyncResult>((resolve, reject) => {
        Office.context.document.customXmlParts.getByNamespaceAsync(namespace, (result: Office.AsyncResult) => {
            return resolve(result);
        });
    });
}

public getByIdAsync(id: string): Promise<Office.AsyncResult> {
    return new Promise<Office.AsyncResult>((resolve, reject) => {
        Office.context.document.customXmlParts.getByIdAsync(id, (result: Office.AsyncResult) => {
            return resolve(result);
        });
    });
}

public getXmlAsync(xmlPart: Office.CustomXmlPart): Promise<Office.AsyncResult> {
    return new Promise<Office.AsyncResult>((resolve, reject) => {
        xmlPart.getXmlAsync((result: Office.AsyncResult) => {
            return resolve(result);
        });
    });
}
公共getByNamespaceAsync(命名空间:字符串):承诺{ 返回新承诺((解决、拒绝)=>{ Office.context.document.customXmlParts.getByNamespaceAsync(命名空间,(结果:Office.AsyncResult)=>{ 返回解析(结果); }); }); } 公共getByIdAsync(id:string):承诺{ 返回新承诺((解决、拒绝)=>{ Office.context.document.customXmlParts.getByIdAsync(id,(结果:Office.AsyncResult)=>{ 返回解析(结果); }); }); } 公共getXmlAsync(xmlPart:Office.CustomXmlPart):承诺{ 返回新承诺((解决、拒绝)=>{ xmlPart.getXmlAsync((结果:Office.AsyncResult)=>{ 返回解析(结果); }); }); } 更新


我不完全理解的一个难题是CustomXMLNode——也许这会有所帮助。似乎有一些方法专门在CustomXMLPart()的节点中获取/设置数据——因此,这可能是一个中间选项,我可以将所有发票放入一个CustomXMLPart中(这样,我只会在一个CustomXMLPart中被文件系统击中),然后我可以有选择地更新该CustomXMLPart的各个部分(使用CustomXMLNode)这样我就不会只进行完全删除和重新保存了?

很好地使用了Promission,我正在对非promise officejs函数进行同样的包装。使用基于promise的api,您现在可以使用promise.all()执行并行操作。您可以同时启动所有操作并等待完成。这应该更快

function getAllParts(ids) {
   return Promise.all(ids.map(id => xmlPartsHelper.getByIdAsync(id)));
}

let namespaces = await this.xmlPartsHelper.getByNamespaceAsync(...);
getAllParts(namespaces.value).then((results) => {
   console.log('invioces are', results);
});
在文档中存储数据的另一种方法是Office.context.document.settings。您可以将其用作键/值存储,并将JSON作为您的值。可以尝试将所有发票放入一个数组中,并将其写入同一个键。以下是我的帮助器函数:

   /** Set a document property. Properties are specific to the document and the Addin-ID.
    * @param {string} propertyName Name of the property.
    * @param {string} value Value of the property.
    * @returns {Promise} A promise without content.
    */
   function setDocumentProperty(propertyName, value) {
      return new Promise((resolve, reject) => {
         if (Office.context.document.settings) {
            Office.context.document.settings.set(propertyName, value);
            Office.context.document.settings.saveAsync((asyncResult) => {
               if (asyncResult.status === Office.AsyncResultStatus.Failed) {
                  reject(`[ExcelApi] Property '${propertyName}=${value}' could not be saved. Error: ${asyncResult.error.message}`);
               } else {
                  resolve(`[ExcelApi] Property '${propertyName}=${value}' saved.`);
               }
            });
         } else {
            reject('[ExcelApi] document.settings is not ready.');
         }
      });
   }

   /** Get a document property.
    * @param {string} propertyName Name of the property. Properties are specific to the document and the Addin-ID.
    * @returns {Promise<object>} A promise that contains the property value.
    */
   function getDocumentProperty(propertyName) {
      return new Promise((resolve, reject) => {
         if (Office.context.document.settings) {
            const result = Office.context.document.settings.get(propertyName);
            if (result === null) reject(`[ExcelApi] Property '${propertyName}' not found.`);
            resolve(result);
         } else {
            reject('[ExcelApi] document.settings is not ready.');
         }
      });
   }
/**设置文档属性。属性特定于文档和加载项ID。
*@param{string}propertyName属性的名称。
*@param{string}属性的值。
*@返回{Promise}一个没有内容的承诺。
*/
函数setDocumentProperty(propertyName,值){
返回新承诺((解决、拒绝)=>{
if(Office.context.document.settings){
Office.context.document.settings.set(propertyName,value);
Office.context.document.settings.saveAsync((asyncResult)=>{
if(asyncResult.status==Office.AsyncResultStatus.Failed){
拒绝(`[ExcelApi]属性'${propertyName}=${value}'无法保存。错误:${asyncResult.Error.message}`);
}否则{
解析(`[ExcelApi]属性'${propertyName}=${value}'已保存。`);
}
});
}否则{
拒绝(“[ExcelApi]document.settings未准备就绪”);
}
});
}
/**获取文档属性。
*@param{string}propertyName属性的名称。属性特定于文档和加载项ID。
*@返回{Promise}一个包含属性值的承诺。
*/
函数getDocumentProperty(propertyName){
返回新承诺((解决、拒绝)=>{
if(Office.context.document.settings){
const result=Office.context.document.settings.get(propertyName);
如果(result==null)拒绝(`ExcelApi]属性'${propertyName}'未找到。`);
决心(结果);
}否则{
拒绝(“[ExcelApi]document.settings未准备就绪”);
}
});
}

谢谢你的回复!事实上,我的第一个测试之一就是做一个承诺。所有测试都是并行的,而且速度更快(正如我提到的,sequential的速度慢了2-3倍)。文档设置是一个不错的选择,但它似乎是