跳到主要内容

模块化开发 TodoList 应用

为什么要模块化开发

在多人协作开发项目时,模块化开发能够让不同开发者编写的代码相互独立,不会相互影响。同时模块化后的代码可以很方便地与其他模块进行组合,产生更多功能。

如何进行模块化设计

在进行模块化设计时,我们通常会遵循以下原则:

方法写在原型对象上,通过原型继承机制进行共享,保持不变。这是因为方法的逻辑一般是固定的。

属性写在构造函数内部。属性是可以由外部传入配置的,属于会改变的部分,所以要写在构造函数中。

下面是一个模块化设计的简单示例

(function () {
function Test(options) {
// 可配置的属性
this.prop = options.prop;
}

Test.prototype = {
// 方法
method1: function () {
// ...
},
method2: function () {
// ...
},
};

// 将模块暴露给全局
window.Test = Test;
})();

// 使用模块
var test = new Test({
prop: 'foo',
});
test.method1();

模块化开发标准

使用 IIFE 分割作用域

我们可以使用立即调用函数表达式(IIFE)来创建独立的作用域,避免污染全局作用域:

var header = (function () {
// header 模块
})();

var footer = (function () {
// footer 模块
})();

IIFE 会形成一个闭包,模块内部的变量和方法都不会影响到外部。

CSS 命名规范

文件命名

对于一些通用的重置样式表,建议使用common.cssglobal.css这样的文件名,不要使用reset.cssnormalize.css,因为它们一般是框架或库中自带的。

常用的通用 CSS 样式

下面是一些常用的通用 CSS 样式:

/* 重置 body 的 margin */
body {
margin: 0;
}

/* 去掉 a 元素的下划线 */
a {
text-decoration: none;
}

/* 重置列表元素的样式 */
ul {
padding: 0;
margin: 0;
list-style: none;
}

/*
对于分组选择器,
建议使用竖排格式书写
*/
h1,
h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
font-weight: bold;
}

CSS 选择器匹配规则

对于形如.a .b .c .d这样的选择器,浏览器的匹配规则是从右到左进行的。

例如.wrap .a .b .c .d .e,匹配时会先找到所有 class 为e的元素,然后再去判断其父元素是否有d类,再判断更上一层是否有c类,以此类推。

所以我们在书写选择器时,应当尽量减少选择器的层级,以提高匹配效率。

面向对象开发 TodoList

下面是使用面向对象的思想开发 TodoList 应用的示例代码

// 待办事项模型
function TodoItem(options) {
this.content = options.content;
this.completed = false;
}

TodoItem.prototype = {
// 切换完成状态
toggle: function () {
this.completed = !this.completed;
},
};

// 待办事项列表视图
function TodoListView(options) {
this.$el = $(options.el);
this.todos = options.todos;

this.initEvents();
this.render();
}

TodoListView.prototype = {
// 初始化事件
initEvents: function () {
this.$el.on('click', '.toggle', this.toggle.bind(this));
this.$el.on('click', '.remove', this.remove.bind(this));
},

// 渲染列表
render: function () {
var html = this.todos
.map(function (todo) {
return `
<li>
<div class="view">
<input class="toggle" type="checkbox" ${todo.completed ? 'checked' : ''}>
<label>${todo.content}</label>
<button class="remove"></button>
</div>
</li>
`;
})
.join('');

this.$el.html(html);
},

// 切换完成状态
toggle: function (e) {
var index = $(e.target).closest('li').index();
this.todos[index].toggle();
this.render();
},

// 删除任务
remove: function (e) {
var index = $(e.target).closest('li').index();
this.todos.splice(index, 1);
this.render();
},
};

// 创建待办事项列表
var todos = [new TodoItem({ content: '学习 JavaScript' }), new TodoItem({ content: '学习 Vue' }), new TodoItem({ content: '整个牛项目' })];

// 创建视图实例
var todoListView = new TodoListView({
el: '#todoapp',
todos: todos,
});