7.1 Introduction / 簡介
當用戶試圖只用Javascript處理響應式網頁的時候无埃,會發(fā)現該過程異常痛苦徙瓶,因為用戶必須根據屏幕大小調整代碼。
==> 因此嫉称,Shopware推出了一個組件 StateManager侦镇,能夠定義新狀態(tài),并觸發(fā)callback函數(callback function)织阅。
另外一方面壳繁,可愛的jQuery插件并不喜歡干這些事。為了簡化這個過程荔棉,Shopware導入了一個插件基類闹炉,它具有jQuery插件開發(fā)的最佳實踐,并能與StateManager完美集成江耀。
7.2 Plugin base class / 插件基類(插件的編寫)
就像上面所提到的那樣剩胁,jQuery插件基類具有jQuery插件開發(fā)的最佳實踐。插件功能一覽:
- 默認配置 + 可通過自定義配置重寫默認配置
- 可通過HTML5的
data
屬性配置插件 - 支持jQuery方法鏈接(jQuery's method chaining)
- 支持事件命名空間(Namespace of events)
- 有內置函數支持刪除事件監(jiān)聽器
- 防止同一元素上的多重實例化
- 檢查元素是否使用某個插件的自定義表達式
- 使用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)
* 可在這里對必要的元素進行緩存觉至,并為插件注冊事件監(jiān)聽器(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"為插件設置一個新的事件監(jiān)聽器(這個"_on"方法其實是在jQuery的"on"方法上添加了一些其他的特性)
* 事件和事件監(jiān)聽器都會在插件特殊事件集中進行注冊挂捻。該集合將自動迭代碉纺,并從元素中刪除已注冊的事件監(jiān)聽器。
* 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" 方法可從插件外部被調用抵卫,或者在被定義的狀態(tài)(state)改變時自動使用StateManager
* 通常你需要刪除那些由插件添加到元素的類狮荔,并將事件監(jiān)聽器也從元素上刪除。
*/
destroy: function() {
var me = this;
me.$el.removeClass(me.opts.activeCls);
/**
* 調用"_destroy"方法:移除所有用"_on"方法在基類插件上注冊的事件監(jiān)聽器介粘。
* 如果想要自行迭代事件監(jiān)聽器殖氏,你可以使用 "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
方法注冊了的事件監(jiān)聽器的集合
7.2.3 Class methods / 類的方法
init()
模板中的方法巴刻,作用類似于插件的構造方法(the constructor of the plugin),可以用于緩存所需的HTML元素蛉签,設置事件監(jiān)聽等等胡陪。destroy()
模板中的方法,用于刪除插件碍舍。通常你會用它刪除你加到某個元素上的類或者事件監(jiān)聽器柠座。當你想要專門對某個特定的狀態(tài)使用插件的時候,該方法很有用片橡。update()
模板中的方法妈经,用于當狀態(tài)發(fā)生改變(進入或離開一個狀態(tài))時更新插件的行為。這個方法只有在你使用了StateManager來初始化插件的時候有用捧书。_destroy()
私人方法吹泡,在插件的_events
屬性中迭代已注冊的事件監(jiān)聽器。此外经瓷,該方法可使用jQuery的removeData()
方法移除插件的內存綁定(inmemory binding)荞胡。并且觸發(fā)一個全局可用的觀察者(observer)。_on(element , event , fn)
:
element
: jQuery或HTMLElement類型了嚎,特殊事件監(jiān)聽器的事件目標(jQuery元素 / DOM節(jié)點)
event
: String類型, 代表要監(jiān)聽的事件
fn
: Function類型廊营, 當特殊類型的事件發(fā)生時歪泳,對象會收到提示(notification)
該方法為jQuery中on()
方法的代理方法,用于連接事件監(jiān)聽器到提供的元素露筒, 并且在_events事件集中進行注冊呐伞。_off(element, event)
:
element
: jQuery或HTMLElement類型,擁有事件監(jiān)聽器的事件目標
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事件監(jiān)聽
在Shopware5中添加了一個全局事件服務器(global event server)级遭,它使得用戶能夠在jQuery對象上定義全局事件望拖,因此每個插件都能監(jiān)聽以下這些事件:
// 注冊事件
$.publish('plugin/some-plugin/onInit', me);
// 監(jiān)聽事件
$.subscribe('plugin/some-plugin/onInit', function() {
console.log('onInit');
})
// 移除事件監(jiān)聽器
$.unsubscribe('plugin/some-plugin/onInit');```
建議在注冊事件監(jiān)聽器時,一直使用命名空間(namespace)挫鸽,能增加精確度说敏,避免誤刪等情況。舉例:
$.subscribe('plugin/some-plugin/onInit.my-plugin', function() {});
// 移除事件監(jiān)聽器
$.unsubscribe('plugin/some-plugin/onInit.my-plugin');```
7.4 The state manager / 狀態(tài)管理器
狀態(tài)管理器(state manager)幫助管理不同屏幕大小時丢郊,不同的行為盔沫。實現了通過斷點(breakpoint)設置不同的狀態(tài)。
斷點即預定義的視窗寬度蚂夕。通過輸入斷點范圍迅诬,被注冊的事件監(jiān)聽器的enter()
函數會被調用。當到達預定義寬度之后婿牍,調用exit()
函數侈贷。
即,已注冊的回調函數 在進入 / 結束預定義狀態(tài)時會被調用等脂。
狀態(tài)管理器提供了許多協(xié)助函數和填充工具俏蛮,對于管理響應式設計(responsive design)十分有效。
7.5 Use the state manager / 使用狀態(tài)管理器
在Shopware的前段javascript代碼中 狀態(tài)管理器是是自包含(self-containing)的全局變量上遥。
狀態(tài)管理器的初始化:
== 狀態(tài) XS ==
范圍:0 - 479px
適用于:智能手機豎屏
== 狀態(tài) S ==
范圍:480 - 767px
適用于:智能手機橫屏
== 狀態(tài) M ==
范圍:768 - 1023px
適用于:平板電腦豎屏
== 狀態(tài) L ==
范圍:1024 - 1259px
適用于:平板電腦橫屏搏屑,筆記本電腦,臺式電腦
== 狀態(tài) 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 / 添加事件監(jiān)聽器
用狀態(tài)管理器注冊或者移除一個事件監(jiān)聽器,我們通常用javascript來寫模软。
注冊事件監(jiān)聽舉例:
StateManager.registerListener([{
state: 'xs',
enter: function() { console.log('onEnter'); },
exit: function() { console.log('onExit'); }
}]);
事件監(jiān)聽器的注冊支持通配符伟骨,所以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類型携狭,最初的狀態(tài)(state)
實例化StateManager,注冊預定義的斷點回俐,給html元素添加瀏覽器特定的類(browser specific class)逛腿,設置特定設備的cookie
`registerBreakpoint(breakpoints)`
`breakpoints`:Array或Object類型稀并,最初的狀態(tài)(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插件基類的合作单默,簡化了特定狀態(tài)下(state)注冊jQuery插件的過程碘举。使組件(component)的行為根據當前state而改變。比如Offcanvas menu插件只在移動端設備(state為xs或s)上顯示雕凹,在tablet和PC端被隱藏殴俱。
state manager的作用域為前端的所有javascript代碼。想要注冊一個插件枚抵,只要調用state manager的`addPlugin()`方法即可线欲。
下面的例子中,我們將自定義的jQuery插件在狀態(tài)為xs和s下進行注冊汽摹。
StateManager.addPlugin('.my-selector', 'myPlugin', [ 'xs', 's' ]);```
注意:有些自定義主題的代碼中沒有初始化StateManager導致上面代碼無效李丰,此時需要在addPlugin之前先用
init()
初始化狀態(tài)管理器(見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'
);```