请求代码优化思路

生成一个tbody的思路优化(注:只能createElement,只能在方法中生成,无HTML,页面标签都在方法中生成)

async geneTableList():Promise<any>{
    let url = 'http://127.0.0.1:5000/test/annotation/genelist?pagesize=20';
    url = url + '&pagenum=' + this.pagenum;
    url = this.filterparam?url + '&filterparam=' + this.filterparam : url;
    url = this.sortname?url + '&sort={"name":"' + this.sortname + '","isAsc":' + this.isasc+'}' : url;
    this.listEle.classList.add('el-table')
    this.listEle.appendChild(this.tbody)
    var httpRequest = new XMLHttpRequest(); //第一步:建立所需的对象
    httpRequest.open('GET', url, true); //第二步:打开连接  将请求参数写在url中  ps:"./Ptest.php?name=test&nameone=testone"
    httpRequest.send(); //第三步:发送请求  将请求参数写在URL中
    let that = this
    httpRequest.onreadystatechange = function () {
        if (httpRequest.readyState == 4 && httpRequest.status == 200) {
            var res = <any>JSON.parse(httpRequest.responseText) ; //获取到json字符串,还需解析
            if(res.code == 200){
                that.totalPage = res.totalpage
                res.data.forEach((item:any)=>{
                    const tr = document.createElement('tr')
                    const tdCheckbox = document.createElement('td')
                    const checkDiv = document.createElement('div')
                    const checkbox = document.createElement('input')
                    checkDiv.classList.add('cell')
                    tdCheckbox.setAttribute('rowspan', '1')
                    tdCheckbox.setAttribute('colspan', '1')
                    checkbox.type = 'checkbox'
                    const tdGene = document.createElement('td')
                    const gene = document.createElement('div')
                    gene.classList.add('cell')
                    tdGene.setAttribute('rowspan', '1')
                    tdGene.setAttribute('colspan', '1')
                    gene.innerText = item.gene
                    gene.setAttribute('value', item.gene)
                    const tdMIDCount = document.createElement('td')
                    const MIDCount = document.createElement('div')
                    MIDCount.classList.add('cell')
                    tdMIDCount.setAttribute('rowspan', '1')
                    tdMIDCount.setAttribute('colspan', '1')
                    MIDCount.innerText = item.MIDcount
                    MIDCount.setAttribute('value', item.gene)
                    const tdE10 = document.createElement('td')
                    const E10 = document.createElement('div')
                    E10.classList.add('cell')
                    tdE10.setAttribute('rowspan', '1')
                    tdE10.setAttribute('colspan', '1')
                    E10.innerText = item.E10.toFixed(2)
                    E10.setAttribute('value', item.gene)
                    tdCheckbox.appendChild(checkDiv)
                    checkDiv.appendChild(checkbox)
                    tdGene.appendChild(gene)
                    tdMIDCount.appendChild(MIDCount)
                    tdE10.appendChild(E10)
                    tr.appendChild(tdCheckbox)
                    tr.appendChild(tdGene)
                    tr.appendChild(tdMIDCount)
                    tr.appendChild(tdE10)
                    that.tbody.appendChild(tr)
                    that.listEle.appendChild(that.tbody)
                })
            }
        }
    };
    return {tableDom: that.listEle, tablePage: that.totalPage}
}
阅读 3.1k
3 个回答

如果有轮子,当然用轮子。但是不能和轮子,那就自己造!


代码前面的 url 和 query string 拼接部分可以用 URLURLSearchParams 工具类来辅助,优化下代码结构就很易读了

    // let url = "http://127.0.0.1:5000/test/annotation/genelist?pagesize=20";
    // url = url + "&pagenum=" + this.pagenum;
    // url = this.filterparam ? url + "&filterparam=" + this.filterparam : url;
    // url = this.sortname ? url + "&sort={\"name\":\"" + this.sortname + "\",\"isAsc\":" + this.isasc + "}" : url;

    // url 处理的部分可以使用 URL 和 URLSearchParams 这两个工具类来处理
    // XMLHttpRequest 应该可以直接使用 URL 对象,如果不行,就用 url.toString() 转一下
    const url = new URL("http://127.0.0.1:5000/test/annotation/genelist?pagesize=20");

    // 需要的参数可以封装成对象,不用在于值是否 undefined
    const qs = {
        pagenum: this.pagenum,
        filterparam: this.filterparam,
        sort: this.sortname
            ? JSON.stringify({
                name: this.sortname,
                isAsc: this.isasc
            })
            : undefined
    };

    // 获取 qs 对象的键值对
    Object.entries(qs)
        // 过滤掉值是 undefined 和 null 的情况
        .filter(([key, value]) => value ?? null !== null)
        // 其余的依次添加到 url.searchParams 中去,
        // URLSearchParams 会自动进行 encodeURIComponent,所以也不用担心中文和特殊符号
        .forEach(([key, value]) => url.searchParams.append(key, value));

下面 DOM 创建的部分,其实大部分过程是相似的,就是创建特定结构的 TD,加上一些元素……可以考虑封装一个 makeElement 来快速创建 HTML 元素:

