做者:陳希章 發表於 2017年12月20日html
我過去發表過一些Office Add-in開發的文章,而且也在不一樣的場合分享過新的開發模式及其帶來的機遇。有很多朋友給我反饋,也討論到一些常見問題,我這裏集中地總結一下給你們參考。git
Office Web Add-in的適用場景github
這是不少人的困惑。我在這篇文章中詳細對照了三種爲Office開發Add-in的技術和表現形式,這裏再總結一下新的Web Add-in適用的場合web
工做原理究竟是怎麼樣的編程
這也是不少人的疑問。咱們能夠稍微回顧一下歷史,VBA是直接運行在Office進程(例如Excel)中的,它應該算是一個腳本,會有主程序動態加載,編譯運行。一旦運行結束,則會釋放資源。而VSTO則更爲複雜,由於它是用.NET
開發出來的託管代碼,因此他自己是不能經過宿主程序直接運行的,而是須要從宿主程序(實際上是COM)經過平臺調用的方式(Interop)發起一個指令,而後由.NET CLR加載Add-in的組件,這個組件若是須要操做Excel的資源,又要經過平臺調用的方式反過來調用COM。api
那麼,今天的Web Add-in到底又是怎麼樣加載和運行的呢?它是經過一個獨立的瀏覽器進程(例如IE)來運行的。下面我將詳細解釋這方面的原理。跨域
在不一樣的平臺上,Office Add-in所依賴的瀏覽器及其版本是不同的,這給開發人員要提一個醒:瀏覽器兼容性測試仍是很重要的。官方文檔有提到對於瀏覽器及其版本的要求:https://docs.microsoft.com/en-us/office/dev/add-ins/concepts/requirements-for-running-office-add-ins。瀏覽器
經過nslookup命令,能夠看出我目前這個託管在azurewebsites.net上面的範例插件,它的服務器IP地址是 13.75.46.26(注意,由於Azure平臺有不少服務器,因此實際上針對一個域名可能會有不少IP地址,若是你用nslookup命令可能獲得的結果跟我不同)緩存
在插件加載後,咱們通常會在進程管理器中看到兩個IE的進程。這裏有一個細節,若是你的Office是32位的,那麼它的核心進程會是一個32位的,你能夠查看若是加載多個插件的話,它所佔用的內存會逐步增長。可是,仍然是一個進程。服務器
可是,若是你的Windows是64位的,此時它會另外建立一個64位的IE進程,這兩個進程實際上是一個調用的關係。從下圖能夠看出來32位的進程實際上是在調用64位那個進程的。
若是要具體來證實這些進程是訪問到咱們那個插件的網站,能夠經過進程查看器來觀察
如何在Web Add-in的Javascript代碼中異步訪問到遠程的服務
既然咱們知道Office Add-in本質上是一個網絡應用,根據你所選擇的開發技術不一樣,對於訪問遠程服務資源的作法也不同。若是你是用ASP.NET MVC來實現的,那麼可能會簡單一些,由於MVC自己就能夠包含一些服務器代碼。但若是你更加喜歡用Javascript代碼來編程,你的服務資源調用,須要注意遵循一個兩個重要原則:
下面我這裏有一個範例代碼可供參考。我專門寫了一個範例的API服務 https://webaddinapisample.azurewebsites.net/api/values,你們若是測試也能夠直接使用它。
$("#run").click(() => tryCatch(run)); async function run() { await Excel.run(async (context) => { await $.get("https://webaddinapisample.azurewebsites.net/api/values").done(async function (result) { //這裏必定要注意,必須是https地址,並且證書要有效,而且設置跨域訪問 var sheet = context.workbook.worksheets.getActiveWorksheet(); var range = sheet.getRange("A1:B1"); range.values = [result]; await context.sync(); }).fail(function (jqXHR, textStatus, errorThrown) { console.log(errorThrown); }); }); } /** 嘗試執行某個方法 */ async function tryCatch(callback) { try { await callback(); } catch (error) { OfficeHelpers.UI.notify(error); OfficeHelpers.Utilities.log(error); } }
網絡斷開是否能夠繼續用
這個問題的答案跟問題自己同樣簡單:不能。因爲Web Add-in本質上是一個網絡應用,因此沒有網絡,Add-in是沒法加載的。
據官方提到,有可能往後會有支持本地緩存的技術實現。但目前尚未看到這方面的路線圖。
能不能經過代碼增長菜單
目前僅支持利用清單文件來定義界面元素,包括Ribbon和快捷菜單。若是你對這方面有興趣,請參考 詳解Office Add-in 清單文件。
這多是跟VBA和VSTO相比較而言,比較大的劣勢,其餘的功能方面,也並非徹底一致,這個還有一個不斷髮展的過程,好消息是,這些API仍是快速地開發中。
怎麼作基於事件的編程?
面向事件的編程,多是絕大部分開發人員根深蒂固的觀念。其實Office Add-in自己就是面向事件的編程。全部的代碼,都是從一個 Office.initialize
的事件開始的。再往深刻地看,針對不一樣的宿主程序,不一樣的資源對象,是否還能夠綁定事件而且進行響應處理呢?咱們在VBA或VSTO中或多或少是能夠這麼作的,例如Workbook的Open事件等等。
在Web Add-in中,事件經過一個特殊的作法來實現:Binding。但目前的支持是有限的,請參考官方文章:https://dev.office.com/reference/add-ins/excel/binding 。下面有一個簡單的實例可供參考:
$("#setup").click(() => tryCatch(setup)); $("#register-data-changed-handler").click(() => tryCatch(registerDataChangedHandler)); async function registerDataChangedHandler() { await Excel.run(async (context) => { const sheet = context.workbook.worksheets.getItem("Sample"); const salesTable = sheet.tables.getItem("SalesTable"); const dataRange = salesTable.getDataBodyRange(); //建立事件綁定 const salesByQuarterBinding = context.workbook.bindings.add(dataRange, "range", "SalesByQuarter"); salesByQuarterBinding.onDataChanged.add(onSalesDataChanged); OfficeHelpers.UI.notify("The handler is registered.", "Change the value in one of the data cells and watch this message banner. (Be sure to complete the edit by pressing Enter or clicking in another cell.)"); await context.sync(); }); } //這是事件處理代碼 async function onSalesDataChanged() { await Excel.run(async (context) => { OfficeHelpers.UI.notify("Data was changed!!!!", ""); await context.sync(); }); } //準備初始化數據 async function setup() { await Excel.run(async (context) => { const sheet = await OfficeHelpers.ExcelUtilities.forceCreateSheet(context.workbook, "Sample"); let salesTable = sheet.tables.add('A1:E1', true); salesTable.name = "SalesTable"; salesTable.getHeaderRowRange().values = [["Sales Team", "Qtr1", "Qtr2", "Qtr3", "Qtr4"]]; salesTable.rows.add(null, [ ["London", 500, 700, 654, null ], ["Hong Kong", 400, 323, 276, null ], ["New York", 1200, 876, 845, null ], ["Port-of-Spain", 600, 500, 854, null ], ["Nairobi", 5001, 2232, 4763, null ] ]); salesTable.getRange().format.autofitColumns(); salesTable.getRange().format.autofitRows(); sheet.activate(); await context.sync(); }); } async function tryCatch(callback) { try { await callback(); } catch (error) { OfficeHelpers.UI.notify(error); OfficeHelpers.Utilities.log(error); } }
能不能編寫自定義函數
這個問題很顯然是一個Excel的開發人員問的。
在Web Add-in的時代,目前已經提供了針對發燒友(Office Insider)的Developer Preview支持,請參考 https://docs.microsoft.com/en-us/office/dev/add-ins/excel/custom-functions-overview
能不能實現文檔打開的時候自動加載某個Add-in
能夠,可是分兩種狀況。
<?xml version="1.0" encoding="UTF-8"?> <!--Created:ce44715c-8c4e-446b-879c-ea9ebe0f09c8--> <OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> <!-- Begin Basic Settings: Add-in metadata, used for all versions of Office unless override provided. --> <!-- IMPORTANT! Id must be unique for your add-in, if you reuse this manifest ensure that you change this id to a new GUID. --> <Id>f43cc685-d5de-464e-a97c-520017b901a3</Id> <!--Version. Updates from the store only get triggered if there is a version change. --> <Version>1.0.0.0</Version> <ProviderName>[Provider name]</ProviderName> <DefaultLocale>en-US</DefaultLocale> <!-- The display name of your add-in. Used on the store and various places of the Office UI such as the add-ins dialog. --> <DisplayName DefaultValue="WordWebAddInSample" /> <Description DefaultValue="WordWebAddInSample"/> <!-- Icon for your add-in. Used on installation screens and the add-ins dialog. --> <IconUrl DefaultValue="~remoteAppUrl/Images/Button32x32.png" /> <SupportUrl DefaultValue="http://www.contoso.com" /> <!-- Domains that will be allowed when navigating. For example, if you use ShowTaskpane and then have an href link, navigation will only be allowed if the domain is on this list. --> <AppDomains> <AppDomain>AppDomain1</AppDomain> <AppDomain>AppDomain2</AppDomain> <AppDomain>AppDomain3</AppDomain> </AppDomains> <!--End Basic Settings. --> <!--Begin TaskPane Mode integration. This section is used if there are no VersionOverrides or if the Office client version does not support add-in commands. --> <Hosts> <Host Name="Document" /> </Hosts> <DefaultSettings> <SourceLocation DefaultValue="~remoteAppUrl/Home.html" /> </DefaultSettings> <!-- End TaskPane Mode integration. --> <Permissions>ReadWriteDocument</Permissions> <!-- End Add-in Commands Mode integration. --> </OfficeApp>