npm i bootstrap dist文件夹 当前模块编译后,供开发者调取使用的 bootstrap.css 没压缩 bootstrap.min.css 压缩的 UI组件库 所有的都自己写好了,我们只需要调用就可以了
思路.png

数据绑定 1.基于AJAX(或者其他方式fetch/跨域)向服务器端发送请求,服务器把需要呈现的数据,返给我们 (JSON格式的数据) 2.客户端获取到数据后,基于相应的方法实现数据的绑定(把数据能够动态绑定在页面中,呈现给用户)



商城排序案例【利用数据驱动思想】

  • 1.利用AJAX获取获取到JSON格式对象
  • 2.利用拿到的JSON对象 进行渲染到页面
  • 3.点击价格 时间 销量 进行排序
    • 数据驱动思想:直接更改数据源【获取的JSON对象】
    • 点击按钮排序完后,再次渲染页面

image.png

JSON

  1. [
  2. {
  3. "id": 1,
  4. "title": "HUAWEI Mate 10 4GB+64GB 全网通版(亮黑色)",
  5. "price": 3899,
  6. "time": "2017-03-15",
  7. "hot": 198,
  8. "img": "img/1.jpg"
  9. },
  10. {
  11. "id": 2,
  12. "title": "HUAWEI 麦芒6 4GB+64GB 全网通版(曜石黑)",
  13. "price": 2399,
  14. "time": "2017-02-08",
  15. "hot": 25,
  16. "img": "img/2.jpg"
  17. },
  18. {
  19. "id": 3,
  20. "title": "华为畅享7 2GB+16GB 全网通标配版(香槟金)",
  21. "price": 899,
  22. "time": "2017-01-25",
  23. "hot": 568,
  24. "img": "img/3.jpg"
  25. },
  26. {
  27. "id": 4,
  28. "title": "HUAWEI nova 2 Plus 4GB+64GB 全网通版(曜石黑)",
  29. "price": 2399,
  30. "time": "2016-12-30",
  31. "hot": 20000,
  32. "img": "img/4.jpg"
  33. },
  34. {
  35. "id": 5,
  36. "title": "HUAWEI nova 2 4GB+64GB 全网通版(玫瑰金)",
  37. "price": 2199,
  38. "time": "2016-01-30",
  39. "hot": 1032654,
  40. "img": "img/5.jpg"
  41. },
  42. {
  43. "id": 6,
  44. "title": "华为畅享7 Plus 4GB+64GB 全网通高配版(香槟金)",
  45. "price": 1699,
  46. "time": "2018-01-01",
  47. "hot": 1,
  48. "img": "img/6.jpg"
  49. },
  50. {
  51. "id": 7,
  52. "title": "HUAWEI nova 青春版 4GB+64GB 全网通版(樱语粉)",
  53. "price": 1799,
  54. "time": "2017-02-19",
  55. "hot": 400,
  56. "img": "img/7.jpg"
  57. },
  58. {
  59. "id": 8,
  60. "title": "HUAWEI P10 4GB+64GB 全网通版(曜石黑)",
  61. "price": 3488,
  62. "time": "2017-01-25",
  63. "hot": 240,
  64. "img": "img/8.jpg"
  65. },
  66. {
  67. "id": 9,
  68. "title": "HUAWEI P10 Plus 6GB+128GB 全网通版(钻雕金)",
  69. "price": 4488,
  70. "time": "2014-01-01",
  71. "hot": 12345678,
  72. "img": "img/9.jpg"
  73. },
  74. {
  75. "id": 10,
  76. "title": "HUAWEI Mate 9 保时捷设计 6GB+256GB 全网通版(曜石黑)",
  77. "price": 8999,
  78. "time": "2014-01-01",
  79. "hot": 12345678,
  80. "img": "img/10.jpg"
  81. }
  82. ]