const makeElement = (
    tag: string,
    classes?: string[],
    attrs?: Record<string, string>): HTMLElement => {
    const el = document.createElement(tag) as HTMLElement;
    el.classList.add(...(classes ?? []));
    Object.entries(attrs ?? {})
        .forEach(([key, value]) => el.setAttribute(key, value));
    return el;
};

以及 makeCell 来创建一个单元格

const makeCell = (
    // 允许传入 innerText,元素,或者是产生他们的函数
    supplier: string | HTMLElement | (() => HTMLElement | string)
): HTMLTableCellElement => {
    const td = makeElement("td", [], defaultSpans) as HTMLTableCellElement;
    const cell = makeElement("div", ["cell"]);

    // 如果是函数,就拿到执行结果(否则本来就是需要的内容对象/字符串)
    const content = typeof supplier === "function" ? supplier() : supplier;
    if (typeof content === "string") {
        cell.innerText = content;
    } else {
        cell.appendChild(content);
    }

    td.appendChild(cell);
    return td;
};

那么第一个单元格的代码大概可以这么写:

// const tdCheckbox = document.createElement("td");
// const checkDiv = document.createElement("div");
// const checkbox = document.createElement("input");
// checkDiv.classList.add("cell");
// tdCheckbox.setAttribute("rowspan", "1");
// tdCheckbox.setAttribute("colspan", "1");
// checkbox.type = "checkbox";
//
// tdCheckbox.appendChild(checkDiv);
// checkDiv.appendChild(checkbox);
const tdCheckbox = makeCell(() => {
    const cb = makeElement("input") as HTMLInputElement;
    // 如果再给 makeElement 加上 props 参数,都不需要写工厂函数了
    cb.type = "checkbox";
    return cb;
});

甚至可以把 tr.appendChild 都放在一句里面

tr.appendChild(makeCell(() => {
    const cb = makeElement("input") as HTMLInputElement;
    cb.type = "checkbox";
    return cb;
}));

后面的参照着改就行了。

话说回来,既然要做大量的 DOM 操作,干嘛不用 jQuery

建议可以看下js的模板引擎相关技术
js模板

写了一个React的JSX作为模板引擎来渲染的样例,如果你不想引入React也可以,我写了一个简单的createElement实现,不过需要tsconfig添加jsx编译。
XMLHttpRequest的get请求也做了简单封装

https://codesandbox.io/s/genetablelist-vuddj
image.png

const React = {
    createElement: <K extends keyof HTMLElementTagNameMap>(
      tag: K,
      props: any = {},
      ...others: any[]
    ): HTMLElementTagNameMap[K] => {
      const el = document.createElement(tag);
      others
        .reduce((a, b) => a.concat(b), [])
        .forEach((c: any) => {
          var type = Object.prototype.toString.call(c);
          if (/\[object\sHTML(\w+)?Element\]/.test(type)) {
            el.appendChild(c);
          } else {
            el.innerText = c || "";
          }
        });
      Object.assign(el, props);
      return el;
    }
  };
  const http = {
    get: <K extends {}>(
      url: string,
      params: Record<string, string | number> = {}
    ): Promise<K> =>
      new Promise((resolve, reject) => {
        const query = Object.entries(params)
          .map(([k, v]) =>
            typeof v === "undefined" ? "" : `${k}=${encodeURIComponent(v)}`
          )
          .join("&");
        const xhr = new XMLHttpRequest();
        xhr.addEventListener("readystatechange", function () {
          if (xhr.readyState === 4) {
            if (xhr.status === 200) {
              try {
                resolve(JSON.parse(xhr.responseText));
              } catch (e) {
                reject("请求错误!");
              }
            } else {
              reject("请求错误!");
            }
          }
        });
        xhr.open("GET", url + `?${query}`, true);
        xhr.send();
      })
  };
  // 测试样例
  http.get = async (url, params) => {
    return {
      totalPage: 10,
      data: [
        { gene: "gene1", MIDcount: 30, E10: 10 },
        { gene: "gene3", MIDcount: 40, E10: 9.99 },
        { gene: "gene2", MIDcount: 50, E10: 8.76 }
      ]
    } as any;
  };
  
  const geneTableList = async (): Promise<any> => {
    const res: {
      totalPage: number;
      data: { gene: string; MIDcount: number; E10: number }[];
    } = await http.get("http://127.0.0.1:5000/test/annotation/genelist", {
      pagesize: 20,
      pagenum: 1,
      sortname: JSON.stringify({ name: "abc", isAsc: false })
    });
    return (
      <table className="table">
        <thead>
          <tr>
            <th>#</th>
            <th>gene</th>
            <th>MIDcount</th>
            <th>E10</th>
          </tr>
        </thead>
        <tbody>
          {res.data.map((item, i) => (
            <tr key={i}>
              <td rowSpan={1} colSpan={1}>
                <div>
                  <input type="checkbox" />
                </div>
              </td>
              <td rowSpan={1} colSpan={1}>
                <div className="cell">{item.gene}</div>
              </td>
              <td rowSpan={1} colSpan={1}>
                <div className="cell">{item.MIDcount}</div>
              </td>
              <td rowSpan={1} colSpan={1}>
                <div className="cell">{item.E10.toFixed(2)}</div>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    );
  };
  
  geneTableList().then((table) => {
    document.body.appendChild(table);
  });
  
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