jQuery 版本viewer.js插件的结构分析与学习

本文分析了viewer.js插件的结构,包括其jQuery插件的封装方式,AMD支持,以及如何防止插件冲突。通过立即执行匿名函数利用闭包特性保护内部变量,同时提供不同情况下的加载机制,如AMD和直接调用。通过$.fn.viewer注册插件,并使用jQuery的data方法管理状态,确保实例复用,避免重复创建。此外,还讲解了插件如何保存并恢复同名插件的引用,以处理潜在的命名冲突问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        viewer.js,其调用方法和使用都很简单。但是开始的时候对于其内部方法的调用,图片更新,插件冲突控制等原理都不是很理解。在这里分析一下viewer.js的框架,和大部分jQuery插件的封装类似,也有很多值得学习的部分。



先省略掉大部分的属性和方法,留下整体框架,再一步步分析结构。

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as anonymous module.
        define('viewer', ['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node / CommonJS
        factory(require('jquery'));
    } else {
        // Browser globals.
        factory(jQuery);
    }
})(function ($) {

    'use strict';


    // Constants
    var NAMESPACE = 'viewer';


    //globle function
    function isUndefined(u) {
        return typeof u === 'undefined';
    }
    function isNumber(n) {
        return typeof n === 'number' && !isNaN(n);
    }
    function isString(s) {
        return typeof s === 'string';
    }
    function toArray(obj, offset) {
        var args = [];
        if (isNumber(offset)) { // It's necessary for IE8
            args.push(offset);
        }
        return args.slice.apply(obj, args);
    }
    //省略
    //省略

    function Viewer(element, options) {
        this.$element = $(element);
        this.options = $.extend({}, Viewer.DEFAULTS, $.isPlainObject(options) && options);
        this.isImg = false;
        this.isBuilt = false;
        this.isShown = false;
        this.isViewed = false;
        this.isFulled = false;
        this.isPlayed = false;
        this.wheeling = false;
        this.playing = false;
        this.fading = false;
        this.tooltiping = false;
        this.transitioning = false;
        this.action = false;
        this.target = false;
        this.timeout = false;
        this.index = 0;
        this.length = 0;
        this.init();
    }

    Viewer.prototype = {
        constructor: Viewer,
        init: function () {
        }
        //省略
        //省略
    };

    Viewer.setDefaults = function (options) {
        $.extend(Viewer.DEFAULTS, options);
    };

    Viewer.TEMPLATE = '省略';
        //省略
        //省略

    // Save the other viewer
    Viewer.other = $.fn.viewer;

    // Register as jQuery plugin
    $.fn.viewer = function (options) {
        var args = toArray(arguments, 1);
        var result;

        this.each(function () {
            var $this = $(this);
            var data = $this.data(NAMESPACE);
            var fn;

            if (!data) {
                if (/destroy|hide|exit|stop|reset/.test(options)) {
                    return;
                }

                $this.data(NAMESPACE, (data = new Viewer(this, options)));
            }

            if (isString(options) && $.isFunction(fn = data[options])) {
                result = fn.apply(data, args);
            }
        });
        return isUndefined(result) ? this : result;
    };

    $.fn.viewer.Constructor = Viewer;
    $.fn.viewer.setDefaults = Viewer.setDefaults;
    // No conflict
    $.fn.viewer.noConflict = function () {
        $.fn.viewer = Viewer.other;
        return this;

    };

});



上面部分只留下了整体结构,看起来就比较清晰明了。注意下面的结构,是一个立即执行的匿名函数。首先定义一个匿名函数function(){},然后用括号括起来,变成(function(){}) 这种形式,然后通过()这个运算符来执行。可以传递参数进去,以供内部函数使用。这里利用了闭包的特性,由于JavaScript是function作用域,这样可以避免内部临时变量影响到全局空间,即外部不能访问到function内部定义的变量。

(function(){ 
//jQuery插件代码

})();

常见的jQuery插件是这种形式,在插件内部继续使用$作为jQuery的别名。在开头部分加上一个;防止其它函数没有正确结束而报错。

