vue组件三大核心概念图文详解

前言

本文主要介绍属性、事件和插槽这三个vue基础概念、使用方法及其容易被忽略的一些重要细节。如果你阅读别人写的组件,也可以从这三个部分展开,它们可以帮助你快速了解一个组件的所有功能。

本文的代码请猛戳 github博客 ,纸上得来终觉浅,大家动手多敲敲代码!

一、属性

1.自定义属性props

prop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的。写通用组件时,props 最好用对象的写法,这样可以针对每个属性设置类型、默认值或自定义校验属性的值,这点在组件开发中很重要,然而很多人却忽视,直接使用 props 的数组用法,这样的组件往往是不严谨的。

// 父组件
 <props name='属性'
      :type='type'
      :is-visible="false"
      :on-change="handlePropChange"
      :list=[22,33,44]
      title="属性Demo"
      class="test1"
      :class="['test2']"
      :style="{ marginTop: '20px' }" //注意:style 的优先级是要高于 style
      style="margin-top: 10px">
 </props>
// 子组件
 props: {
  name: String,
  type: {
  //从父级传入的 type,它的值必须是指定的 'success', 'warning', 'danger'中的一个,如果传入这三个以外的值,都会抛出一条警告
   validator: (value) => {
    return ['success', 'warning', 'danger'].includes(value)
   }
  },
  onChange: {
  //对于接收的数据,可以是各种数据类型,同样也可以传递一个函数
   type: Function,
   default: () => { }
  },
  isVisible: {
   type: Boolean,
   default: false
  },
  list: {
   type: Array,
   // 对象或数组默认值必须从一个工厂函数获取
   default: () => []
  }
 }

从上面的例中,可以得出props 可以显示定义一个或一个以上的数据,对于接收的数据,可以是各种数据类型, 同样也可以传递一个函数。

2.inheritAttrs

这是2.4.0 新增的一个API,默认情况下父作用域的不被认作 props 的特性绑定将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。可通过设置 inheritAttrs 为 false,这些默认行为将会被去掉。注意: 这个选项不影响 class 和 style 绑定

上个例中,title属性没有在子组件中props中声明,就会默认挂在子组件的根元素上,如下图所示:

3. data与props区别

相同点

两者选项里都可以存放各种类型的数据,当行为操作改变时,所有行为操作所用到和模板所渲染的数据同时都会发生同步变化。

不同点

data 被称之为动态数据,在各自实例中,在任何情况下,我们都可以随意改变它的 数据类型和数据结构 ,不会被任何环境所影响。

props 被称之为静态数据,在各自实例中,一旦在初始化被定义好类型时,基于 Vue 是单向数据流,在数据传递时始终不能改变它的数据类型,而且不允许在子组件中直接操作 传递过来的props数据,而是需要通过别的手段,改变传递源中的数据。至于如何改变,我们接下去详细介绍:

4.单向数据流

这个概念出现在组件通信。props的数据都是通过父组件或者更高层级的组件数据或者字面量的方式进行传递的,不允许直接操作改变各自实例中的props数据,而是需要通过别的手段,改变传递源中的数据。那如果有时候我们想修改传递过来的prop,有哪些办法呢?

方法1:过渡到 data 选项中

在子组件的 data 中拷贝一份 prop,data 是可以修改的

export default {
 props: {
  type: String
 },
 data () {
  return {
   currentType: this.type
  }
 }
}

在 data 选项里通过 currentType接收 props中type数据,相当于对 currentType= type进行一个赋值操作,不仅拿到了 currentType的数据,而且也可以改变 currentType数据。

方法2:利用计算属性

export default {
 props: {
  type: String
 },
 computed: {
  normalizedType: function () {
   return this.type.toUpperCase();
  }
 }
}

以上两种方法虽可以在子组件间接修改props的值,但如果子组件想修改数据并且同步更新到父组件,却无济于事。在一些情况下,我们可能会需要对一个 prop 进行『双向绑定』,此时就推荐以下这两种方法:

方法3:使用.sync

