文章目录
1 基本使用
1-1 模板(插值表达式,指令)
{{}}
v-html(有xss风险,会覆盖子元素)
1-2 computed和watch
- computed有缓存,data不变时不会重新计算
- watch深度监听
- watch监听引用类型无法拿到oldValue
1-3 class和style
class 使用动态属性
- 对象式
- 数组式
<p :class="{ black: isBlack, yellow: isYellow }">使用 class</p>
<p :class="[black, yellow]">使用 class (数组)</p>
style 使用驼峰式写法
styleData
<p :style="styleData">使用 style</p>
data() {
return {
isBlack: true,
isYellow: true,
black: 'black',
yellow: 'yellow',
styleData: {
fontSize: '40px', // 转换为驼峰式
color: 'red',
backgroundColor: '#ccc' // 转换为驼峰式
}
}
}
1-4 条件渲染
v-if/v-else/v-else-if, v-show
- 可使用变量或者===表达式
- 必须是兄弟html元素
v-if 和 v-show 的区别
- v-if 条件渲染,会触发创建和销毁
- v-show 条件显示,创建后不再销毁,切换仅仅是注释掉
v-if 和 v-show的使用场景
- v-if 适用于切换不频繁的场景
- v-show 切换频繁时使用
1-5 循环
v-for
- 数组:v-for="{{ item, index}}"
- 对象:v-for"{{ val, key, index}}"
key使用
- 尽量是一个数据的唯一值
- v-for和v-if不能在一个元素上同时使用,可分别用在子父元素
1-6 事件
v-on/简写为@,可写事件名或者表达式
event参数,自定义参数
- 不传入参数时,默认事件第一个参数为事件对象
- 传入参数时,要获取事件对象则需要传入$event
- event是原生的
- 事件被挂载到当前元素
修饰符
事件修饰符
- .stop 阻止点击事件传播
- .prevent 提交事件不再重载页面
- .capture 添加监听事件时使用事件捕获模式,即内部元素触发的事件先在此处理,然后再交给内部元素
- self 只在event.target是自身时触发
按键修饰符
- @click.ctrl 同时按下alt或者shift也会触发
- .ctrl.exact 只有同时按下ctrl时才会触发
- @click.exact 没有同时按任何修饰符时才会触发
观察事件被绑定到哪里(与react有所不同)
1-7 表单
v-model
常见表单项
textarea
checkbox
radio
select
修饰符
lazy
number
trim
2 组件
2-1 props
父组件通过v-bind绑定动态属性传参
// index.vue
<template>
<div>
<Input @add="addHandler"/>
<List :list="list" @delete="deleteHandler"/>
</div>
</template>
子组件接收父组件参数
list.vue
props: {
// prop 类型和默认值
list: {
type: Array,
default() {
return []
}
}
},
2-2 v-on 和 $emit
子组件触发事件删除父组件数据
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{item.title}}
<button @click="deleteItem(item.id)">删除</button>
</li>
</ul>
</div>
</template>
由于单向数据流的原因,需要使用$emit
提交事件给父组件,让父组件操作
// List.vue
// ...
methods: {
deleteItem(id) {
this.$emit('delete', id)
},
addTitleHandler(title) {
// eslint-disable-next-line
console.log('on add title', title)
}
},
子组件emit
中的事件名对应 父组件中的事件名
父组件实现数据删除操作
// index.vue
// ...
methods: {
deleteHandler(id) {
this.list = this.list.filter(item => item.id !== id)
}
},
2-3 组件通讯-自定义事件
创建用于自定义事件的Vue实例
创建一个Vue的实例专门用于创创建自定义事件,因为Vue里有一个自定义事件的方法
event.$on 绑定自定义事件
// list.vue
mounted() {
// eslint-disable-next-line
console.log('list mounted')
// 绑定自定义事件
event.$on('onAddTitle', this.addTitleHandler)
},
event.$emit 调用自定义事件
// Inout.vue
methods: {
addTitle() {
// 调用父组件的事件
// ...
// 调用自定义事件
event.$emit('onAddTitle', this.title)
this.title = ''
}
}
及时在beforeDestroy
时销毁自定义事件
beforeDestroy() {
// 及时销毁,否则可能造成内存泄露
event.$off('onAddTitle', this.addTitleHandler)
}
2-4 组件生命周期
- 创建阶段
- 挂载阶段
- 更新阶段
- 销毁阶段
mouted 和 created的区别
- mouted是页面已经渲染完成
- created 是页面未渲染完成,但是vue实例 已经初始化完成
单个组件
父子组件
创建与挂载顺序
- 父组件先创建js模型,初始化vue实例,然后子组件创建
- 子组件先挂载,渲染页面,父组件再挂载
更新顺序
类似于洋葱模型
3 高级特性
3-1 自定义v-model - 使用model选项
<template>
<!-- 例如:vue 颜色选择 -->
<input type="text"
:value="text1"
@input="$emit('change1', $event.target.value)"
>
</template>
<script>
export default {
model: {
prop: 'text1', // 对应 props text1
event: 'change1'
},
props: {
text1: String,
default() {
return ''
}
}
}
</script>
- 上面的 input标签 使用了
:value
而不是v-model
- 上面的
$emit
提交事件change1 和 model.选项中的event
事件名 要对应起来 - text1 属性值要和model选项中的
prop
值对应起来
3-2 refs
ref属性,用于获取dom元素
只要元素上添加这个属性以及名称
3-3 $nextTick vue组件更新之后获取最新DOM
- Vue是一步渲染框架
- data 改变之后,DOM不会立刻渲染
- $nextTick 会在DOM渲染触发后触发,以获取最新DOM节点
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">{{item}}</li>
</ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script>
export default {
name: "app",
data() {
return {
list: ["a", "b", "c"]
};
},
methods: {
addItem() {
this.list.push(`${Date.now()}`);
this.list.push(`${Date.now()}`);
// 获取 DOM 元素
const ulElem = this.$refs.ul1;
// eslint-disable-next-line
console.log(ulElem);
// eslint-disable-next-line
console.log("普通获取", ulElem.childNodes.length);
this.$nextTick(() => {
// 获取 DOM 元素
const ulElem = this.$refs.ul1;
// eslint-disable-next-line
console.log(ulElem);
// eslint-disable-next-line
console.log("使用$nextTick", ulElem.childNodes.length);
});
}
}
};
</script>
使用$nextTick
能立即拿到结果
总结
- 异步渲染,$nextTick 等待 DOM 渲染完再回调
- 页面渲染时会将 data 的修改做整合,多次 data 修改只会渲染一次
3-4 slot 插槽
基本使用
父组件传数据给子组件,并调用子组件
<SlotDemo :url="website.url">
{{website.title}}
</SlotDemo>
...
<script>
import SlotDemo from './SlotDemo'
export default {
components: {
SlotDemo,
},
data() {
return {
name: 'lc',
website: {
url: 'http://bilibili.com/',
title: '哔哩哔哩弹幕网',
subTitle: '看番网站'
},
}
}
}
</script>
子组件接受数据,设置插槽
<template>
<a :href="url">
<slot>
默认内容,即父组件没设置内容时,这里显示
</slot>
</a>
</template>
<script>
export default {
props: ['url'],
data() {
return {}
}
}
</script>
子组件<slot>
标签下就会被父组件调用子组件时写入的内容替换
不给父组写内容就会展示子组件默认的内容
作用域插槽 v-slot
让父组件获取到子组件数据,并传入插槽
子组件设置数据
export default {
props: ['url'],
data() {
return {
website: {
url: 'http://baidu.com/',
title: '百度',
subTitle: '百度一下,你就知道'
}
}
}
}
如果想要使父组件可以传入子组件的数据该怎么办?
首先给子组件插槽设置一个动态属性
父组件设置v-slot
指令,等于一个自定义数据名
这样子组件数据就通过v-model
传入了自定义数据名slotProps
中
通过以下方式获取数据,第二个slotData需要和子组件动态属性名对应
{{slotProps.slotData.title}}
具名插槽
插槽多了之后,可以给插槽命名,用于区分并对应插入
子组件定义插槽名
<template>
<a :href="url">
<div id="s1">
<slot name="s1">
默认内容,即父组件没设置内容时,这里显示
</slot>
</div>
<div id="s2">
<slot name="s2">
默认内容,即父组件没设置内容时,这里显示
</slot>
</div>
<div id="s3">
<slot name="s3">
默认内容,即父组件没设置内容时,这里显示
</slot>
</div>
</a>
</template>
父组件通过slot:slotName
对应插入
<SlotDemo :url="website.url">
<template v-slot:s1>
<div>
s1
</div>
</template>
<template v-slot:s2>
<div>
s2
</div>
</template>
<template v-slot:s3>
</template>
</SlotDemo>
3-5 动态组件
- 有的时候,我们需要在多个不同的组件之间进行切换。
- 虽然我们可以通过
v-if
来处理,但是会比较麻烦,而且有些问题- 有销毁过程,如果频繁切换,开销大
- 无法保存组件状态
- 使用
v-show
同样会有问题- 无论后续是否切换组件,组件都会统一进行渲染
- 组件结构在html中可以查看
vue
提供了一个更方便的方式来处理这种情况
component 组件
component
是vue
内置的一个组件,它提供一个is
属性用来动态渲染不同的组件- 但是当组件切换的时候,都会触发组件的销毁和重建。
- 首先,性能不好。其次,会丢失组件状态
- 所以后面会有
keep-alive
组件来优化这些问题
<!-- 动态组件 -->
<component :is="dynamicComponent"/>
data() {
return {
dynamicComponent: "NextTick",
}
}
3-6 异步组件
- import() 函数
- 按需加载,异步加载大组件
- 什么时候用,什么时候加载
<!-- 异步组件 -->
<FormDemo v-if="showFormDemo"/>
<button @click="showFormDemo = true">show form demo</button>
点击切换状态,然后通过v-if判断加载组件
data() {
showFormDemo: false
}
}
点击才会发送请求加载,这就是异步组件
3-7 keep-alive 组件持久化-缓存组件
keep-alive 组件
当在这些组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。keep-alive
是一个内置容器组件, 使用 >keep-alive
以后,内部包含的组件将增加 激活
和 失活/冻结
的状态
使用格式<keep-alive><component :is=""></component></keep-alive>
<keep-alive>
<!-- is 接收的是要显示的组件名称 -->
<component :is="currentComponent"></component>
</keep-alive>
这样只会在第一次切换组件的时候调用生命周期中的mounted/created
函数
并且不再调用destroyed
函数销毁组件
keep-alive 生命周期
使用了 keep-alive
的组件会触发 activated
、deactivated
两个生命周期函数
activated
keep-alive
组件激活时调用
deactivated
keep-alive
组件停用时调用
3-8 mixin 混合-抽离组件公共逻辑
- 多个组件有相同的逻辑,可以抽离出来多个组件共用
- mixin 并不是完美的解决方案 会有些问题
- Vue3 提供了 Composition API 用于解决mixin的问题
一个mixin
// mixin.js
export default {
data() {
return {
city: '成都'
}
},
methods: {
showName() {
// eslint-disable-next-line
console.log(this.name)
}
},
mounted() {
// eslint-disable-next-line
console.log('mixin mounted', this.name)
}
}
组件通过mixins: [myMixin]
格式混入mixin文件
<template>
<div>
<p>{{name}} {{major}} {{city}}</p>
<button @click="showName">显示姓名</button>
</div>
</template>
<script>
import myMixin from './mixin'
export default {
mixins: [myMixin], // 可以添加多个,会自动合并起来
data() {
return {
name: 'lc',
major: 'web 前端'
}
},
methods: {
},
mounted() {
// eslint-disable-next-line
console.log('component mounted', this.name)
}
}
</script>
组件就能直接拿到mixin中的数据,相当于复制进来了
缺点:
- 变量来源不明确,不利于阅读
- 多个mixin可能造成命名冲突,造成数据覆盖的情况
- mixin可能出现多对多的引用关系,复杂度高, 耦合性很高
3 vuex
通信解决方案
通信核心:找到数据交互之间的桥梁。如果两者之间能直接沟通,那就直接沟通,如果不能,则找一个两者都能说得上话的人。
- props/$emit(父子通信)
- $refs/ref(父子通信)
- children/parent(父子通信)
- attrs/listeners(父子通信)
- provide/inject(父子通信、跨级通信)
- eventBus(父子通信、跨级通信、兄弟通信)
- localStorage/sessionStorage等基于浏览器客户端的存储(父子通信、跨级通信、兄弟通信、路由视图级别通信)
- vuex(父子通信、跨级通信、兄弟通信、路由视图级别通信)
vuex
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式,类似 redux
vuex(父子通信、跨级通信、兄弟通信、路由视图级别通信)
- state : 存储应用状态数据(React 中的 State)
- mapState把
store
的state
通过mapState
函数映射到组件的computed
上
- mapState把
- getters类似组件的
data
与computed
,store
也提供了一个getters
对象来处理派生数据- mapGetters
- Vue Component : 消费 State
- actions : 提交修改 State 的动作(React 中的 action)异步操作只能在此执行
- mapActions
- dispatch
- mutations : 唯一更改
State
的位置(React 中的 Reducer)- mapMutations
mapMutations
函数的使用与mapState
和mapGetters
类似,但是其一般是把组件的methods
映射为store
的mutations
的commit
调用 - commit
- mapMutations
vue-router
路由模式
- hash模式(默认):http://baidu.com/#/user/10
- H5 history 模式:http://abc.com/user/10 (需要server端支持)