js商品列表组件开发(数据驱动显示)

商品列表

作为一个前端程序员,最重要的之一就是数据驱动显示。mvc和mvvc框架是我们熟知的,其中vue框架使用的就是mvvc结构,但不论哪种结构,
重要的都是有数据驱动我们显示出来的页面,例如商品列表,每个数据都是别人发过来的,我们总不能根据设计图用css和html写成固定的格式,否则数据换了,怎么办?
需求分析

1.根据设计图展示不同的商品
2.后端数据随时会换,也要能展示出来,增加商品和减少商品,样式不能变化
3.大图刚开始默认为小图的第一张,鼠标滑过小图时,大图要跟着变化
4.小图第一张有默认的边框,鼠标划过时,边框在滑过的图片上
3.性能优化,不能加载时间长

效果图

默认样式:

鼠标滑过小图:

分析
  • 不能按照平时的写行内样式,js每次操作DOM都会给性能造成影响
  • 通过js书写html标签和css样式
  • 分开模块写,html标签模块,css模块,写入数据模块,鼠标滑过大图随小图改变模块

下面,附上代码
html页面根据数据数量,实例化对象,传入数据,引入组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script type="module">
      import GoodsItem from "./js/GoodsItem.js";
      var arr = [
            {
                id: 1001,
                icon: ["./img/a1.jpg"],
                miniIcon: ["./img/mini_a1.jpg"],
                price: 1199,
                info: "荣耀Play4T 全网通6GB+128GB大内存 蓝水翡翠 4000mAh大电池 4800万AI摄影 6.39英寸魅眼屏",
                info1: "",
                used: false,
                evaluate: "31万+",
                shopName: "荣耀京东自营旗舰店",
                presell: "",
                tag: ["自营", "放心购"]
            },

            {
                id: 1002,
                icon: ["./img/a2.jpg"],
                miniIcon: ["./img/mini_a2.jpg"],
                price: 1389,
                info: "荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 幻夜黑",
                info1: "",
                used: false,
                evaluate: "10万+",
                shopName: "荣耀京东自营旗舰店",
                presell: "预售中",
                tag: ["自营", "放心购", "秒杀", "赠"]
            },

            {
                id: 1003,
                icon: ["./img/b1_1.jpg", "./img/b1_2.jpg", "./img/b1_3.jpg"],
                miniIcon: ["./img/mini_b1_1.jpg", "./img/mini_b1_2.jpg", "./img/mini_b1_3.jpg"],
                price: 1389,
                info: "荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 蓝水翡翠",
                info1: "",
                used: false,
                evaluate: "37万+",
                shopName: "荣耀京东自营旗舰店",
                presell: "",
                tag: ["自营", "放心购", "本地仓", "秒杀", "赠"]
            },

            {
                id: 1004,
                icon: ["./img/b2_1.jpg", "./img/b2_2.jpg", "./img/b2_3.jpg"],
                miniIcon: ["./img/mini_b2_1.jpg", "./img/mini_b2_2.jpg", "./img/mini_b2_3.jpg"],
                price: 1199,
                info: "荣耀Play4T 全网通6GB+128GB大内存 蓝水翡翠 4000mAh大电池 4800万AI摄影 6.39英寸魅眼屏",
                info1: "",
                used: false,
                evaluate: "31万+",
                shopName: "荣耀京东自营旗舰店",
                presell: "",
                tag: ["自营", "放心购", "本地仓", "赠"]
            },

            {
                id: 1005,
                icon: ["./img/c1.jpg"],
                miniIcon: ["./img/mini_c1.jpg"],
                price: 1389,
                info: "荣耀Play4T Pro 麒麟810芯片 OLED屏幕指纹 4800万高感光夜拍三摄 22.5W超级快充 全网通6GB+128GB 幻夜黑",
                info1: "",
                evaluate: "10万+",
                used: false,
                shopName: "荣耀京东自营旗舰店",
                presell: "",
                tag: ["自营", "放心购", "秒杀", "赠"],
            },

            {
                id: 1006,
                icon: ["./img/c2_1.jpg", "./img/c2_2.jpg", "./img/c2_3.jpg", "./img/c2_4.jpg"],
                miniIcon: ["./img/mini_c2_1.jpg", "./img/mini_c2_2.jpg", "./img/mini_c2_3.jpg", "./img/mini_c2_4.jpg"],
                price: 1389,
                info: "麒麟980芯片;超感光徕卡四摄10倍混合变焦;店铺首页领白条免息券",
                info1: "",
                evaluate: "81万+",
                used: true,
                shopName: "华为京东自营官方旗舰店",
                presell: "",
                tag: ["自营", "放心购", "秒杀", "赠"],
            },

            {
                id: 1007,
                icon: ["./img/d1_1.jpg", "./img/d1_2.jpg", "./img/d1_3.jpg", "./img/d1_4.jpg", "./img/d1_5.jpg"],
                miniIcon: ["./img/mini_d1_1.jpg", "./img/mini_d1_2.jpg", "./img/mini_d1_3.jpg", "./img/mini_d1_4.jpg", "./img/mini_d1_5.jpg"],
                price: 2489,
                info: "荣耀Play4 Pro 5G双模 麒麟990 4000万超感光暗拍 40W超级快充 8GB+128GB幻夜黑",
                info1: "",
                used: false,
                evaluate: "21万+",
                shopName: "荣耀京东自营官方旗舰店",
                presell: "",
                tag: ["自营", "放心购", "秒杀", "赠"]
            },

            {
                id: 1008,
                icon: ["./img/d2_1.jpg", "./img/d2_2.jpg", "./img/d2_3.jpg", "./img/d2_4.jpg"],
                miniIcon: ["./img/mini_d2_1.jpg", "./img/mini_d2_2.jpg", "./img/mini_d2_3.jpg", "./img/mini_d2_4.jpg"],
                price: 2399,
                info: " HUAWEI nova 5 Pro 前置3200万人像超级夜景4800万AI四摄麒麟980芯片8GB+128GB亮黑色全网通双4G",
                info1: "",
                used: true,
                evaluate: "57万+",
                shopName: "华为京东自营官方旗舰店",
                presell: "",
                tag: ["自营", "放心购", "秒杀"]
            },

            {
                id: 1009,
                icon: ["./img/e1_1.jpg", "./img/e1_2.jpg", "./img/e1_3.jpg", "./img/e1_4.jpg", "./img/e1_5.jpg"],
                miniIcon: ["./img/mini_e1_1.jpg", "./img/mini_e1_2.jpg", "./img/mini_e1_3.jpg", "./img/mini_e1_4.jpg", "./img/mini_e1_5.jpg"],
                price: 2489,
                info: "荣耀V30 5G 双模 麒麟990 突破性相机矩阵 游戏手机 8GB+128GB 幻夜星河 移动联通电信5G 双卡双待",
                info1: "",
                used: false,
                evaluate: "21万+",
                shopName: "荣耀京东自营旗舰店",
                presell: "",
                tag: ["自营", "放心购", "秒杀", "赠"],

            },

            {
                id: 1010,
                icon: ["./img/e2_1.jpg", "./img/e2_2.jpg", "./img/e2_3.jpg", "./img/e2_4.jpg", "./img/e2_5.jpg"],
                miniIcon: ["./img/mini_e2_1.jpg", "./img/mini_e2_2.jpg", "./img/mini_e2_3.jpg", "./img/mini_e2_4.jpg", "./img/mini_e2_5.jpg"],
                price: 1889,
                info: "荣耀Play4 5G双模 6400万锐力四摄 4300mAh大电池 VC液冷散热 8GB+128GB 幻夜黑TNNH-AN00",
                info1: "",
                used: false,
                evaluate: "170万+",
                shopName: "荣耀京东自营旗舰店",
                presell: "",
                tag: ["自营", "放心购", "秒杀", "赠"],

            },

            {
                id: 1011,
                icon: ["./img/f1_1.jpg", "./img/f1_2.jpg", "./img/f1_3.jpg"],
                miniIcon: ["./img/mini_f1_1.jpg", "./img/mini_f1_2.jpg", "./img/mini_f1_3.jpg"],
                price: 1889,
                info: "荣耀Play4 5G双模 6400万锐力四摄 4300mAh大电池 VC液冷散热",
                info1: "",
                used: true,
                evaluate: "1.3万+",
                shopName: "荣耀京东自营旗舰店",
                presell:"",
                tag: ["自营", "放心购", "秒杀", "赠"],
            },

            {
                id: 1012,
                icon: ["./img/f2_1.jpg", "./img/f2_2.jpg", "./img/f2_3.jpg"],
                miniIcon: ["./img/mini_f2_1.jpg", "./img/mini_f2_2.jpg", "./img/mini_f2_3.jpg"],
                price: 1889,
                info: "华为畅享10e 手机 翡冷翠 移动全网通(4G+64G)",
                info1: "【4 + 64移动绿秒杀低至828元!直降170元!】现货速发,5000mAh超长续航,支持反向充电~!《畅享20pro咨询减钱》戳~",
                used: true,
                evaluate: "1.3万+",
                shopName: "荣耀京东自营旗舰店",
                presell:"",
                tag: ["京东物流", "放心购", "秒杀", "免邮", "险"],
            }
        ]
          var list=[];
        arr.forEach(item=>{
           let goods=new GoodsItem();
           goods.appendTo("body");
           goods.setData(item);
           list.push(goods);
        });

