Vue中的指令(标签属性)
就是对标签属性的拓展
极简插件:https://chrome.zzzmh.cn/#/index
指令 (Directives) 是带有
v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。指令即是特殊的属性(以v-为前缀)
指令分为内置指令与自定义指令。
注意:组件是对标签的扩展,指令是对属性的扩展。
指令 (Directives) 是带有 v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。回顾我们在介绍中看到的例子
<p v-if="seen">现在你看到我了</p>
这里,v-if
指令将根据表达式 seen
的值的真假来插入/移除 <p>
元素。
参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML attribute:
<a v-bind:href="url">...</a>
在这里 href
是参数,告知 v-bind
指令将该元素的 href
attribute 与表达式 url
的值绑定。
另一个例子是 v-on
指令,它用于监听 DOM 事件:
<a v-on:click="doSomething">...</a>
在这里参数是监听的事件名。我们也会更详细地讨论事件处理。
动态参数
2.6.0 新增
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName
会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data
property attributeName
,其值为 "href"
,那么这个绑定将等价于 v-bind:href
。
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
<a v-on:[eventName]="doSomething"> ... </a>
在这个示例中,当 eventName
的值为 "focus"
时,v-on:[eventName]
将等价于 v-on:focus
。
修饰符
修饰符 (modifier) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault()
:
<form v-on:submit.prevent="onSubmit">...</form>
在接下来对 v-on
和 v-for
等功能的探索中,你会看到修饰符的其它例子。
表达式
表达式结果的类型除了字符串
<a v-bind:[attributeName]="url"> ... </a>
"" 中的是一个表达式 => url是一个表达式,表达式的返回值是一个字符串
<a attributeName="aquaIsAngle"> ... </a>
attributeName是一个属性,他的值是一个字符串类型的数据:"aquaIsAngle",aquaIsAngle是一个String类型的对象
缩写
v-
前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v-
前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的单页面应用程序 (SPA - single page application) 时,v-
前缀也变得没那么重要了。因此,Vue 为 v-bind
和 v-on
这两个最常用的指令,提供了特定简写:
v-html 文本
更新元素的 innerHTML
- 期望的绑定值类型:
string
v-html属性的特点:
1- 值是一个字符串
2- 该字符串会作为Vue实例的属性名,将对应的属性值在指令所在的标签中进行输出。
3- 会覆盖原有数据。
4- 属性名可以使用双引号,单引号
5- 属性名也可以省略双引号,单引号
6- 如果使用的是反引号会将包裹的值在标签中直接输出
7- 无法与template结合使用。
<div id="root">
<h3>v-html</h3>
<p v-html="userName">{{str}}</p>
<p v-html='userName'>{{str}}</p>
<p v-html=userName>{{str}}</p>
<p v-html=`userName`>{{str}}</p>
<template v-html="userName">123</template>
</div>
new Vue({
el:"#root",
data:{
str:"我是一个字符串",
userName:"zhangsan"
}
})
v-text 文本
更新元素的文本内容。将覆盖元素中所有现有的内容
- 期望的绑定值类型:
string
1- 值是一个字符串
2- 该字符串会作为Vue实例的属性名,将对应的属性值在指令所在的标签中进行输出。
3- 会覆盖原有数据。
4- 属性名可以使用双引号,单引号
5- 属性名也可以省略双引号,单引号
6- 如果使用的是反引号会将包裹的值在标签中直接输出
7- 无法与template结合使用。
<span v-text="msg"></span>
<!-- 等同于 -->
<span>{{msg}}</span>
v-html VS v-text
区别
:v-text不支持HTML标签而v-html支持。
v-if 条件
基于表达式值的真假性,来条件性地渲染元素或者模板片段。
v-if与v-show的表达式的返回值都是布尔值。
期望的绑定值类型:
any
当
v-if
元素被触发,元素及其所包含的指令/组件都会销毁和重构。如果初始条件是假,那么其内部的内容根本都不会被渲染。可用于
<template>
表示仅包含文本或多个元素的条件块。当条件改变时会触发过渡效果。
当同时使用时,
v-if
比v-for
优先级更高。我们并不推荐在一元素上同时使用这两个指令
v-if以及v-else-if的值是一个布尔值,如果成立则渲染指令所在的元素,不成立则不渲染
v-else-if需要与v-if结合使用
v-else需要与v-if或v-else结合使用
v-if,v-else-if,v-else指令所在的元素之间不要出现其它元素
v-if = "表达式", 表达式中的变量使用的是实例中的属性或方法
v-show 显示
基于表达式值的真假性,来改变元素的可见性。
期望的绑定值类型:
any
详细信息
v-show
通过设置内联样式的display
CSS 属性来工作,当元素可见时将使用初始display
值。当条件改变时,也会触发过渡效果。不同之处在于
v-show
会在 DOM 渲染中保留该元素;v-show
仅切换了该元素上名为display
的 CSS 属性。v-show
不支持在<template>
元素上使用,也不能和v-else
搭配使用。
v-if与v-show的表达式的返回值都是布尔值。
v-if为true表示渲染元素,false表示不渲染元素
v-show为true表示显示元素,false表示隐藏元素(通过display:none)
<div v-show = "表达式"></div>
空字符串,0,undefined,null作为判断条件相当于false
非空字符串,1作为判断条件相当于true
v-if VS v-show
v-if
是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if
也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display
属性会被切换。
总的来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show
较好;如果在运行时绑定条件很少改变,则 v-if
会更合适。
v-bind 单向绑定
动态的绑定一个或多个 attribute,也可以是组件的 prop
缩写:
:
或者.
(当使用.prop
修饰符)- 值可以省略 (当 attribute 和绑定的值同名时) 3.4+
期望绑定值:
any (带参数) | Object (不带参数)
参数:
attrOrProp (可选的)
修饰符
.camel
- 将短横线命名的 attribute 转变为驼峰式命名。.prop
- 强制绑定为 DOM property。3.2+.attr
- 强制绑定为 DOM attribute。3.2+
当用于组件 props 绑定时,所绑定的 props 必须在子组件中已被正确声明。
当不带参数使用时,可以用于绑定一个包含了多个 attribute 名称-绑定值对的对象。
示例代码
<!-- 绑定 attribute -->
<img v-bind:src="imageSrc" />
<!-- 动态 attribute 名 -->
<button v-bind:[key]="value"></button>
<!-- 缩写 -->
<img :src="imageSrc" />
<!-- 动态 attribute 名的缩写 -->
<button :[key]="value"></button>
<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName" />
<!-- 缩写形式的动态 attribute 名 (3.4+),扩展为 :src="src" -->
<img :src />
绑定HTML Class
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,所以我们可以用 v-bind
处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
对象语法
<div v-bind:class="{ active: isActive }"></div>
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
<div v-bind:class="classObject"></div>
<a href="javascript:void(0)"
@click.prevent="item.skuNum<200?item.skuNum++:200"
:class="{'plus':true,'disabled':item.skuNum>200}">+</a>
// 需要手动添加disabled样式
&.disabled{
color:#ccc;
cursor:not-allowed;
}
数组语法
<div v-bind:class="[activeClass, errorClass]"></div>
绑定内联样式style
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<div v-bind:style="styleObject"></div>
数组语法
数组语法可以将多个样式对象应用到同一个元素上
div v-bind:style="[baseStyles, overridingStyles]"></div>
自动添加前缀
当 v-bind:style
使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀。
v-on 事件
给元素绑定事件监听器。
- 缩写:
@
- 期望的绑定值类型:
Function | Inline Statement | Object (不带参数)
- 参数:
event
(使用对象语法则为可选项)
语句:如果操作语句比较简单,建议直接写语句
函数:如果操作比较复杂或要在多个不同的地方调用且不需要传递参数使用函数
内联声明:如果操作比较复杂或要在多个不同的地方调用且需要传递参数使用函数()
如果要传递事件对象可以通过 $event全局对象
v-on可以简写:@
//api中的描述
事件类型由参数来指定。表达式可以是一个方法名,一个内联声明,如果有修饰符则可省略。
当用于普通元素,只监听原生 DOM 事件。
当用于自定义元素组件,则监听子组件触发的自定义事件。
当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。
如果使用内联声明,声明可以访问一个特殊的 $event 变量
v-on:click="handle('ok', $event)"
v-on 还支持绑定不带参数的事件/监听器对象。
请注意,当使用对象语法时,不支持任何修饰符。
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
// ...
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) {
event.preventDefault()
}
alert(message)
}
}
<!-- 方法处理函数 -->
<button v-on:click="doThis"></button>
<!-- 动态事件 -->
<button v-on:[event]="doThis"></button>
<!-- 内联声明 -->
<button v-on:click="doThat('hello', $event)"></button>
事件修饰符
.stop
- 调用event.stopPropagation()
。.prevent
- 调用event.preventDefault()
。.capture
- 在捕获模式添加事件监听器。.self
- 只有事件从元素本身发出才触发处理函数。.{keyAlias}
- 只在某些按键下触发处理函数。.once
- 最多触发一次处理函数。.left
- 只在鼠标左键事件触发处理函数。.right
- 只在鼠标右键事件触发处理函数。.middle
- 只在鼠标中键事件触发处理函数。.passive
- 通过{ passive: true }
附加一个 DOM 事件。
示例代码
<!-- 停止传播 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认事件 -->
<button @click.prevent="doThis"></button>
<!-- 不带表达式地阻止默认事件 -->
<form @submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 按键用于 keyAlias 修饰符-->
<input @keyup.enter="onEnter" />
<!-- 点击事件将最多触发一次 -->
<button v-on:click.once="doThis"></button>
<!-- 对象语法 -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 2.3.0新增-->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<!--.passive 修饰符尤其能够提升移动端的性能。-->
<div v-on:scroll.passive="onScroll">...</div>
监听子组件的自定义事件 (当子组件的“my-event”事件被触发,处理函数将被调用):
<MyComponent @my-event="handleThis" />
<!-- 内联声明 -->
<MyComponent @my-event="handleThis(123, $event)" />
@change语法
<!-- @change语法 -->
@change 事件会在表单元素的值发生变化并失去焦点,事件触发时机。通常是用户输入后按下回车键或点击其他地方)时触发。这与 @input 事件不同,后者会在每次输入时都触发。
双向绑定: 通常,你会将 @change 事件与 v-model 一起使用。v-model 是 Vue.js 提供的一种指令,用于实现表单元素和数据之间的双向绑定。当用户输入内容时,v-model 会自动更新数据,而当数据发生变化时,输入框的值也会随之更新
如果需要访问事件对象,可以在方法中使用 $event 参数来获取。例如,如果你想要获取输入框的新值,可以这样做:@change=“handleChange($event)”,然后在方法中使用 $event.target.value 来获取新值
事件处理器: 你需要在 Vue 组件中定义一个方法,作为 @change 事件的处理器。这个方法将在表单元素的值发生变化后被调用,你可以在其中执行任何操作,比如验证用户输入、向服务器发送请求、触发其他组件的更新等。
应用场景: @change 通常用于处理用户输入的最终确认,比如搜索框中的关键字输入、表单的提交等。在这些情况下,你可能只想在用户完成输入并准备进行下一步操作时才执行相应的逻辑。
按键修饰符
<!-- 只有在 `keyCode` 是 13 时调用 `vm.submit()` -->
<input v-on:keyup.13="submit">
<!-- 回车键 -->
<input v-on:keyup.enter="submit">
- .enter
- .tab
- .delete (捕获“删除”和“退格”键)
- .esc
- .space
- .up
- .down
- .left
- .right
可以通过全局 config.keyCodes 对象自定义按键修饰符别名:
// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112
v-model表单输入绑定
在表单输入元素或组件上创建双向绑定。
基础用法
文本
<input v-model="message" placeholder="edit me">
<textarea v-model="message" placeholder="add multiple lines"></textarea>
复选框
单个复选框,绑定到布尔值:
<input type="checkbox" id="checkbox" v-model="checked">
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
多个复选框,绑定到同一个数组
<div id='example'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
单选按钮
绑定value对应的字符串
<div id="example">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
选择框
单选,绑定对应所选的值
<div id="example">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
多选时 ,绑定到一个数组
<div id="example">
<select v-model="selected" multiple style="width: 50px;">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
</div>
修饰符
.lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步
添加 lazy 修饰符,从而转变为使用 change 事件进行同步
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符
<input v-model.number="age" type="number">
.trim
自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符
<input v-model.trim="msg">
v-for 遍历
基于原始数据多次渲染元素或模板块。
期望的绑定值类型:
Array | Object | number | string | Iterable
详细信息
指令值必须使用特殊语法
alias in expression
为正在迭代的元素提供一个别名:- javascript
<div v-for="item in items"> {{ item.text }} </div>
或者,你也可以为索引指定别名 (如果用在对象,则是键值):
- javascript
<div v-for="(item, index) in items"></div> <div v-for="(value, key) in object"></div> <div v-for="(value, name, index) in object"></div>
v-for
的默认方式是尝试就地更新元素而不移动它们。要强制其重新排序元素,你需要用特殊 attributekey
来提供一个排序提示:- javascript
<div v-for="item in items" :key="item.id"> {{ item.text }} </div>
示例
<!-- 1- 数组-->
<div v-for="item in arr">{{item}}</div>
<div v-for="(value,index) in arr">{{index}}:{{value}}</div>
<!-- 2- 对象-->
<div v-for="item in obj">{{item}}</div>
<div v-for="(value,key) in obj">{{key}}:{{value}}</div>
<!-- 3- 数字-->
<div v-for="item in num">{{item}}</div>
<div v-for="(n,i) in num">{{i}}:{{n}}</div>
<!-- 4- 字符串-->
<div v-for="item in str">{{item}}</div>
<div v-for="(s,i) in str">{{i}}:{{s}}</div>
new Vue({
el:"#root",
data:{
arr:["zhangsan","wangwu","zhaoliu","yanqi","shenba","qianjiu"],
obj:{
userName:"zhangsan",
age:12
},
num:10,// [1,2,3,4,5,6,7,8,9,10]
str:"我爱你中国,我亲爱的母亲,我为你流泪也为你自豪!"
}
})
</script>
key 替换
复用已有元素
- Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染
- 要想每次都重新渲染,只需添加一个具有唯一值的 key 属性
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。
这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。因为在遍历,需要用 v-bind 来绑定动态值
建议尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
key
这个特殊的 attribute 主要作为 Vue 的虚拟 DOM 算法提示,在比较新旧节点列表时用于识别 vnode。
预期:
number | string | symbol
详细信息
在没有 key 的情况下,Vue 将使用一种最小化元素移动的算法,并尽可能地就地更新/复用相同类型的元素。如果传了 key,则将根据 key 的变化顺序来重新排列元素,并且将始终移除/销毁 key 已经不存在的元素。
同一个父元素下的子元素必须具有唯一的 key。重复的 key 将会导致渲染异常。
最常见的用例是与
v-for
结合:javascript<ul> <li v-for="item in items" :key="item.id">...</li> </ul>
也可以用于强制替换一个元素/组件而不是复用它。当你想这么做时它可能会很有用:
- 在适当的时候触发组件的生命周期钩子
- 触发过渡
举例来说:
javascript<transition> <span :key="text">{{ text }}</span> </transition> 当 text 变化时,<span> 总是会被替换而不是更新,因此 transition 将会被触发。
<!-- 使用ID-->
<div :key="item.id" v-for="item in arr">
<h3>《{{item.bookName}}》</h3>
<p>作者:{{item.author}}</p>
<hr/>
</div>
<!-- 使用下标 -->
<div :key="index" v-for="(item,index) in arr">
<h3>《{{item.bookName}}》</h3>
<p>作者:{{item.author}}</p>
<hr/>
</div>
<script>
new Vue({
el:"#root",
data:{
arr:[
{
id:1,
bookName:"天龙八部",
author:"金庸"
},{
id:2,
bookName: "一念永恒",
author:"耳根"
}
]
}
})
</script>
v-cloak
用于隐藏尚未完成编译的 DOM 模板
当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
v-cloak 会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像 [v-cloak] { display: none } 这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板
[v-cloak] {
display: none;
}
<div v-cloak>
{{ message }}
</div>
直到编译完成前,<div>
将不可见。
v-cloak并不需要添加到每个标签,只要在el挂载的标签上添加就可以
v-pre
跳过该元素及其所有子元素的编译。
<span v-pre>{{ this will not be compiled }}</span>
v-once
仅渲染元素和组件一次,并跳过之后的更新。
在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。
<!-- 单个元素 -->
<span v-once>This will never change: {{msg}}</span>
<!-- 带有子元素的元素 -->
<div v-once>
<h1>Comment</h1>
<p>{{msg}}</p>
</div>
<!-- 组件 -->
<MyComponent v-once :comment="msg" />
<!-- `v-for` 指令 -->
<ul>
<li v-for="i in list" v-once>{{i}}</li>
</ul>
自定义指令
全局指令
// 1 在src/utils/文件夹中 新建一个ts文件 directive.ts 新建自定义指令
//引入用户相关的小仓库
import pinia from '@/stores';
import {useUserInfoStore} from '@/stores/userInfo';
//获取用户小仓库
const userStore = useUserInfoStore(pinia);
export const has = (app:any)=>{
// 设置全局指令has 使用directive函数
// 第一个参数是 指令名字,第二个参数是 自定义指令钩子函数
app.directive("has",{
// 当使用了自定义指令的按钮dom元素,挂载完成后执行回调函数
// mounted(element,options)函数的
// 第一个参数element是 使用了自定义指令的按钮dom元素
// 第二个参数options是 自定义指令对象(可以设置修饰符,获取自定义指令的value值)
mounted(element,options){
// 通过JS原生dom操作 删除按钮元素
}
})
}
// 2 在入口文件全局自定义指令
// vue2中注册全局自定义指令 vue.directive()
// vue3中注册全局自定义指令 app.directive()
import {has} from '@/utils/directive.ts'
// 将app注入
has(app)
// 3 在组件中使用
<!-- 添加按钮 -->
<el-button
type="primary"
:icon="Plus"
@click="addSpu"
v-has="'btn.Spu.add'"
:disabled="categoryStore.c3Id ? false : true">添加SPU
</el-button>
判断是否渲染按钮思路
在所有按钮上设置 v-has 属性
获取用户仓库中的按钮数组数据
当按钮仓库中的数组元素中没有 option.value 中的值时,不渲染按钮
当按钮仓库中的数组元素中存在 option.value 中的值时,渲染按钮
使用案例
定义自定义指令:src/utils/directive.ts
// 在非组件页面下使用小仓库,需要先引入大仓库
import pinia from "@/stores"
import {useUserInfoStore} from "@/stores/userInfo"
// 注入大仓库
const userStore = useUserInfoStore(pinia)
// 暴漏一个函数
//对外暴露一个函数
export const has = (app:any)=>{
//生成按钮权限全局自定义指令
//第一个参数:自定义指令的名字
app.directive("has",{
//当使用v-has这个全局自定义指令的DOM|组件挂载完毕的时候会立即执行一次
//element:使用这个指令的DOM元素
//options:能获取有自定义只有右侧数值 v-has="btn.Trademark.add"
mounted(element:any,options:any) {
//判断自定义指令右侧数值在仓库的buttons数组当中是否出现!!!
if(!userStore.buttons.includes(options.value)){
//获取当前绑定自定义指令DOM元素父组件,通过removeChild方法将当前元素删除!!!
element.parentNode.removeChild(element);
}
console.log("vue ele is mounted",options)
},
})
}
在用户仓库中存储 按钮 数组数据
src/store/userinfo.ts
// 引入定义小仓库方法
import { defineStore } from 'pinia';
// 本地存储操作token
import { getToken, removeToken, setToken } from '../utils/token-utils';
// state类型的数据类型
import type { UserInfoState } from './interface';
// 消息提示
import { ElMessage } from 'element-plus'
// 静态路由
import { staticRoutes } from '@/router/routes'
// 导入用户相关的API
import { getUserInfo, reqUserLogin, reqUserlogout } from '@/api/user';
// 引入类型
import type {LoginResponseData, UserInfoResponseData} from '@/api/user/type/index'
/**
* 用户信息
* @methods setUserInfos 设置用户信息
*/
export const useUserInfoStore = defineStore('userInfo', {
state: (): UserInfoState => {
return {
token: getToken() as string,
name: '',
avatar: '',
menuRoutes: [],
buttons:[]
}
},
actions: {
// 登陆
async login(username: string, password: string) {
// 定义请求体
const data = {
username,
password
}
// 获取响应数据
const result:LoginResponseData = await reqUserLogin(data)
// 存储token数据(pinia)
this.token = result.token
// 存储token数据(localstore)
setToken(result.token)
},
// 获取用户信息(路由鉴权那里进行调用)
async getInfo() {
const result:UserInfoResponseData = await getUserInfo()
// console.log("result ",result)
// 设置小仓库中的数据状态
this.name = result.name
this.avatar = result.avatar
//存储当前用户拥有哪些按钮权限标识 ['btn.模块的名字.xxxx']
this.buttons = result.buttons;
// 设置用户的路由
this.menuRoutes = staticRoutes
},
// 退出登陆
async reset() {
// 发送请求
await reqUserlogout()
// 删除local中保存的token
removeToken()
// 提交重置用户信息的mutation
this.token = ''
this.name = ''
this.avatar = ''
},
},
});
在入口文件 注册为全局指令
src/main.ts
import {has} from '@/utils/directive';
//引入自定义指令文件函数
has(app);
在组件中使用:src/views/product/spu/index.vue
<!-- 添加按钮 -->
<el-button
type="primary"
:icon="Plus"
@click="addSpu"
v-has="'btn.Spu.add'"
:disabled="categoryStore.c3Id ? false : true">添加SPU
</el-button>