vue函数式组件详解
本篇将详细介绍vue组件化之函数式组件,会用到以下api:
Vue.component()、Vue.extend()、$createElement、patch()。
从事vue开发的小伙伴,平时组件化的过程中大多都采用的vue文件+模块化系统的方式吧。例如:
import ComponentA from './ComponentA.vue'
export default {
  components: {
    ComponentA
  },
  // ...
}
如果你看过官方文档,了解过vue的组件化,你会发现vue提供创建组件的另一种思路:函数式组件。我身边有从事vue开发的朋友,他们有的对函数式组件并没什么概念,也没有在项目中实际的使用过,下面将和大家一起复习函数式组件的创建和使用。
官网的函数式组件示例:
Vue.component('my-component', {
  functional: true,
  // Props 是可选的
  props: {
    // ...
  },
  // 为了弥补缺少的实例
  // 提供第二个参数作为上下文
  render: function (createElement, context) {
    // ...
  }
})
将以上示例适当修改,并引入到我们项目中:
child.js
import Vue from 'vue';
export default Vue.component('my-component',{ // 该组件抽成js文件,
    functional: true,
    // Props 是可选的
    props: {
      // ...
    },
    // 为了弥补缺少的实例
    // 提供第二个参数作为上下文
    render: function (createElement, context) {
      return createElement('h1', '我是函数式子组件')
    }
  })
这里我将该组件抽成单独的js文件,便于复用和维护。在父组件引入该组件:
parent.vue:
<template>
<div>
<h1>我是父组件</h1>
<child />
<my-component />
</div>
</template> <script>
import child from './chid.js'
export default {
name: "parent",
components: {
child
},
data() {
return { };
},
mounted(){ },
methods:{ }
};
</script>
效果:

你会发现 <child />和<my-component />都能引入到父组件中,前者好理解,import引入后component中注册,后者为啥能直接用呢?是因为Vue.component()注册的是全局组件!
我们再增加一个子组件(跟上面的组件同名):
import Vue from 'vue';
//这是函数式组件2
export default Vue.component('my-component',{ // 同名
functional: true,
// Props 是可选的
props: {
// ...
},
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
return createElement('h1', '我是函数式子组件2')
}
})
现在看看运行效果:

结果会发现,"函数式组件2"被覆盖了!由于Vue.component()同名的组件会覆盖,也因为全局组件不好辨别当前的组件名是否已经注册,所以建议使用Vue.extend()来新建函数式组件。
Vue.extend使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
Vue.extend相当于一个扩展实例构造器,用于创建一个具有初始化选项的Vue子类,在实例化时可以进行扩展选项,最后使用$mount方法绑定在元素上。$mounte会替换被挂载节点下的内容!
Vue.extend和Vue.component之间的关系:
<template>
<div>
<h1>我是父组件</h1>
<div id="parent"></div>
<com />
</div>
</template> <script>
import child from "./child.js";
import Vue from 'vue';
Vue.component('com', child) ...
Vue.extend可以当做Vue.component的组件选项。
下面用Vue.extend()创建组件:
child.js:
import Vue from 'vue';
export default Vue.extend({,
  // Props 是可选的
  props: {
  },
  template: `<div>我是extend函数式子组件</div>`
})
这里使用的template写法,vue底层执行的时候会将template解析成AST,然后将AST转化为render函数,render的过程vue帮我们处理就好了,所以不习惯写render函数的同学可以用template。
parent.vue:
<template>
<div>
<h1>我是父组件</h1>
<div id="parent"></div> </div>
</template> <script>
import child from './child.js'
export default {
name: "parent",
components: { },
data() {
return { };
},
created(){ },
mounted(){
new child(
{
props: {
val:{
default:6
}
},
methods:{
func1(){
console.log("我是方法")
}
}
}
).$mount("#parent") // 用$mount()将child产生的实例挂载到id为parent的dom下
},
methods:{ }
};
</script>
child.js:
import Vue from 'vue';
export default Vue.extend({
  template: `<div>我是extend函数式子组件{{val}}</div>`,
  mounted(){
    this.func1()
  }
})
效果:

以上不管是Vue.component()还是Vue.extend()最终都是创建Vue的组件实例,它既不是虚拟dom也不是真实的dom节点。

业务中,有些ui库要求我们传入vNode或者真实的dom,例如element UI中的$confirm弹框中的message属性,既可以传普通的字符串,也可以传vNode。下面来手写一段vNode:
...
const h = this.$createElement; // 对$createElement不熟悉的可以查看vue文档 const vNode = h('p', null, [
h('span', null, '内容可以是 '),
h('i', { style: 'color: teal' }, 'VNode')
]); console.log(vNode);
打印:

上面的vNode结构非常简单,h函数的children参数可以手写,但如果vNode结构很复杂的话,手写就显得很凌乱。因此在h函数的第一个参数,我们可以传一个component组件。
<template>
<div>
<h1>我是父组件</h1>
<div id="parent"></div> </div>
</template> <script>
import child from './child.js'
export default {
name: "parent",
components: { },
data() {
return { };
},
created(){ },
mounted(){
const h = this.$createElement;
let vNode = h(child,{
props: {
val:8,
func1: ()=>{console.log('哈哈哈哈')}
}
})
console.log(vNode)
},
methods:{ }
};
</script>

接下来在elementUI中使用:
<template>
<div>
<h1>我是父组件</h1>
<div id="parent"></div>
</div>
</template> <script>
import child from "./child.js";
export default {
name: "parent",
components: {},
data() {
return {};
},
created() {},
mounted() {
const h = this.$createElement;
let vNode = h(child, {
props: {
val: 8,
func1: () => {
console.log("哈哈哈哈");
},
},
});
this.$confirm("提示", {
title: "提示",
message: vNode,
showCancelButton: true,
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => { });
console.log(vNode);
},
methods: {},
};
</script>

继续思考,上面ui组件会帮我们将vNode虚拟节点渲染到页面中,如果我想将vNode渲染到我们页面中的某个节点,该怎么实现呢?
其实vue的实例原型上有一个方法:__patch__,而patch函数就是vue中diff算法的核心函数,可以利用它来帮我们完成dom节点的"上树" !
<template>
<div>
<h1>我是父组件</h1>
<div id="parent"></div>
</div>
</template> <script>
import child from "./child.js";
export default {
name: "parent",
components: {},
data() {
return {};
},
created() {},
mounted() {
const h = this.$createElement;
let vNode = h(child, {
props: {
val: 8,
func1: () => {
console.log("哈哈哈哈");
},
},
});
const dom = document.getElementById("parent")
this.__patch__(dom,vNode) //dom是老节点(id为parent),vNode是我们即将渲染的新节点,通过diff算法重新渲染parent节点
},
methods: {},
};
</script>

总结,以上就是我对vue中创建函数式组件的理解,如果还有更佳的实现方式,欢迎留言~
脚踏实地行,海阔天空飞~
vue函数式组件详解的更多相关文章
- Vue.js 源码分析(三十) 高级应用 函数式组件 详解
		
函数式组件比较特殊,也非常的灵活,它可以根据传入该组件的内容动态的渲染成任意想要的节点,在一些比较复杂的高级组件里用到,比如Vue-router里的<router-view>组件就是一个函 ...
 - vue的组件详解
		
什么是组件 组件(Component)是 Vue.js 最强大的功能之一.(好比电脑中的每一个元件(键盘,鼠标,CPU),它是一个具有独立的逻辑和功能或界面,同时又能根据规定的接口规则进行互相融合,变 ...
 - Vue(七) 组件详解
		
组件 (Component) 是 Vue.js 最核心的功能,也是整个框架设计最精彩的部分,当然也是最难掌握的. 组件与复用 组件用法 组件与创建 Vue 实例类似,需要注册后才可以使用.注册有全局注 ...
 - vue.js基础知识篇(6):组件详解
		
第11章:组件详解 组件是Vue.js最推崇也最强大的功能之一,核心目标是可重用性. 我们把组件代码按照template.style.script的拆分方式,放置到对应的.vue文件中. 1.注册 V ...
 - vue 源码详解(二): 组件生命周期初始化、事件系统初始化
		
vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...
 - vue 文件目录结构详解
		
vue 文件目录结构详解 本篇文章主要介绍了vue 文件目录结构详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 项目简介 基于 vue.js 的前端开发环境,用于前后 ...
 - Angular6 学习笔记——组件详解之组件通讯
		
angular6.x系列的学习笔记记录,仍在不断完善中,学习地址: https://www.angular.cn/guide/template-syntax http://www.ngfans.net ...
 - Vue props用法详解
		
Vue props用法详解 组件接受的选项之一 props 是 Vue 中非常重要的一个选项.父子组件的关系可以总结为: props down, events up 父组件通过 props 向下传递数 ...
 - main.js index.html与app.vue三者关系详解
		
main.js index.html与app.vue三者关系详解 2019年01月23日 11:12:15 Pecodo 阅读数 186 main.js与index.html是nodejs的项目启 ...
 
随机推荐
- [Linux]经典面试题 - 网络基础 - TCP三次握手
			
[Linux]经典面试题 - 网络基础 - TCP三次握手 目录 [Linux]经典面试题 - 网络基础 - TCP三次握手 一.TCP报文格式 1.1 TCP报头 1.2 报文图例 二.TCP三次握 ...
 - csp-s模拟测试59(10.4)「Reverse」(set)·「Silhouette」(容斥)
			
A. Reverse 菜鸡wwb又不会了..... 可以线段树优化建边,然而不会所以只能set水了 发现对于k和当前反转点固定的节点x确定奇偶性所到达的节点奇偶性是一定的 那么set维护奇偶点,然后每 ...
 - 「模拟8.29」chinese(性质)·physics·chemistry(概率期望)
			
T1 chinese 根据他的问题i*f[i]我们容易联想到,答案其实是每种方案中每个点的贡献为1的加和 我们可以转变问题,每个点在所有方案的贡献 进而其实询问就是1-k的取值,有多少中方案再取个和 ...
 - Devops 改变coding —— 安装个指定版本的 jenkins 发现和想象的不太一样?
			
你好呀,我是小猿来也,一个刚开始折腾 Devops 的程序猿. 写在前面 前两天在池大那里看到了一段话,原话出自美团首席科学家夏华夏老师,具体内容我贴到了下面. 对于图片里的内容你们是怎么认为的呢?我 ...
 - [Django REST framework - 序列化组件、source、钩子函数]
			
[Django REST framework - 序列化组件.source.钩子函数] 序列化器-Serializer 什么是rest_framework序列化? 在写前后端不分离的项目时: 我们有f ...
 - Docker 镜像针对不同语言的精简策略
			
导航: 这里分为几个部分. 相关转载云原生:米开朗基杨 1.Docker减小镜像体积 2.Docker镜像针对不同语言的精简策略 对于刚接触容器的人来说,他们很容易被自己制作的 Docker 镜像体积 ...
 - Html:行级元素和块级元素标签列表
			
块级元素 div p h1-h6 form ul ol dl dt dd li table tr td th hr blockquote address table menu pre HTML5: h ...
 - Http2.0详解
			
前言 HTTP/1.1协议为现在网络提供了20年的支持.从那时起,网站已经从静态的.文本驱动的文档发展为交互式的.富媒体的应用程序.在此期间底层协议保持不变这一事实正好说明了它的通用性和能力.但随着网 ...
 - Linux | 搜索命令
			
grep grep 命令用于在文本中执行关键词搜索,并显示匹配的结果,格式:grep[选项][文本] grep命令的参数及其作用 参数 作用 -b 将可执行文件当作文本文件对待 -c 公显示找到的行数 ...
 - 源码解析Java Attach处理流程
			
前言 当Java程序运行时出现CPU负载高.内存占用大等异常情况时,通常需要使用JDK自带的工具jstack.jmap查看JVM的运行时数据,并进行分析. 什么是Java Attach 那么JVM自带 ...