js代码,书写样式,填入数据,引入工具包

import Utils from "./Utils.js";
export default class GoodsItem {
    static styleBool = false;
    data;
    preIcon;
    constructor() {
        this.elem = this.createElem();
        if (!GoodsItem.styleBool) GoodsItem.setStyle();   //通过bool值控制每个元素css样式的创建
        this.renderHTML();   
    }
    createElem() {     //通过实例创建,不需要传参
        if (this.elem) return this.elem;
        var div = Utils.ce("div");
        div.className = "goodsItem"; //每一个元素的最外层div   根据数据有多少个创建多少个
        return div;
    }
    appendTo(parent) {
        if (typeof parent === "string") parent = document.querySelector(parent);
        parent.appendChild(this.elem);
    }

    setData(_data) {   //外部数据的传入    每次实例化都要执行
        this.data = _data;   //后面能用到的数据设置成全局的
        this.icon = this.elem.querySelector(".icon");  
        let presell = this.elem.querySelector(".presell");
        this.miniIconCon = this.elem.querySelector(".miniIconCon");
        var price = this.elem.querySelector(".price");
        var infoCon = this.elem.querySelector(".infoCon");
        var evaluate = this.elem.querySelector(".evaluate");
        var shopCon = this.elem.querySelector(".shopCon");
        var tagCon = this.elem.querySelector(".tagCon");


        if (_data.presell.trim().length > 0) {
            presell.textContent = _data.presell;
        } else {
            presell.style.display = "none"
        }
        var miniIconStr = "";
        _data.miniIcon.forEach(item => {         //设置小图片
            miniIconStr += `<img src=${item} class="miniIcon">`
        })
        this.miniIconCon.innerHTML = miniIconStr;
        this.miniIconCon.addEventListener("mouseover", e => this.iconMouseHandler(e))   //小图片的划过事件
        this.changeIcon(0);  //大图的默认显示图片

        price.textContent = _data.price.toFixed(2);

        infoCon.textContent = _data.info

        evaluate.textContent = _data.evaluate;

        shopCon.textContent = _data.shopName;

        var dic={    //下面小标签   数据表
            "自营":"goods_icon_0",
            "放心购":"goods_icon_1",
            "本地仓":"goods_icon_0",
            "赠":"goods_icon_2",
            "京东物流":"goods_icon_2",
            "秒杀":"goods_icon_2",
            "免邮":"goods_icon_2",
            "险":"goods_icon_3",
        }
        var tagStr="";
         _data.tag.forEach(item=>{
            tagStr+=`<span class='${dic[item]}'>${item}</span>`
         });
         tagCon.innerHTML=tagStr;
    }

