vue 组件封装#

vue 组件封装#

vue 组件封装 #本文记录一下 Vue 组件封装的基本实践和一些组件的相关知识。主要涉及以下知识点:

封装一个组件的代码组织形式;vue 组件的三大核心: 属性(props、data);事件插槽样式其他一些杂项 $nextTick 函数的使用获取 DOM 元素及在父级组件中执行子组件方法使用第三方计数库 countup.js 创建一个 count-to 组件对以上知识进行总结。

文件组织形式 #在组件文件夹 component 下创建一个与组件名相同的文件,文件夹内必须有 index.js , 并将组件导入到该文件中,这样方便我们引用组件。

count-to 文件夹内:

js// index.js

import CountTo from './count-to.vue'

export default CountTo

123使用组件时,只需这样引入:

jsimport CountTo from '_c/count-to' // _c 是组件存放路径

1Vue 组件的三大核心 #属性(props、data 和样式) #props 定义了组件 可配置 的数据,确定的组件的核心功能。封装组件时,props 推荐写成对象形式,方便对数据进行验证,提高了代码健壮性也能明确如何使用。

常见的检查类型: Number 、 String 、 Boolean 、 Array 、 Object 、 Date 、 Function 、 Symbol 、 构造函数 。 null|undefined 会通过所有类型。

还可以自定义验证函数,指定是否必须和默认值。

jsprops: {

// 多个可能的类型

propB: [String, Number],

// 必填的字符串

propC: {

type: String,

required: true

},

// 带有默认值的数字

propD: {

type: Number,

default: 100

},

// 带有默认值的对象

propE: {

type: Object,

// 对象或数组默认值必须从一个工厂函数获取

default: function() {

return {

message: 'hello'

}

}

},

// 自定义验证函数

propF: {

validator: function(value) {

// 这个值必须匹配下列字符串中的一个

return ['success', 'warning', 'danger'].indexOf(value) !== -1

}

}

}

12345678910111213141516171819202122232425262728293031通过阅读 countUP 文档,了解到构造函数 CountUp 的参数

jsCountUp(eleDOM, startValue, endValue, decimals, duration, options) // eleDOM 是数值显示的元素;endValue 是数值的最终值,这两个参数必须的。

1组件代码如下:

html

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798代码说明: this._uid 用于生成 组件内唯一 的 id 值,可用作元素的 id,值是递增的。

this.$nextTick 函数接收一个回调函数作为参数,回调函数会在 DOM更新 之后执行,如果某些操作必须在 DOM 更新之后,可将这些操作作为其参数。

计数组件的基本功能就满足了。

这样使用组件:

html

123456789101112131415161718192021html

1prop 的命名:

组件中使用 小驼峰 命名,传递值是使用 - 。

关于 props 传递静态值:

不使用 v-bind 即 : 传递的是静态值,是一个字符串字常量,而不是变量,而使用 : 指令传递的值,是有类型的。 :duration="5" 传递是 数值 5, duration="5" 传递字符串 '5' 。 duration="true" 传递的是字符串 true 而不是 Boolean 值真值。

默认值:

传递是引用类型的值(对象和数组),默认值需要使用一个工厂函数返回一个引用类型的值。

inheritAttrs:

如果传递一个组件中没有声明的属性,该属性会挂载都组件元素上,可在组件中将 inheritAttrs 设置为 false 取消这一行为。上面的 title 属性会挂载到组件的 div 上。该属性不应 style 和 class 的传递。

html

1title 会成为 count-to 组件的根元素的属性:

html

10,000.00

123$attrs 接收没有声明的属性

title 和 test 属性没有在组件中声明,依然可以在组件中使用 attrs 接收到些属性: 没有props接收的父组件数据:{}

最后的结果:

html

没有props接收的父组件数据:{ "title": "这是标题", "test": "测试" }


10,000.00

12345inheritAttrs: false 和 $attrs 结合使用:

有了 inheritAttrs: false 和 $attrs,你就可以手动决定这些特性会被赋予哪个元素,而不需要声明变量接收。

