[TOC]
## 概況
### 背景
### ShowCase

Screenshot
GitHub:?[https://github.com/phodal/congee](https://github.com/phodal/congee)
### CKEditor + Ractive
選用怎樣的前端框架是一個有趣的話題,我需要一個數據綁定和模板。首先,我排除了React這個框架,我覺得他的模板會給我帶來一堆麻煩事。Angluar是一個不錯的選擇,但是考慮Angluar 2.0就放棄了,Backbone也用了那么久。Knockout.js又進入了我的視野,但是后來我發現數據綁定到模板有點難。最后選了Ractive,后來發現果然上手很輕松。
Ractive這個框架比React誕生早了一個月,還是以DOM為核心。Ractive自稱是一個模板驅動UI的庫,在Github上說是下一代的DOM操作。因為Virtual Dom的出現,這個框架并沒有那么流行。
起先,這個框架是在衛報創建的用于產生新聞的應用程序 。有很多工具可以幫助我們構建Web應用程序 ,但是很少會考慮基本的問題:HTML,一個優秀的靜態模板,但是并沒有為交互設計。Ractive可以將一個模板插到DOM中,并且可以動態的改變它。
## 步驟
在創建這個項目的時候,我的迭代過程大致如下:
* 創建hello,world —— 結合不同的幾個框架
* 創建基本的樣式集
* 引用ColorPicker來對顏色進行處理
* 重構代碼
### Step 1: hello,world
下面是一個簡單的hello,world。
~~~
<script id='template' type='text/ractive'>
<p>Hello, {{name}}!</p>
</script>
<script>
var ractive = new Ractive({
template: '#template',
data: { name: 'world' }
});
</script>
~~~
這個hello,world和一般的MVC框架并沒有太大區別,甚至和我們用的Backbone很像。然后,讓我們來看一個事件的例子:
~~~
listView = new Ractive({
el: 'sandboxTitle',
template: listTemplate,
data: {color: config.defaultColor, 'fontSize': config.defaultFontSize}
});
listView.on('changeColor', function (args) {
listView.set('color', args.color);
});
~~~
這是在監聽,意味著你需要在某個地方Fire這個事件:
~~~
titleView.fire('changeColor', {color: color.toHexString()});
~~~
接著,問題來了,這和我們jQuery的on,或者React的handleClick似乎沒有太大的區別。接著Component來了:
~~~
var Grid = Ractive.extend({
isolated: false,
template: parasTemplate,
data: {
}
});
var dataValue = 5;
var category = 'category-3';
var color = config.defaultColor;
parasView = new Ractive({
el: 'parasSanbox',
template: '<Grid Style="{{styles}}" />',
components: {Grid: Grid},
data: {
styles: [
{section_style: 'border: 2px dotted #4caf50; margin: 8px 14px; padding: 10px; border-radius: 14px;', p_style: 'font-size: 14px;', color: color, data_value: dataValue, category: category},
]
}
});
parasView.on('changeColor', function(args) {
parasView.findComponent('Grid').set('Style.*.color', args.color);
});
~~~
上面是在[https://github.com/phodal/congee](https://github.com/phodal/congee)中用到的多個模板的View,他們用了同一個component。
對比和介紹就在這里結束了,我們就可以開始這個項目的實戰了。
### Step 2: Require.js模塊化
同樣的在這里,我們也使用Require.js來作模塊化和依賴管理。我們的項目的配置如下:
~~~
require(['scripts/app', 'ractive', 'scripts/views/titleView', 'scripts/views/hrView', 'scripts/views/parasView', 'scripts/views/followView', 'jquery', 'spectrum'],
function (App, Ractive, TitleView, ParasView, HRView, FollowView, $) {
'use strict';
App.init();
Ractive.DEBUG = false;
var config = App.config;
var titleView = TitleView.init(config);
var hrView = HRView.init(config);
var parasView = ParasView.init(config);
var followView = FollowView.init(config);
App.colorPicker(function (color) {
hrView.fire('changeColor', {color: color.toHexString()});
titleView.fire('changeColor', {color: color.toHexString()});
parasView.fire('changeColor', {color: color.toHexString()});
followView.fire('changeColor', {color: color.toHexString()});
});
$('input#mpName').keyup(function () {
followView.fire('changeName', {mpName: $(this).val()});
});
});
~~~
在那之前,你自然需要先clone代碼。然后在這里我們不同的幾個模塊進行初始化,并且為colorPicker配置了相應的監聽事件。現在,讓我們先到App模塊中,看看我們做了些什么事?
### Step 3: 初始化
初始化模塊一共分為兩部分,一部分是對CKEditor的初始化,一部分則是對colorPicker的初始化。
#### CKEditor初始化
CKEditor自身的編輯器配置比較長,我們就不在這里面列出這些代碼了。
~~~
var init = function () {
/**
* @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
CKEDITOR.editorConfig = function (config) {
// ...
};
var congee = CKEDITOR.replace('congee', {
uiColor: '#fafafa'
});
congee.on('change', function (evt) {
});
congee.on('instanceReady', function (ev) {
$('.tabset8').pwstabs({
effect: 'slideleft',
defaultTab: 1,
tabsPosition: 'vertical',
verticalPosition: 'left'
});
$('#Container').mixItUp().on('click', '.mix', function (event) {
var template = $(event.currentTarget).html();
congee.insertHtml(template);
});
});
$(document).ready(function () {
$('#Container').niceScroll({
mousescrollstep: 40
});
});
~~~
`instanceReady`事件主要就是在編程器初始化后進行的。因此我們在這里初始化了jQuery插件PWS Tabs,以及jQuery插件mixItUp,他們用于進行頁面的排版。
#### ColorPicker初始化
下面的代碼便是對ColorPicker進行初始化,我們設置了幾個常用的顏色放在調色板上。
~~~
var colorPicker = function (changeCB) {
$('#colorpicker').spectrum({
showPaletteOnly: true,
togglePaletteOnly: true,
togglePaletteMoreText: 'more',
togglePaletteLessText: 'less',
color: '#4caf50',
palette: [
['#1abc9c', '#16a085', '#2ecc71', '#27ae60', '#4caf50', '#8bc34a', '#cddc39'],
['#3498db', '#2980b9', '#34495e', '#2c3e50', '#2196f3', '#03a9f4', '#00bcd4', '#009688'],
['#e74c3c', '#c0392b', '#f44336'],
['#e67e22', '#d35400', '#f39c12', '#ff9800', '#ff5722', '#ffc107'],
['#f1c40f', '#ffeb3b'],
['#9b59b6', '#8e44ad', '#9c27b0', '#673ab7', '#e91e63', '#3f51b5'],
['#795548'],
['#9e9e9e', '#607d8b', '#7f8c8d', '#95a5a6', '#bdc3c7'],
['#ecf0f1', 'efefef']
],
change: changeCB
});
};
~~~
而實際上在這里我們已經完成了大部分的工作。
### Step 4: 創建對應的View
在這個項目里,比較麻煩的地方就是使用同樣的顏色來顯示一個模板,如下的代碼是用于顯示水平線的模板:
~~~
{{#hrStyle}}
<div class="mix {{.category}}" data-value={{.data_value}}>
<section {{#section_style}}style="{{.section_style}}"{{/section_style}}>
<p style="{{.p_style}}{{#color}};border-color: {{.color}};{{/color}}"></p>
</section>
</div>
{{/hrStyle}}
~~~
下面的代碼就是對應的View:
~~~
parasView = new Ractive({
el: 'sandboxHr',
template: '<Grid hrStyle="{{styles}}" />',
components: {Grid: Grid},
data: {
styles: [
{section_style: '', p_style: 'background-color: #fff;border-top: 1px solid', color: color, data_value: dataValue, category: category},
{section_style: '', p_style: 'background-color: #fff;border-top: 3px double', color: color, data_value: dataValue, category: category},
{section_style: '', p_style: 'background-color: #fff;border-top: 1px dashed', color: color, data_value: dataValue, category: category},
{section_style: '', p_style: 'background-color: #fff;border-top: 1px dotted', color: color, data_value: dataValue, category: category},
{section_style: '', p_style: 'background-color: #fff;border-top: 2px dashed', color: color, data_value: dataValue, category: category},
{section_style: '', p_style: 'background-color: #fff;border-top: 2px dotted', color: color, data_value: dataValue, category: category},
{section_style: '', p_style: 'background-color: #fff;border-bottom: 1px solid #fff;border-top: 1px solid', color: color, data_value: dataValue, category: category},
{section_style: 'border-top: 1px solid #8c8b8b; border-bottom: 1px solid #fff;', p_style: 'content: "";display: block;margin-top: 2px;border-top: 1px solid #8c8b8b;border-bottom: 1px solid #fff;', data_value: dataValue, category: category},
{section_style: '', p_style: 'height: 6px;background: url(\'styles/images/hr/hr-11.png\') repeat-x 0 0;border: 0;', data_value: dataValue, category: category},
{section_style: '', p_style: 'height: 6px;background: url(\'styles/images/hr/hr-12.png\') repeat-x 0 0;border: 0;', data_value: dataValue, category: category},
{section_style: '', p_style: 'height: 10px;border: 0;box-shadow: 0 10px 10px -10px #8c8b8b inset;', data_value: dataValue, category: category},
{section_style: '', p_style: 'border: 0;height: 1px;background-image: -webkit-linear-gradient(left, #f0f0f0, #8c8b8b, #f0f0f0);background-image: -moz-linear-gradient(left, #f0f0f0, #8c8b8b, #f0f0f0);background-image: -ms-linear-gradient(left, #f0f0f0, #8c8b8b, #f0f0f0);background-image: -o-linear-gradient(left, #f0f0f0, #8c8b8b, #f0f0f0);', data_value: dataValue, category: category}
]
}
});
~~~
我們所做的只是拿出每個不同的布局,再將這些布局顯示到頁面上。最后在值被修改時,改變這其中的值:
~~~
parasView.on('changeColor', function(args) {
parasView.findComponent('Grid').set('hrStyle.*.color', args.color);
});
~~~
### 練習建議