Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue2 #10

Open
caroundsky opened this issue Jan 11, 2023 · 4 comments
Open

Vue2 #10

caroundsky opened this issue Jan 11, 2023 · 4 comments
Labels

Comments

@caroundsky
Copy link
Owner

caroundsky commented Jan 11, 2023

JSX

使用slot插槽

// 方案一
<ctreeselect
  scopedSlots = {{
    'value-label': (row) => {
      return (
        <div title={row.node.raw.label}>
          {row.node.raw.label}
        </div>
      )
    }
  }}
/>

// 方案二
<ctreeselect
  {...{
    scopedSlots: {
      'value-label': (row) => {
        return (
          <div title={row.node.raw.label}>
            {row.node.raw.label}
          </div>
        )
      }
    }
  }}
/>

在map中获取ref,如果要获取数组,需要配置refInFor = {true}

使用原生键盘按下事件

nativeOnKeydown={ (e) =>xxx }
@caroundsky caroundsky added the vue label Jan 11, 2023
@caroundsky
Copy link
Owner Author

caroundsky commented Jan 11, 2023

组件

Snipaste_2023-01-12_15-53-51

/**
 * 合并整行:不合并的列需要返回[0, 0]
 * colspan: 列长
**/
arraySpanMethod({ row, columnIndex }) {
  if (this.colspan && row.children || row.hasChildren) {
    if (columnIndex === 0) {
      return [1, this.colspan]
    } else {
      return [0, 0]
    }
  }
}

懒加载配合一次性展开时,因为官方的手动方法toggleRowExpansion不支持懒加载,所以需要另寻方案;
提供两个思路:
1、如果只需要单个展开,可获取table store内的指定行后,手动更改其状态
2、如果一次展开多个,上述方法因为异步获取数据,数据无法获取到最新的,所以需要在数据返回后,再执行上述方法。

expendAll() {
  this.expandAll = !this.expandAll
  // hack 因为toggleRowExpansion不支持懒加载,所有手动触发点击事件
  const expandIcons = [...this.tableRef.querySelectorAll('.el-table__expand-icon')]
  expandIcons.forEach(icon => {
    if (this.expandAll && !icon.classList.contains('el-table__expand-icon--expanded')) {
      icon.click()
    }
    if (!this.expandAll && icon.classList.contains('el-table__expand-icon--expanded')) {
      icon.click()
    }
  })
}

async loadTree(tree, treeNode, resolve) {
  const res = await getData(tree.id)

  // 获取el-table内store
  const { store } = this.tableRef
  const { states: { treeData } } = store
  const _data = treeData[tree.id]

  resolve(data)

  // hack 同时展开全部时部分行无法更新状态,手动去store更新
  _data.loaded = true
  _data.loading = false
  _data.expanded = true
},
  • el-table 简单封装 el-table-column,以便数据配置化和jsx书写

<el-table xxx>
  <template v-for="(item, index) in tableColumns">
    <VNodes :key="index" :vnodes="renderTableColumn(item)" />
  </template>
<el-table>

components: {
  VNodes: {
    functional: true,
    render: (h: any, ctx: any) => {
      const { vnodes } = ctx.props;
      return typeof vnodes === "object" ? vnodes : <span>{vnodes}</span>;
    },
  },
}

methods: {
  renderTableColumn(columns) {
    const _createColumn = (column) => {
      if (!column.align) {
        column.align = 'center'
      }
      const { render, renderHeader = null, children, ...props } = column
      const VNodeData: any = { props }
      if (render) {

      // 可以操作render,给其外部包裹一些自定义内容
      // let _render = (_col) => {
      //   const prop = props.prop
      //     ? `supplier_info_list.${_col.$index}.${props.prop}`
      //     : null
      //   _col.props = {
      //     disabled: _col.row._disabled,
      //   }
      //   return (
      //     <el-form-item
      //       prop={prop}
      //       key={_col._uuid}
      //       rules={[
      //         {
      //           required: props.required,
      //           message: `请输入${props.label}`,
      //           // trigger: '乱写即可不校验',
      //         },
      //       ]}
      //     >
      //       {render(_col)}
      //     </el-form-item>
      //   )
      // }

        let _renderHeader
        if (renderHeader) {
          _renderHeader = (
            <div>
              {renderHeader(VNodeData)}
              {VNodeData.required && <i class="required" />}
            </div>
          )
        } else {
          _renderHeader = (
            <div>
              {props.label}
              {props.required && <i class="required" />}
            </div>
          )
        }

        VNodeData.scopedSlots = {
          default: (row) => render(row),
          header: () => _renderHeader,
        }
      }
      const VNodeChildren = Array.isArray(children)
        ? children.map((child) => _createColumn(child))
        : null
      return this.$createElement('el-table-column', VNodeData, VNodeChildren)
    }
    return typeof columns === 'object' ? _createColumn(columns) : null
  }
}
  • 场景:el-date-picker 设置不可选时间段 :picker-options="pickerOptions"

