

JavaScript 模块化探索 - vivaxy
source link: https://vivaxyblog.github.io/2015/04/06/object-oriented-module-view-in-javascript.html?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

JavaScript 模块化探索
前端模块化的实现意味着要将一个模块中的数据,逻辑和渲染合并在一个单元中,往往这个单元是一个 JS 文件。本文采用的方法也是如此。
React.js 的实现
React.js 采用单向的数据流:通过 mudule 层上的数据更新,触发 view 层的更新。使用 state
保存数据状态。React.js 会算出那些 view 需要重新渲染,然后再做 DOM 上的重绘。
crystal 的实现
采用 Knockout.js 作为 MVVM,使用 browserify 作为模块管理器。页面状态由 hash 记录。html 方式书写 view,<script></script>
中书写 module,然后编译成 JS 文件,模块化加载到应用中。
原生 JS 的实现
基于事件机制的 on
和 fire
方法传递数据,JS 的渲染方式实现 view,原型链方式继承。
基础类:Base
/*
* @author: vivaxy
* @date: 2015-04-06 15:52:47
* @last modified by: vivaxy
* @last modified time: 2015-04-06 17:35:10
*/
'use strict';
var Base = function () {
},
p = {};
Base.prototype = p;
p.on = function (event, callback) {
if (!this.hasOwnProperty('events')) throw new Error('`events` not defined in object');
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
return this;
};
p.off = function (event, callback) {
if (!this.hasOwnProperty('events')) throw new Error('`events` not defined in object');
var callbacks = this.events[event];
if (callbacks && callback) {
callbacks = callbacks.filter(function (cb) {
return cb !== callback;
});
} else {
callbacks = [];
}
return this;
};
p.fire = function (event) {
if (!this.hasOwnProperty('events')) throw new Error('`events` not defined in object');
var callbacks = this.events[event],
_this = this,
_arguments = arguments;
if (callbacks) {
callbacks.forEach(function (callback) {
callback.apply(_this, Array.prototype.slice.call(_arguments, 1));
});
}
return this;
};
p.render = function () {
if (!this.hasOwnProperty('container')) throw new Error('`container` not defined in object');
if (!this.hasOwnProperty('data')) throw new Error('`data` not defined in object');
this.container.appendChild(this.template(this.data));
return this;
};
p.template = function () {
if (!this.hasOwnProperty('data')) throw new Error('`data` not defined in object');
var fragment = document.createDocumentFragment();
return fragment;
};
p.update = function (data) {
if (!this.hasOwnProperty('data')) throw new Error('`data` not defined in object');
this.data = data;
var _this = this;
Array.prototype.slice.call(this.container.children).forEach(function (child) {
_this.container.removeChild(child);
});
this.render();
return this;
};
包含 on
, off
, fire
的事件方法,和 render
, template
, update
的渲染方法。
其中事件方法需要用到 module 中的 events
对象,而渲染方法需要用到 module 中的 container
, data
对象。
组件类 Module
var Module = function(options){
this.events = {};
this.data = options.data;
this.container = options.container;
this.render();
},
p = new Base();
Module.prototype = p;
// @Override
p.template = function(){
// ...
};
继承了基础类的中的事件方法和渲染方法,同时可以在原型上重写两类方法中的具体方法。
组件需要定义 this.events = {}
, this.data
, this.container
以使用基础类中的方法。
组件需要调用 this.render()
实现渲染。
组件中可以包含其他自定义方法,只需要在原型 p
上添加对象即可。
var module = new Module({});
module.render
将寻找 module
构造函数上的 render
方法;如果没有找到,会寻找 module
原型链上的 render
方法,由于 module
原型链是基础类的实体 base
,所以相当于在 base
中找字面量 render
;如果没有找到,会寻找 base
原型链上的 render
方法。这样继承能最大化 JS 原型链的使用,复用相同的函数,以减少内存开销;同时,还保证了方法重写的可能性,提高了方便性,可以在继承的对象中的任意一个构造函数上重写。
可以根据不同试用场景修改 render
方法的具体实现方式,如:innerHTML
, appendChild
, jQuery 的 html
等。
可以根据更改内容的影响范围修改 update
方法,实现局部渲染,以减少浏览器重绘,提高性能。
父级对象中使用 new
方式构造子对象,并且可以在子对象上使用 on
方法添加事件监听。
子对象内采用底层的事件监听方式,如 addEventListener
, jQuery的 on
等方法监听用户输入等触发事件后,执行 fire
方法,就可以把响应数据传递到父级对象中。
子对象在任何过程中都可以执行 fire
方法,并传递数据。
off
可以去掉采用函数名绑定的事件。
父级对象将拿到的数据用 options
的方式在 new
子对象的时候传入。
子对象将完整数据,或者变化的数据用 fire
的形式传递到父级对象中。
如果修改的是用一个对象,则在任何时候拿到的数据都是完整的,并且实时的。子对象内的修改数据操作会反馈到父级对象中!
例子
© Copyright 2011 ~ 2020 by vivaxy
</div
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK