vlambda博客
学习文章列表

Vue-cli的函数式组件与构造函数(整理文档-新手向)


在开始函数式组件前,首先需要介绍一下构造函数render的用法与基础原理,因为在实际开发中通过构造函数来对函数式组件进行复用也是常见的现象。

构造函数 Render

在官方文档中,对渲染函数(或构造函数)render的解释多少有些生涩,但具体介绍的内容,大概就是以下几点:

  • 首先是理论基础:虚拟DOM:

    • 这点当我看完《VUE.js的设计与实现》之后再做补充

  • 接着就是render函数的参数,在render函数中有一个固定的第一个参数的createElement函数作为参数,这个参数可以通过接收参数,返回一个vnode,即虚拟的DOM节点:render:function(createElement){},但有时候在开发中也会使用语法糖的写法render(h,params){}或者render:(h,params)=>{},其中的h,就是createEment语法的一种实现方式,代表的含义相同,params是根据需求传入的一些动态数据。

    • 此处,官网文档中有描述,内容如下:

      将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。从 Vue 的 Babel 插件的 3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入const h = this.$createElement,这样你就可以去掉 (h) 参数了。对于更早版本的插件,如果 h 在当前作用域中不可用,应用会抛错。

  • 而createElement 函数,在 HTML 文档中,Document.createElement() 方法用于创建一个由标签名称 tagName 指定的 HTML 元素。如果用户代理无法识别 tagName,则会生成一个未知 HTML 元素 HTMLUnknownElement

  • var element = document.createElement(tagName[, options]);
    • tagName:指定要创建元素类型的字符串,创建元素时的 nodeName 使用 tagName 的值为初始化,该方法不允许使用限定名称(如:"html:a"),在 HTML 文档上调用 createElement() 方法创建元素之前会将tagName 转化成小写,在 Firefox、Opera 和 Chrome 内核中,createElement(null) 等同于 createElement("null")

    • options:一个可选的参数 ElementCreationOptions 是包含一个属性名为 is 的对象,该对象的值是用 customElements.define() 方法定义过的一个自定义元素的标签名。为了向前兼容较老版本的 Custom Elements specification, 有一些浏览器会允许你传一个值为自定义元素的标签名的字符串作为该参数的值。可以参考本页下方的 Web component example Google 的 Extending native HTML elements 文档仔细了解如何使用该参数。

    • 以上是官方文档对createElement函数的定义,总之,此函数在作为render函数的参数时,可以传入三个参数,第一个参数来给定创建虚拟节点的节点名称,如:'div','a','el-image'等,也可以定义一段html格式的代码片段作为对象直接传入(也就是这个参数可选的三种格式):

    •     Vue.component('child', {
             props: ['level'],
             render: function (createElement) {
                 //string:html标签
                 return createElement('h1')
                 //object:一个含有数据选项的对象
                 return createElement({
                     template: '<div>谈笑风生</div>'
                })
                 //function:返回一个含有数据选项的对象
                 var domFun = function () {
                     return {
                         template: `<div>谈笑风生</div>`
                    }
                }
                 return createElement(domFun())
            }
        })
    • createElement函数的第二个参数就是在此节点下的数据对象,对象内可以配置当前节点的几个固定属性,接下来就常用的说明一下:

       render: function(createElement) {//this.$slots在render函数中的应用
           // debugger
            let header = this.$slots.header;
            console.log(header);
            let main = this.$slots.default;
            let footer = this.$slots.footer;
            return createElement('div',[
            createElement('header',header),
            createElement('main',main),
            createElement('footer',footer)
            ] )
          }
    • 第三个参数:常常类型为 String 或 Array (Array用的更多一些),作为构建函数的子节点来使用的。上面在介绍第二个参数的时候创建子节点时就已经用到此参数,下面摘抄别人的一些使用介绍:

    render: (h, params) => {
       return h("div", [
           h("el-image", {
               props: {//当前组件从父组件那里接收的参数,通俗的讲和$attr差不多,但是只包括在当前组件中定义了的props属性。Vue 实例代理了对其 props 对象 property 的访问。
                   size: 40
              },
               attrs: {//当前组件的属性,通俗的讲也就是在组件标签定义的一系列属性,如input的value,placeholder等,但是不包括在当前组件里面定义的props属性
                   src: params.row.goodsImageUrl,
                   previewSrcList: [params.row.goodsImageUrl]//elementUi 通过 previewSrcList 预览大图
              },
               on:{},//甚至能添加事件,我还没用过。
               style: {//样式,不多说
                   width: "60px",
                   height: "60px",
                   borderRadius: "5px",
              }
          }),
      ]);
    },
       
    属性小汇总:
    class: v-bind/:class

    style:v-bind/:style

    attrs:dom属性,如id

    props:props,

    on:自定义事件等,

    nativeOn:原生事件
    • createElement函数的第二个参数就是在此节点下的数据对象,对象内可以配置当前节点的几个固定属性,接下来就常用的说明一下:

    • 第三个参数:常常类型为 String 或 Array (Array用的更多一些),作为构建函数的子节点来使用的。上面在介绍第二个参数的时候创建子节点时就已经用到此参数,下面摘抄别人的一些使用介绍:

    函数式组件

  • 函数式组件其实与render函数在使用上有很多相似之处,在我看来,函数式组件就是render的一种模块化的实现方式,先放出一段代码:

  • export default {

     name: 'RenderSlot',//顾名思义,给组件定义名字的属性
     functional: true,//我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。即设置为true 表示该组件为一个函数组件
     props: {//注意:在 2.3.0 之前的版本中,如果一个函数式组件想要接收 prop,则 props 选项是必须的。在 2.3.0 或以上的版本中,你可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。当使用函数式组件时,该引用将会是 HTMLElement,因为他们是无状态的也是无实例的。
       row: Object,
       render: Function,//这里从调用时接收的render函数
       index: Number,
       column: {
         type: Object,
         default: null
      }
    },
     //这个render函数是用来挂载组件本身,因为函数式组件没有实例,所以这里使用data代替了this指向了当前组件中的属性
     render(h, data) {
       const params = {
         row: data.props.row,
         index: data.props.index
      }
       if (data.props.column) params.column = data.props.column
       return data.props.render(h, params)//通过接收从父组件传过来的render函数,并接受了当前组件内接收到的数据,达到了组件结构和组件数据对象的动态配置。
    }
    }


    //父组件内调用
    <renderSlot
       v-if="render"
      :row="scope.row"
      :index="scope.$index"
      :render="render"
      :column=""
    />
           
    render: (h, params) => {
       return h("div", [
           h("el-image", {
               props: {
                   size: 40
              },
               attrs: {
                   src: params.row.goodsImageUrl,
                   previewSrcList: [params.row.goodsImageUrl]
              },
               style: {
                   width: "60px",
                   height: "60px",
                   borderRadius: "5px",
              }
          }),
      ]);
    },
  • 函数式组件和普通组件有啥区别?


    • 不维护响应数据

    • 没有instance实例所以在组件内部没有办法像传统组件一样通过this来访问组件属性实现原理见下面代码中的中文注释

    • 渲染快

    • 没有实例,意味着没有(this)

    • 没有生命周期(没有钩子函数,没有响应式数据)