See the Pen $attrs使用 by JackZhouMine (@JackZhouMine) on CodePen.

data vs props #props 从父级组件入,传入的值由父级组件维护,不允许在子组件中直接操作, 是否必需和数据类型都是确定的,我们不能改变。

data 是组件内部维护的状态,组件可直接操作,可随时改变值、类型等。

相同点:都是组件的属性,改变两者都会响应到模板上。

打破 props 单向数据流 #Vue 不允许在子组件中直接操作 props , 否则会报错,因为父组件和子组件都可直接操作 props,会使得 props 的管理变得混乱。可通过一些间接的方式操作 props:

将 props 赋值给 data ,然后操作 data;

在计算属性中返回 props;

以上两种方式,修改后的值,是不能会响应到父组件的,想要在父级组件中也看到修改,需要用到下面的方式:

.sync 和 $emit 结合传递 props 时加上 .sync 修饰符,在子组件内部使用 $emit 更新 props。

使用 .sync 需要注意:

不能和表达式一起使用:v-bind:title.sync="doc.title + '!'";不能传递对象字面量:v-bind.sync="{ title: doc.title }"。传递引用类型的 props传递数组和对象,在子组件中修改他们,会直接反应到父组件上。

事件 #传统的 web 开发使用事件驱动:

查询节点 → 绑定事件监听;用在页面上触发事件 → 执行监听器,修改 DOM, 反馈到页面上; 这种模式开发效率低成本高。Vue 的核心思想是数据驱动,视图由数据决定。MVVM 架构的页面变化流程:

View(用户操作) → 执行 DOMlistenrs (ViewModel) → Data 改变 (Model)→ View 改变。

组件和绑定原生事件和自定义事件,绑定原生事件时,需要添加 native 修饰符。

可以在组件的原生事件处理器中触发一个自定义事件,就能在父级组件中监听该事件,执行相关操作。

在 count-to 声明一个 changeValue 事件:

增加一个按钮:

html

1在事件处理器 add 中触发一个自定义事件:

jsadd() {

this.$emit("changeValue", Math.random() * 100);

}

123$emit 的第一个参数是事件名称,第二个参数是传递到该事件监听器的参数。

在组件上监听 changValue :

html

12345678910111213141516171819202122232425自定义一个更新结束事件:

html

12345678910111213141516171819202122232425262728在组件上使用监听 on-end :

html

1234567891011121314151617181920212223242526表单修饰符 #lazy : 在change事件同步数据;trim : 删除首尾空格;number :只能输入数字;事件修饰符 #stop:阻止冒泡;prevent : 阻止默认行为;html

123456插槽 #props 传递普通的数据类型,插槽提供了 传递 HTML 代码 的方式,父组件中给的插槽内容,会被放置到子组件的指定为位置。

父组件决定是否显示插槽和怎样显示,子组件决定插槽显示的位置。

三种插槽:

匿名插槽;命名插槽;作用域插槽。我们现在想要在 数值左边显示一个从父级组件传递到组件中的文字提示,数值右边显示人民币符号。

可使用插槽接收文字提示和人民币符号:

html

123456789在父级组件传递插槽内容:

html

12345678最后的 html 是这样的:

html

金额:

4,000.00

12345不传递插槽内容时,可以在组件中设置一个默认的插槽内容:

html

1234567父级组件的作用域和子组件的作用是独立的,在父级组件的插槽内容中,获取不到子组件的数据。

html

12345678parentDecimals 是父级组件中的属性,插槽内容属于父级作用域,可获取父级的数据; decimals 是子级组件中的属性,插槽内容属于父级作用域,获取不到值;

想要在父级插槽内容中获取子组件的数据,就需要用到作用域插槽。

现在想要把数值前面的文字从父级组件传递到子组件,并且还要传递文字的颜色:

jstext: {

name: "本月工资",

color: "#F4D03F"

},

1234子组件这样定义:

html

123456789101112131415161718192021这样使用组件:

html

1234567891011121314151617181920212223242526272829奖金额度: ,向父级组件传递数据; slot-scope="data" 用来接收插槽传递到父组件的数据;

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

