7. jQuery plugins & the stateManager / jQuery插件基类与StateManager的完美配合

7.1 Introduction / 简介

当用户试图只用Javascript处理响应式网页的时候,会发现该过程异常痛苦,因为用户必须根据屏幕大小调整代码。
==> 因此,Shopware推出了一个组件 StateManager,能够定义新状态,并触发callback函数(callback function)。
另外一方面,可爱的jQuery插件并不喜欢干这些事。为了简化这个过程,Shopware导入了一个插件基类,它具有jQuery插件开发的最佳实践,并能与StateManager完美集成。

7.2 Plugin base class / 插件基类(插件的编写)

就像上面所提到的那样,jQuery插件基类具有jQuery插件开发的最佳实践。插件功能一览:

  • 默认配置 + 可通过自定义配置重写默认配置
  • 可通过HTML5的 data 属性配置插件
  • 支持jQuery方法链接(jQuery's method chaining)
  • 支持事件命名空间(Namespace of events)
  • 有内置函数支持删除事件监听器
  • 防止同一元素上的多重实例化
  • 检查元素是否使用某个插件的自定义表达式
  • 使用jQuery的 data -方法自动将插件绑定到元素
    由此可见,该jQuery基类为用户编写jQuery插件提供了许多有效的方法。

7.2.1 Getting started / 基础
以下代码展示了,如何使用插件基类编写jQuery插件的过程:

/**
 * $.plugin(): 自动连接全局jQuery对象。
 * 该方法的两个参数:
 * para1: 插件名
 * para2: 一个对象,包含了默认配置和插件的实际实现
 */
$.plugin('example', {
    
    /**
     * 插件的默认配置对象
     * 用户可以自定义设置。自定义后的设置会被自动整合到新对象里。该对象可通过"this.opts"在这个插件的任意方法中被访问到
     */
    defaults: {
        activeCls: 'js--is-active'
    },
    
    /**
     * "init" 方法的作用就像是插件的构造函数(constructor)
     * 可在这里对必要的元素进行缓存,并为插件注册事件监听器(Event Listener)
     * 另外,还可以现有配置的基础上修改插件的function
     */
    init: function() {
        var me = this;
        
        /**
         * 调用"applyDataAttributes" 会自动读出所有的 "data-" 属性并重写配置。
         * 如果你想要用HTML来配置你的插件,而不是通过提供一个配置对象的话,这个属性很有用。
         *
         * 比如,我们要在下面这个div元素中调用插件:
         *    <div data-activeCls="some-other-class">...</div>
         *  "data"属性会用值"some-other-class"重写 "activeCls"
         */
        me.applyDataAttributes();
        
        /**
         * 接下来,用内置函数"_on"为插件设置一个新的事件监听器(这个"_on"方法其实是在jQuery的"on"方法上添加了一些其他的特性)
         * 事件和事件监听器都会在插件特殊事件集中进行注册。该集合将自动迭代,并从元素中删除已注册的事件监听器。
         * Additionally the event name will be namespaced on the fly which
         * provides us with a safe way to remove a specific event listener from
         * an element and doesn't affect other plugins which are listening on
         * the same event.
         * (这段翻译无能。。。求助)
         */
        me._on(me.$el, 'click', function(event) {
            event.preventDefault();
            
            /**
             * 在接下来的条件语句中,拍段元素是否在使用该插件,若在被使用,则终止
             * 此外,这里使用 "this.$el"来初始化插件
             */
            if(me.$el.is('plugin-example')) {
                
                /**
                 * 现在用变量 "this.opts" 来访问插件的配置
                 */
                me.$el.toggleClass(me.opts.activeCls);
            }
        });
    },
    
    /**
     * "destroy" 方法可从插件外部被调用,或者在被定义的状态(state)改变时自动使用StateManager
     * 通常你需要删除那些由插件添加到元素的类,并将事件监听器也从元素上删除。
     */
    destroy: function() {
        var me = this;
    
        me.$el.removeClass(me.opts.activeCls);
        
        /**
         * 调用"_destroy"方法:移除所有用"_on"方法在基类插件上注册的事件监听器。
         * 如果想要自行迭代事件监听器,你可以使用 "this._events" 在插件中访问事件集
         */
        me._destroy();
    }
});

注意:强烈推荐jQuery插件使用基类!

接下来,我们删去注解,看着代码解释一遍给自己听:

$.plugin('example', {
    
    defaults: {
        activeCls: 'js--is-active'
    },
    
    init: function() {
        var me = this;
        me.applyDataAttributes();
        me._on(me.$el, 'click', function(event) {
            event.preventDefault();
            if(me.$el.is('plugin-example')) {
                me.$el.toggleClass(me.opts.activeCls);
            }
        });
    },
    
    destroy: function() {
        var me = this;
        me.$el.removeClass(me.opts.activeCls);      
        me._destroy();
    }
});

7.2.2 Class properties / 类的属性

  • _name: String类型, 插件的名字
  • $el : jQuery类型, 将插件实例化成jQuery对象的HTML元素
  • opts : Object类型, 默认配置和用户配置的结果。==> 通过调用this.applyDataAttributes()能重写对象的属性值
  • _events : Array类型, 一个包含所有用 _on 方法注册了的事件监听器的集合

7.2.3 Class methods / 类的方法

  • init() 模板中的方法,作用类似于插件的构造方法(the constructor of the plugin),可以用于缓存所需的HTML元素,设置事件监听等等。

  • destroy() 模板中的方法,用于删除插件。通常你会用它删除你加到某个元素上的类或者事件监听器。当你想要专门对某个特定的状态使用插件的时候,该方法很有用。

  • update() 模板中的方法,用于当状态发生改变(进入或离开一个状态)时更新插件的行为。这个方法只有在你使用了StateManager来初始化插件的时候有用。

  • _destroy() 私人方法,在插件的_events 属性中迭代已注册的事件监听器。此外,该方法可使用jQuery的removeData()方法移除插件的内存绑定(inmemory binding)。并且触发一个全局可用的观察者(observer)。

  • _on(element , event , fn) :
    element : jQuery或HTMLElement类型,特殊事件监听器的事件目标(jQuery元素 / DOM节点)
    event : String类型, 代表要监听的事件
    fn : Function类型, 当特殊类型的事件发生时,对象会收到提示(notification)
    该方法为jQuery中on()方法的代理方法,用于连接事件监听器到提供的元素, 并且在_events事件集中进行注册。

  • _off(element, event) :
    element : jQuery或HTMLElement类型,拥有事件监听器的事件目标
    event : String类型, 一个或者多个空间分离时间类型(space-separated event types),可添加可选的命名空间。或者就只是命名空间,比如 "click" 或者 "keydown.myPlugin"

  • getName() getter插件名

  • getEventName(event)
    event : String 或 Array类型,一个或多个空间分离事件类型
    将事件命名空间应用到事件类型上

  • getElement() 获取实例化插件的元素

  • getOptions() 获整合后的配置对象

  • getOption(key)
    key : String类型,配置属性的key
    自定义配置属性的getter方法

  • setOption(key, value)
    key : String类型, 配置属性的key
    value : Mixed类型, 所提供的key值
    用value值重写属性key

  • applyDataAttributes()
    获取提供的配置key,并根据元素的data属性覆盖值它

7.3 Global jQuery event observer / 全局jQuery事件监听

在Shopware5中添加了一个全局事件服务器(global event server),它使得用户能够在jQuery对象上定义全局事件,因此每个插件都能监听以下这些事件:

// 注册事件
$.publish('plugin/some-plugin/onInit', me);

// 监听事件
$.subscribe('plugin/some-plugin/onInit', function() {
    console.log('onInit');
})

// 移除事件监听器
$.unsubscribe('plugin/some-plugin/onInit');```
建议在注册事件监听器时,一直使用命名空间(namespace),能增加精确度,避免误删等情况。举例:

$.subscribe('plugin/some-plugin/onInit.my-plugin', function() {});

// 移除事件监听器
$.unsubscribe('plugin/some-plugin/onInit.my-plugin');```

7.4 The state manager / 状态管理器

状态管理器(state manager)帮助管理不同屏幕大小时,不同的行为。实现了通过断点(breakpoint)设置不同的状态。
断点即预定义的视窗宽度。通过输入断点范围,被注册的事件监听器的enter()函数会被调用。当到达预定义宽度之后,调用exit()函数。
即,已注册的回调函数 在进入 / 结束预定义状态时会被调用。
状态管理器提供了许多协助函数和填充工具,对于管理响应式设计(responsive design)十分有效。

7.5 Use the state manager / 使用状态管理器

在Shopware的前段javascript代码中 状态管理器是是自包含(self-containing)的全局变量。
状态管理器的初始化:

== 状态 XS ==
范围:0 - 479px
适用于:智能手机竖屏

== 状态 S ==
范围:480 - 767px
适用于:智能手机横屏

== 状态 M ==
范围:768 - 1023px
适用于:平板电脑竖屏

== 状态 L ==
范围:1024 - 1259px
适用于:平板电脑横屏,笔记本电脑,台式电脑

== 状态 XL ==
范围:1260 - 5160px
适用于:台式电脑,高分辨率的显示屏

具体代码举例:

window.StateManager.init([
        {
            state: 'xs',
            enter: 0,
            exit: 29.9375   // 479px
        },
        {
            state: 's',
            enter: 30,      // 480px
            exit: 47.9375   // 767px
        },
        {
            state: 'm',
            enter: 48,      // 768px
            exit: 63.9375   // 1023px
        },
        {
            state: 'l',
            enter: 64,      // 1024px
            exit: 78.6875   // 1259px
        },
        {
            state: 'xl',
            enter: 78.75,   // 1260px
            exit: 322.5     // 5160px
        }
    ]);

注意:若默认断点与用户所要求的有出入,可以在window.StateManager.init([ ]);方法中直接修改enterexit的值

7.5.1 Adding an event listner / 添加事件监听器
用状态管理器注册或者移除一个事件监听器,我们通常用javascript来写。
注册事件监听举例:

StateManager.registerListener([{
    state: 'xs',
    enter: function() { console.log('onEnter'); },
    exit: function() { console.log('onExit'); }
}]);

事件监听器的注册支持通配符,所以enter()exit()这两个方法也可以在每个断点都被调用,具体如下:

StateManager.registerListener([{
    state: '*',
    enter: function() { console.log('onEnter'); },
    exit: function() { console.log('onExit'); }
}]);

7.5.2 Register additional breakpoints / 注册额外的断点
默认的断定可以通过StateManager的registerBreakpoint()方法被继承。

注意:额外的断点不能和已存在的完全重叠

StateManager.registerBreakpoint({
    state: 'xxl',
    enter: 78.75  // = 1260px
    exit: 90      // = 1440px
});```

**7.5.3 Class methods / 类的方法**
`init(breakpoints)`
`breakpoints`:Array或Object类型,最初的状态(state)
实例化StateManager,注册预定义的断点,给html元素添加浏览器特定的类(browser specific class),设置特定设备的cookie

`registerBreakpoint(breakpoints)`
`breakpoints`:Array或Object类型,最初的状态(state)
给State Manager注册额外断点

`removeBreakpoint(state)`
`state`:String类型,

`registerListener(listener)`
`listener`:


`addPlugin(selector, pluginName, config, viewport)`
`selector`:
`pluginName`:
`config`:
`viewport`:

`removePlugin(selector, pluginName, viewport)`
`selector`:
`pluginName`:
`viewport`:

`updatePlugin(selector, pluginName)`
`selector`:
`pluginName`:

`destroyPlugin(selector, pluginName)`
`selector`:
`pluginName`:

`getViewportWidth()`

`getViewportHeight()`

`getPreviousState()`

`isPreviousState(state)`
`state`:

`getCurrentState()`

`isCurrentState(state)`
`state`:

`isPortraitMode()`

`isLandscapeMode()`

`getDevicePixelRatio()`

`isBrowser(browser)`
`browser`:

`getScrollBarHeight()`

`matchMedia()`

`requestAnimationFrame()`

`cancelAnimationFrame()`

`getVendorProperty(property, softError)`
`property`:
`softError`:

##7.6 Working with stateful jQuery plugins / jQuery插件的使用
StateManager和jQuery插件基类的合作,简化了特定状态下(state)注册jQuery插件的过程。使组件(component)的行为根据当前state而改变。比如Offcanvas menu插件只在移动端设备(state为xs或s)上显示,在tablet和PC端被隐藏。
state manager的作用域为前端的所有javascript代码。想要注册一个插件,只要调用state manager的`addPlugin()`方法即可。
下面的例子中,我们将自定义的jQuery插件在状态为xs和s下进行注册。

StateManager.addPlugin('.my-selector', 'myPlugin', [ 'xs', 's' ]);```

注意:有些自定义主题的代码中没有初始化StateManager导致上面代码无效,此时需要在addPlugin之前先用init()初始化状态管理器(见7.5 Use the state manager)

7.6.1 Passing a user configuration to the jQuery plugin / 为jQuery插件传递一个用户自定义的配置
用户也可以将自定义的配置传递给plugin,被传递的自定义配置将与插件的默认配置进行融合(该重写的重写,没被重写的保留blablabla...)。被融合(merge)之后的配置可以通过插件中的this.opts对象取得。

// 自定义插件配置
$.plugin('myPlugin', {
    defaults: {
        'speed': 300
    }
});

// 注册插件
StateManager.addPlugin('.my-selector', 'myPlugin', {
    'speed': 2000
}, [ 'xs', 's' ]);```
如果用户需要给插件传递一个修改过的配置,可以参照下面的模式:

StateManager.addPlugin('.my-selector', 'myPlugin', {
'speed': 300
}).addPlugin('.my-selector', 'myPlugin', {
'speed': 2000
}, 's');```

7.7 Adding javascript files to your theme / 在用户主题中添加js文件

将js文件放在/frontend/_public目录下,然后将该路径写在Theme.php中,举例:

/** @var array Defines the files which should be compiled by the javascript compressor */
protected $javascript = array(
    'src/js/jquery.my-plugin.js'
);```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • 原文链接 http://blog.poetries.top/2016/10/20/review-jQuery 关注...
    程序员poetry阅读 16,578评论 18 503
  • (续jQuery基础(1)) 第5章 DOM节点的复制与替换 (1)DOM拷贝clone() 克隆节点是DOM的常...
    凛0_0阅读 1,268评论 0 8
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,300评论 6 344
  • 该文章内容摘自谢宇衡在微信社群中的分享 何为高效学习?1.更加有效的学习;2.学习完后有结果。 原则1:做自己真正...
    仞月psy阅读 548评论 0 3