Skip to content

Instantly share code, notes, and snippets.

@xgqfrms-GitHub
Last active June 28, 2017 06:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xgqfrms-GitHub/a5dd596353c8aaf201a27413ca2b93e3 to your computer and use it in GitHub Desktop.
Save xgqfrms-GitHub/a5dd596353c8aaf201a27413ca2b93e3 to your computer and use it in GitHub Desktop.
Virtual DOM 算法

Virtual DOM 算法

building-yourself-virtual-dom

算法实现

  1. 步骤一:用JS对象模拟DOM树
  2. 步骤二:比较两棵虚拟DOM树的差异
  3. 步骤三:把差异应用到真正的DOM树上

livoras/blog#13

https://github.com/occultskyrong/blog/blob/master/_demo/virtual_dom/index.js

    
//todo
/**
 * created by xgqfrms on 2017/06/28.
 * @version 1.0.0 
 */
"use strict";

// import json from './data.json';

const datas = (() => {
    // fetch data
    const url = `./data.json`;
    fetch(url)
    .then((response) => response.json())
    .then((json)=> {
        console.log(`json, ${json}`);
        return datas = json;
    })
})();


/* globals svd */

// 模拟商品数据
let GOODS = datas;

let el = svd.el;                // 创建虚拟元素
// let diff = svd.diff;            // 比对区别
// let patch = svd.patch;          // 更新不同,打补丁
let Root, Tree;// Dom树和虚拟树


// 价格计算
let Calc = (function () {
    // 合计价格数据
    let _;
    // 总计价格的重置
    let reset = function () {
        _ = {
            sum: 0,       // 商品总价
            activity: 0,    // 活动减价
            dd: 0        // 商品达豆总价
        };
    };
    // 金额条显示
    let render = function () {
        return el('div', {
            class: 'col-sm-12',
            style: 'text-align:right;'
        }, ['合计金额 ¥' + _.sum + ' 元']);
    };
    // 获取商品价格
    let getPrice = function (g) {
        return g.price;
    };
    // 商品类
    let Good = function (g) {
        this.price = Calc.getPrice(g);
        this.sum = g.quantity * this.price;
        this.dd = 'dadou' in g && g.dadou || 0;
        this.dc = this.dd * g.quantity;
        // 合计价格、达豆
        _.sum = parseFloat(_.sum) + this.sum;
        _.dc = parseInt(_.dc) + this.dc; //dadouCount
    };
    return {
        getPrice: getPrice
        , render: render
        , reset: reset
        , Good: Good
    };
}());