HTML

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7. <title>商城排序</title>
  8. <!-- IMPORT CSS -->
  9. <link rel="stylesheet" href="css/bootstrap.min.css" />
  10. <link rel="stylesheet" href="css/index.css" />
  11. </head>
  12. <body>
  13. <!-- 一般都是根据模块 最外层的增加id -->
  14. <div class="container" id="productBox">
  15. <nav class="navbar navbar-expand-lg navbar-light bg-light">
  16. <a class="navbar-brand" href="javascript:;">商城排序</a>
  17. <div class="collapse navbar-collapse show" id="navbarNav">
  18. <ul class="navbar-nav">
  19. <li class="nav-item">
  20. <a class="nav-link" data-pai="price" href="javascript:;">价格</a>
  21. </li>
  22. <li class="nav-item">
  23. <a class="nav-link" data-pai="time" href="javascript:;">时间</a>
  24. </li>
  25. <li class="nav-item">
  26. <a class="nav-link" data-pai="hot" href="javascript:;">热度</a>
  27. </li>
  28. </ul>
  29. </div>
  30. </nav>
  31. <div class="content">
  32. <!-- <div class="card">
  33. <img src="https://res.vmallres.com/pimages//product/6941487220748/428_428_19EA25FFB51AB0AB79D82E3C8D317F67BEC741CBA0849F78mp.png"
  34. class="card-img-top" alt="..." />
  35. <div class="card-body">
  36. <h5 class="card-title">HUAWEI Mate 40 Pro</h5>
  37. <p class="card-text">价格:¥6999</p>
  38. <p class="card-text">时间:2021-01-22</p>
  39. <p class="card-text">热度:666</p>
  40. </div>
  41. </div> -->
  42. </div>
  43. </div>
  44. <!-- IMPORT JS -->
  45. <script src="index.js"></script>
  46. </body>
  47. </html>

CSS

.container{
    /* background-color: lightcoral; */
    min-width: 1000px;
}

.navbar-collapse{
    flex-basis: 80%; 
    /* 设置弹性盒子初始长度 */
}

/* ul 里设置 */
.navbar-nav{
    flex-direction: row;
}

.nav-item{
    padding: 0 10px;
}

.content{
    display: flex;
    flex-wrap: wrap; /* 换行 */   
    justify-content: space-between;
}

.card{
    width: 15rem;
    margin-bottom: 10px;
}
.card.no_bg{
    border: none;
    background: none;
}

JS 使用单例模式

// =====使用高级单例模式
let shopModel = (function () {
    // 1.获取数据,把获取到的数据渲染到页面上
    let data;
    const getData = function getData() {
        let xmlHttp = new XMLHttpRequest();
        xmlHttp.open('GET', './json/product.json', false); //false同步
        xmlHttp.onreadystatechange = function () {
            if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
                data = JSON.parse(xmlHttp.responseText);
            }
        }
        xmlHttp.send();
    }


    //2 根据数据渲染视图
    const render = function render() {
        let contet = document.querySelector('.content');
        let str = '';
        data.forEach(item => {
            str += `<div class="card">
                    <img src="./${item.img}" class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">${item.title}</h5>
                        <p class="card-text">价格:¥${item.price}</p>
                        <p class="card-text">时间:${item.time}</p>
                        <p class="card-text">销量:${item.hot}</p>
                        <a href="javascript:;" class="btn btn-primary">购买</a>
                    </div>
                </div>`;
        });
        // 最后一行补齐
        let num = 4 - data.length % 4;//num表示最后一行需要补上几个div
        for (let i = 0; i < num; i++) {
            str += `<div class="card no-bg"></div>`;
        }
        contet.innerHTML = str;
    }

    //清除其他按钮的排列效果
    let clear = function (btns) {
        //this是点击的按钮
        //形参btns是所有的点击按钮
        //让这个函数不是当前点击的按钮的元素flag=-1;
        // ==相对等于 !=相对不等于  ===绝对等于 !==绝对不等于
        btns.forEach(item => {
            if (item !== this) {
                item.flag = -1;
            }
        })
    }


    // 点击排序
    const dealEle = function dealEle() {
        let btns = document.querySelectorAll('.nav-link');
        btns.forEach(ele => {
            ele.flag = -1;//用flag判断升序还是降序
            ele.onclick = function () {
                //清除其他按钮的排列效果
                clear.call(this, btns);
                //让clear的this等于当前的点击按钮
                //再把所有点击按钮的Btns传过去
                let type = this.innerText;
                switch (type) {
                    case '价格':
                        type = 'price';
                        break;
                    case '时间':
                        type = 'time';
                        break;
                    case '销量':
                        type = 'hot';
                        break;
                    default:
                        type = 'price';
                };
                //第一次点击 flag是-1 进来乘-1 变成1  {-1*1=-1}
                //第二次点击 flag是1 进来还是乘以-1 变成-1 {-1*-1}=1
                this.flag *= -1;
                data.sort((a, b) => {
                    //处理time是字符串的特殊情况
                    a = (a[type] + '').replace(/-/g, '');
                    b = (b[type] + '').replace(/-/g, '');
                    return (a - b) * this.flag;
                });
                //把排好序的数组重新渲染到DOM上
                render();
            };
        });
    };
    return {
        init() {
            //init执行就是把这三个函数执行
            getData(); //获取数据
            render(); //根据数据渲染视图
            dealEle();//点击按钮排序
        }
    }
})();

