itemtemplate:今天,Vue3 联想搜索组件开发遇见了个

 2021-06-30 08:47    77  

今天,在实现 Vue3 的联想搜索组件的时候,遇到了一个小问题,先简单看下我想要实现的需求itemtemplate:

itemtemplate:今天,Vue3 联想搜索组件开发遇见了个小问题

对于联想搜索结果itemtemplate,有默认的 HTML 显示结构,但是用户想要自定义结果的显示内容的话,是不是可以通过 vSlot 来动态配置,自行定制呢?

itemtemplate:今天,Vue3 联想搜索组件开发遇见了个小问题

也就是说itemtemplate,拿到 vSlot 定义的 template 内容,然后再遍历搜索结果,将 template 内指定的字段替换成搜索结果的内容即可,简单的模板如下图所示:

itemtemplate:今天,Vue3 联想搜索组件开发遇见了个小问题

自定义模板(待替换的为 MiSearchkey 组件)

<template> <mi-search search-key="title"> <!-- 这是搜索列表的模板 --> <template v-slot:itemTemplate> <div class="title"> <span>这是标题,下面一行是待替换的字符</span> <mi-search-key name="title"></mi-search-key> </div> </template> </mi-search></template>遍历搜索结果至于如何获取 template 模板内容,这部分直接略过,重点不是这个,直接看下面的遍历代码,大家瞧瞧有什么问题没有?

目的就是要替换 template 模板内 MiSearchkey 这个组件内,该组件的 name 值为指定的数据字段名称,即 <mi-search-key name="title"></mi-search-key> 将搜索结果数据中的 title 字段内容替换掉模板中 MiSearchkey 组件部分(可看上图)

// 直接看方法内的遍历// 重点看第一个 if 分支,带说明部分getListResultElem() { let res = [] const template = getSlotContent(this, 'itemTemplate') if (template) { // 获取的模板数据统一转为数组 const templates = isVNode(template) ? [template] : template // 开始遍历搜索结果的数据列表 for (let n = 0, len = this.list.length; n < len; n++) { const item = {...this.list[n]} const elems = [] // 遍历模板内容 for (let i = 0, l = templates.length; i < l; i++) { const curTemplate = templates[i] if (isVNode(curTemplate)) { let elem = curTemplate // 若为 MiSearchKey 组件, 则进行替换操作 if ((curTemplate.type as any).name === MiSearchKey.name) { elem = createVNode( <MiSearchKey name={name} data={item[name]}> </MiSearchKey> ) } this.getCustomItemDetailElem(elem, item) elems.push(elem) } } res.push( <div class={`${this.prefixCls}-item`}> { elems } </div> ) } } else { for (let i = 0, l = this.list.length; i < l; i++) { // ... } } return res.length > 0 ? ( <div class={`${this.prefixCls}-items`}>{ res }</div> ) : null}

再看 getCustomItemDetailElem 方法// 就是替换 MiSearchKey// 如果vNode节点类型是 MiSearchKey 组件// 则直接替换该 children,重新生成一个已经替换好内容的 MiSearchKeygetCustomItemDetailElem(node: VNode, item: any) { if ( node && node.children && node.children.length > 0 ) { const data = {...item} for (let i = 0, l = node.children.length; i < l; i++) { let child = node.children[i] if (isVNode(child)) { if ((child.type as any).name === MiSearchKey.name) { const name = child.props.name node.children[i] = createVNode( <MiSearchKey name={name} data={item[name]}> </MiSearchKey> ) } this.getCustomItemDetailElem(node.children[i], data) } } }}

上面这一段出来的结果是什么?搜索出来的列表内容变成全部一样了,全部成了最后一条匹配的数据内容,足足花了将近 20 分钟才解决,到底出了什么问题?前10分钟我想的都是如下一般的问题:

我又再去看了 render 渲染机制难道是 render 的时序问题导致的?难道是 MiSearchKey 组件没有加 key 标识符?难道 MiSearchKey 组件的 key 重复了?<template> 的渲染,最后都会跑到 render 去处理成虚拟 Dom,总不能想着倒退一把,把 vNode 转成 HTML 来处理?一点都不合理 .....

那个,那个 .... 我都有点不好意思说出来,这个费了我20分钟的问题了,因为 ...... 是因为获取到的 vNode 为 Readonly .......... 哈哈哈哈哈哈,像下面这样 cloneVNode 即可。

getCustomItemDetailElem(node: VNode, item: any) { if ( node && node.children && node.children.length > 0 ) { const data = {...item} const children = [] for (let i = 0, l = node.children.length; i < l; i++) { let child = cloneVNode(node.children[i]) if (isVNode(child)) { if ((child.type as any).name === MiSearchKey.name) { const tag = child.props.tag const name = child.props.name const type = child.props.type children[i] = createVNode( <MiSearchKey name={name} data={item[name]} tag=

type={type}> </MiSearchKey> ) } child = this.getCustomItemDetailElem(child, data) } } node.children = children } return node}{tag}

本文标签:组件联想遇见

原文链接:https://www.xgfox.com/bcrm/164.html

本文版权:如无特别标注,本站文章均为原创。