    iconMouseHandler(e) {    //根据划过的图片在小图片中的坐标  ,来执行大图的改变事件
        if (e.target.constructor !== HTMLImageElement) return;
        var index = Array.from(e.currentTarget.children).indexOf(e.target);
        this.changeIcon(index);   //大图改变
    }

    changeIcon(index) {
        if (this.preIcon) {
            this.preIcon.style.border = "1px solid #CCCCCC";
        }

        this.icon.src = this.data.icon[index];
        this.preIcon = this.miniIconCon.children[index];  //????this.preIcon
        this.preIcon.style.border = "2px solid #e4393c";
    }
    renderHTML() {    //设置每个元素内部 html标签
        this.elem.innerHTML = `
            <div class="iconCon">    <!--图片容器-->
                <img class="icon">    <!--图片-->
                <div class="presell">   <!--预售-->
            </div>
            <div class="miniIconCon"></div>   <!--小图-->
            <div class="priceCon">   <!--价钱-->
                <span class="money">¥</span>
                <span class="price"></span>
            </div>
            <a class="infoCon" href="#"></a>  <!--信息-->
            <div class="evaluateCon"><span class="evaluate"></span>条评价</div>  <!--评价-->
            <div class="shopCon"></div>  <!--商店-->
            <div class="tagCon">   <!--标签-->
            </div>
        `
    }