// 父组件
<template>
 <div class="hello">
  <div>
   <p>父组件msg:{{ msg }}</p>
   <p>父组件数组:{{ arr }}</p>
  </div>
  <button @click="show = true">打开model框</button>
  <br />
  <demo :show.sync="show" :msg.sync="msg" :arr="arr"></demo>
 </div>
</template>

<script>
import Demo from "./demo.vue";
export default {
 name: "Hello",
 components: {
  Demo
 },
 data() {
  return {
   show: false,
   msg: "模拟一个model框",
   arr: [1, 2, 3]
  };
 }
};
</script>
// 子组件
<template>
 <div v-if="show" class="border">
  <div>子组件msg:{{ msg }}</div>
  <div>子组件数组:{{ arr }}</div>
  <button @click="closeModel">关闭model框</button>
  <button @click="$emit('update:msg', '浪里行舟')">
   改变文字
  </button>
  <button @click="arr.push('前端工匠')">改变数组</button>
 </div>
</template>
<script>
export default {
 props: {
  msg: {
   type: String
  },
  show: {
   type: Boolean
  },
  arr: {
   type: Array //在子组件中改变传递过来数组将会影响到父组件的状态
  }
 },
 methods: {
  closeModel() {
   this.$emit("update:show", false);
  }
 }
};

父组件向子组件 props 里传递了 msg 和 show 两个值,都用了.sync 修饰符,进行双向绑定。

不过.sync 虽好,但也有限制,比如:

1) 不能和表达式一起使用 (如 v-bind:title.sync="doc.title + '!'" 是无效的);

2) 不能用在字面量对象上 (如 v-bind.sync="{ title: doc.title }" 是无法正常工作的)。

方法4:将父组件中的数据包装成对象传递给子组件

这是因为在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。比如上例中在子组件中修改父组件传递过来的数组arr,从而改变父组件的状态。

5.向子组件中传递数据时加和不加 v-bind?

对于字面量语法和动态语法,初学者可能在父组件模板中向子组件中传递数据时到底加和不加 v-bind 会感觉迷惑。

v-bind:msg = 'msg'

这是通过 v-bind 进行传递数据并且传递的数据并不是一个字面量,双引号里的解析的是一个表达式,同样也可以是实例上定义的数据和方法(其实就是引用一个变量)。

msg='浪里行舟'

这种在没有 v-bind 的模式下只能传递一个字面量,这个字面量只限于 String 类量,字符串类型。那如果想通过字面量进行数据传递时, 如果想传递非String类型,必须props名前要加上v-bind ,内部通过实例寻找,如果实例方没有此属性和方法,则默认为对应的数据类型。

:msg='11111' //Number
:msg='true' //Bootlean
:msg='()=>{console.log(1)}' //Function
:msg='{a:1}' //Object

二、事件

1.事件驱动与数据驱动

用原生JavaScript事件驱动通常是这样的流程:

先通过特定的选择器查找到需要操作的节点 -> 给节点添加相应的事件监听 然后用户执行某事件(点击,输入,后退等等) -> 调用 JavaScript 来修改节点

这种模式对业务来说是没有什么问题,但是从开发成本和效率来说会比较不理想,特别是在业务系统越来越庞大的时候。另一方面,找节点和修改节点这件事,效率本身就很低,因此出现了数据驱动模式。

Vue的一个核心思想是数据驱动。所谓数据驱动,是指视图是由数据驱动生成的,我们对视图的修改,不会直接操作 DOM,而是通过修改数据,其流程如下:

用户执行某个操作 -> 反馈到 VM 处理(可以导致 Model 变动) -> VM 层改变,通过绑定关系直接更新页面对应位置的数据

可以简单地理解:数据驱动不是操作节点的,而是通过虚拟的抽象数据层来直接更新页面。主要就是因为这一点,数据驱动框架才得以有较快的运行速度(因为不需要去折腾节点),并且可以应用到大型项目。

2.修饰符事件

Vue事件分为普通事件和修饰符事件,这里我们主要介绍修饰符事件。

Vue 提供了大量的修饰符封装了这些过滤和判断,让开发者少写代码,把时间都投入的业务、逻辑上,只需要通过一个修饰符去调用。我们先来思考这样问题:怎样给这个自定义组件 custom-component 绑定一个原生的 click 事件?

<custom-component>组件内容</custom-component>

如果你的回答是 <custom-component @click="xxx"> ,那就错了。这里的 @click 是自定义事件 click,并不是原生事件 click。绑定原生的 click 是这样的:

<custom-component @click.native="xxx">组件内容</custom-component>

实际开发过程中离不开事件修饰符,常见事件修饰符有以下这些:

表单修饰符

1).lazy

在默认情况下, v-model 在每次  input 事件触发后将输入框的值与数据进行同步 。你可以添加  lazy 修饰符,从而转变为使用  change 事件进行同步。适用于输入完所有内容后,光标离开才更新视图的场景。

2).trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

这个修饰符可以过滤掉输入完密码不小心多敲了一下空格的场景。需要注意的是, 它只能过滤首尾的空格 !首尾,中间的是不会过滤的。

3).number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="value" type="text" />

从上面例子,可以得到如果你先输入数字,那它就会限制你输入的只能是数字。如果你先输入字符串,那它就相当于没有加.number

事件修饰符

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

三、插槽

插槽分为普通插槽和作用域插槽,其实两者很类似,只不过作用域插槽可以接受子组件传递过来的参数。

1.作用域插槽

我们不妨通过一个todolist的例子来了解作用域插槽。如果当item选中后,文字变为黄色(如下图所示),该如何实现呢?

// 父组件
<template>
 <div class="toList">
  <input v-model="info" type="text" /> <button @click="addItem">添加</button>
  <ul>
   <TodoItem v-for="(item, index) in listData" :key="index">
    <template v-slot:item="itemProps"> // 这是个具名插槽
    // 其中itemProps的值就是子组件传递过来的对象
     <span
      :style="{
       fontSize: '20px',
       color: itemProps.checked ? 'yellow' : 'blue'
      }"
      >{{ item }}</span
     >
    </template>
   </TodoItem>
  </ul>
 </div>
</template>
<script>
import TodoItem from "./TodoItem";
export default {
 components: {
  TodoItem
 },
 data() {
  return {
   info: "",
   listData: []
  };
 },
 methods: {
  addItem() {
   this.listData.push(this.info);
   this.info = "";
  }
 }
};
</script>
// 子组件
<template>
 <div>
  <li class="item">
   <input v-model="checked" type="checkbox" />
   <slot name="item" :checked="checked"></slot> // 将checked的值传递给父组件
  </li>
 </div>
</template>
<script>
export default {
 data() {
  return {
   checked: false
  };
 }
};
</script>

值得注意:v-bind:style 的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。

2.v-slot新语法

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了  slot 和  slot-scope

我们通过一个例子介绍下默认插槽、具名插槽和作用域插槽的新语法:

// 父组件
<template>
 <div class="helloSlot">
  <h2>2.6 新语法</h2>
  <SlotDemo>
   <p>默认插槽:default slot</p>
   <template v-slot:title>
    <p>具名插槽:title slot1</p>
    <p>具名插槽:title slot2</p>
   </template>
   <template v-slot:item="props">
    <p>作用域插槽:item slot-scope {{ props }}</p>
   </template>
  </SlotDemo>
 </div>
</template>
<script>
import Slot from "./slot";
export default {
 components: {
  SlotDemo: Slot
 }
};
</script>
// 子组件
<template>
 <div>
  <slot />
  <slot name="title" />
  <slot name="item" :propData="propData" />
 </div>
</template>
<script>
export default {
 data() {
  return {
   propData: {
    value: "浪里行舟"
   }
  };
 }
};
</script>

总结

以上所述是小编给大家介绍的vue组件三大核心概念图文详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • vue里如何主动销毁keep-alive缓存的组件

    问题产生的背景 我们一个后台,在切换一些标签页的时候,是使用的 keep-alive 缓存的标签页,也使用了 include 属性来决定哪个页面进行缓存,而标签页的切换实际上是路由的切换,也就是说打开一个新标签页的时候,url 会跟着变化,老的标签页如果在 keep-alive 的 include 范围内那就会缓存下来. 然后客服人员就反馈页面开的久了就会崩溃,因为他们基础上不会刷新页面(工作需要),又总有切换标签的习惯,最后导致内存越来越大最后崩溃. 依赖环境 这个项目是基于一个开源 vue

  • 如何在Vue.js中实现标签页组件详解

    前言 标签页组件,即实现选项卡切换,常用于平级内容的收纳与展示. 因为每个标签页的内容是由使用组件的父级控制的,即这部分内容为一个 slot.所以一般的设计方案是,在 slot 中定义多个 div,然后在接到切换消息时,再显示或隐藏相关的 div.这里面就把相关的交互逻辑也编写进来了,我们希望在组件中处理这些交互逻辑,slot 只单纯处理业务逻辑.这可以通过再定义一个 pane 组件来实现,pane 组件嵌在 tabs 组件中. 1 基础版 因为 tabs 组件中的标题是在 pane 组件中定义

  • Vue中Table组件Select的勾选和取消勾选事件详解

    简述 之间设计的界面中使用的是复选框控件,但是经过对官网了一些了解,使我们更加倾向于使用一些官网已经封装好的事件,就比如Table组件的Select勾选和取消勾选的这样一个事件. 勾选 首先我们需要说一下这个需求,如下图: 勾选要实现如下的一个效果:对左侧Table的内容进行勾选,然后勾选行的数据传给右侧的Table中. 实现代码如下: ============1.按照官网封装好的样式去写Table组件======= <template> <div> <Table>&l

  • 详解VUE里子组件如何获取父组件动态变化的值

    在VUE里父组件给子组件间使用props方式传递数据,但是希望父组件的一个状态值改变然后子组件也能监听到这个数据的改变来更新子组件的状态. 场景:子组件通过props获取父组件传过来的数据,子组件存在操作传过来的数据并且传递给父组件. 比如想实现一个switch开关按钮的公用组件: 1.父组件可以向按钮组件传递默认值. 2.子组件的操作可以改变父组件的数据. 3.父组件修改传递给子组件的值,子组件能动态监听到改变. 比如父组件点击重置,开关组件的状态恢复为关闭状态: 方法1: 1.因为存在子组件

  • Vue 实现手动刷新组件的方法

    开发过程遇到了一个问题,就是我的 router-view 里面渲染出来的组件输入数据之后,我点击 路由视图外边的导航栏 router-link 按钮,可以实现清除 router-view 里面的数据,也就是使组件重新渲染.vm.$forceUpdate()这个方法可以使当前组件调用这个方法时,重新渲染组件.给 router-view 标签添加 key 属性将 key 绑定的值放在状态管理容器里面,通过 状态管理容器的 mutations 或者 actions 触发 key 值的变化,即可实现重新

  • vue组件三大核心概念图文详解

    前言 本文主要介绍属性.事件和插槽这三个vue基础概念.使用方法及其容易被忽略的一些重要细节.如果你阅读别人写的组件,也可以从这三个部分展开,它们可以帮助你快速了解一个组件的所有功能. 本文的代码请猛戳 github博客 ,纸上得来终觉浅,大家动手多敲敲代码! 一.属性 1.自定义属性props prop 定义了这个组件有哪些可配置的属性,组件的核心功能也都是它来确定的.写通用组件时,props 最好用对象的写法,这样可以针对每个属性设置类型.默认值或自定义校验属性的值,这点在组件开发中很重要,

  • vue组件间的参数传递实例详解

    场景分析 在前端开发中,我们常常会运用到"组件库".在main入口中引入组件库,就可以很轻松的在页面中引入,并做一些基本的配置,如样式,颜色等.只需要在引入的组件中写入特定的属性,就能够定义. 举例说明 例如:element-ui组件库中使用switch开关,有个属性active-color是设置"打开时"的背景色.change事件是触发状态的事件. <el-switch v-model="value" :active-color=&quo

  • vue组件间通信子与父详解(二)

    接着vue组件父与子通信详解继续学习. 二.组件间通信(子组件传值给父组件) 通过事件的方式来完成数据的传输. ①在父组件中 定义一个方法,用来接收子组件所通过事件传来的值 methods:{ recvMsg:function(msg){ //参数msg就是子组件通过事件出来的数据 } } ②绑定事件处理函数 事件一般情况 都是自定义事件 <child-component @myEvent="recvMsg"></child-component> ③在子组件触发

  • vue组件 keep-alive 和 transition 使用详解

    1.keep-alive 能在组件切换过程中将状态保留在内存中,防止重复渲染DOM. 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们.和 相似, 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中. 当组件在 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行. include: 字符串或正则表达式.只有匹配的组件会被缓存. exclude: 字符串或正则表达式.任何匹配的组件都不会被缓存. // 组件 export

  • webpack-dev-server核心概念案例详解

    webpack-dev-server 核心概念 Webpack 的 ContentBase vs publicPath vs output.path webpack-dev-server 会使用当前的路径作为请求的资源路径(所谓 当前的路径 就是运行 webpack-dev-server 这个命令的路径,如果对 webpack-dev-server 进行了包装,比如 wcf,那么当前路径指的就是运行 wcf命令的路径,一般是项目的根路径),但是读者可以通过指定 content-base 来修改这

  • Vue组件之间四种通信方式详解

    目录 前言 父子组件通信​ 父组件与子孙组件的通信​ 父组件获取子组件数据​ 无需考虑组件关系的通信​ 前言 vue 框架提供了前端开发组件的思想,可以通过组件来组合成一个完整的页面,都是随着组件数量原来越多,组件之间难免需要相互通信,那么如何实现组件之间的通信呢?下面介绍 vue 组件通信的几种方法 父子组件通信​ 父组件传递 props 给子组件(数据以及改变数据的方法),子组件通过 $emit 来更新父组件的状态 父组件定义,声明状态 { name: 'baidu.com' } 以及可以改

  • Vue命令行工具Vue-CLI图文详解(推荐!)

    目录 阐述 vue全家桶 vue-cli Vue CLI 的安装 ① 默认安装 vue/cli 脚手架 3 ② 默认安装失败,解决mode版本问题 ③ nvm下载太慢问题解决 ④ 安装 Node ^12.0.0 ⑤ 从新安装 VueCli 脚手架 3 ④ 拉取 2.x 模板 (旧版本) ⑤ 安装vue指定版本 ⑥ 博主node版本 初始化项目 ① vue init命令讲解 ② 项目初始化 ③ 目录结构详解 ④ 运行项目 node 安装适合小白 1 去node官网下载 2 下载完成后双击安装包,跳

  • React的diff算法核心复用图文详解

    目录 引言 Fiber 架构 React 的 diff 算法 总结 引言 React 是基于 vdom 的前端框架,组件 render 产生 vdom,然后渲染器把 vdom 渲染出来. state 更新的时候,组件会重新 render,产生新的 vdom,在浏览器平台下,为了减少 dom 的创建,React 会对两次的 render 结果做 diff,尽量复用 dom,提高性能. diff 算法是前端框架中比较复杂的部分,代码比较多,但今天我们不上代码,只看图来理解它. 首先,我们先过一下 r

  • vue组件父与子通信详解(一)

    本文实例为大家分享了vue组件父与子通信的具体代码,供大家参考,具体内容如下 一.组件间通信(父组件    -->  子组件) 步骤: ①父组件在调用子组件 传值 <child-component myValue="123"> </child-component> ②在子组件中 获取父组件传来的值 Vue.component('child-component',{ props:['myValue'], template:'' }) 代码1: <!do

  • Vue组件库发布到npm详解

    制作了一套自己的组件库,并发布到npm上,项目代码见https://github.com/hamger/hg-vcomponents 前期准备 有一个npm账号 安装了vue-cli 搭建项目 vue init webpack hg-vcomponents cd hg-vcomponents cnpm install 目录结构 - vue-flag-list + build + dist // 存放发布到npm的代码 - src - components // 存放组件源代码 和 README.

随机推荐