1. 类图
image.png2. 用到的设计模式
◆工厂模式 单例模式
◆装饰器模式 观察者模式
◆状态模式 模板方法模式 代理模式
3. 目录结构
├── src
| ├── api // 购物车列表接口
| ├── list.json
| ├── config // 配置文件
| ├── config.js
| ├── Item // 列表中的每一项
| ├── CreateItem.js
| └── Item.js
| ├── List // 商品列表
| ├── List.js
| ├── ShoppingCart // 购物车
| ├── GetCart.js
| └── ShoppingCart.js
| ├── util
| ├── logs.js // 日志
| ├── App.js
| ├── index.js // 入口
| ├── index.html
| ├── webpack.config.js
http-server -p 8080
4. 各模块用到的设计模式
◆工厂模式:$('xxx'),创建商品
◆单例模式:购物车
◆装饰器模式:打点统计
◆观察者模式:网页事件,Promise
◆状态模式:添加到购物车&从购物车删除
◆模板方法模式:渲染有统一的方法,内部包含了各模块渲染
◆代理模式:打折商品信息处理
5. 代码实现
index.html
<body>
<div id="app"></div>
</body>
index.js(入口)
import App from './demo/App.js'
let app = new App('app')
app.init() // 初始化
App.js
import $ from 'jquery'
import ShoppingCart from './ShoppingCart/ShoppingCart.js'
import List from './List/List.js'
export default class App {
constructor(id) {
this.$el = $(`#${id}`)
}
// 初始化购物车
initShoppingCart() {
let shoppingCart = new ShoppingCart(this)
shoppingCart.init()
}
// 初始化商品列表
initList() {
let list = new List(this)
list.init()
}
init() { // 初始化
this.initShoppingCart()
this.initList()
}
}
List.js(商品列表)
import $ from 'jquery'
import createItem from '../Item/CreateItem.js'
import { GET_LIST } from '../config/config.js'
export default class List {
constructor(app) {
this.app = app
this.$el = $('<div>')
}
// 获取数据
loadData() {
// 使用 fetch (低版本浏览器可使用 https://github.com/github/fetch 兼容)
// 返回 promise
return fetch(GET_LIST).then(result => {
return result.json()
})
}
// 生成列表
initItemList(data) {
data.map(itemData => {
let item = createItem(this, itemData)
item.init()
return item
})
}
// 渲染
render() {
this.app.$el.append(this.$el)
}
init() { // 初始化列表
this.loadData().then(data => {
this.initItemList(data)
}).then(() => {
// 最后再一起渲染 DOM ,以避免重复渲染的性能问题
this.render()
})
}
}
Item(列表中的每一项)
// CreateItem.js
import Item from './Item.js'
function createDiscount(item) {
// 用代理做折扣显示
return new Proxy(item, {
get: function (target, key, receiver) {
if (key === 'name') {
return `${target[key]}【折扣】`
}
if (key === 'price') {
return target[key] * 0.8
}
return target[key]
}
})
}
// 工厂函数
export default function (list, itemData) {
if (itemData.discount) {
itemData = createDiscount(itemData)
}
return new Item(list, itemData)
}
// Item.js
import $ from 'jquery'
import StateMachine from 'javascript-state-machine'
import { log } from '../util/log.js'
import getCart from '../ShoppingCart/GetCart.js'
export default class Item {
constructor(list, data) {
this.list = list
this.data = data
this.$el = $('<div>')
this.cart = getCart()
}
initContent() { // 列表内容
let $el = this.$el
let data = this.data
$el.append($(`<p>名称:${data.name}</p>`))
$el.append($(`<p>价格:${data.price}</p>`))
}
initBtn() { // 按钮
let $el = this.$el
let $btn = $('<button>')
// 状态管理
let _this = this
let fsm = new StateMachine({
init: '加入购物车',
transitions: [
{
name: 'addToCart',
from: '加入购物车',
to: '从购物车删除'
},
{
name: 'deleteFromCart',
from: '从购物车删除',
to: '加入购物车'
}
],
methods: {
// 加入购物车
onAddToCart: function () {
_this.addToCartHandle()
updateText()
},
// 删除
onDeleteFromCart: function () {
_this.deleteFromCartHandle()
updateText()
}
}
})
function updateText() {
$btn.text(fsm.state)
}
$btn.click(() => {
if (fsm.is('加入购物车')) {
fsm.addToCart()
} else {
fsm.deleteFromCart()
}
})
updateText()
$el.append($btn)
}
// 加入购物车
@log('add') // 日志
addToCartHandle() {
this.cart.add(this.data)
}
// 从购物车删除
@log('del') // 日志
deleteFromCartHandle() {
this.cart.del(this.data.id)
}
render() {
this.list.$el.append(this.$el)
}
init() {
this.initContent()
this.initBtn()
this.render()
}
}
ShoppingCart(购物车)
// GetCart.js
class Cart {
constructor() {
this.list = []
}
add(data) { // 添加
this.list.push(data)
}
del(id) { // 删除
this.list = this.list.filter(item => {
if (item.id === id) {
return false
}
return true
})
}
getList() { // 获取购物车列表
return this.list.map(item => {
return item.name
}).join('\n')
}
}
// 返回单例
let getCart = (function () {
let cart
return function () {
if (!cart) {
cart = new Cart();
}
return cart
}
})()
export default getCart
// ShoppingCart.js
import $ from 'jquery'
import getCart from './GetCart.js'
export default class ShoppingCart {
constructor(app) {
this.app = app
this.$el = $('<div>').css({
'padding-bottom': '10px',
'border-bottom': '1px solid #ccc'
})
this.cart = getCart()
}
// 显示购物车内容
showCart() {
alert(this.cart.getList())
}
// 初始化按钮
initBtn() {
let $btn = $('<button>购物车</button>')
$btn.click(() => {
this.showCart()
})
this.$el.append($btn)
}
// 渲染
render() {
this.app.$el.append(this.$el)
}
init() {
this.initBtn()
this.render()
}
}
log.js(日志)
export function log(type) {
return function (target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function() {
// 此处统一上报日志
console.log(`日志上报 ${type}`);
// 执行原有方法
return oldValue.apply(this, arguments);
};
return descriptor;
}
}
config.js(配置文件)
export const GET_LIST = '/api/list.json'
list.json(列表接口)
[
{
"id": 1,
"name": "《JS 基础面试题》",
"price": 149,
"discount": 1
},
{
"id": 2,
"name": "《JS 高级面试题》",
"price": 366,
"discount": 1
},
{
"id": 3,
"name": "《React 模拟大众点评 webapp》",
"price": 248,
"discount": 0
},
{
"id": 4,
"name": "《zepto 设计与源码解读》",
"price": 0,
"discount": 0
}
]
网友评论