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


Posted in Javascript onMay 30, 2019

前言

本文主要介绍属性、事件和插槽这三个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中声明,就会默认挂在子组件的根元素上,如下图所示:

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

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);
  }
 }
};

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

父组件向子组件 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" />

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

从上面例子,可以得到如果你先输入数字,那它就会限制你输入的只能是数字。如果你先输入字符串,那它就相当于没有加.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选中后,文字变为黄色(如下图所示),该如何实现呢?

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

// 父组件
<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组件三大核心概念图文详解

总结

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

Javascript 相关文章推荐
Area 区域实现post提交数据的js写法
Apr 22 Javascript
使用jQuery判断IE浏览器版本的代码
Jun 14 Javascript
js文本框走动跑马灯效果代码分享
Aug 25 Javascript
Bootstrap3.0学习教程之JS折叠插件
May 27 Javascript
jQuery ajaxSubmit 实现ajax提交表单局部刷新
Jul 04 Javascript
基于JavaScript实现本地图片预览
Feb 08 Javascript
js正则表达式验证表单【完整版】
Mar 06 Javascript
JS监控关闭浏览器操作的实例详解
Sep 12 Javascript
在vue中添加Echarts图表的基本使用教程
Nov 22 Javascript
浅谈vue中关于checkbox数据绑定v-model指令的个人理解
Nov 14 Javascript
javascript定时器的简单应用示例【控制方块移动】
Jun 17 Javascript
vue使用axios实现excel文件下载的功能
Jul 16 Javascript
详解一次Vue低版本安卓白屏问题的解决过程
May 30 #Javascript
基于iview的router常用控制方式
May 30 #Javascript
深入了解js原型模式
May 30 #Javascript
js逆向解密之网络爬虫
May 30 #Javascript
Vue.js中的组件系统
May 30 #Javascript
Vue+Django项目部署详解
May 30 #Javascript
了解重排与重绘
May 29 #Javascript
You might like
强制PHP命令行脚本单进程运行的方法
2014/04/15 PHP
解决cPanel无法安装php5.2.17
2014/06/22 PHP
详谈PHP文件目录基础操作
2014/11/11 PHP
PHP在线书签系统分享
2016/01/04 PHP
Yii2框架自定义类统一处理url操作示例
2019/05/25 PHP
php面向对象基础详解【星际争霸游戏案例】
2020/01/23 PHP
解决jQuery插件tipswindown与hintbox冲突
2010/11/05 Javascript
Function.prototype.bind用法示例
2013/09/16 Javascript
JavaScript控制table某列不显示的方法
2015/03/16 Javascript
JS使用ajax从xml文件动态获取数据显示的方法
2015/03/24 Javascript
基于Jquery和html5实现炫酷的3D焦点图动画
2016/03/02 Javascript
jQuery筛选数组之grep、each、inArray、map的用法及遍历json对象
2016/06/20 Javascript
浅谈javascript:两种注释,声明变量,定义函数
2016/09/29 Javascript
javascript实现数字配对游戏的实例讲解
2017/12/14 Javascript
解决js ajax同步请求造成浏览器假死的问题
2018/01/18 Javascript
微信小程序methods中定义的方法互相调用的实例代码
2018/08/07 Javascript
Vue-Router的使用方法
2018/09/05 Javascript
CKEditor4配置与开发详细中文说明文档
2018/10/08 Javascript
[00:36]DOTA2上海特级锦标赛 Archon战队宣传片
2016/03/04 DOTA
多版本Python共存的配置方法
2017/05/22 Python
python实现Adapter模式实例代码
2018/02/09 Python
一篇文章彻底搞懂Python中可迭代(Iterable)、迭代器(Iterator)与生成器(Generator)的概念
2019/05/13 Python
Python中字符串与编码示例代码
2019/05/20 Python
python随机数分布random均匀分布实例
2019/11/27 Python
tensorflow 保存模型和取出中间权重例子
2020/01/24 Python
Python打包工具PyInstaller的安装与pycharm配置支持PyInstaller详细方法
2020/02/27 Python
波兰数码相机及配件网上商店: Cyfrowe.pl
2017/06/19 全球购物
《诺贝尔》教学反思
2014/02/17 职场文书
怎么写好自荐书
2014/03/02 职场文书
解除财产保全担保书
2014/05/20 职场文书
计算机科学技术自荐信
2014/06/12 职场文书
政协工作总结2015
2015/05/20 职场文书
教师考核鉴定意见
2015/06/05 职场文书
读书笔记格式
2015/07/02 职场文书
求职信如何撰写?
2019/05/22 职场文书
解决Windows Server2012 R2 无法安装 .NET Framework 3.5
2022/04/29 Servers