//用ShopModel拿到init函数并且执行
shopModel.init();

JS 使用单例模式【命令模式】 加自定义属性

设置闭包 ,防止代码冲突 闭包的“保护” 高级单例设计模式 =>可以把当前版块中的一些内容暴露到外面,供其他版块使用

let productModel = (function () {
    //操作谁获取谁
    let productBox = document.querySelector('#productBox'),
        navlinks = productBox.querySelectorAll('.nav-link'), //按钮
        content = productBox.querySelector('.content'); //动态获取数据的位置
    // 私有上下文当中的公共的
    // data数据模型
    let data;
    //从服务器获取数据
    const queryData = function queryData() {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", './product.json', false); //同步
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {//status网络状态码
                data = JSON.parse(xhr.responseText);
            }
        };
        xhr.send();
    };

    // 根据数据渲染视图
    const render = function render() {
        let str = '';
        data.forEach(item => {
            let { title, price, time, hot, img } = item; //解构赋值
            str += `<div class="card">
                    <img src="${img}"
                        class="card-img-top" alt="${title}" />
                    <div class="card-body">
                        <h5 class="card-title">${title}</h5>
                        <p class="card-text">价格:¥${price}</p>
                        <p class="card-text">时间:${time}</p>
                        <p class="card-text">热度:${hot}</p>
                    </div>
                 </div>`;
        });

        //最后一行
        let num = 4 - data.length % 4;
        for (let i = 0; i < num; i++) {
            str += `<div class="card no_bg"></div>`;
        }
        content.innerHTML = str;
    };
    const clear=function clear(){
        // 点击某个按钮,让除当前按钮之外的其他按钮,他们的排序表示都回归“-1” 这样后续再点击其它按钮的时候,才能从升序开始
        navlinks.forEach(navlink=>{
            if(this!==navlink){
                navlink.flag=-1;
            };
        });
    };
    // 点击进行排序
    const handle = function handle() {
        // 循环
        navlinks.forEach(navlink=>{
            // 循环给每一个按钮,设置排序标识&事件绑定 会产生闭包
            navlink.flag=-1;
            navlink.onclick=function(){
                clear.call(this);//让clear中的this也是当前点击的按钮
                // this->当前点击的按钮
                this.flag*=-1;
                // pai->存储的是当前点击按钮该按照哪个字段排序
                let pai=this.getAttribute('data-pai');
                data.sort((a,b)=>{ //箭头函数没有this 箭头函数里的this是上级上下文中的this
                    a=a[pai];
                    b=b[pai];
                    // 时间特殊处理
                    if(pai==="time"){
                        a=a.replace(/-/g,'');
                        b=b.replace(/-/g,'');
                    }
                    return (a-b)*this.flag;
                });
                render();
            };
        });
    };
    return {
        // 命令模式:模块入口“管控当前版块各方法执行的顺序”
        init() {
            queryData();
            render();
            handle();
        }
    };
})();
productModel.init();