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值重写属性keyapplyDataAttributes()
获取提供的配置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([ ]);
方法中直接修改enter
和exit
的值
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'
);```