// Dom操作
let Dom = (function () {
    // 活动列表
    let _activityList = {}
        , _goodDomArray = []; // 非活动商品dom数组
    // 拼装商品
    let setGood = function (good) {
        let _opt = {class: 'col-sm-12 form-group good-group', 'data-gid': good.gid}
            , _g = new Calc.Good(good);
        if (good.quantity <= 0) { //fixme 删除商品,则隐藏,否则有个侦听的bug
            _opt['style'] = 'display:none;';
        }
        return el('div', _opt, [
            el('div', {class: 'col-sm-5'}, [good.name])
            , el('div', {class: 'col-sm-2'}, ['¥' + _g.price]
                + (_g.dd ? ' ( ' + _g.dd + '达豆 ) ' : '')
            )
            , el('div', {class: 'col-sm-3 input-group'}, [
                el('div', {class: 'input-group-addon good-sub', 'data-gid': good.gid}, ['-'])
                , el('input', {
                    class: 'form-control good-quantity',
                    type: 'text',
                    value: good.quantity,
                    style: 'width:3em;'
                })
                , el('div', {class: 'input-group-addon good-add'}, ['+'])
            ])
            , el('div', {class: 'col-sm-2'}, ['¥' + _g.sum]
                + (_g.dc ? ' ( ' + _g.dc + '达豆 ) ' : '')
            )
        ]);
    };
    // 重置活动信息
    let resetActivity = function () {
        _activityList = {};
        _goodDomArray = [];
    };
    // 写入活动信息
    let setActivity = function (good) {
        if ('activity' in good && good.activity) {
            let _act = good.activity
                , _aid = _act.aid
                , _good = new Calc.Good(good);
            if (_aid in _activityList) {// 当前活动已存在
                let __act = _activityList[_aid];
                __act.sum = parseFloat(__act.sum) + _good.sum;
                __act.goods.push(setGood(good));
            } else {
                _activityList[_aid] = {
                    sum: _good.sum
                    , baseLine: good.activity.baseLine
                    , off: good.activity.off
                    , goods: [setGood(good)]
                };
            }
        } else { // 非活动商品列表
            _goodDomArray.push(setGood(good));
        }
    };
    // 拼接活动信息
    let getActivity = function () {
        let _act = _activityList
            , _ = [];
        for (let a in _act) {
            if (_act.hasOwnProperty(a)) {
                let __act = _act[a], __dom;
                if (__act.sum >= __act.baseLine) {
                    __dom = ' 已满 ¥' + __act.baseLine + ' 元,减 ¥' + __act.off;
                } else if (__act.sum <= 0) {// 该活动总价为0
                    continue;//fixme   
                } else {
                    __dom = ' 还差 ¥' + (__act.baseLine - __act.sum) + ' 元满 ' + __act.baseLine;
                }
                _.push(el('div', {
                    class: 'col-sm-12 form-group activity-group',
                    'data-aid': a
                }, [__dom]));
                _ = _.concat(__act.goods);
            }
        }
        return _;
    };
    // 拼装商品列表
    let setList = function () {
        let _gld = [];//商品列表dom数组
        resetActivity();
        for (let g in GOODS) {
            if (GOODS.hasOwnProperty(g)) {
                let good = GOODS[g];
                setActivity(good);
            }
        }
        _gld = _gld.concat(getActivity());
        _gld = _gld.concat(_goodDomArray);
        _gld.push(Calc.render());
        return el('div', {
            class: 'col-sm-12 form-inline',
            style: 'margin:10px;border:1px #f00 dashed;padding:10px;'
        }, _gld);
    };
    return {
        set: setList
    };
}());
// 侦听
let Listener = (function () {
    // 内容拼接
    let container = function () {
        if (Tree) {                                 // 虚拟树存在则更新补丁
            let newTree = Dom.set()                     // 生成新的虚拟树
                , patches = svd.diff(Tree, newTree);    // 与已有虚拟树对比
            svd.patch(Root, patches);                   // 打补丁到原有Dom树中
            Tree = newTree;                             // 更新虚拟树
        } else {                                    // 无dom时构造
            Tree = Dom.set();                           // 构造虚拟树
            Root = Tree.render();                       // 将虚拟树构造为Dom树
            $('#cart_content').html(Root);              // 将Dom树写入html
        }
    };
    // 事件侦听
    let setListener = function () {
        let changeQuantity = function (e, qua) {
            let gid = $(e).parents('.good-group').data('gid')
                , good = GOODS[gid];
            good.quantity = parseInt(good.quantity) + qua;
            Calc.reset();
            container();
        };
        // fixme 监听事件错误
        $('#cart_content')
            .on('click', '.good-add', function () {// 增加数量
                changeQuantity($(this), 1);
            })
            .on('click', '.good-sub', function () {// 减少数量
                changeQuantity($(this), -1);
            });
    };
    // 初始化
    let init = function () {
        Calc.reset();
        container();
        setListener();
    };
    return {
        init: init
    };
}());

$(function () {
    Listener.init();
});

/*
MDN
// DOMContentLoaded 

// FileReader.onload

// GlobalEventHandlers.onload
*/



fetch & IIFE & ES6 & return undefined

https://gist.github.com/xgqfrms-GitHub/b252342f28497eadc96134a5873814fb

HTML5 history API

window.history.pushState(state object, title, url);

http://html5doctor.com/history-api/

https://developer.mozilla.org/en-US/docs/Web/API/History