    static setStyle() {   //设置css样式
        GoodsItem.styleBool = true;
        Utils.setStyle({
            ".goodsItem": {  //最外层div样式
                 "240px",
                height: "466px",
                float: "left",
                margin: "5px"
            },
            ".goodsItem:hover": {
                boxShadow: "0px 0px 4px #999999"
            },
            ".iconCon": {   //大图
                 "220px",
                height: "220px",
                marginBottom: "5px",
                position: "relative",
                margin: "auto",
                left: 0,
                right: 0,
            },
            ".presell": {   //预售
                 "200px",
                height: "25px",
                position: "absolute",
                bottom: "0px",
                backgroundColor: "rgba(0,0,0,0.4)",
                fontSize: "12px",
                color: "#FFFFFF",
                paddingLeft: "20px",
                lineHeight: "25px"
            },
            ".miniIcon": {  //小图
                 "25px",
                height: "25px",
                border: "1px solid #CCCCCC",
                marginRight: "5px"
            },
            ".priceCon": {  //价钱
                 "220px",
                height: "22px",
                color: "#e4393c",
                fontSize: "20px",
                marginTop:"5px",
            },
            ".money": {
                fontSize: "16px",
            },
            ".price": {
                marginLeft: "-10px"
            },
            ".infoCon": {   //信息
                 "220px",
                height: "40px",
                wordWrap: "break-word",
                overflow: "hidden",
                display: "block",
                fontSize: "12px",
                color: "#333333",
                lineHeight: "20px",
                marginTop: "10px",
                textDecoration: "none"
            },
            ".evaluateCon": {   //评价
                 "220px",
                height: "18px",
                fontSize: "12px",
                color: "#333333",
                marginTop: "5px"
            },
            ".evaluate": {
                color: "#646FB0",
                fontWeight: "600",
            },
            ".shopCon": {   //商店
                fontSize: "12px",
                marginTop: "5px",
                color: "#AAAAAA",
                overflow: "hidden",
                whiteSpace: "nowrap",
                textOverflow: "ellipsis",
                 "122px",
                height: "18px"
            },
            ".goods_icon_0": {  //下面标签
                float: "left",
                height: "16px",
                lineHeight: "16px",
                padding: "0 3px",
                marginRight: "3px",
                overflow: "hidden",
                textAlign: "center",
                fontStyle: "normal",
                fontSize: "12px",
                fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif',
                background: "#e23a3a",
                color: "#FFF",
                cursor: "default",
                borderRadius: "2px",
            },
            ".goods_icon_1": {
                border: "1px solid #e23a3a",
                borderColor: "#4d88ff",
                color: "#4d88ff",
                float: "left",
                height: "14px",
                lineHeight: "14px",
                padding: "0 3px",
                marginRight: "3px",
                overflow: "hidden",
                textAlign: "center",
                fontStyle: "normal",
                fontSize: "12px",
                fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif"',
                borderRadius: "2px",
            },
            ".goods_icon_2": {
                float: 'left',
                height: '14px',
                lineHeight: '14px',
                padding: '0 3px',
                border: '1px solid #e23a3a',
                marginRight: '3px',
                overflow: 'hidden',
                textAlign: 'center',
                fontStyle: 'normal',
                fontSize: '12px',
                fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif',
                borderRadius: '2px',
                color: '#e23a3a',
            },
            ".goods_icon_3": {
                float: 'left',
                height: '16px',
                lineHeight: '16px',
                padding: '0 3px',
                marginRight: '3px',
                overflow: 'hidden',
                textAlign: 'center',
                fontStyle: 'normal',
                fontSize: '12px',
                fontFamily: '"Helvetica Neue","Hiragino Sans GB",SimSun,serif',
                background: '#e23a3a',
                color: '#FFF',
                cursor: 'default',
                borderRadius: '2px',
                background: "#4b9bfc",
            },
            ".tagCon":{
                marginTop:"10px"
            }
        })
    }

}

