[UPDATE] Famo.us 官方已捨棄此模組,詳情請看官方 Github
見山不是山,大概就是在說這個 View 模組。
View 核心
核心模組 core/View
是用於提供一個或是多個 RenderNode
的容器給框架引擎使用,它本身在輸出的時候並不會產生 DOM 實例,所以你在畫面上其實是看不到 View
這個模組的。
先解釋一下 RenderNode
這個東西,
- 整個框架系統中,只要是產出與畫面有關的模組,都算是一種
RenderNode
- 核心
core/RenderNode
只包含簡單的方法,一切由其他元件自行擴展 - 核心
core/View
就是由RenderNode
擴展而來
View
的擴展包含有,
EventHandler
OptionsManager
RanderNode
那麼,哪一些東西算是 RenderNode
的容器或是會產生 RenderNode
的呢?
以下物件算是 RenderNode
的容器,
core/View
,core/ViewSequence
,views/*
核心與其模組core/Surface
,surfaces/*
核心與其模組wigets/*
模組core/RenderNode
自身或是自行延展的模組
以下物件會產生 RenderNode
core/Context
core/Engine
core/RenderNode
core/Scene
core/View
views/Flipper
views/GridLayout
views/HeaderFooterLayout
views/Lightbox
views/RenderController
**請注意!**上述物件所帶有的 RenderNode
並不是在相同的物件屬性上,舉例來說,你在 core/View
中能透過 _node
取得 RenderNode
物件(核心原件大部份都使用 _node
),但是在 views/Flipper
裡,frontNode
與 backNode
才是 RenderNode
。
所以,如果把 RenderNode
放進不是 RenderNode
的容器裡面會怎樣?
報告學長,沒有畫面
View 核心操作解說
先來看個例子,
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var View = require('famous/core/View');
var mainContext = Engine.createContext();
var view = new View();
var surf = new Surface({
size: [200, 200],
content: "test",
properties: {
backgroundColor: "red"
}
});
view.add(surf);
mainContext.add(view);
});
上述這個例子,你有沒有加入 View
輸出的結果都會一樣。原因在於,Engine
的 createContext
所提供的 Context
模組,自身就會提供 RenderNode
,所以當然有沒有加上 View
差異就不大了。
既然有沒有 View
好像都無差別,那麼使用時機到底在哪裡?
- 當模組必須也只接受
RenderNode
或其容器的時候 - 使用
views/*
其下所有模組的時候 - 轉換、包裝或是擴充模組
- 自行開發的模組
上述頭兩項應該沒有異議,第三點提出來說明一下。或許你會問,包裝子元件我不也可以使用 ContainerSurface
嗎?其實可以(因為 ContainerSurface
也是 RenderNode
(僅止於擴展方式不同
舉例來說,如果需要一個捲軸物件,
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var View = require('famous/core/View');
var ContainerSurface = require('famous/surfaces/ContainerSurface');
var ScrollView = require('famous/views/ScrollView');
var mainContext = Engine.createContext();
var views = [];
var view = new ScrollView();
view.sequenceFrom(views);
for (var i = 0; i < 50; i++) {
var _container = _createContainer(view, i);
views.push(_container);
}
function _createContainer(view, i) {
var surface, view = view, i = i;
surface = new Surface({
size: [100, 100],
content: 'box '+ i,
properties: {
backgroundColor: "red"
}
});
surface.pipe(view);
return surface;
}
mainContext.add(view);
});
我們就單獨看 _createContainer
這個 function,如果改寫成這樣,
function _createContainer(view, i) {
var surface, _view, view = view, i = i;
surface = new Surface({
size: [100, 100],
content: 'box '+ i,
properties: {
backgroundColor: "red"
}
});
surface.pipe(view);
_view = new View();
_view.add(surface);
return _view;
}
他還是可以運作,而且輸出的 HTML 與上述第一個例子完全一樣。或者,我們換成 ContainerSurface
,
function _createContainer(view, i) {
var surface, container, view = view, i = i;
surface = new Surface({
size: [100, 100],
content: 'box '+ i,
properties: {
backgroundColor: "red"
}
});
container = new ContainerSurface({
size: [100, 100]
});
container.pipe(view);
container.add(surface);
return container;
}
畫面結果會跟上述兩個例子完全一樣,但是 HTML 會有不同(請參考 #1 所述關於 ContainerSurface
的部分
那什麼情況不行,例如,你把 View
扔給 Surface
(**注意!**扔給 ContainerSurface
是合法的
// 這樣做,你的畫面會是空的(而且也不會有錯誤訊息
var view = new View();
var surface = new Surface();
surface.add(view);
mainContext.add(surface);
擴展應用
由於 View
自身有 EventHandler
模組,所以在自行開發模組的時候,通常可以擴展這個核心來使用。而由於 View
本身與畫面呈現又有高度相關性,所以直接擴展使用其實是很方便的做法。
以下取自我的 500px 範例其中一段,
// 上略
function PhotosView() {
View.apply(this, arguments);
_createContent.call(this);
this.createPhotoRowView = createPhotoRowView;
}
// 中略
container.add(this.scrollView);
this._add(mod).add(container);
// 下略
自行擴展出一個 PhotosView
模組,自己把 container
加入,最後返回給 Engine
去做最後輸出。View
最大的用意在於,讓開發者自行去擴展所需要的輸出模組,或是做出更複雜(或龐大)的元件控制,最後收斂在 View
這個容器裡面,再輸出給 Engine
處理。