子组件:

html

123456789101112131415161718192021这样使用组件:

html

12345678910子组件传递过来的变量被放置在一个对象中,使用解构赋值的方式提取出来。

html

123v-slot 指令后跟一个 slot 的名字,插槽具有名字时,可简写为 # 。

html

123不管有几个插槽,都把插槽内容放置在 template 中是很好的做法。

其他杂项 #组件生成 id #使用 this_uid 其他字母,可成组件内唯一的 id。 count-to 组件中,我们使用计算属性,设置 span 的 id。

jseleId() {

// 使用 this.uid 生成全局唯一id

return `count_up_uid${this._uid}`;

},

1234在组件内部,可以通过 id 或者 class 等获取到 dom,但是不推荐这么做。可通过 ref 属性,获取到 DOM ,更加简洁,并且可以直接通过 ref 获取组件或者 DOM 元素。

在下面的函数中获取 DOM:

js getCount() {

// TODO: 获取 DOM

// 使用 ref 属性获取 DOM 元素

// console.log(this.$refs.number.innerText)

// return this.$refs.number.innerText

// 使用 id 获取 DOM

let span = document.getElementById(this.eleId);

let currentValue = Number.parseFloat(span.innerText.split(",").join(""));

return currentValue.toFixed(this.decimals);

},

1234567891011$nextTick 函数的使用 #this.$nextTick 接收一个回调函数作为参数,参数会在 Vue 完成 DOM 更新后立即调用。如果某些操作是依赖 DOM 更新后的,可以把这些操作放在回调函数里执行。

在 created 和 mounted 阶段,如果需要操作渲染后的试图,也要使用 nextTick 方法。mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。Vue.$nexttick 全局的, this.$nexttick 是局部的。

jsvar vm = new Vue({

el: '#example',

data: {

message: '123'

}

})

vm.message = 'new message' // 更改数据

vm.$el.textContent === 'new message' // false 此时DOM还没渲染

Vue.nextTick(function() {

vm.$el.textContent === 'new message' // true

})

1234567891011Vue DOM 的更新是异步的,数据变化后,组件不会立即渲染,而是在事件队列刷新时,在下一个事件循环 tick 中渲染。

$nexttick 返回一个 Promise,可使用 await 关键词调用。

jsmethods: {

updateMessage: async function() {

this.message = '已更新'

console.log(this.$el.textContent) // => '未更新'

await this.$nextTick()

console.log(this.$el.textContent) // => '已更新'

}

}

12345678在父级组件中调用子组件的方法 #有时候需要再父级组件中调用子组件的方法。可以在使用组件时指定 ref ,然后使用 ref 调用。 比如调用组件的暂停方法,使得数据变化暂停。

在组件中定义暂停方法:

html

1234567891011121314151617181920212223在父组件中使用调用组件暂停方法。

html

123456789101112131415161718192021222324252627282930313233样式 #组件使用样式,用三种方式:

外部样式;内部样式;通过 props 传入 类名,以指定使用内部样式中的哪个类名。外部样式两种方法引入: 在 script 标签中引入和在 style 标签中引入。

html

1234567891011121314151617181920212223242526272829303132333435通过 props 传递类名,实际是在父级组件中指定使用内部样式中的哪个类。

通过 style 也可以应用样式到组件上。

关于组件的常见面试题 #面试题

$nextTick 原理是什么?

总结 #封装一个组件 props 和 data 决定了组件的核心功能,插槽用于向组件传递 html 标签,使得组件更加具有扩展性。通过事件我们可以对组件进行某些操作。改天分析一个第三方组件,好好体会一下这些概念。

参考 #详解 vue 组件三大核心概念简单理解 Vue 中的 nextTickvue.nextTick 的原理和用途nextTickWhat the Tick is Vue.nextTick?vue 文档 Prop

风雨相关

木珠手串一般是多少颗
爱享365

木珠手串一般是多少颗

🌀 11-02 💧 阅读 6357
各种职业名称的英文
爱享365

各种职业名称的英文

🌀 09-03 💧 阅读 241