Utils.js是个工具包

export default class Utils{
   static time=0;
   static ids=0;
   static timeManage={};
   //代码执行时间
   static timeStart(){
        if(Utils.time) return;
        Utils.time=new Date().getTime();
    }
   static timeEnd(){
        var t=new Date().getTime()-Utils.time;
        Utils.time=0;
        return t;
    }
    static ts(){
        Utils.ids++;
        Utils.timeManage[Utils.ids]=new Date().getTime();
        return ids;
    }
    static te(id){
        if(!Utils.timeManage[Utils.id]) return 0;
        var t=new Date().getTime()-Utils.timeManage[Utils.id];
        delete Utils.timeManage[Utils.id];
        return t;
    }
    //随机颜色
    static randomColor(){
        var col="#";
        for(var i=0;i<6;i++){
            col+=Math.floor(Math.random()*16).toString(16);
        }
        return col;
    }
    //随机数值
    static random(min,max){
        return Math.floor(Math.random()*(max-min)+min);
    }
    //创建元素  设置样式  插入父级元素
     static ce(type,style,parent){
        var elem=document.createElement(type);
        if(style){
            for(var prop in style){
                elem.style[prop]=style[prop];
            }
        }
        if(typeof parent==="string") parent=document.querySelector(parent);
        if(parent) parent.appendChild(elem);
        return elem;
    }
    //读取css样式
    static setStyle(styles){
        var style=document.createElement("style");
        document.head.appendChild(style);
        var styleSheet=document.styleSheets[document.styleSheets.length-1];
        for(var prop in styles){
            Utils.addCss(styleSheet,prop,styles[prop]);
        }
    }
    //添加css样式
    static addCss(styleSheet,selector,style){
        var str=selector+" {";
        for(var prop in style){
            var value=style[prop]
            prop=prop.replace(/([A-Z])/g,function($1){
                return "-"+$1.toLowerCase();
            })
            str+=prop+":"+value+";"
        }
        str+=" }";
        styleSheet.insertRule(str,styleSheet.cssRules.length);
    }
    //css样式转化为js内样式
    static CSStoString(str){
       return str.replace(/(?<=:)(.*?)(?=;)|-[a-z](?=.+:)|;/g,function(item){
            if(item===";") return ","
            if(item[0]==="-")  return item[1].toUpperCase();
            return "'"+item.trim()+"'";
        });
    }
    //字符串css转换为js内样式
    static CSStoObject(str){
        str=Utils.CSStoString(str);
       return  str.split(",").reduce((value,item)=>{
           item=item.replace(/
/g,"");
            var arr=item.split(":");
            arr[0]=arr[0].replace(/s/g,"");
            if(arr[1]===undefined) return value;
            arr[1]=arr[1].replace(/'/g,"");
            value[arr[0]]=arr[1];
            return value;
        },{})
    }
}
原文地址:https://www.cnblogs.com/94-Lucky/p/13394078.html