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元素中调用插件:
         *    
...
* "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'
);```

你可能感兴趣的:(7. jQuery plugins & the stateManager / jQuery插件基类与StateManager的完美配合)