之前做过一个Vue JSX和函数式编程组合应用,使组件的部分Dom的具体实现可以变的更加灵活
(# Vue JSX 和 函数式组件)。
但是在实际使用中发现JSX的组件和Vue的函数式编程,只能做一些比较简单的DOM操作兼容性不好,如果需要在这个函数式组件中,进行一些复杂的点击事件或者逻辑判断交互时,就会有一些力不从心。随着对Vue的持续了解,原来不太懂的h渲染函数也有了一些新的理解。发现其实h渲染函数和函数时组件结合使用,是可以满足我对于这个组件的一些复杂逻辑的操作和点击事件的处理。
当然在实际工作中,还会遇到一些DOM嵌套之后数据处理的问题,因为JSX的组件在Vue 2.0 版本中并不好用,h渲染函数来处理DOM嵌套的数据又显得非常笨重复杂,此时想要处理一些复杂嵌套DOM的问题,就需要使用slot-scope作用域插槽来处理了DOM嵌套比较复杂的问题。
下面正文开始,我将我的一些使用心得总结一下,希望对大家有所帮助。
h渲染函数与函数式组件 结合使用的方式:
函数式组件:
简单的来说,函数式组件就是一个对象,这个对象有一个functional属性为true,一个render属性对应一个函数,这个函数的第一个参数就是渲染函数h,第二参数是整个组件的上下文context,里面包含很多这个组件声明的一些信息,还有一个props属性用于接收外界传给这个组件的数据。
let cusSlot = {
functional: true,
props: {
render: function (h,context) {
渲染函数h(也是createElement)
这个渲染函数返回的是一个虚拟的VNode,然后渲染挂载到浏览器上生成我们希望得到的DOM。
其实VNode是Vuejs中的一个类VNode创建出来的虚拟DOM节点,其实就是包含了这个这个虚拟DOM节点的所有的信息和属性。对应的就是DOM中的节点Node,本质上是一样的,只不过Vue对这个VNode做了自己的封装,然后这个节点可以更好的使用在这个框架页面上。(纯属个人理解,可能并不准确)
h函数有三个参数:
必填 {String | Object | Function}
一个 HTML 标签名、组件选项对象,或者是函数返回的一个值。
可选 {Object}
一个与模板中 attribute 对应的数据对象,这个对象的属性包含了这个VNode的所有的属性、事件和自定义命令等。
class:{String|Object|Array} 和页面组件的class一致。=== :class,
style:{String|Object|Array}。 === :style
attrs:{
...普通的 HTML attribute
props:{
...组件的prop
domProps: {
innerHTML: 'baz'
...DOM property
on:{
...VNode点击事件。
nativeOn:{
...1.仅用于组件,用于监听原生事件,而不是组件内部使用2.`vm.$emit` 触发的事件。
directives:[
...自定义指令
scopedSlots: {
default: props => createElement('span', props.text)
...作用域插槽的格式为 { name: props => VNode | Array<VNode> }
slot: 'name-of-slot',
key: 'myKey',
ref: 'myRef',
refInFor: true
可选 {String | Array}
子级虚拟节点 (VNodes)。因为一个VNode中的子级节点可以是一个VNode,也可以是一段文字,也可以是一个组件渲染而成的VNode,甚至是一段注释。所以这个参数可以是由 createElement()
构建而成的VNode,也可以使用字符串来生成“文本虚拟节点”。例如:[ '子级文本数据'
, createElement('h1', '虚拟DOM节点')
, createElement(MyComponent, { props: { someProp: 'foobar' } })
]
tableComponent.vue
<template>
<div class="tableBox">
<h3>当前日期:{{ $utlizeFun.dateFormat(Date.now()) }}</h3>
<table width="100%" v-if="tableDatas.length > 0">
<colgroup>
v-for="title in tableTitles"
:key="title.prop"
:width="title.width"
</colgroup>
<thead>
<tr v-for="i of titleNum" :key="i + 'theadNum'" height="40px">
<template v-for="title in tableTitles">
<template
v-if="
parseInt(title[`colspan${i}`]) > 0 &&
parseInt(title[`rowspan${i}`]) > 0
:key="title.prop + 'th' + i"
:class="title[`thClass${i}`]"
:colspan="parseInt(title[`colspan${i}`])"
:rowspan="parseInt(title[`rowspan${i}`])"
class="th-style"
:class="title[`thDivClass${i}`]"
:style="[title[`thDivStyle${i}`]]"
{{ title[`label${i}`] }}
</div>
</template>
<template
v-else-if="
parseInt(title[`colspan${i}`]) > 0 && !title[`rowspan${i}`]
:key="title.prop + 'th' + i"
:class="title[`thClass${i}`]"
:colspan="parseInt(title[`colspan${i}`])"
class="th-style"
:class="title[`thDivClass${i}`]"
:style="[title[`thDivStyle${i}`]]"
{{ title[`label${i}`] }}
</div>
</template>
<template
v-else-if="
parseInt(title[`rowspan${i}`]) > 0 && !title[`colspan${i}`]
:key="title.prop + 'th' + i"
:class="title[`thClass${i}`]"
:rowspan="parseInt(title[`rowspan${i}`])"
class="th-style"
:class="title[`thDivClass${i}`]"
:style="[title[`thDivStyle${i}`]]"
{{ title[`label${i}`] }}
</div>
</template>
<template
v-else-if="!title[`rowspan${i}`] && !title[`colspan${i}`]"
:key="title.prop + 'th' + i"
:class="title[`thClass${i}`]"
class="th-style"
:class="title[`thDivClass${i}`]"
:style="[title[`thDivStyle${i}`]]"
{{ title[`label${i}`] }}
</div>
</template>
</template>
</thead>
<tbody>
v-for="(data, index) in tableDatas"
:key="index + 'td'"
height="40px"
v-for="title in tableTitles"
:key="title.prop + 'tbodyTd'"
:class="title.tdClass"
<template v-if="title.render">
<cusSlot
:column="title"
:row="data"
:index="index"
:render="title.render"
</template>
<template v-else>
<slot :name="title.prop" :row="data">
:class="title.tdDivClass"
:style="[data[`${title.prop}Style`]]"
{{ data[title.prop] }}
</div>
</slot>
</template>
</tbody>
</table>
</div>
</template>
<script>
* tableTitles: 表头 label1:代表第一层级的表头名;
* prop:代表该表头对应的这一列的取值的属性值。
* width:表示这个表头的宽度。
* colspan1: 代表第一层的表头的列合并数
* rowspan2:代表第二层的表头的行合并数
* tableDatas: 表体 表格数据。
let cusSlot = {
functional: true,
props: {
column: Object,
row: Object,
index: Number,
render: Function,
render: (h, context) => {
let cell = {
column: context.props.column,
row: context.props.row,
index: context.props.index,
return context.props.render(h, cell);
export default {
name: "tableBox",
components: {
cusSlot,
props: {
tableTitles: {
type: Array,
default: () => [],
tableDatas: {
type: Array,
default: () => [],
titleNum: {
type: Number,
default: 1,
methods: {},
</script>
<style lang="less">
.tableBox {
width: 100%;
table {
border-collapse: collapse;
border-spacing: 0px; //去除表格一些默认样式
thead tr th {
padding: 7px 20px;
box-sizing: border-box;
tbody tr td {
padding: 6px 20px;
box-sizing: border-box;
thead {
border-top: 1px solid #000;
border-bottom: 1px solid #000;
position: relative;
&.th-style::before {
display: block;
content: "";
height: 30%;
width: 1px;
position: absolute;
background-color: #000;
left: 0;
top: 50%;
transform: translateY(-50%);
&.th-style:first-child::before {
display: none;
div {
font-size: 16px;
line-height: 1.5;
text-align: left;
tbody {
border-bottom: 1px solid #000;
border-bottom: 1px solid #ccc;
&:last-child {
border-bottom: unset !important;
div {
font-size: 16px;
line-height: 1.5;
text-align: left;
</style>
tableView.vue
<template>
<div class="home">
<h1>原生表格组件</h1>
<tableComponent :tableTitles="tableTitle" :tableDatas="tableDatas">
<template v-slot:projectType="{ row }">
<span style="color: red">{{ row.projectType }}</span>
</template>
<template v-slot:developmentType="{ row }">
<span style="color: blue">{{ row.developmentType }}</span>
</template>
<template v-slot:lifeCycleType="{ row }">
<template v-if="row.softType === '非嵌入'">
style="
height: 100%;
color: green;
border: 1px solid #000;
border-radius: 1em;
padding-left: 4px;
box-size: border-box;
{{ `${row.lifeCycleType} MVP` }}
</div>
</template>
<template v-else>
style="
height: 100%;
color: blue;
border: 1px solid gold;
border-radius: 1em;
padding-left: 4px;
{{ `${row.lifeCycleType} FMVP` }}
</div>
</template>
</template>
</tableComponent>
</div>
</template>
<script>
import { tableViewDataMixin } from "./tableViewData/tableViewDataMixin";
export default {
name: "HomeView",
mixins: [tableViewDataMixin],
</script>
tableViewDataMixin.js
export const tableViewDataMixin = {
data() {
return {
tableTitle: [
label1: "序号",
width: "100px",
prop: "SN",
label1: "项目类别",
prop: "projectType",
width: "200px",
label1: "软件类型",
prop: "softType",
width: "150px",
label1: "研制类型",
prop: "developmentType",
width: "150px",
label1: "生命周期类型",
prop: "lifeCycleType",
width: "200px",
label1: "更改规模是否必填",
prop: "changeIsRequired",
width: "200px",
label1: "更改规模是否显示",
prop: "changeIsShow",
width: "200px",
label1: "子项目包含类型",
prop: "subprojectContainsType",
width: "200px",
label1: "是否必须包含配置项",
prop: "isMustIncludeConfigurationItems",
width: "200px",
label1: "是否包含开发计划",
prop: "isIncludeDevelopmentPlan",
width: "200px",
label1: "操作",
prop: "operation",
width: "",
render: (h, cell) => {
return h("button", {
style: {
color: "red",
fontSize: "14px",
"background-color": "aquamarine",
"border-radius": "1em",
border: "1px solid #000",
on: {
click: () => this.deleteClick(cell),
domProps: {
innerText: "删除",