指令
v-text:更新元素的innerText
v-html:更新元素的innerHTML
v-show:元素显隐
v-if:条件渲染
v-else-if:前面的不渲染就渲染我
v-else:前面的都不渲染就渲染我
v-for:列表渲染
v-on:绑定事件监听器
v-bind:属性绑定,在绑定 class 和 style 属性时,支持数组和对象
v-model:表单双向绑定
v-slot:提供具名插槽或需要接收 prop 的插槽,只能使用在 template 标签上,简写 #
v-pre:跳过这个元素和它的子元素的编译过程
v-cloak
v-once:元素或组件只渲染一次
v-show 和 v-if
v-show 通过切换元素 display 属性的值来实现元素的显隐
v-if 根据表达式的值来判断元素是否渲染,在切换时元素及它的数据绑定/组件被销毁并重建
使用
如果一个元素需要在页面上频繁的切换显示或隐藏,用 v-show
如果只是根据状态一次性的决定有或没有,用 v-if
v-for
( item, index ) in XXX || ( item, index ) of XXX
( item, index ) in arr
( item, index ) of 3
( item, index ) in 'abcdefg'
tips
v-for 不建议与 v-if 一起使用,v-for 的优先级大于 v-if,每次渲染都会先循环再判断,造成性能上的浪费
在元素外部嵌套一个 template 标签,在 template 标签进行判断,在元素上循环
v-model
v-model 是实现表单数据的双向绑定的语法糖
v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用 value property 和 input 事件
- checkbox 和 radio 使用 checked property 和 change 事件
- select 字段将 value 作为 prop 并将 change 作为事件
配置项
el:挂载根节点
template:组件模板
data:状态
methods:自定义方法
computed:计算属性
watch:侦听器
filters:过滤器(V3移除)
props:接收属性
组件通信之父子通信(props, $emit)
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<script>
Vue.component("father",{
template:`<div>
<div>{{ title }}</div>
<hr>
<son a="1" :b="param" @jump="abc"></son>
</div>`,
data(){
return{
title:"我是爸爸",
param:2
}
},
methods:{
abc(val){ console.log("子组件传过来的值为:", val) }
}
})
Vue.component("son",{
props:[ "a", "b" ],
template:`<div>
<div>{{ title }}</div>
<div>参数一 {{ a }}</div>
<div>参数二 {{ b }}</div>
<button @click="fn1">向父组件传参</button>
</div>`,
data(){
return{
title:"我是儿子"
}
},
methods:{
fn1(){ this.$emit("jump",12345) }
}
})
const app = new Vue({
el: '#app' ,
})
</script>
</body>
</html>
props检测
props:{
// 基础类型检查
propA: number
propB: string
// 可能存在多个类型
propC: [ string, number ]
// 必填
propD:{
type: string,
required:true
}
// 默认值
propE:{
type: number,
default:123
}
// 带有默认值的对象或数组
propF:{
type: object,
// 对象或数组必须以函数形式返回
default:function(){
return{ title:"hello" }
// return [1,2,3]
}
}
// 自定义验证函数
propG:{
validator:function(value){
// 这个值必须匹配下面字符串中的一个
return[ "success", "warning", "danger" ].includes(value)
}
}
}
组件通信之 provide&inject
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<Cp3></Cp3>
</div>
<script>
Vue.component('Cp1',{
template:` <div>
我是cp1
<div> c is {{ c }}</div>
<div> d is {{ d }}</div>
<div> e is {{ e }}</div>
</div>`,
// inject:['a','b']
inject:{
c:'a', // 从供应的 值当中 检索出 'a' 指定一个key 的值
d: {
from:'b'
},
e: {
from :'e',
default: '默认值' // 默认值 如果没有e的供应的时候就显示默认值
}
}
})
Vue.component('Cp2',{
template:` <div>
我是cp2
<Cp1></Cp1>
</div>`
})
Vue.component('Cp3',{
template:` <div>
我是cp3
<Cp2></Cp2>
</div>`,
provide:{
a:28,
b:'mnb',
// e:'我是马牛b'
}
})
const app = new Vue({
el: '#app' ,
})
</script>
</body>
</html>
实例属性
$refs转发对象
<div ref="helloVue"> Hello Vue </div>
this.$refs.helloVue // <div> HelloVue <div>
// 如果是给 html 节点设置 ref,那么得到的就是节点对象
<cp ref="hi"><cp>
this.$refs.hi // Vue.component{...}
// 如果是给自定义组件设置 ref,那么得到的就是组件实例
<div v-for="item of 3" :key="item" ref="myList">{{ item }}</div>
this.$refs.myList // [ <div>1</div>, <div>2</div>, <div>3</div> ]
// 如果是给 v-for 的标签设置 ref,那么得到的就是一个数组,数组的每个成员就是循环列表的每一个节点对象
$parent / $children / $root
获取父组件实例对象 / 获取当前实例的直接子组件 / 获取根组件实例对象
tips:$children 不到万不得已不要使用,其获取到的子组件并不保证顺序,且没有响应式
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<father></father>
</div>
<script>
Vue.component("father",{
template:`<div>
<div>{{ title }}</div>
<div>我今年【 {{ fatherAge}} 】岁</div>
<button @click="fn1">获取全部儿姿信息</button>
<button @click="fn2">查看 father 的根组件并调用根组件的方法</button>
<hr />
<bigSon></bigSon>
<hr />
<centerSon></centerSon>
<hr />
<smallSon></smallSon>
</div>`,
data(){
return{
title:"我是爸爸",
fatherAge:40
}
},
methods:{
fn1(){
console.log("获取全部儿姿信息",this.$children);
},
fn2(){
console.log("查看 father 的根组件",this.$root);
this.$root.fn1()
}
}
})
Vue.component("bigSon",{
template:`<div>
<div>{{ title }}</div>
<div>我今年【 {{ sonAge}} 】岁</div>
<button @click="fn1">获取父组件实例并修改父亲年龄</button>
</div>`,
data(){
return{
title:"我是大儿子",
sonAge:20
}
},
methods:{
fn1(){
console.log("获取父组件实例",this.$parent);
this.$parent.fatherAge++
}
}
})
Vue.component("centerSon",{
template:`<div>
<div>{{ title }}</div>
<div>我今年【 {{ sonAge}} 】岁</div>
<button @click="fn2">查看 son 的根组件并调用根实例方法</button>
</div>`,
data(){
return{
title:"我是中儿子",
sonAge:15
}
},
methods:{
fn2(){
console.log("查看 son 的根组件",this.$root);
this.$root.fn1()
}
}
})
Vue.component("smallSon",{
template:`<div>
<div>{{ title }}</div>
<div>我今年【 {{ sonAge}} 】岁</div>
</div>`,
data(){
return{
title:"我是小儿子",
sonAge:10
}
}
})
const app = new Vue({
el: '#app',
data:{
money:0
},
methods:{
fn1(){
console.log("我是根实例");
}
}
})
</script>
</body>
</html>
Vue.mixin / mixin
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<cat></cat>
</div>
<script>
//===> 局部混入
const partMixin = {
template:`<div>我是局部混入的template</div>`,
data() {
return{
partMixin:"我是局部混入的data:partMixin",
name:"cat",
gender:"female"
}
},
created: function() { console.log("我是局部混入的partMixin") },
methods: {
fn1(){
console.log( "我是局部混入fn1" )
}
},
}
//===>全局混入(一般写在 main.js 里)
Vue.mixin({
template:`<div>我是全局混入的template</div>`,
data(){
return{
mixin:"我是全局混入的data:mixin",
name:"汤姆",
gender:"male"
}
},
created(){
console.log( "我是全局混入的created生命周期:mixin",this );
},
methods: {
fn1(){
console.log( "我是全局混入fn1" )
}
},
})
Vue.component("cat",{
template:`<div>
<div>{{ name }}</div>
<div>{{ gender }}</div>
<div>{{ partMixin }}</div>
<div>{{ mixin }}</div>
<button @click="fn1">点我看控制台有惊喜</button>
</div>`,
data(){
return{
name:"Tom",
}
},
methods:{
fn1(){
console.log( "我的性别是",this.gender,"我的名字是",this.name )
}
},
created(){
console.log( "我是组件的created生命周期" );
},
mixins: [partMixin]
})
const app = new Vue({
el: '#app',
})
</script>
</body>
</html>
tips
如果一个组件内既有全局混入又有局部混入,那么:
混入对象的选项将与组件自身选项合并,且:
全局混入template优先级 > 组件自身 > 局部混入
组件自身data优先级 > 局部混入 > 全局混入
组件自身生命周期和混入对象的生命周期都会执行,且当生命周期相同时执行顺序如下
全局混入生命周期最先执行 => 局部混入生命周期 => 组件生命周期
组件的自定义方法与混入对象方法重名时,执行顺序如下
组件自身自定义方法优先级 > 局部混入 > 全局混入
Vue.extend / extends
Vue.extend / extends 选项与 Vue.mixin / mixin 类似
允许声明扩展另一个组件 (可以是一个简单的选项对象或构造函数)
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
</div>
<script>
//===> 基于 Vue 的构造器创建的一个“子类”
const Profile = Vue.extend({
template:`<div>我是基于 Vue 构造器创建的一个子类 Profile </div>`,
data(){
return{
name:"Vue"
}
},
created: function() { console.log("我是Vue.extend添加的created") },
methods: {
fn1(){
console.log( "我是Vue.extend添加的fn1" )
}
},
})
//===> 局部拓展对象 partExtends
// 对象式
const partExtends = {
template:`<div>我是局部拓展对象 partExtends </div>`,
data(){
return{
name:"唐太宗"
}
},
created: function() { console.log("我是局部拓展对象partExtends的created") },
methods: {
fn1(){
console.log( "我是局部拓展对象partExtends的fn1" )
}
},
}
// 构造函数式
// const partExtends = Vue.extend({
// template:`<div>我是局部拓展 partExtends </div>`,
// data(){
// return{
// name:"唐太宗"
// }
// },
// created: function() { console.log("我是局部拓展partExtends的created") },
// methods: {
// fn1(){
// console.log( "我是局部拓展partExtends的fn1" )
// }
// },
// })
//===> 通过“子类”创建的组件
const app = new Profile({
template:`<div>
<div> 我是组件 </div>
<div> {{ name }} </div>
<button @click="fn1"> 点我控制台有惊喜 </button>
</div>`,
data(){
return{
name:"李世民"
}
},
created: function() { console.log("我是组件的created") },
methods: {
fn1(){
console.log( "我是组件的fn1" )
}
},
extends: partExtends,
});
app.$mount('#app')
</script>
</body>
</html>
tips
如果一个组件既是通过“子类”创建的,还有 extends 扩展对象,那么:
“子类”添加/拓展对象 的选项将与组件自身选项合并,且:
组件自身template优先级 > 拓展对象 > “子类”添加
组件自身data优先级 > 拓展对象 > “子类”添加
组件自身生命周期和 “子类”添加/拓展对象 的生命周期都会执行,且当生命周期相同时执行顺序如下
“子类”添加生命周期最先执行 => 拓展对象生命周期 => 组件生命周期
组件的自定义方法与 “子类”添加/拓展对象 方法重名时,执行顺序如下
组件自身自定义方法优先级 > 拓展对象 > “子类”添加
全局过滤器
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<son></son>
</div>
<script>
Vue.filter("gender",(val)=>{
if(val === 1)return "男"
if(val === 0)return "女"
})
Vue.component("son",{
template:`<div>
<div>{{ title }}</div>
<div>{{ 1 | gender }}</div>
<div>{{ 0 | gender }}</div>
</div>`,
data(){
return{
title:"我是儿子"
}
},
})
const app = new Vue({
el: '#app' ,
})
</script>
</body>
</html>
tips
全局过滤器的实现也可通过 mixin/extend 全局混入一个 filter 实现
组件通信之bus总线
对于一个Vue实例,可以通过 $on 和 $emit 来创建一个事件总线(EventBus)
$once:事件只触发一次
$off:移除监听,若不移除则一直存在
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<tom></tom>
<jerry></jerry>
</div>
<script>
const bus = new Vue();
Vue.component("tom",{
template:`<div>
<div>{{ title }}</div>
<div>{{ count }}</div>
</div>`,
data(){
return{
title:"我是汤姆",
count:1
}
},
created() {
bus.$on("incrementCount",()=>{
this.count++
})
// 监听事件只触发一次
// bus.$once("incrementCount",()=>{
// this.count++
// })
},
beforeDestroy() {
// 移除监听
bus.$off("incrementCount")
},
})
Vue.component("jerry",{
template:`<div>
<div>{{ title }}</div>
<button @click="fn1"> 点我count+1 </button>
</div>`,
data(){
return{
title:"我是杰瑞"
}
},
methods: {
fn1(){
bus.$emit("incrementCount")
}
},
})
const app = new Vue({
el: '#app' ,
})
</script>
</body>
</html>
render函数
template 选项的替代方案
Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<tom></tom>
<!-- <jerry></jerry> -->
</div>
<script>
Vue.component("tom",{
data(){
return{
title:"我是汤姆",
}
},
render(h) {
// h("标签名",{属性},"标签内容")
const VNode = h(
"h1",
{
class:["a","b","c"],
style:{
color:"red",
fontSize:"30px"
},
on:{
click:this.fn1
}
},
" I Love Jerry ")
return VNode
},
methods: {
fn1(){
alert("我是fn1")
}
},
})
// 只在完整版时可用
// const rendJerry = Vue.compile("<h1> I Am Jerry <h1>")
// Vue.component("jerry",{
// render:rendJerry.render
// })
const app = new Vue({
el: '#app' ,
})
</script>
</body>
</html>
动态组件与缓存组件
缓存组件 -> <keep-alive> </keep-alive>
缓存组件里不能并行多个组件
v-if 使用缓存组件,在组件第一次加载完毕时会执行 created(创建) 和 activated(激活) 生命周期,且 activated 在 created 之后执行
当缓存组件的父组件被销毁时,缓存组件才会被销毁,且缓存组件的 deactivated(失活) 生命周期在 beforeDestroy(销毁前) 之前执行
选择性缓存
props:
include:字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude:字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max:数字。最多可以缓存多少组件实例。
tips:
匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 动态组件 -->
<!-- <component :is="cpIs"></component> -->
<!-- 缓存组件 -->
<keep-alive>
<component :is="cpIs"></component>
</keep-alive>
<button @click="switchComponent">切换 cpa / cpb</button>
<hr>
<!-- v-if使用缓存组件 -->
<keep-alive>
<cpc v-if="cpcIsShow"></cpc>
</keep-alive>
<button @click="cpcIsShow=!cpcIsShow">切换 cpc</button>
<hr>
<!-- 缓存组件的销毁 -->
<cpd v-if="cpdIsShow"></cpd>
<button @click="cpdIsShow=!cpdIsShow">切换 componentD</button>
<hr>
<!-- 选择性缓存 -->
<!-- <keep-alive :include=["a1","a2"]> -->
<!-- <keep-alive :include=["cp1","cp2"]> -->
<keep-alive :exclude=["cp1"]>
<cp1 v-if="cp1IsShow"></cp1>
<cp2 v-else-if="cp2IsShow"></cp2>
<cp3 v-else></cp3>
</keep-alive>
<button @click="cp1IsShow=true">渲染cp1</button>
<button @click="showCp2">渲染cp2</button>
<button @click="showCp3">渲染cp3</button>
</div>
<script>
//===> 动态组件与缓存组件的基本使用
const cpa = {
template:`<h1>我是cpa组件</h1>`,
created() {
console.log("cpa => created => 创建完毕");
},
beforeDestroy() {
console.log("cpa => beforeDestroy => 销毁前");
},
}
const cpb = {
template:`<h1>我是cpb组件</h1>`,
created() {
console.log("cpb => created => 创建完毕");
},
beforeDestroy() {
console.log("cpb => beforeDestroy => 销毁前");
},
}
//===> v-if 使用缓存组件
Vue.component("cpc",{
template:`<h1>我是cpc组件</h1>`,
created() {
console.log("cpc => created => 创建完毕");
},
beforeDestroy() {
console.log("cpc => beforeDestroy => 销毁前");
},
// 组件第一次加载完毕也会执行,在created之后
activated() {
console.log("cpc => activated => 激活");
},
deactivated() {
console.log("cpc => deactivated => 失活");
},
})
//===> 销毁缓存组件
Vue.component("cpd",{
template:`<div>
<h1>我是cpd组件</h1>
<keep-alive>
<cpe></cpe>
</keep-alive>
</div>`,
})
Vue.component("cpe",{
template:`<h1>我是cpe组件</h1>`,
beforeDestroy() {
console.log("cpe => beforeDestroy => 销毁前");
},
deactivated() {
console.log("cpe => deactivated => 失活");
},
})
//===> 选择性缓存
Vue.component("cp1",{
// name:"a1",
template:`<h1>我是cp1组件</h1>`,
created() {
console.log("cp1 创建");
},
activated() {
console.log("cp1 激活");
},
})
Vue.component("cp2",{
// name:"a2",
template:`<h1>我是cp2组件</h1>`,
created() {
console.log("cp2 创建");
},
activated() {
console.log("cp2 激活");
},
})
Vue.component("cp3",{
// name:"a3",
template:`<h1>我是cp3组件</h1>`,
created() {
console.log("cp3 创建");
},
activated() {
console.log("cp3 激活");
},
})
// =======================================================
new Vue({
el:"#app",
data: {
cpIs:cpa,
isCpa:true,
cpcIsShow:true,
cpdIsShow:true,
cp1IsShow:true,
cp2IsShow:true,
},
watch: {
isCpa(nv){
this.cpIs = nv ? cpa : cpb
}
},
methods: {
switchComponent(){
this.isCpa = !this.isCpa
},
showCp2(){
this.cp1IsShow = false
this.cp2IsShow = true
},
showCp3(){
this.cp1IsShow = false
this.cp2IsShow = false
}
},
})
</script>
</body>
</html>
实例方法
$watch / $attrs / $slot
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
</head>
<body>
<div id="app">
<cp1 a="1" :b="1" c="2" d="3">
垃圾1
<template #ljt>垃圾2</template>
</cp1>
</div>
<script>
Vue.component("cp1",{
props:["a"],
template:`<div>
<slot></slot>
<div>{{ title }}</div>
<button @click="title++">title递增</button>
<slot name="ljt"></slot>
</div>`,
data(){
return{
title:1,
isWatch:null
}
},
created() {
// this.$watch("监听的属性名","处理函数(nv,ov){}",{deep:深度监听,immediate:是否立即执行})
this.isWatch = this.$watch("title",(nv,ov)=>{console.log(nv,ov);})
console.log("props接收到的参数", this.a, this.b, this.c, this.d)
// this.$attrs 接收除了 props 声明接收的属性及 class 和 style 以外的其他属性
console.log("attrs接收到的参数",this.$attrs);
// 获取组件上对应插槽的内容
console.log("$slots",this.$slots);
},
beforeDestroy() {
// 组件销毁前移除监听
this.unwatch();
},
})
const app = new Vue({
el: '#app' ,
})
</script>
</body>
</html>
插件的使用
插件通常用来为 Vue 添加全局功能。插件的功能范围没有严格的限制,一般有下面几种:
1.添加全局方法或者 property
2.添加全局资源:指令/过滤器/过渡等
3.通过全局混入来添加一些组件选项
4.添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。
5.一个库,提供自己的 API,同时提供上面提到的一个或多个功能
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.7/dist/vue.js"></script>
<script src="./pluging.js"></script>
</head>
<body>
<div id="app">
<div>{{ name }}</div>
<div>{{ gender }}</div>
<plug></plug>
<div>{{ 1 | gender }}</div>
</div>
<script>
Vue.use(myPluging,"参数一","参数二")
// Vue.use( 插件, 参数 )
// 实际上 use 方法就是执行插件中的 install 函数
new Vue({
el:"#app"
})
</script>
</body>
</html>
const myPluging = {
install(Vue,arg1,arg2){
console.log("Vue",Vue,arg1,arg2);
Vue.filter("gender",(val)=>{
if(val === 1)return "男"
if(val === 0)return "女"
})
Vue.mixin({
data(){
return{
name:"汤姆",
gender:"male"
}
}
})
Vue.component("plug",{
template:`<h1>plug插件</h1>`,
})
}
}
Vue3-全局配置
通过 app.config.globalProperties.XXX = XXX 拓展 app 实例上的方法或者属性