适用于SharePoint的JavaScript页面处理器机制

在进行SharePoint前端开发时,难免要对页面进行定制。通常我们会使用SharePoint Designer直接打开页面进行编辑,这种方法简单直观,但却存在一些问题:

  1. SharePoint Designer的前端编辑能力较弱;
  2. 定制分散于网站的不同位置,难以维护、跟踪和迁移;
  3. 在某些托管环境下,某些页面的编辑是受限的(比如笔者公司内部的SharePoint网站是不开放编辑_layouts目录下的页面的)。

因此,我们需要一种新的方式来定制页面,它必须能够解决以上问题。

编辑器无关性是最容易达成的一个目标,使用gulp任务等发布方法允许我们将前端资源文件存放于本地进行编辑,可以选择任何合适的编辑器。

传统的SharePoint前端定制方法是直接修改需要定制的网页,即便是引入.js和.css文件,为了方便编辑,也会把这些文件存放在该网页的附近。当项目中需要定制的页面比较分散时,这种方法的维护成本就较高了;而当项目的每一阶段都有专门的网站时,对于变化的跟踪和迁移也成了棘手的事情,每次修改都需要记住位置和文件清单,修改完成后手工同步到其他网站。时间一长,几个网站的代码就很容易出现不同步的情况,慢慢的,所谓的开发网站和测试网站也就被弃用了,所有修改都直接在生产网站上进行了。

想要扭转这种情况,最好的方法是把所有前端资源文件都存放于统一的位置,比如Site Assets下。需要定制的页面都从这个统一的位置来引用这些文件,除此之外,需要定制的页面上不应该再有任何修改,否则定制还是分散了,定制应当完全交由引入的.js和.css文件来完成。由于所有文件都存放于统一的位置,在网站之间迁移就会变得非常简单,而且也不再需要跟踪变化:平常在开发网站上进行开发调试;接着就可以将Site Assets下的所有文件全部的复制到测试网站,交由测试人员进行测试;需要上线的时候,再把测试网站的Site Assets全部复制到生产网站。

对于受限的页面,我们无法直接修改它来引入.js和.css文件,传统的解决方法是修改母版页,然而随着项目复杂度的攀升母版页也会越来越臃肿,越来越难以维护,另外,母版页很有可能本身就属于禁止编辑的受限文件。幸好SharePoint允许我们使用Custom Action往页面上注册.js文件,注册之后,网站内所有页面上都会自动引入这个文件,我们就可以将它作为一个加载器,根据当前页面来加载相应的资源文件,不用修改任何页面,也就不用担心页面受限的问题了。

使用Custom Action注册.js文件的方法可以参考SharePoint文档,部署时可以使用沙盒解决方案,也可以使用客户端对象模型,笔者推荐第二种方法,它更加灵活。

这里假设我们注册了两个.js文件:一个是require.js,用来加载其他资源文件及其依赖项;另一个是page-handlers.js,用来维护所有页面处理器(page handler)的配置,并实现自动加载。

首先在page-handlers.js中定义网站中的所有页面处理器:

var handlers = {
"simple": {
url: /page-handler-simple\.aspx/i,
module: "modules/page-handlers/simple/simple"
}
};

handlers的每一个属性都是一个页面处理器,这里只定义了一个处理器:simple。其url属性是一个正则表达式,会用来测试当前页面的Url,测试通过即表示该处理器可以被引入该页面;其module属性是一个标准的RequireJS模块路径,当url通过测试后,就会使用RequireJS加载这个路径的模块。

接下来编写一个遍历所有处理器,并进行测试和加载的init函数:

function init () {
var name,
handler,
path = window.location.href;

for (name in handlers) {
if (handlers.hasOwnProperty(name)) {
handler = handlers[name];
if (handler.url instanceof RegExp === false) {
throw new Error("The type of handler.url must be RegExp.");
} else if (handler.url.test(path)) {
initPageHandler(handler);
}
}
}
}

通过测试的处理器会传递给initPageHandler进行初始化:

function initPageHandler (handler) {
require([handler.module], function () { });
}

最后,在page-handlers.js的末尾调用init函数:

init();

页面处理器的基本原理还是很简单的,现在我们导航到page-handler-simple.aspx的话,page-handlers.js就会自动将modules/page-handlers/simple/simple.js加载到页面上,我们可以利用simple.js对页面进行定制。

由于处理器的module是一个标准的RequireJS模块路径,所以还可以利用RequireJS的插件来加载.css文件,比如我们新增加一个default-styles处理器:

var pageHandlers = {
"simple": {},
"default-styles" : {
url: /.*/i,
module: "css!modules/page-handlers/default-styles/default-styles"
}
};

default-styles处理器的url会匹配网站中的所有页面,其module则使用RequireJS的require-css插件来将modules/page-handlers/default-styles/default-styles.css文件加载到页面上。

此外,我们还可以对处理器的定义稍加扩展来实现一些更有趣的功能,比如在加载模块之前以模态对话框的形式提示用户,我们可以在处理器定义中添加一个workingMessage属性:

var handlers = {
"simple": {
url: /page-handler-simple\.aspx/i,
workingMessage: "Loading simple page handler...",
module: "modules/page-handlers/simple/simple"
},
"default-styles" : { }
};

然后我们就可以在initPageHandler函数中添加代码来显示对话框了。

function initPageHandler (handler) {
var hasWorkingMessage = typeof handler.workingMessage === "string";

if (hasWorkingMessage === true) {
workingDialog.show(handler.workingMessage);
}

require([handler.module], function () {
if (hasWorkingMessage === true) {
workingDialog.hide(handler.workingMessage);
}
});
}

workingDialog是笔者自己对SP.UI.ModalDialog的再封装,非常简单,就不在此展示代码了。

One Comment

  1. 大雁飞

    老师讲的很好,后期有没有时间到51CTO学院专门开课将一些Sharepoint开发呢?学员都期待很久了,我的QQ:2881841993,微信:lyf693

发表评论

电子邮件地址不会被公开。 必填项已用*标注