有這麼多人開發 jQuery 插件,遇到一個簡單的 - 由於缺乏更好的語言 - 糟糕透頂的情況並不少見。沒有範例或文檔,該插件不遵循最佳實踐等。但您是幸運者之一:本文將詳細介紹您必須避免的陷阱。
#對於那些經常使用 Nettuts 的人來說,jQuery 並不陌生。 Jeffrey Way 的《30 天學習 jQuery》(以及此處和其他地方的各種其他教程)很棒,帶領我們所有人走上了 Sizzle 支持的 Awesomesauce 之路。在所有的炒作中(以及開發人員和瀏覽器供應商在 JavaScript 採用方面的巨大飛躍),大量的插件已經出現。這就是 jQuery 成為最受歡迎的 JavaScript 函式庫的部分原因!唯一的問題是其中許多都不是太好。
在本文中,我們將不再專門關注 JavaScript,而是更專注於插件交付的最佳實踐。
有一些模式或多或少被普遍認為是創建 jQuery 外掛的「正確方法」。如果您不遵循這些約定,您的插件可能...很糟糕!考慮最常見的模式之一:
(function($, window, undefined){ $.fn.myPlugin = function(opts) { var defaults = { // setting your default values for options } // extend the options from defaults with user's options var options = $.extend(defaults, opts || {}); return this.each(function(){ // jQuery chainability // do plugin stuff }); })(jQuery, window);
首先,我們建立一個自呼叫匿名函數來避免使用全域變數。我們傳入 $
、window
和 undefined
。呼叫自呼叫函數的參數是jQuery
和window
;沒有為undefined 傳入任何內容,因此,如果我們決定在插件中使用undefined 關鍵字,「undefined」實際上將是未定義的。
這可以防止其他腳本可能會向
undefined
指派惡意值,例如true
!
$
作為 jQuery 傳遞;我們這樣做是為了確保在匿名函數之外, $
仍然可以完全引用其他內容,例如 Prototype。
傳遞全域可存取的 window
物件的變數允許透過縮小過程(您也應該這樣做)來壓縮更多的程式碼。
接下來,我們使用 jQuery 外掛模式,$.fn.PluginName
。這是註冊插件以使用 $(selector).method()
格式的方法。它只是用您的新方法擴展了 jQuery 的原型。如果您想建立一個在 jQuery 物件上定義函數的插件,請直接新增它,如下所示:
$.PluginName = function(options){ // extend options, do plugin stuff }
這種類型的插件無法鏈接,因為定義為 jQuery 物件屬性的函數通常不會傳回 jQuery 物件。例如,考慮以下程式碼:
$.splitInHalf = function(stringToSplit){ var length = stringToSplit.length; var stringArray = stringToSplit.split(stringToSplit[Math.floor(length/2)]); return stringArray; }
在這裡,我們回傳一個字串陣列。簡單地將其作為數組返回是有意義的,因為這可能是用戶想要使用的(如果他們願意,他們可以輕鬆地將其包裝在 jQuery 物件中)。相反,請考慮以下人為的範例:
$.getOddEls = function(jQcollection){ // return jQcollection.filter(function(index){ var i = index+1; return (index % 2 != 0); }); }
在這種情況下,使用者可能期望從 $.getOddEls
傳回 jQuery 物件;因此,我們傳回filter方法,該方法傳回由傳遞的函數定義的jQuery集合。一個好的經驗法則是將傳回的元素包裝在 jQuery 函數中,特別是如果它們可以連結起來;如果您要傳回陣列、字串、數字、函數或其他資料類型,請將它們開啟。
可以說,發佈程式碼時可以做的最重要的事情就是添加必要的文件。您向開發人員解釋的內容與程式碼實際執行或可以執行的操作之間的差距是用戶不想浪費時間來弄清楚程式碼的來龍去脈。
文件是一種沒有任何硬性規則的實踐;但是,人們普遍認為擁有的(組織良好的)文檔越多越好。
這個過程應該既是內部實踐(在程式碼中/散佈在整個程式碼中),也是外部實踐(在 wiki 或自述文件中徹底解釋每個公共方法、選項和多個用例)。
最受歡迎的插件提供對使用者可能想要控制的變數(大多數插件稱為「選項」物件)的完全存取。他們還可能提供插件的許多不同配置,以便它可以在許多不同的上下文中重複使用。例如,讓我們考慮一個簡單的滑桿插件。使用者可能想要控制的選項包括動畫的速度、類型和延遲。
最好也讓使用者存取新增到外掛插入或操作的 DOM 元素中的類別名稱/ID 名稱。但除此之外,他們可能還希望在每次投影片轉換時,或者當投影片轉換回開頭(一個完整的「循環」)時存取回調函數。
您的工作是考慮插件的所有可能用途和需求。
讓我們考慮另一個例子:呼叫 API 的插件應該提供對 API 回傳物件的存取。以下面的簡單外掛概念為例:
$.fn.getFlickr = function(opts) { return this.each(function(){ // jQuery chainability var defaults = { // setting your default options cb : function(data){}, flickrUrl : // some default value for an API call } // extend the options from defaults with user's options var options = $.extend(defaults, opts || {}); // call the async function and then call the callback // passing in the api object that was returned $.ajax(flickrUrl, function(dataReturned){ options.cb.call(this, dataReturned); }); }); }
这使我们能够做以下事情:
$(selector).getFlickr(function(fdata){ // flickr data is in the fdata object });
宣传这一点的另一种方式是提供“钩子”作为选项。从 jQuery 1.7.1 及更高版本开始,我们可以在插件调用之后使用 .on(eventName, function(){})
将行为分离到它们自己的函数中。例如,使用上面的插件,我们可以将代码更改为如下所示:
$.fn.getFlickr = function(opts) { return this.each(function(i,el){ var $this = el; var defaults = { // setting your default options flickrUrl : "http://someurl.com" // some default value for an API call } var options = $.extend(defaults, opts || {}); // call the async function and then call the callback // passing in the api object that was returned $.ajax(flickrUrl, function(dataReturned){ // do some stuff $this.trigger("callback", dataReturned); }).error(function(){ $this.trigger("error", dataReturned); }); }); }
这允许我们调用 getFlickr
插件并链接其他行为处理程序。
$(selector).getFlickr(opts).on("callback", function(data){ // do stuff }).on("error", function(){ // handle an error });
您可以看到提供这种灵活性绝对重要;您的插件的操作越复杂,可用的控件就越复杂。
好的,第三条建议是,您的插件的操作越复杂,可用的控制就越复杂。可用。然而,一个很大的错误是为插件功能提供了太多的选项。例如,基于 UI 的插件最好具有无参数默认行为。
$(selector).myPlugin();
当然,有时这是不现实的(例如,用户可能正在获取特定的提要)。在这种情况下,您应该为他们做一些繁重的工作。有多种方式将选项传递给插件。例如,假设我们有一个简单的推文获取器插件。该推文获取器应该有一个默认行为,带有一个必需选项(您要从中获取的用户名)。
$(selector).fetchTweets("jcutrell");
例如,默认情况下可能会抓取一条推文,将其包装在段落标记中,然后使用该 html 填充选择器元素。这是大多数开发人员所期望和欣赏的行为。细粒度选项应该就是:选项。
当然,根据插件的类型,如果高度基于 UI 操作,则必须包含 CSS 文件,这是不可避免的。一般来说,这是一个可以接受的问题解决方案;大多数插件都与图像和 CSS 捆绑在一起。但不要忘记第二点 - 文档还应包括如何使用/引用样式表和图像。开发人员不想浪费时间查看源代码来弄清楚这些事情。
事情应该只是......工作。
话虽如此,使用注入样式(可以通过插件选项高度访问)或基于类/ID 的样式绝对是最佳实践。这些 ID 和类也应该可以通过前面提到的选项进行访问。然而,内联样式会覆盖外部 CSS 规则;不鼓励将两者混合使用,因为开发人员可能需要很长时间才能弄清楚为什么插件创建的元素不遵守他们的 CSS 规则。在这些情况下请运用您的最佳判断。
根据经验,内联 CSS 很糟糕 - 除非它很小到无法保证有自己的外部样式表。
证据就在布丁中:如果您无法提供一个实际示例来说明您的插件如何使用随附的代码,人们很快就会放弃使用您的插件。就那么简单。不要偷懒。
一个很好的示例模板:
jQuery,像任何优秀的代码库一样,随着每个版本的发布而成长。即使在弃用支持后,大多数方法仍会保留。然而,添加了新的方法;一个完美的例子是 .on()
方法,它是 jQuery 的新的事件委托一体化解决方案。如果您编写一个使用 .on()
的插件,那么使用 jQuery 1.6 或更早版本的人将不走运。现在,我并不是建议您针对最低公分母进行编码,但是,在您的文档中,请务必解释您的插件支持哪个版本的 jQuery。如果您引入了支持 jQuery 1.7 的插件,那么即使 1.8 发布,您也应该强烈考虑维持对 1.7 的支持。您还应该考虑利用 jQuery 中新的/更好的/更快的功能。
鼓励开发人员升级,但不要太频繁地破坏您的插件!一种选择是提供插件的“旧版”、已弃用、不受支持的版本。
如果您还没有学会如何使用版本控制,那么是时候咬紧牙关了。
除了将 jQuery 版本支持/兼容性作为文档的一部分之外,您还应该进行版本控制。版本控制(具体来说,通过 GitHub)在很大程度上是社交编码的发源地。如果您正在开发一个 jQuery 插件并希望最终发布到官方存储库中,那么无论如何它都必须存储在 GitHub 存储库中;如果您还没有学会如何使用版本控制,那么是时候硬着头皮了。版本控制有无数的好处,所有这些都超出了本文的范围。但核心好处之一是,它允许人们查看您所做的更改、改进和兼容性修复以及您何时进行这些更改、改进和兼容性修复。这也为您编写的插件的贡献和定制/扩展打开了大门。
世界不需要另一个滑块插件。
好吧,我们在这里忽略它已经足够长的时间了:一些“插件”是无用的或太浅,不足以保证被称为插件。世界不需要另一个滑块插件!然而,应该指出的是,内部团队可能会开发自己的插件供自己使用,这是完全可以的。但是,如果您希望将您的插件推向社交编码领域,请找到编写更多代码的理由。俗话说,没有理由重新发明轮子。相反,接过别人的方向盘,建造一辆赛车。当然,有时会有新的、更好的方法来做已经做过的同样的事情。例如,如果您使用更快或新技术,您很可能会编写一个新的滑块插件。
这个相当简单:提供代码的缩小版本。这使得它更小、更快。它还确保您的 Javascript 在编译时不会出现错误。当您缩小代码时,不要忘记也提供未压缩的版本,以便您的同行可以查看底层代码。对于各种经验水平的前端开发人员来说,都有免费且廉价的工具。
有关自动化解决方案,请参阅提示十三。
当你编写一个插件时,它的目的就是供其他人使用,对吗?因此,最有效的源代码是具有高度可读性的。如果您正在编写无数巧妙的单行 lambda 样式函数,或者您的变量名称没有语义,那么当错误不可避免地发生时,将很难对其进行调试。不要编写短变量名来节省空间,而是遵循技巧九(缩小!)中的建议。这是优秀文档的另一部分;优秀的开发人员应该能够检查您的代码并了解其用途,而无需花费太多精力。
如果您发现自己调用变量“
a
”或“x
”,那么您就做错了。
此外,如果您发现自己查阅文档来记住您自己的看起来奇怪的代码正在做什么,那么您也可能需要不那么简洁并更具解释性。将每个函数的行数限制为尽可能少;如果它们延伸三十行或更多行,则可能会有代码味道。
尽管我们都喜欢使用 jQuery,但重要的是要了解它是一个库,而且成本很小。一般来说,您不需要太担心 jQuery 选择器性能之类的事情。不要令人讨厌,你会没事的。 jQuery 是高度优化的。也就是说,如果您需要 jQuery(或插件)的唯一原因是在 DOM 上执行一些查询,您可能会考虑完全删除抽象,而坚持使用普通 JavaScript 或 Zepto。
注意:如果您决定坚持使用普通 JavaScript,请确保您使用的是跨浏览器的方法。对于较新的 API,您可能需要一个小型的 polyfill。
使用咕噜声。期间。
Grunt 是一个“用于 JavaScript 项目的基于任务的命令行构建工具”,最近在 Nettuts+ 上对此进行了详细介绍。它允许你做这样的事情:
grunt init:jquery
此行(在命令行中执行)将提示您一系列问题,例如标题、描述、版本、git 存储库、许可证等。这些信息有助于自动化设置文档、许可等的过程。
Grunt 所做的不仅仅是为您制作一些定制的样板代码;它还提供内置工具,例如代码 linter JSHint,只要您安装了 PhantomJS(由 Grunt 负责),它就可以为您自动执行 QUnit 测试。这样,您可以简化工作流程,因为测试在保存时会立即在终端中运行。
Oh, by the way - you didtest your code, right? If not, how do you ensure/claim that your code works as expected? Manual testing has its place, but if you find yourself refreshing your browser countless times an hour, you're doing it wrong. Consider using tools like QUnit, Jasmine or even Mocha.
Tests are especially useful when merging pull requests on GitHub. You can require all requests to provide tests to ensure new/modified code does not break your existing plugins.
If the concept of testing jQuery plug-ins is new to you, consider watching our Premium-exclusive screencast, Testing the Technology That Drives jQuery Plugins. Additionally, we are launching a new JavaScript Testing with Jasmine course on the website later this week!
We won't do you any good by simply telling you what you did wrong. Here are some links to get you back on the right path!
If you are writing a jQuery plugin, it is crucial to stay away from the pitfalls listed above. Did I miss any key signs that the plugin is performing poorly?
以上是jQuery 外掛程式未使用的 14 種可能解釋的詳細內容。更多資訊請關注PHP中文網其他相關文章!