视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Vue学习笔记进阶篇之函数化组件解析
2020-11-27 22:34:24 责编:小采
文档


这两天学习了Vue.js 感觉函数化组件这个地方知识点挺多的,而且很重要,所以,今天添加一点小笔记

介绍

之前创建的锚点标题组件是比较简单,没有管理或者监听任何传递给他的状态,也没有生命周期方法。它只是一个接收参数的函数。

在这个例子中,我们标记组件为 functional, 这意味它是无状态(没有 data),无实例(没有 this 上下文)。

一个 函数化组件 就像这样:

Vue.component('my-component', {
 functional: true,
 // 为了弥补缺少的实例
 // 提供第二个参数作为上下文
 render: function (createElement, context) {
 // ...
 },
 // Props 可选
 props: {
 // ...
 }
})

组件需要的一切都是通过上下文传递,包括:

  1. props: 提供props 的对象
  2. children: VNode 子节点的数组
  3. slots: slots 对象
  4. data: 传递给组件的 data 对象
  5. parent: 对父组件的引用
  6. listeners: (2.3.0+) 一个包含了组件上所注册的 v-on 侦听器的对象。这只是一个指向 data.on 的别名。
  7. injections: (2.3.0+) 如果使用了 inject 选项, 则该对象包含了应当被注入的属性。

在添加 functional: true 之后,锚点标题组件的 render 函数之间简单更新增加context参数,this.$slots.default 更新为 context.children,之后this.level 更新为 context.props.level。

因为函数化组件只是一个函数,所以渲染开销也低很多。另外,这也意味着函数化组件不会出现在 VueJS Chrome 开发者工具的组件树里。

在作为包装组件时它们也同样非常有用,比如,当你需要做这些时:

程序化地在多个组件中选择一个

在将 children, props, data 传递给子组件之前操作它们。

下面是一个依赖传入 props 的值的smart-list组件例子,它能代表更多具体的组件:

var EmptyList = { /* ... */ }
var TableList = { /* ... */ }
var OrderedList = { /* ... */ }
var UnorderedList = { /* ... */ }
Vue.component('smart-list', {
 functional: true,
 render: function (createElement, context) {
 function appropriateListComponent () {
 var items = context.props.items
 if (items.length === 0) return EmptyList
 if (typeof items[0] === 'object') return TableList
 if (context.props.isOrdered) return OrderedList
 return UnorderedList
 }
 return createElement(
 appropriateListComponent(),
 context.data,
 context.children
 )
 },
 props: {
 items: {
 type: Array,
 required: true
 },
 isOrdered: Boolean
 }
})

slots()和children对比

你可能想知道为什么同时需要 slots()childrenslots().default 不是和 children 类似的吗?在一些场景中,是这样,但是如果是函数式组件和下面这样的 children 呢?

<my-functional-component>
 <p slot="foo">
 first
 </p>
 <p>second</p>
</my-functional-component>

对于这个组件,children 会给你两个段落标签,而 slots().default 只会传递第二个匿名段落标签,slots().foo 会传递第一个具名段落标签。同时拥有 children 和 slots() ,因此你可以选择让组件通过 slot() 系统分发或者简单的通过 children 接收,让其他组件去处理。

示例

渐进过渡

之前的Vue学习笔记进阶篇——列表过渡及其他中可复用的过渡提到用函数组件实现合适,下面就用函数化组件来实现那个渐进过渡

<div id="app5">
 <input v-model="query">
 <my-transition :query="query" :list="list">
 <li v-for="(item, index) in computedList"
 :key="item.msg"
 :data-index="index">
 {{item.msg}}
 </li>
 </my-transition>
</div>
 Vue.component('my-transition', {
 functional:true,
 render:function (h, ctx) {
 var data = {
 props:{
 tag:'ul',
 css:false
 },
 on:{
 beforeEnter:function (el) {
 el.style.opacity = 0
 el.style.height = 0
 },
 enter:function (el, done) {
 var delay = el.dataset.index * 150
 setTimeout(function () {
 Velocity(el, {opacity:1, height:'1.6em'},{complete:done})
 }, delay)
 },
 leave:function (el, done) {
 var delay = el.dataset.index * 150
 setTimeout(function () {
 Velocity(el, {opacity:0, height:0}, {complete:done})
 }, delay)
 }
 }
 }
 return h('transition-group', data, ctx.children)
 },
 props:['query', 'list']
 })

 var app5 = new Vue({
 el:'#app5',
 data:{
 query:'',
 list:[
 {msg:'Bruce Lee'},
 {msg:'Jackie Chan'},
 {msg:'Chuck Norris'},
 {msg:'Jet Li'},
 {msg:'Kung Furry'},
 {msg:'Chain Zhang'},
 {msg:'Iris Zhao'},
 ]
 },
 computed:{
 computedList:function () {
 var vm = this
 return this.list.filter(function (item) {
 return item.msg.toLowerCase().indexOf(vm.query.toLowerCase()) !== -1
 })
 }
 },
 })

运行结果:

下载本文
显示全文
专题