// 月份区间
pickerOptions() {
  return {
    disabledDate(time) {
      let myDate = new Date()
      let t = myDate.getDate()
      // 如果想包含本月本月 - 8.64e7 * t 就不需要了,
      // 如果想之前的不能选择把 > 换成 <
      return time.getTime() > Date.now() - 8.64e7 * t
    }
  }
}

@caroundsky
Copy link
Owner Author

Vue

通过F12的 $0.value 方式修改,触发双向绑定

此时是无法触发的,可通过发射自定义事件解决

(function(){
    $0.value = '1111'
    $0.dispatchEvent(new Event('input'))
})()

@caroundsky
Copy link
Owner Author

caroundsky commented Mar 8, 2023

热加载

问题描述:

在使用keep-alive包裹router时,页面的热更新会导致白屏,同样的问题可参看 https://github.com/vuejs/vue-loader/issues/1332。

问题原因是值在 keep-alive 中的缓存 key 为 router-view 的 key 值,它是不变的,但热更新后组件 cid 会变化。

image

解决方案:缓存 key的取法 加上cid,并替换原keep-alive组件

import { isRegExp } from 'lodash'
import Vue from 'vue'
/*
 * https://github.com/vuejs/vue-loader/issues/1332#issuecomment-601572625
 */
function isDef(v) {
  return v !== undefined && v !== null
}
function isAsyncPlaceholder(node) {
  return node.isComment && node.asyncFactory
}
function getFirstComponentChild(children) {
  if (Array.isArray(children)) {
    for (var i = 0; i < children.length; i++) {
      var c = children[i]
      if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
        return c
      }
    }
  }
}
function getComponentName(opts) {
  return opts && (opts.Ctor.options.name || opts.tag)
}
function matches(pattern, name) {
  if (Array.isArray(pattern)) {
    return pattern.indexOf(name) > -1
  } else if (typeof pattern === 'string') {
    return pattern.split(',').indexOf(name) > -1
  } else if (isRegExp(pattern)) {
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}
function remove(arr, item) {
  if (arr.length) {
    var index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}
function pruneCacheEntry(cache, key, keys, current) {
  var cached$$1 = cache[key]
  if (cached$$1 && (!current || cached$$1.tag !== current.tag)) {
    cached$$1.componentInstance.$destroy()
  }
  cache[key] = null
  remove(keys, key)
}
function pruneCache(keepAliveInstance, filter) {
  var cache = keepAliveInstance.cache
  var keys = keepAliveInstance.keys
  var _vnode = keepAliveInstance._vnode
  const cachedNameKeyMap = keepAliveInstance.cachedNameKeyMap
  for (var key in cache) {
    var cachedNode = cache[key]
    if (cachedNode) {
      var name = getComponentName(cachedNode.componentOptions)
      if (name && !filter(name)) {
        delete cachedNameKeyMap[name]
        pruneCacheEntry(cache, key, keys, _vnode)
      }
    }
  }
}
const patternTypes = [String, RegExp, Array]
const KeepAlive = {
  name: 'keep-alive',
  abstract: true,

  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number],
  },

  created() {
    this.cache = Object.create(null)
    this.cachedNameKeyMap = Object.create(null)
    this.keys = []
  },
  destroyed() {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },
  mounted() {
    this.$watch('include', (val) => {
      pruneCache(this, (name) => matches(val, name))
    })
    this.$watch('exclude', (val) => {
      pruneCache(this, (name) => !matches(val, name))
    })
  },
  render() {
    const slot = this.$slots.default
    const vnode = getFirstComponentChild(slot)
    const componentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      const name = getComponentName(componentOptions)
      const { include, exclude } = this
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
      const { cache, cachedNameKeyMap, keys } = this
      // 解决方案:处理key值,加上componentOptions.Ctor.cid
      const key = vnode.key
        ? componentOptions.Ctor.cid +
          (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key + componentOptions.Ctor.cid

      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key)
        keys.push(key)
      } else {
        cache[key] = vnode
        keys.push(key)
        // prune old component for hmr
        if (name && cachedNameKeyMap[name] && cachedNameKeyMap[name] !== key) {
          pruneCacheEntry(cache, cachedNameKeyMap[name], keys)
        }
        cachedNameKeyMap[name] = key
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  },
}
// ovveride original keep-alive
process.env.NODE_ENV === 'development' && Vue.component('KeepAlive', KeepAlive)

@caroundsky
Copy link
Owner Author

Vue2 在template中渲染vnode

要在视图中渲染vnode,需要用组件进行中转,比如定义一个函数式组件VNodes,其render函数内返回vnode

<SomeComponent v-slot="{ vnodes }">
  <div>
    <VNodes :vnodes="vnodes"/>
  </div>
</SomeComponent>
components: {
  VNodes: {
    functional: true,
    render: (h, ctx) => ctx.props.vnodes,
  },
},

@caroundsky caroundsky changed the title Vue Vue2 May 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant