敲碎时间,铸造不朽的个人专栏
上一篇

CKEditor5——Template理解(一)

广告
选中文字可对指定文章内容进行评论啦,→和←可快速切换按钮,绿色背景文字可以点击查看评论额。
大纲

CKEditor5——Template理解

上一节,我们学习并大致理解了CK5的UI中最重要的一个类,那就是View,可以看出这个类其实有点像一个门面,而很多实际的功能包括renderbind等都是委托给Template这个类来实现的,因此,我们今天来靴子这个类。

export default class Template {
	/**
	 * Creates an instance of the {@link ~Template} class.
	 *
	 * @param {module:ui/template~TemplateDefinition} def The definition of the template.
	 */
	constructor( def ) {
		Object.assign( this, normalize( clone( def ) ) );

		/**
		 * Indicates whether this particular Template instance has been
		 * {@link #render rendered}.
		 *
		 * @readonly
		 * @protected
		 * @member {Boolean}
		 */
		this._isRendered = false;

		/**
		 * The tag (`tagName`) of this template, e.g. `div`. It also indicates that the template
		 * renders to an HTML element.
		 *
		 * @member {String} #tag
		 */

		/**
		 * The text of the template. It also indicates that the template renders to a DOM text node.
		 *
		 * @member {Array.<String|module:ui/template~TemplateValueSchema>} #text
		 */

		/**
		 * The attributes of the template, e.g. `{ id: [ 'ck-id' ] }`, corresponding with
		 * the attributes of an HTML element.
		 *
		 * **Note**: This property only makes sense when {@link #tag} is defined.
		 *
		 * @member {Object} #attributes
		 */

		/**
		 * The children of the template. They can be either:
		 * * independent instances of {@link ~Template} (sub–templates),
		 * * native DOM Nodes.
		 *
		 * **Note**: This property only makes sense when {@link #tag} is defined.
		 *
		 * @member {Array.<module:ui/template~Template|Node>} #children
		 */

		/**
		 * The DOM event listeners of the template.
		 *
		 * @member {Object} #eventListeners
		 */

		/**
		 * The data used by the {@link #revert} method to restore a node to its original state.
		 *
		 * See: {@link #apply}.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/template~RenderData}
		 */
		this._revertData = null;
	}
}

我们首先看构造函数,这个函数实际上就是将一些属性复制到当前模板对象,因此,我们看看具体的方法:

我们先看看clone方法

function clone( def ) {
	const clone = cloneDeepWith( def, value => {
		// Don't clone the `Template.bind`* bindings because of the references to Observable
		// and DomEmitterMixin instances inside, which would also be traversed and cloned by greedy
		// cloneDeepWith algorithm. There's no point in cloning Observable/DomEmitterMixins
		// along with the definition.
		//
		// Don't clone Template instances if provided as a child. They're simply #render()ed
		// and nothing should interfere.
		//
		// Also don't clone View instances if provided as a child of the Template. The template
		// instance will be extracted from the View during the normalization and there's no need
		// to clone it.
		if ( value && ( value instanceof TemplateBinding || isTemplate( value ) || isView( value ) || isViewCollection( value ) ) ) {
			return value;
		}
	} );

	return clone;
}

看看这个方法可以知道,并不是所有的属性都需要拷贝,如果遇上Template.bind,View等都直接返回原始的引用,而不用再拷贝一份新的实例。主要有四类属性不用拷贝,至于为什么?大家可以思考一下,我们后文再分析。

拷贝完成之后,我们需要归一化,也就是这个方法:normalize()

function normalize( def ) {
	if ( typeof def == 'string' ) {
		def = normalizePlainTextDefinition( def );
	} else if ( def.text ) {
		normalizeTextDefinition( def );
	}

	if ( def.on ) {
		def.eventListeners = normalizeListeners( def.on );

		// Template mixes EmitterMixin, so delete #on to avoid collision.
		delete def.on;
	}

	if ( !def.text ) {
		if ( def.attributes ) {
			normalizeAttributes( def.attributes );
		}

		const children = [];

		if ( def.children ) {
			if ( isViewCollection( def.children ) ) {
				children.push( def.children );
			} else {
				for ( const child of def.children ) {
					if ( isTemplate( child ) || isView( child ) || isNode( child ) ) {
						children.push( child );
					} else {
						children.push( new Template( child ) );
					}
				}
			}
		}

		def.children = children;
	}

	return def;
}

从这个归一化的方法,可以看出对模板的def处理主要分成几类:

1、如果def是一个字符该怎么处理,实际上就是作为一个文本出行返回

function normalizePlainTextDefinition( def ) {
	return {
		text: [ def ]
	};
}
// "foo" ---> { text: [ 'foo' ] }

 

2、如果def是一个对象,且存在一个text属性,那么处理方法如下

function normalizeTextDefinition( def ) {
	def.text = toArray( def.text );
}
// children: [
//			{ text: 'def' },
//			{ text: {@link module:ui/template~TemplateBinding} }
//]

// become 
children: [
//			{ text: [ 'def' ] },
//			{ text: [ {@link module:ui/template~TemplateBinding} ] }
//		]

实际上就是将def的text属性的值转化为数组

3、如果def是一个对象,且存在一个on属性,那么处理如下:

// Normalizes "on" section of {@link module:ui/template~TemplateDefinition}.
//
//		on: {
//			a: 'bar',
//			b: {@link module:ui/template~TemplateBinding},
//			c: [ {@link module:ui/template~TemplateBinding}, () => { ... } ]
//		}
//
// becomes
//
//		on: {
//			a: [ 'bar' ],
//			b: [ {@link module:ui/template~TemplateBinding} ],
//			c: [ {@link module:ui/template~TemplateBinding}, () => { ... } ]
//		}
function normalizeListeners( listeners ) {
	for ( const l in listeners ) {
		arrayify( listeners, l );
	}

	return listeners;
}
function arrayify( obj, key ) {
	obj[ key ] = toArray( obj[ key ] );
}

可以看出实际上就是将对象listeners的值转化成数组,同时返回这个对象,并添加到def属性上。

4、如果def.text不存在,那么就是处理def的属性和子节点

// Normalizes "attributes" section of {@link module:ui/template~TemplateDefinition}.
//
//		attributes: {
//			a: 'bar',
//			b: {@link module:ui/template~TemplateBinding},
//			c: {
//				value: 'bar'
//			}
//		}
//
// becomes
//
//		attributes: {
//			a: [ 'bar' ],
//			b: [ {@link module:ui/template~TemplateBinding} ],
//			c: {
//				value: [ 'bar' ]
//			}
//		}
function normalizeAttributes( attributes ) {
	for ( const a in attributes ) {
		if ( attributes[ a ].value ) {
			attributes[ a ].value = toArray( attributes[ a ].value );
		}

		arrayify( attributes, a );
	}
}

这里的处理就是将属性的值转化为数组,处理子节点就是将子节点打开放到一个数组当中。

归一化之后,将这些属性复制到this对象,然后设置一些基本的值之后就算完成构造函数。让复制完成之后,我们知道当然的this对象可能有的属性包括,注意,为什么归一化方法没有处理tag属性

tag属性,attributes属性,children属性,text属性,eventListeners属性这些属性会成为我们后文分析的重点,一定要掌握这些属性的作用。另外值得一提的是还有一个this._revertData属性,这个属性暂时没有用到,因此可以略过,后文分析方法的时候会介绍。最后一个就是模板是否渲染的boolean属性,用于判断是否已经模板渲染到dom。

 

版权声明:著作权归作者所有。

X

欢迎加群学习交流

联系我们