;(function($){ 
//jQuery插件代码

})(jQuery);


在这个插件中也是这种形式,不过这个立即执行的匿名函数传递的参数是一个function。在执行部分考虑到了多种情况。

(function(factory){
  if (typeof define === 'function' && define.amd) {
        // AMD. Register as anonymous module.
        define('viewer', ['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node / CommonJS
        factory(require('jquery'));
    } else {
        // Browser globals.
        factory(jQuery);
    }

})(function($){
 
//jQuery插件代码
});


情况一:typeof define === 'function' && define.amd

AMD (异步模块定义)。它吸取了CommonJS的一些优点,但又不照搬它的格式。AMD最开始是CommonJS中模块格式的草案规范,但由于无法与CommonJS开发者达成一致而独立存在。它有独立的wiki 和讨论组 。AMD设计出一个简洁的写模块API:define 。
define(id, dependencies, factory);
其中:

id: 模块标识,可以省略。
dependencies: 所依赖的模块,可以省略。
factory: 模块的实现,或者一个JavaScript对象。

define('viewer', ['jquery'], factory);这里jquery是依赖的模块。也就是 define('viewer', ['jquery'], function($){ //jQuery插件代码}); 依赖jquery作为参数映射到$上。



情况二:typeof exports === 'object'
jquery是我们需要的一个依赖, factory(require('jquery')); 就相当于下面这样,在插件内部继续使用$作为jQuery的别名。

function($){
//jQuery插件代码
}(require('jquery'));


情况三: 一般情况
factory(jQuery);
也就是

(function($){ 
//jQuery插件代码
})(jQuery);


下面这部分代码,$.fn.viewer = function (options) {};注册了一个名为viewer 的jQuery插件。外部可以用$(selector).viewer(options);  这种方式调用,为了防止类似$(selector).length>1的情况,用jQuery对象的each方法遍历所有元素。 jQuery.data()方法用于向被选元素附加数据,或者从被选元素获取数据。如果data为undefined,说明是第一次初始化。并且创建Viewer的实例对象,然后将该实例对象附加到被选元素上。否则判断Viewer实例上有没有对应方法,如果有就直接在Viewer实例上调用这个方法。这样可以防止每次调用$(selector).viewer(options);的时候创建新的Viewer实例。所以当实例存在的时候,调用$(selector).viewer(options); 传递字符串时可以直接调Viewer.prototype 原型链上的方法,比如说update()更新图片,destroy()解除事件绑定,移除被选元素附加数据等。 最后插件返回一个jQuery对象,以保证插件的可链式调用。


注意  fn.apply(data,args);把调用$.fn.viewer传递的第一个参数之后的参数传递给了对应的实例function

 $.fn.viewer = function (options) {
        var args = toArray(arguments, 1);
        var result;

        this.each(function () {
            var $this = $(this);
            var data = $this.data(NAMESPACE);
            var fn;

            if (!data) {
                if (/destroy|hide|exit|stop|reset/.test(options)) {
                    return;
                }

                $this.data(NAMESPACE, (data = new Viewer(this, options)));
            }

            if (isString(options) && $.isFunction(fn = data[options])) {
                result = fn.apply(data, args);
            }
        });
        return isUndefined(result) ? this : result;
    };


防止同名插件的冲突

来看一下它是怎么防止冲突的,在定义jQuery插件时,所有的对象方法都应该附加到jQuery.fn对象上。在同一个页面上可能会有同名的插件,这种概率比较小,但如果遇到同名的话要如何选择调用哪一个插件呢?在未注册插件之前,先将同名插件用一个变量保存起来,本质上是增加一个引用,然后再注册插件。如果要调用另一个同名插件就还原该引用。

var NAMESPACE = 'viewer';
Viewer.other = $.fn.viewer;

 $.fn.viewer.noConflict = function () {
    $.fn.viewer = Viewer.other;
    return this;

  };


viewer.js原作者github地址:https://github.com/fengyuanchen/viewer





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值