1. 程式人生 > >jQuery 版本購物車(融入設計模式)

jQuery 版本購物車(融入設計模式)

jQuery 版本購物車(融入設計模式)

1. 介紹

1.1 功能

  • 顯示購物列表、加入購物車、從購物車刪除

1.2 用到的設計模式

  • 工廠模式
    • $('XXX'),建立商品
  • 單例模式
    • 購物車
  • 裝飾器模式
    • 打點統計
  • 觀察者模式
    • 網頁事件、Promise
  • 狀態模式
    • 新增/刪除購物車
  • 模板方法模式
    • init(),統一的方法渲染
  • 代理模式
    • 打折商品資訊處理

1.3 UML 演示

在這裡插入圖片描述

2. 程式碼演示

2.1 程式碼目錄

在這裡插入圖片描述

2.2 api

  • list.json
    [
      {
        id: 1,
        name: '《JS高階程式設計》',
        price: 87,
        discount: 1
      },
      {
        id: 2,
        name: '《新零售》',
        price: 42,
        discount: 1
      },
      {
        id: 3,
        name: '《幹法》',
        price: 56,
        discount: 0
      },
      {
        id:
    4, name: '《劍指offer》', price: 32, discount: 0 } ]

2.3 config

  • config.js
    export const GET_LIST = '../api/list.json'
    

2.4 List

  • CreateItem.js

    import Item from './Item.js'
    
    //優惠商品處理邏輯
    function createDiscount(itemData) {
      //代理模式
      return new Proxy(itemData, {
        get: function
    (target, key, receicer) { if (key === 'name') { return `${target[key]}【折扣】` } if (key === 'price') { return Math.floor(target[key] * 0.8 * 100) / 100 } 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 getCart from '../ShoppingCart/GetCart.js'
    import StateMachine from 'javascript-state-machine'
    import { log } from '../util/log'
    
    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()
      }
    }
    
  • List.js

    import $ from 'jquery'
    import { GET_LIST } from '../config/config.js'
    import createItem from './CreateItem.js'
    
    export default class List {
      constructor(app) {
        this.app = app
        this.$el = $('<div>')
      }
    
      //獲取資料
      loadData() {
        //觀察者模式
        //返回Promise例項
        return fetch(GET_LIST).then(result => {
          return result.json()
        })
      }
      //生成列表
      initItemList(data) {
        data.forEach(itemData => {
          let item = createItem(this, itemData)
          item.init()
        })
      }
      //渲染
      render() {
        this.app.$el.append(this.$el)
      }
    
      init() {
        //觀察者模式
        this.loadData()
          .then(data => {
            this.initItemList(data)
          })
          .then(() => {
            //渲染
            this.render()
          })
      }
    }
    

2.5 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'
    
    export default class ShoppingCart {
      constructor(app) {
        this.$el = $('<div>').css({
          'padding-bottom': '10px',
          'border-bottom': '1px solid #ccc'
        })
        this.app = app
        this.cart = getCart()
      }
      init() {
        this.initBtn()
        this.render()
      }
      initBtn() {
        let $btn = $('<button>購物車</button>')
        $btn.click(() => {
          this.showCart()
        })
        this.$el.append($btn)
      }
    
      showCart() {
        alert(this.cart.getList())
      }
    
      render() {
        this.app.$el.append(this.$el)
      }
    }
    

2.6 util

  • log.js
    //裝飾器模式
    export function log(type) {
      return function(target, name, descriptor) {
        let oldValue = descriptor.value
        descriptor.value = function() {
          //列印日誌
          console.log('日誌上報${type}')
          //執行原有的方法
          return oldValue.apply(this, arguments)
        }
        return descriptor
      }
    }
    

2.7 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()
  }
}

2.8 index.js

import App from './demo/App.js'

let app = new App('app')
app.init()

2.9 index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>購物車</title>
  </head>

  <body>
    <div id="app"></div>

    <script type="text/javascript"></script>
  </body>
</html>

2.10 webpack.dev.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {
    path: __dirname,
    filename: './release/bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js?$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html'
    })
  ],
  devServer: {
    contentBase: path.join(__dirname, './release'), //根目錄
    open: true, //自動開啟瀏覽器
    port: 9000,
    proxy: {
      '/api/*': {
        target: 'http://localhost:8880'
      }
    }
  }
}