https://developer.mozilla.org/zh-CN/docs/Web/API/History

https://developer.mozilla.org/en-US/docs/Web/API/History_API

https://developer.mozilla.org/zh-CN/docs/Web/API/History_API

@xgqfrms-GitHub
Copy link
Author

    
{
    "229": {
        "gid": 229,
        "name": "统一阿萨姆奶茶 500ml*15瓶/箱",
        "price": 45,
        "dadou": 0,
        "left": 10,
        "quantity": 1,
        "activity": {
            "aid": 1,
            "baseLine": 100,
            "off": 10
        }
    },
    "133470": {
        "gid": 133470,
        "name": "洽洽山核桃味瓜子108g/袋",
        "price": 22,
        "dadou": 0,
        "left": 4,
        "quantity": 1,
        "activity": {
            "aid": 2,
            "baseLine": 55,
            "off": 20
        }
    },
    "157704": {
        "gid": 157704,
        "name": "山果印象蓝莓味1L*6瓶/箱",
        "price": 70,
        "dadou": 0,
        "left": 3,
        "quantity": 2,
        "activity": {
            "aid": 1,
            "baseLine": 100,
            "off": 10
        }
    },
    "146513": {
        "gid": 146513,
        "name": "【买10赠1】加多宝凉茶250ml*18盒/箱",
        "price": 275,
        "dadou": 50,
        "left": 20,
        "quantity": 1,
        "activity": {
            "aid": 0,
            "baseLine": 0,
            "off": 0
        }
    }
}

@xgqfrms-GitHub
Copy link
Author

bad json

{
    229:{
        gid:229,
        name:"统一阿萨姆奶茶 500ml*15瓶/箱",
        price:45,
        dadou:0,
        left:10,
        quantity:1,
        activity:{
            aid:1,
            baseLine:100,
            off:10
        }
    },
    133470:{
        gid:133470,
        name:"洽洽山核桃味瓜子108g/袋",
        price:22,
        dadou:0,
        left:4,
        quantity:1,
        activity:{
            aid:2,
            baseLine:55,
            off:20
        }
    },
    157704:{
        gid:157704,
        name:"山果印象蓝莓味1L*6瓶/箱",
        price:70,
        dadou:0,
        left:3,
        quantity:2,
        activity:{
            aid:1,
            baseLine:100,
            off:10
        }
    },
    146513:{
        gid:146513,
        name:"【买10赠1】加多宝凉茶250ml*18盒/箱",
        price:275,
        dadou:50,
        left:20,
        quantity:1,
        activity:null
    }
}

https://jsonformatter.curiousconcept.com/

https://jsonformatter.org/

@xgqfrms-GitHub
Copy link
Author

virtual dom & DocumentFragment & insertAdjacentHTML

https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment

DocumentFragment 接口表示表示一个没有父级文件的最小文档对象。

https://developer.mozilla.org/zh-CN/docs/Web/API/DocumentFragment

https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createDocumentFragment

let docFragment = document.createDocumentFragment();

// assuming it exists (ul element)

let ul = document.getElementsByTagName("ul")[0],
    docfrag = document.createDocumentFragment();

const browserList = [
    "Internet Explorer", 
    "Mozilla Firefox", 
    "Safari", 
    "Chrome", 
    "Opera"
];

browserList.forEach((e) => {
    let li = document.createElement("li");
    li.textContent = e;
    docfrag.appendChild(li);
});

ul.appendChild(docfrag);

https://stackoverflow.com/questions/16126960/what-is-the-difference-between-appendchild-insertadjacenthtml-and-innerhtml

insertAdjacentHTML & insertAdjacentElement & insertAdjacentText

https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML

https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement

https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentText

element.insertAdjacentHTML(position, text);


<!-- beforebegin -->
    <p>
        <!-- afterbegin -->
        foo
        <!-- beforeend -->
    </p>
<!-- afterend -->

https://gist.github.com/xgqfrms-gildata/f2b41a63feb21081e9f51d464d7434d7#gistcomment-2134601

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment