论坛首页 Web前端技术论坛

jquery插件开发范例---A Plugin Development Pattern

浏览 9731 次
精华帖 (2) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-03-14   最后修改:2009-03-18
原文地址:http://www.learningjquery.com/2007/10/a-plugin-development-pattern
Claim only a single name in the jQuery namespace
This implies a single-plugin script. If your script contains multiple plugins, or complementary plugins (like $.fn.doSomething() and $.fn.undoSomething()) then you'll claim multiple names are required. But in general when authoring a plugin, strive to use only a single name to hold all of its implementation details.
这是一个单一插件的脚本。如果你的脚本中包含多个插件,或者互逆的插件,那么你需要声明多个函数名字。但是,通常当我们编写一个插件时,力求仅使用一个名字来包含它的所有内容。
In our example plugin we will claim the name "hilight".
我们的示例插件命名为“hilight”
PLAIN TEXT
JavaScript:
// plugin definition
$.fn.hilight = function() {
  // Our plugin implementation code goes here.
};
And our plugin can be invoked like this:
我们的插件通过这样被调用:
PLAIN TEXT
JavaScript:
$('#myDiv').hilight();
But what if we need to break up our implementation into more than one function? There are many reasons to do so: the design may require it; it may result in a simpler or more readable implementation; and it may yield better OO semantics.
但是如果我们需要分解我们的实现代码为多个函数该怎么办?有很多原因:设计上的需要;这样做更容易或更易读的实现;而且这样更符合面向对象。
It's really quite trivial to break up the implementation into multiple functions without adding noise to the namespace. We do this by recognizing, and taking advantage of, the fact that functions are first-class objects in JavaScript. Like any other object, functions can be assigned properties. Since we have already claimed the "hilight" name in the jQuery prototype object, any other properties or functions that we need to expose can be declared as properties on our "hilight" function. More on this later.
这真是一个麻烦事,把功能实现分解成多个函数而不增加多余的命名空间。出于认识到和利用函数是javascript中最基本的类对象,我们可以这样做。就像其他对象一样,函数可以被指定为属性。因此我们已经声明“hilight”为jQuery的属性对象,任何其他的属性或者函数我们需要暴露出来的,都可以在"hilight" 函数中被声明属性。稍后继续。

Accept an options argument to control plugin behavior
Let's add support to our hilight plugin for specifying the foreground and background colors to use. We should allow options like these to be passed as an options object to the plugin function. For example:
让我们为我们的插件添加功能指定前景色和背景色的功能。我们也许会让选项像一个options对象传递给插件函数。例如:
PLAIN TEXT
JavaScript:
// plugin definition
$.fn.hilight = function(options) {
  var defaults = {
    foreground: 'red',
    background: 'yellow'
  };
  // Extend our default options with those provided.
  var opts = $.extend(defaults, options);
  // Our plugin implementation code goes here.
};
Now our plugin can be invoked like this:
我们的插件可以这样被调用:
PLAIN TEXT
JavaScript:
$('#myDiv').hilight({
  foreground: 'blue'
});

Provide public access to default plugin settings
An improvement we can, and should, make to the code above is to expose the default plugin settings. This is important because it makes it very easy for plugin users to override/customize the plugin with minimal code. And this is where we begin to take advantage of the function object.
我们应该对上面代码的一种改进是暴露插件的默认设置。这对于让插件的使用者更容易用较少的代码覆盖和修改插件。接下来我们开始利用函数对象。
PLAIN TEXT
JavaScript:
// plugin definition
$.fn.hilight = function(options) {
  // Extend our default options with those provided.
  // Note that the first arg to extend is an empty object -
  // this is to keep from overriding our "defaults" object.
  var opts = $.extend({}, $.fn.hilight.defaults, options);
  // Our plugin implementation code goes here.
};
// plugin defaults - added as a property on our plugin function
$.fn.hilight.defaults = {
  foreground: 'red',
  background: 'yellow'
};
Now users can include a line like this in their scripts:
现在使用者可以包含像这样的一行在他们的脚本里:
PLAIN TEXT
JavaScript:
// this need only be called once and does not
// have to be called from within a 'ready' block
$.fn.hilight.defaults.foreground = 'blue';
And now we can call the plugin method like this and it will use a blue foreground color:
接下来我们可以像这样使用插件的方法,结果它设置蓝色的前景色:
PLAIN TEXT
JavaScript:
$('#myDiv').hilight();
As you can see, we've allowed the user to write a single line of code to alter the default foreground color of the plugin. And users can still selectively override this new default value when they want:
如你所见,我们允许使用者写一行代码在插件的默认前景色。而且使用者仍然在需要的时候可以有选择的覆盖这些新的默认值:
PLAIN TEXT
JavaScript:
// override plugin default foreground color
$.fn.hilight.defaults.foreground = 'blue';
// ...
// invoke plugin using new defaults
$('.hilightDiv').hilight();
// ...
// override default by passing options to plugin method
$('#green').hilight({
  foreground: 'green'
});

Provide public access to secondary functions as applicable
This item goes hand-in-hand with the previous item and is an interesting way to extend your plugin (and to let others extend your plugin). For example, the implementation of our plugin may define a function called "format" which formats the hilight text. Our plugin may now look like this, with the default implementation of the format method defined below the hilight function.
这段将会一步一步对前面那段代码通过有意思的方法扩展你的插件(同时让其他人扩展你的插件)。例如,我们插件的实现里面可以定义一个名叫"format"的函数来格式化高亮文本。我们的插件现在看起来像这样,默认的format方法的实现部分在hiligth函数下面。
PLAIN TEXT
JavaScript:
// plugin definition
$.fn.hilight = function(options) {
  // iterate and reformat each matched element
  return this.each(function() {
    var $this = $(this);
    // ...
    var markup = $this.html();
    // call our format function
    markup = $.fn.hilight.format(markup);
    $this.html(markup);
  });
};
// define our format function
$.fn.hilight.format = function(txt) {
return '<strong>' + txt + '</strong>';
};
We could have just as easily supported another property on the options object that allowed a callback function to be provided to override the default formatting. That's another excellent way to support customization of your plugin. The technique shown here takes this a step further by actually exposing the format function so that it can be redefined. With this technique it would be possible for others to ship their own custom overrides of your pluging, in other words, it means others can write plugins for your plugin.
我们很容易的支持options对象中的其他的属性通过允许一个回调函数来覆盖默认的设置。 这是另外一个出色的方法来修改你的插件。这里展示的技巧是进一步有效的暴露format函数进而让他能被重新定义。通过这技巧,是其他人能够传递他们自己设置来覆盖你的插件,换句话说,这样其他人也能够为你的插件写插件。
Considering the trivial example plugin we're building in this article, you may be wondering when this would ever be useful. One real-world example is the Cycle Plugin. The Cycle Plugin is a slideshow plugin which supports a number of built-in transition effects scroll, slide, fade, etc. But realistically, there is no way to define every single type of effect that one might wish to apply to a slide transition. And that's where this type of extensibility is useful. The Cycle Plugin exposes a "transitions" object to which users can add their own custom transition definitions. It's defined in the plugin like this:
考虑到这个篇文章中我们建立的无用的插件,你也许想知道究竟什么时候这些会有用。一个真实的例子是Cycle插件.这个Cycle插件是一个滑动显示插件,他能支持许多内部变换作用到滚动,滑动,渐变消失等。但是实际上,没有办法定义也许会应用到滑动变化上每种类型的效果。那是这种扩展性有用的地方。Cycle插件对使用者暴露"transitions"对象,使他们添加自己变换定义。插件中定义就像这样:
PLAIN TEXT
JavaScript:
$.fn.cycle.transitions = {
// ...
};
This technique makes it possible for others to define and ship transition definitions that plug-in to the Cycle Plugin.
这个技巧使其他人能定义和传递变换设置到Cycle插件。

Keep private functions private
The technique of exposing part of your plugin to be overridden can be very powerful. But you need to think carefully about what parts of your implementation to expose. Once it's exposed, you need to keep in mind that any changes to the calling arguments or semantics may break backward compatibility. As a general rule, if you're not sure whether to expose a particular function, then you probably shouldn't.
这种技巧暴露你插件一部分来被覆盖是非常强大的。但是你需要仔细思考你实现中暴露的部分。一但被暴露,你需要在头脑中保持任何对于参数或者语义的改动也许会破坏向后的兼容性。一个通理是,如果你不能肯定是否暴露特定的函数,那么你也许不需要那样做。
So how then do we define more functions without cluttering the namespace and without exposing the implementation? This is a job for closures. To demonstrate, we'll add another function to our plugin called "debug". The debug function will log the number of selected elements to the Firebug console. To create a closure, we wrap the entire plugin definition in a function (as detailed in the jQuery Authoring Guidelines).
那么我们怎么定义更多的函数而不搅乱命名空间也不暴露实现呢?这就是闭包的功能。为了演示,我们将会添加另外一个“debug”函数到我们的插件中。这个debug函数将为输出被选中的元素格式到firebug控制台。为了创建一个闭包,我们将包装整个插件定义在一个函数中。
PLAIN TEXT
JavaScript:
// create closure
(function($) {
  // plugin definition
  $.fn.hilight = function(options) {
    debug(this);
    // ...
  };
  // private function for debugging
  function debug($obj) {
    if (window.console && window.console.log)
      window.console.log('hilight selection count: ' + $obj.size());
  };
//  ...
// end of closure
})(jQuery);
Our "debug" method cannot be accessed from outside of the closure and thus is private to our implementation.
我们的“debug”方法不能从外部闭包进入,因此对于我们的实现是私有的。

Support the Metadata Plugin
Depending on the type of plugin you're writing, adding support for the Metadata Plugin can make it even more powerful. Personally, I love the Metadata Plugin because it lets you use unobtrusive markup to override plugin options (which is particularly useful when creating demos and examples). And supporting it is very simple! Update: This bit was optimized per suggestion in the comments.
在你正在写的插件的基础上,添加对Metadata插件的支持能使他更强大。个人来说,我喜欢这个Metadata插件,因为它让你使用不多的"markup”覆盖插件的选项(这非常有用当创建例子时)。而且支持它非常简单。更新:注释中有一点优化建议。
PLAIN TEXT
JavaScript:
// plugin definition
$.fn.hilight = function(options) {
  // ...
  // build main options before element iteration
  var opts = $.extend({}, $.fn.hilight.defaults, options);
  return this.each(function() {
    var $this = $(this);
    // build element specific options
    var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
    //...
This changed line does a couple of things:
这些变动行做了一些事情:
it tests to see if the Metadata Plugin is installed
它是测试Metadata插件是否被安装
if it is installed, it extends our options object with the extracted metadata.
如果它被安装了,它能扩展我们的options对象通过抽取元数据
This line is added as the last argument to jQuery.extend so it will override any other option settings. Now we can drive behavior from the markup if we choose:
这行作为最后一个参数添加到JQuery.extend,那么它将会覆盖任何其它选项设置。现在我们能从"markup”处驱动行为,如果我们选择了“markup”:
<!--  markup  -->
<div class="hilight { background: 'red', foreground: 'white' }">
  Have a nice day!
</div>
<div class="hilight { foreground: 'orange' }">
  Have a nice day!
</div>
<div class="hilight { background: 'green' }">
  Have a nice day!
</div>

And now we can hilight each of these divs uniquely using a single line of script:
现在我们能高亮哪些div仅使用一行脚本:
PLAIN TEXT
JavaScript:
$('.hilight').hilight();

Putting it All Together
Below is the completed code for our example:
下面使我们的例子完成后的代码:
PLAIN TEXT
JavaScript:
//
// create closure
//
(function($) {
  //
  // plugin definition
  //
  $.fn.hilight = function(options) {
    debug(this);
    // build main options before element iteration
    var opts = $.extend({}, $.fn.hilight.defaults, options);
    // iterate and reformat each matched element
    return this.each(function() {
      $this = $(this);
      // build element specific options
      var o = $.meta ? $.extend({}, opts, $this.data()) : opts;
      // update element styles
      $this.css({
        backgroundColor: o.background,
        color: o.foreground
      });
      var markup = $this.html();
      // call our format function
      markup = $.fn.hilight.format(markup);
      $this.html(markup);
    });
  };
  //
  // private function for debugging
  //
  function debug($obj) {
    if (window.console && window.console.log)
      window.console.log('hilight selection count: ' + $obj.size());
  };
  //
  // define and expose our format function
  //
  $.fn.hilight.format = function(txt) {
    return '<strong>' + txt + '</strong>';
  };
  //
  // plugin defaults
  //
  $.fn.hilight.defaults = {
    foreground: 'red',
    background: 'yellow'
  };
//
// end of closure
//
})(jQuery);
This design pattern has enabled me to create powerful, consistently crafted plugins. I hope it helps you to do the same.
这段设计已经让我创建了强大符合规范的插件。我希望它能让你也能做到。
   发表时间:2009-06-16  
请问
var opts = $.extend({}, $.fn.hilight.defaults, options);
这句如何理解?
0 请登录后投票
   发表时间:2009-09-27  
同楼上疑问?
0 请登录后投票
   发表时间:2009-09-30  
zzknight 写道
请问
var opts = $.extend({}, $.fn.hilight.defaults, options);
这句如何理解?

jQuery.extend([deep], target, object1, [objectN]) 返回值:Object

参数
deep     (可选)Object如果设为true,则递归合并。

target   Object待修改对象。

object1  Object待合并到第一个对象的对象。

objectN  (可选)Object待合并到第一个对象的对象。



参考文档里的, 一起学习了。
0 请登录后投票
   发表时间:2009-09-30   最后修改:2009-09-30
zzknight 写道
请问
var opts = $.extend({}, $.fn.hilight.defaults, options);
这句如何理解?


把$.fn.hilight.defaults的属性和options的属性复制到一个空的对象里,并返回这个对象.(浅度复制,后面的同名属性会覆盖前面的).

个人觉得这里的extend不如叫做merge更容易理解. 遗憾的是jq里的merge有点大材小用(一个很像concat的东西,并且确实只能合并array或者array-like).
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics