Vue 中的无状态组件使用说明_组件指南教程

啥是应用程序状态,为什么咱们需要它?状态管理通常在较小的项目并不需要,但是当涉及到更大的范围时,如企业级的应用大部分需要它了。简单的说,状态是一个包含应用程序使用的最新值的对象。但是,如果咱们从结构的、更抽象的角度来看待它,就会清楚地看到,状态是复杂应该中重要一块,它使能够构建干净的体系结构,并将关注点强有力地分离开来。通常,缺乏经验的开发人员无法预测对状态

Vue 中的无状态组件使用说明

啥是应用程序状态,为什么咱们需要它?

状态管理通常在较小的项目并不需要,但是当涉及到更大的范围时,如企业级的应用大部分需要它了。简单的说,状态是一个包含应用程序使用的最新值的对象。但是,如果咱们从结构的、更抽象的角度来看待它,就会清楚地看到,状态是复杂应该中重要一块,它使能够构建干净的体系结构,并将关注点强有力地分离开来。

Vue 中的无状态组件使用说明_组件指南教程

通常,缺乏经验的开发人员无法预测对状态管理的需求,以及如何实现状态管理,因此很难了解状态管理的重要性。如果基于状态的组件堆积起来,它们之间的数据管理和共享将成为一场噩梦。从长远来看,拥有的基于状态的组件越多,出现的问题就越多。

如果没有使用外部包进行状态管理,那么最好尽可能少地使用基于状态的组件,而展示组件则使用围绕它们构建的状态。

Vue 和无状态(函数)组件

Vue 中的无状态组件其实就是函数组件。但函数组件又是啥呢? 要回答这个问题,咱们首先必须理解什么是函数式编程。

与将程序分解为对象的面向对象方法不同,函数式编程鼓励将程序分解为小函数,这些小函数用于形成更高级的程序。我们创建的函数不依赖于或可以改变任何外部状态,这导致另一个观察结果,对于给定的输入,它们总是返回相同的输出。

因此,函数组件是没有状态的组件,并且可以更改它。函数组件输出总是基于给定的输入。在 Vue 方面,这类组件会根据给定的props给出不同的输出。

语法

Vue 提供了一种定义函数组件的简单方法。咱们只需要给个 functional 关键字就可以。在 2.5.0 及以上版本中,如果使用了单文件组件,那么基于模板的函数式组件可以这样声明::

<template functional>
  <div> 函数/无状态组件 </div>
</template>

或者

export default {
  functional: true,
  props: {
    // ...
  },
  render(createElement, context) {
    return createElement(
      'div', '函数/无状态组件'
    )
  }
}

需要注意的是,传递给函数组件的惟一数据是props。这些组件是完全无状态的(没有响应数据),它们忽略传递给它们的任何状态,并且不触发任何生命周期方法(created、mounted等等)。

而且,咱们也不能通过使用 this 关键字来访问实例,因为这些组件也是不实例化的。相反,组件需要的所有东西都是通过context提供的。在render函数中,它作为createElement方法的第二个参数传递。

组件需要的一切都是通过 context 参数传递,它是一个包括如下字段的对象:

  • props:提供所有 prop 的对象
  • children: VNode 子节点的数组
  • slots: 一个函数,返回了包含所有插槽的对象
  • scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
  • data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
  • parent:对父组件的引用
  • listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
  • injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

为什么咱们需要无状态组件

到目前为止,咱们已经了解到函数组件是无状态的,在它们的核心中,它们只是可执行的函数,接受一些输入并根据其提供输出。

就它们的用法而言,因为函数式组件只是函数,所以渲染开销也低很多,这也意味着它们是非常高效的,不需要花太多时间渲染。同时,考虑高阶组件,它们不需要任何状态,它们所要做的就是用额外的逻辑或样式包装给定的子组件。

接下来,通例事例展示一样啥时使用函数组件,函数组件非常适合此类任务。

实例

在这个示例中,咱们创建一个panel组件,它充当一个包装器,并提供所需的样式。子组件将在panel 主体中渲染:

