基于框架开发 Todo App
- 继承 Model 模块
- 实现 Controller 业务逻辑
- 编写自定义 View
- 业务整合与调试
开发环境搭建
1.创建开发目录
cd ~/Desktop && mkdir todoApp
2.进入开发目录并初始化NPM包管理
cd todoApp && npm init
3.下载依赖包
rollup
npm install --save-dev rollup
babel ES6转译
npm install --save-dev babel-core babel-preset-env
npm install --save-dev rollup-plugin-babel babel-plugin-external-helpers
uglify 压缩
npm install --save-dev rollup-plugin-uglify
env 环境变量
npm install --save-dev cross-env
4.配置 .babelrc
touch .babelrc && vim .babelrc
{
"presets": [
[
"env",
{
"modules": false
}
]
],
"plugins": [
"external-helpers"
]
}
5.rollup.config.js
touch rollup.config.js && vim rollup.config.js
import babel from 'rollup-plugin-babel'
import { uglify } from 'rollup-plugin-uglify'
const plugins = [babel()]
if (process.env.NODE_ENV === 'production') plugins.push(uglify())
export default({
entry: 'src/app/index.js',
dest: 'dist/bundle.js',
format: 'iife',
moduleName: 'todoApp',
plugins
})
6.配置 package.script
{
"scripts": {
"dev": "cross-env NODE_ENV=dev rollup -c --watch",
"build": "cross-env NODE_ENV=production rollup -c"
}
}
7.创建开发入口文件
src/app/index.js
let str = 'ok';
console.log(str);
8.创建测试文件
examples/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>todo MVC Demo</title>
</head>
<body>
<div id="app"></div>
<script src="../dist/bundle.js"></script>
</body>
</html>
9.运行开发打包命名
运行后查看输出文件,并打开测试文件查看效果。
npm run dev
创建基类对象
公共的 Model 和 Controller,上述章节,已经具体描述过其实现的原理。现在就要引入使用啦!
src/framework/index.js
export class Model {
constructor (data) {
this.data = data
this.subscribers = []
}
publish (data) {
this.subscribers.forEach(callback => callback(data))
}
}
export class Controller {
constructor (conf) {
this.el = document.querySelector(conf.el)
this.model = conf.model
this.view = conf.view
this.render = this.render.bind(this)
this.el.addEventListener('click', (e) => {
e.stopPropagation()
const rules = Object.keys(conf.onClick || {})
rules.forEach((rule) => {
if (e.path[0].matches(rule)) conf.onClick[rule].call(this, e)
})
})
}
getTargetAttr (e, attr) {
return e.path[0].getAttribute(attr)
}
getChild (selector) {
return this.el.querySelector(selector)
}
render () {
this.el.innerHTML = this.view(this.model)
}
}
创建 View 视图
src/app/view.js
export function TodoView ({ todos }) {
const todosList = todos.map(todo => `
<div>
<span>${todo.text}</span>
<button data-id="${todo.id}" class="btn-delete">
Delete
</button>
<span>
<input data-id="${todo.id}"/>
<button data-id="${todo.id}" class="btn-update">
Update
</button>
</span>
</div>
`).join('')
return (`
<main>
<input class="input-add"/>
<button class="btn-add">Add</button>
<div>${todosList}</div>
</main>
`)
}
创建 TodoModel 数据模型
TodoModel 继承于基类的 Model,绑定 todos 的数据变化
src/app/model.js
import { Model } from '../framework/index'
export class TodoModel extends Model {
constructor () {
super({ todos: [] })
}
get todos () {
return this.data.todos
}
set todos (todos) {
this.data.todos = todos
this.publish(todos)
}
}
创建 TodoController 控制器
TodoController 继承于基类的 Controller,并绑定视觉图的事件绑定及数据变化逻辑
- 新增数据
- 修改数据
- 删除数据
src/app/controller.js
import { Controller } from '../framework/index'
export class TodoController extends Controller {
constructor (model, view) {
super({
model,
view,
el: '#app',
onClick: {
'.btn-add' () {
// 新增 Todo 时对数据全量赋值
this.model.todos = this.model.todos.concat([{
id: new Date().getTime().toString(),
// 使用 getter 获取绑定至 DOM 元素的数据
text: this.addInputText
}])
},
'.btn-delete' (e) {
const id = this.getTargetAttr(e, 'data-id')
// 根据 id 过滤掉待删除元素
this.model.todos = this.model.todos.filter(
todo => todo.id !== id
)
},
'.btn-update' (e) {
const id = this.getTargetAttr(e, 'data-id')
const text = this.getUpdateText(id)
// 根据 id 更新元素
this.model.todos = this.model.todos.map(
todo => ({
id: todo.id,
text: todo.id === id ? text : todo.text
})
)
}
}
})
// 订阅 Model 更新事件
this.model.subscribers.push(this.render)
}
getUpdateText (id) {
return super.getChild(`input[data-id="${id}"]`).value
}
get addInputText () {
return super.getChild('.input-add').value
}
}
引用
在入口文件中引用各个自定义实例对象
src/app/index.js
import { TodoModel } from './model'
import { TodoController } from './controller'
import { TodoView as view } from './view'
const model = new TodoModel()
const controller = new TodoController(model, view)
controller.render()
打包
使用生产环境压缩打包
npm run build