vue高阶组件介绍及使用
inheritAttrs 介绍
Vue默认情况下,父组件是可以直接给子组件的根元素添加class和style的,但是有时候我们可能需要在父组件上给子组件添加一些特性绑定(attribute bindings)到子组件的根元素上,inheritAttrs就是用来控制子组件根元素上是否允许添加父组件在子组件上定义的特性属性,因为inheritAttrs默认为true,所以我们在父组件中给子组件添加所有特性绑定,都能绑定到根元素
场景介绍
给组件的根元素添加自定义属性
示例代码
<template>
<div>
<base-item class="box"
key="1"
data-id="1"
style="font-size: 14px;">
</base-item>
</div>
</template>
<script>
export default {
components: {
baseItem: {
inheritAttrs: false,
template: `
<div>
<span>good</span>
</div>
`
}
}
}
</script>
渲染后的dom节点:
<div default-id="1" class="box" style="font-size: 14px;">
<span>good</span>
</div>
把inheritAttrs设为false后,渲染后的dom节点:
<div class="box" style="font-size: 14px;">
<span>good</span>
</div>
可以得出上述的结论:
inheritAttrs是用来控制子组件根元素上是否允许添加父组件在子组件上定义的特性属性
。
注意:
inheritAttrs是用来控制父组件中传递的特性属性,class和style不是特性属性,不能用inheritAttrs来控制
$attr 介绍
[官方地址](cn.vuejs.org/v2/api/#vm-…
我们可以把父作用域中传递的所有属性看作一个大的对象obj,而$attrs
会继承obj中的一部分属性,这一部分属性的key不能为class
,和style
,也不能是当前组件声明的prop
s值,并且父组件为v-model
的话,也是不能继承指令封装的value值的,若当前组件无props设置,$attrs则继承除class和style的所有属性。
场景介绍
通常我们给已封装的组件进行中间处理的时候使用,例如element-ui的el-input,我们需要把父组件中的传递的props直接给子组件的子组件的时候,我们就可以用到$attrs。
示例代码
父组件为:
// 父组件为:
<self-input class="self-input"
:limit="2"
v-model="item.count"
count="234">
</self-input>
子组件为:
// 子组件为:
<el-input
type="text"
v-bind="$attrs"
v-model="defaultValue">
</el-input>
export default {
props: {limit: Number}
}
根据上面demo,去掉class
,去掉指定的value
,去掉子组件props
声明的limit
,那时我们此的$attrs是:
{count: '234' }
$listenters 介绍
[官方地址](cn.vuejs.org/v2/api/#vm-…
通常大多数情况下都要对UI框架中的组件进行再封装,例如element-ui中的组件el-input,我们将它封装到项目组件内部时,需要把el-input组件上自定义的事件传递进去,那么就要用到$listenters
。
官网的描述:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。
很容易理解,.native
是给子组件根元素添加事件,自然不是用来传给子组件上的子组件或元素。而我们在父组件上给子组件绑定的所有事件,都会放入$listeners
中,因此可以在子组件中手动过滤修改后传给子组件中的子组件或者元素上,也可以用v-on="$listeners"
,直接全部传给子组件或者元素上。
场景介绍
对ui框架中的组件进行再封装的时候,我们不可能把组件所有事件写一遍传递进去,而是通过$listeners作为一个整体传递进去。
示例代码
// 父组件:
<template>
<self-input
@change="changeHandler"
@input="inputHandler"
v-model="count">
</self-input>
</template>
<script>
export default {
data () {
return {count: ''}
},
methods: {
changeHandler () {},
inputHandler () {}
}
}</script>
// 子组件:
<template>
<el-input
v-on="$listeners"
v-model="defaultValue">
</el-input>
</template>
<script>
export default {
data () {
return {
defaultValue: ''
}
},
props: {
value: [String, Number] // value值
},
mounted () {
console.log(this.$listeners)
}
}</script>
由于父组件传入的是一个change
事件和input
事件,此时打印的``this.$listeners```是
{
change: fn,
input: fn,
}
provide/inject 介绍
官方地址 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
<el-form size="small">
<el-row>
<el-col>
<el-form-item label="xxx"></el-form-item>
</el-col>
</el-row>
</el-form>
el-form中传入属性size,如果在el-form-item上也要应用size属性,这时候父组件el-form和子组件el-form-item中存在着多级嵌套。在传递size属性的时候,假如用$parent
来取值的话,就存在多级$parent的情况,这样业务中是可以做到,但是组件就不能做到复用,所以就需要使用inject/provide来做到。
场景介绍
在开发多级嵌套组件时,可能存子组件被多个组件包裹,然后放入父组件中。而父组件中有着嵌套组件的全局配置。
示例代码
父组件:
<template>
<div>
<div class="defaultWrap clearfix">
<el-card>
<div slot="header">新增產品</div>
<div class="card-body">
<el-tabs v-model="activeName" >
<el-tab-pane label="基本信息" name="1">
<base-info class="baseInfo" ref="baseInfo"></base-info>
</el-tab-pane>
...
</el-tabs>
</div>
</el-card>
</div>
</div>
</template>
<script>
export default {
data () {
return {
activeName: '1' // 當前所處選項卡位置
}
},
provide () { // 给子孙组件提供当前组件的实例
return {
addProduct: this
}
},
mounted () {
...
}
}
</script>
子组件:
第一种方法, 需要一层层查找
if (this.$parent.$parent.$parent.$parent.isEdit) {
params.id = this.$route.query.id
}
...
第二种方法
{
name: 'baseInfo',
inject: ['addProduct'], // 注入父组件中有provide的属性
...
}
//大多数情况下这种新增和编辑是同一个页面时就很方便
if (this.addProduct.isEdit) { //this.addProduct.isAdd
params.id = this.$route.query.id
}
...
显然第二种方法要简单很多。
提示:provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
directives 指令介绍
[官方地址](cn.vuejs.org/v2/guide/cu…
在 Vue2.0 中,代码复用和抽象的主要形式是组件。但是,有的情况下,你仍然需要对普通 DOM 元素进行底层操作这时候就会用到自定义指令。
场景介绍
对按钮级别权限进行操作,和业务前关联,看业务体系权限是那种ABAC/RBAC,登录后返回权限数组,根据当前角色判断有无权限(这里一般都路由守卫拦截)
示例代码
import Vue from 'vue'
/**
* 用法
* v-permission:edit
* 解释:按鈕權限名稱為冒號後面的字段,我們只需要修改這個字段就行了
import Vue from 'vue'
Vue.directive('permission', {
inserted: function (el, binding, vnode) { // 第一次插入父節點的時候執行
const {
roleLabels
} = vnode.context.$route.meta // vnode 虚拟dom
const arg = binding.arg || 'noBtnPermission'
if (!roleLabels) {
return
}
if (roleLabels.indexOf(arg) === -1) { // 当前路由没有此按钮权限将会移除按钮
if (el.parentNode != null) {
el.parentNode.removeChild(el)
}
}
}
})
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<!-- v-permission:open -->
<el-button type="text" @click="lookActive(scope.row)">查看活動</el-button>
<el-button v-permission:check type="text" @click="lookDetail(scope.row)">查看</el-button>
<el-button v-permission:edit type="text" v-if="scope.row.typeStatus == 2 && scope.row.whetherEdit === 1" @click="editActive(scope.row)">編輯</el-button>
<el-button v-permission:add type="text" @click="copyActive(scope.row)">複製</el-button>
<el-button v-permission:enable type="text" v-if="scope.row.typeStatus == 2" @click="disableActive(scope.row, 1)">禁用</el-button>
<el-button v-permission:enable type="text" v-if="scope.row.typeStatus == 1" @click="disableActive(scope.row, 2)">启用</el-button>
<el-button type="text" @click="recordActive(scope.row)">操作記錄</el-button>
</template>
</el-table-column>
Plugins 插件介绍
[官方地址](cn.vuejs.org/v2/guide/pl…
在项目中设置一些全局的方法,全局的属性等,通常就是用来为 Vue 添加全局功能。比如一些图表插件
场景介绍
全局需要添加时间格式化以及资源懒加载异步处理方法,以及一些资源混合等选项。
示例代码
文件夹路径为 common/utils
let MyPlugin = {}
MyPlugin.install = function (Vue, options) {
// 1. 添加实例方法
Vue.prototype.$loadResources = function (resources, type = 'script') {}
// 2. 添加全局资源
Vue.directive('my-directive', {
bind (el, binding, vnode, oldVnode) {
// 逻辑...
}
...
})
// 3. 注入组件选项
Vue.mixin({
created: function () {
// 逻辑...
}
...
})
// 4. 添加全局方法或属性
Vue.myGlobalMethod = function () {
// 逻辑...
}
}
export default MyPlugin
vue中引入
import method from './common/utils'
Vue.use(method)
mixins介绍
[官方地址](cn.vuejs.org/v2/guide/mi…
混入主要是提取文件中可复用的功能和属性。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
场景介绍
全局使用
如果项目业务模块相同的情况,并且业务与模块不存在耦合,为了提高代码的复用性,维护一处代码全局更改的效果,提取出来作为一个全局的mixins对象是一个好的选择。当然vue3组合式的API就是为更小的颗粒度,更好的复用
局部使用
项目局部模块,多个业务模块存在拥有同样功能,如果相同的功能放到不同页面,需求变更后,可能会出现每个页面都要修改这个方法,有必要提取出来作为一个局部的mixins对象是一个好的选择。
全局使用
目录 src/mixins
const renderUserRule = {
data () {
return {
userPreviewList: [] // 格式化後的視圖數組
}
},
methods: {
...
handingBack () {}, // 将后端返回的数据转化成符合前端展示的格式
async getRuleUserListMixins () {} // 提供给组件的异步调用方法
}
}
组件中使用
import renderUserRule from '~mixins/renderUserRule'
export default {
...
mixins: [renderUserRule],
...
}
局部使用
目录 productManage/productList/mixins
parentMixins.js
const mixins = {
components: {
...
},
methods: {
...
}
}
组件中使用
import parentMixins from './mixins/parentMixins'
export default {
...
mixins: [parentMixins],
...
}
软文推荐
VUE插件大全汇总:github.com/vuejs/aweso…
vue 技术内幕:hcysun.me/vue-design/
Vue 技术揭秘:ustbhuangyi.github.io/vue-analysi…
vue-cli3.x 新特性及踩坑记
写在最后
因项目原因Vue3目前为止,还没在项目实践,此篇也是好多年前,做了一个大型石油化工类的项目,全套vue,当时一口气搭建了17个应用,SSR为Nuxt2.x,钉钉小程序,H5,electron等,不过已经做了大量的技术预演,相信之后在新项目中肯定会有很多好玩的业务实践,到时再做分享!
转载自:https://juejin.cn/post/7245854874196951099