export default {
  name: 'panel',
  functional: true,
  props: {
    title: String
  },
  render(createElement, context) {
    const slots = context.slots();

    const header = createElement('header', {
      attrs: { class: 'panel-header'}
    }, context.props.title);
    
    const body = createElement('main', {
      attrs: { class: 'panel-body'}
    }, slots.default);

    return createElement('section', {
      attrs: { class: 'panel' }
    }, [header, body]);
  }
}

如上所述,此组件的唯一目的是提供类似于面板(卡片)的样式,它有header 和main元素,分别保存面板标题和HTML内容。整个过程是通过使用render函数中的createElement参数在中完成。createElement是 Vue 核心中实现的虚拟 Dom 系统的一部分。

虚拟 DOM

Vue 通过建立一个虚拟 DOM 来追踪自己要如何改变真实 DOM。请仔细看这行代码:

return createElement('h1', this.blogTitle)

createElement 到底会返回什么呢?其实不是一个实际的 DOM 元素。它更准确的名字可能是 createNodeDescription,因为它所包含的信息会告诉 Vue 页面上需要渲染什么样的节点,包括及其子节点的描述信息。我们把这样的节点描述为“虚拟节点 (virtual node)”,也常简写它为“VNode”。“虚拟 DOM”是我们对由 Vue 组件树建立起来的整个 VNode 树的称呼。

createElement 参数

接下来你需要熟悉的是如何在 createElement 函数中使用模板中的那些功能。这里是 createElement 接受的参数:

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一个 HTML 标签名、组件选项对象,或者
  // resolve 了上述任何一种的一个 async 函数。必填项。
  'div',

  // {Object}
  // 一个与模板中属性对应的数据对象。可选。
  {
    // (详情见下一节)
  },

  // {String | Array}
  // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
  // 也可以使用字符串来生成“文本虚拟节点”。可选。
  [
    '先写一些文字',
    createElement('h1', '一则头条'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

面板 CSS 样式如下:

.panel {
  margin-bottom: .5rem
}

.panel, .panel-header {
    border: 1px solid #d3d3d3;
    border-radius: 4px;
}

.panel-header, .panel-body, .panel {
  padding: .5rem;
}

.panel-header {
  background-color:#efefef;
  color: #eeeee
}

这是一个简单直接的 CSS,提供了一些padding 和color。

子组件

现在,为了让例子更加生动为此,咱们再创建两个附加组件,一个显示汽车列表,另一个只是一个简单lorem-ipsum的文本组件,要求它们具有相同的面板样式和外观。

列表组件:

export default {
  name: 'cars',
  props: {
    data: Array
  }
}

template:

<template>
  <ul>
    <li v-for="car in data" :key="car">{{car}}</li>
  </ul>
</template>

文本组件:

export default {
  name: 'lorem-ipsum'
}

template:

<template>
  <p>
   终身学习者,终身学习者,终身学习者,终身学习者,终身学习者
  </p>
</template>

现在,有了可用的子组件,咱们所需要做的就是用panel组件将它们封装到应用程序中,如下所示:

<div class="vue-app">
  <panel :title="'Car Manufacturers'">
    <cars :data="['Mazda', 'Ford', 'Mercedes']"></cars>
  </panel>
  <panel :title="'Lorem Ipsum'">
    <lorem-ipsum></lorem-ipsum>
  </panel>
</div>

完整代码

hmtl

<div class="vue-app">
  <panel :title="'Car Manufacturers'">
    <cars :data="['Mazda', 'Ford', 'Mercedes']"></cars>
  </panel>
  <panel :title="'Lorem Ipsum'">
    <lorem-ipsum></lorem-ipsum>
  </panel>
</div>

<script type="text/x-template" id="cars">
  <template>
    <ul>
      <li v-for="car in data" :key="car">{{car}}</li>
    </ul>
  </template>
</script>

<script type="text/x-template" id="lorem-ipsum">
  <template>
    <p>前端小智, 终身学习者,终身学习者,终身学习者,终身学习者,终身学习者</p>
  </template>
</script>

css

body {
  padding: .5rem
}

* {
  padding: 0;
  margin:0;
  box-sizing: border-box;
}

.panel {
  margin-bottom: .5rem
}

.panel, .panel-header {
    border: 1px solid #d3d3d3;
    border-radius: 4px;
}

.panel-header, .panel-body, .panel {
  padding: .5rem;
}

.panel-header {
  background-color:#efefef;
  color: #eeeee
}

ul {
  list-style: none;
}

ul > li {
  padding: .5rem .2rem
}

js

// the wrapper panel
const panel = {
  functional: true,
  name: "panel",
  props: {
    title: String
  },
  render(createElement, context) {
    const slots = context.slots();

    const header = createElement('header', {
      attrs: { class: 'panel-header'}
    }, context.props.title);
    
    const body = createElement('main', {
      attrs: { class: 'panel-body'}
    }, slots.default);

    return createElement('section', {
      attrs: { class: 'panel' }
    }, [header, body]);
  }
}

// sample components

const cars = {
  name: 'cars',
  template: '#cars',
  props: {
    data: Array
  }
}

const loremIpsum = {
  name: 'lorem-ipsum',
  template: '#lorem-ipsum'
}

new Vue({
  el: '.vue-app',
  components: {
    panel,
    cars,
    'lorem-ipsum': loremIpsum
  }
});
海计划公众号
(0)
上一篇 2020/03/23 01:37
下一篇 2020/03/23 01:37

您可能感兴趣的内容

  • Node.js FS模块方法速查基础入门_node小白常识

    1. File System所有文件操作提供同步和异步的两种方式,本笔记只记录异步的API异步方式其最后一个参数是回调函数。回调函数的第一个参数往往是错误对象,如果没有发生参数,那么第一个参数可能是null或者undefinded。同步函数可以使用try catch 捕获异常多个异步函数在同一层次执行,是无法保证顺序的。最好将一个函数放在另一个函数的回调函数

    2020/04/03
  • Proxy及其优势小白教程_Proxy小白攻略

    什么是 Proxy通常,当谈到JavaScript语言时,我们讨论的是ES6标准提供的新特性,本文也不例外。 我们将讨论JavaScript代理以及它们的作用,但在我们深入研究之前,我们先来看一下Proxy的定义是什么。MDN上的定义是:代理对象是用于定义基本操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。换句话说,我们可以说代理对象是我们的目标

    2020/03/29
  • 前端程序员应该知道的15个jQuery小技巧入门教程_jquery小白教程

    1、返回顶部按钮通过使用jQuery中的animate 和scrollTop 方法,不用插件就可以创建一个滚动到顶部的简单动画:// Back to top
    $(‘.top’).click(function (e) {
    e.preventDefault();
    $(‘html, body’).animate({scrollTop: 0}, 800);
    });

    2020/04/05
  • 小程序实现瀑布流和上拉加载入门知识_瀑布流使用教程

    小程序实现瀑布流和上拉加载入门知识 瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式。视觉表现为参差不齐的多栏布局,即多行等宽元素排列,后面的元素依次添加到其后,等宽不等高…

    2020/03/19
  • 被嫌弃的程序员的一生使用攻略_程序员基础知识入门

    本文转载于100offer公众号(ID:im100offer)。100offer是一个帮助高端人才找工作的平台,长期关注互联网行业动态与职业发展。 程序员从早前的一种职业发展至今,俨然已经成为大众眼中的「特殊物种」。关于程序员的调侃与段子也盛产于网络,常常引起全网围观。但是程序员说到底并不是「两耳不闻窗外事,一心只用敲代码」,他们也有生活和工作上的烦恼与曲

    2020/03/30
  • js获取任意一天的0点和23:59:59时间基础教程_时间基础教程

    最近写代码时,需要获取任意一天的起始和结束时间,0点和23:59:59这两个时间的时间戳使用了setHours() 方法setHours() 方法用于设置指定的时间的小时字段1. 获取当天开始时间new Date(new Date(new Date().toLocaleDateString()).getTime()))2. 获取当天结束时间new Date(

    2020/03/26
  • JavaScript中,数组和对象的遍历方法总结基础知识_遍历入门攻略

    循环遍历是写程序很频繁的操作,JavaScript 提供了很多方法来实现。这篇文章将分别总结数组和对象的遍历方法,新手可以通过本文串联起学过的知识。数组遍历方法一:for 循环for 循环是使用最多,也是性能优化最好的一种遍历方式。var arr = [“a”, “b”, “c”];
    for (var i = 0; i < arr.length; i++)

    2020/03/20
  • CSS开发中的十大错误用法基础知识教程_错误入门知识

    自从接触前端软件开发以来,我发现开发猿一直在努力征服着CSS。 理由也很充分,开发人员是用逻辑思考的生物。添加一个DIV元素导致所有代码都不得不往下移一行,而另一个DIV“浮”到左侧,感觉没有任何意义。你也一定听到过开发人员的抱怨:“我们只需要向左边移动五个像素,但是…天哪!为什么整个都向下移动了一行。到底是哪里错了?!?!?!”所以,这篇文章就和大家来聊一

    2020/03/20
  • javascript是什么?有哪些特点?小白指南_特点攻略教程

    JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它是广泛用于客户端的脚本语言,最早是在HTML网页上使用,用来给HTML网页增加动态功能。JavaScript是一种属于网络的脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。通常JavaScript脚本是通

    2020/03/31
  • 深入理解requestAnimationFrame的动画循环入门基础知识_动画使用指南

    一、初识requestAnimationFrame
    requestAnimationFrame解决了浏览器不知道javascript动画什么时候开始、不知道最佳循环间隔时间的问题。它是跟着浏览器的绘制走的,如果浏览器绘制间隔是16.7ms,它就按这个间隔绘制;如果浏览器绘制间隔是10ms, 它就按10ms绘制。这样就不会存在过度绘制的问题,动画不会丢帧。

    2020/03/20
  • h5网页加背景音乐指南攻略_音乐入门攻略

    html插入mp3代码: 上面的代码在浏览器是不显示的,因此,我们给个div放播放的图标:

    <

    2020/03/20
  • js中箭头函数的编码规范,如何更好的使用箭头函数教程视频_规范使用帮助

    当您必须使用匿名函数(如在传递一个内联回调时),请使用箭头函数表示法,它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。如果你有一个相当复杂的函数,你或许可以把逻辑部分转移到一个声明函数上。// bad
    [1, 2, 3].map(function (x) {const y = x + 1;return x * y

    2020/04/05
  • js中typeof和instanceof原理小白知识_类型入门知识

    JavaScript数据类型JavaScript有八种内置类型空值(null)未定义(undefined)布尔值(boolean)数字(number)字符串(string)对象 (object)符号(symbol, ES6中新增)大整数(BigInt, ES2020 引入)除对象外,其他统称为“基本类型”。typeof null // ‘object’
    ty

    2020/03/20
  • MySQL设置时区和默认编码小白帮助_mysql入门基础

    情况描述学习spring boot时要在Windows本地安装MySQL5.7,配置好之后项目,启动之后提示需要设置时区,并且在使用过程中发现出现乱码,中文无法显示,出现上述问题的主要是MySQL5.7中没有设置时区和编码。解决方案1、在spring boot的配置文件application.properties​中设置MySQL的数据源urlspring.

    2020/03/24
  • 程序员的故事:坚持,十年如一日菜鸟攻略_故事攻略教程

    我是谁2000年,刚上初一,第一次去网吧看到别人都有网名,自己也取个吧,当时在看古龙的武侠小说,想着自己也要当个潇洒的侠客,想了想,就叫“浪子神剑”吧,然而却想不到的是,这一用就是将近20年。从此,浪哥、剑哥、浪子、神剑等琅琅上口!高中时一直有个梦想就是要考上军校“国防科技大学”,因为儿时就有着兵哥的梦想,可是事与愿违,最后去了东北上起了大学。还好自己心态调

    2020/04/03
  • 如何利用 策略模式 优化表单验证小白入门_表单使用帮助

    如何利用 策略模式 优化表单验证小白入门 背景 在做移动端项目开发的时候,经常会遇到各种表单验证,有时候不同的页面,验证规则是一样的 ,如图 之前项目里的验证代码写的很凌乱,最近刚…

    2020/03/20