面试题
v-show和v-if的区别
违和v-for中药用key
描述Vue组件的生命周期(有父子组件的情况)
Vue组件如何通讯
1.父子组件通讯用属性或触发事件的方式
2.两个组件没有关系或层级比较深,用自定义事件的方式
3.Vuex的方式组件通讯
描述组件渲染和更新过程
双向数据绑定v-model的实现原理
一、vue的基本使用
1、computed & watch的去区别
- computed计算属性,有缓存,data不变则不会从新计算
- watch如何深度监听? handler true
- watch监听引用类型,拿不到oldVal
<template>
<div>
<input v-model="name"/>
<input v-model="info.city"/>
</div>
</template>
<script>
export default {
data() {
return {
name: 'ariel_bo',
info: {
city: '北京'
}
}
},
watch: {
name(oldVal, val) {
// eslint-disable-next-line
console.log('watch name', oldVal, val) // 值类型,可正常拿到 oldVal 和 val
},
//深度监听
info: {
handler(oldVal, val) {
// eslint-disable-next-line
console.log('watch info', oldVal, val) // 引用类型,拿不到 oldVal 。因为指针相同,此时已经指向了新的 val
},
deep: true // 深度监听
}
}
}
</script>
2. class和style
使用动态属性
使用驼峰式写法
<template>
<div id="app">
<div>
<p :class="{black: isBlack, yellow: isYellow}">使用class</p>
<p :class="[black, yellow]">使用class数组</p>
<p :style="styleData">styleData</p>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
isBlack: true,
isYellow: true,
black: 'black',
yellow: 'yellow',
styleData: {
fontSize: '40px',
color: 'red',
backgroundColor: '#ccc'
}
}
},
mounted() {
},
methods: {
}
}
</script>
<style scoped>
@import './assets/styles/reset.css';
.black {
background: #999;
}
.yellow {
color: yellow;
}
</style>
3.条件渲染v-if v-show
- v-if v-show用法,可使用变量,也可使用===表达式
- v-if与v-show的区别
- v-if和v-show的使用场景
4.循环(列表)渲染
1.如何便利对象? -----也可以用v-for
2.key的总要性。不能乱写,如random和index
3.v-for和v-if不能一起使用
如下,v-for会先于v-if执行,就循环三次,然后每次都执行v-if,不建议这样
<template>
<div id="app">
<p>遍历数组</p>
<ul>
<li v-for="(item, index) in listArr" :key="item.id">
{{index}} -- {{item.id}} --- {{item.title}}
</li>
</ul>
<p>遍历对象</p>
<ul v-if=”flag“>
<li v-for="(val, key, index) in listObj" :key="key">
{{index}} ---- {{key}} ----- {{val.title}}
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
flag: false,
listArr: [
{id: 'a', title: '标题1'}, // 数据结构中最好有id值,方便使用key值
{id: 'b', title: '标题2'},
{id: 'c', title: '标题3'}
],
listObj: {
a: {title: '标题1'},
b: {title: '标题2'},
c: {title: '标题3'}
}
}
}
}
</script>
5.事件
event参数,自定义参数
事件修饰符、按键修饰符
事件被绑定到了哪里?
<template>
<div id="app">
<p>{{ num }}</p>
<button @click="increment1">+1</button>
<button @click="increment2(2, $event)">+1</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
num: 0
}
},
methods: {
increment1(event) {
console.log(event) // 打印的是MouseEvent对象,即原生的event对象
console.log(event.__proto__.constructor, 'constructor----')
console.log(event.target); // <button>+1</button> 事件挂载的对象地方
console.log(event.currentTarget) // <button>+1</button> 注意,事件是被注册到当前元素的,即事件是在当前对象触发的,和 React 不一样
this.num++;
// 1、 event是原生的
// 2. 事件被挂载到当前元素
// 3. 和Dom事件一样
},
increment2(val, event) {
console.log(event.target)
this.num = this.num + val;
},
loadHandler() {}
},
mounted() {
window.addEventListener('load', this.loadHandler)
},
beforeDestroy() {
// [注意]用vue绑定事件,组件销毁时会自动解绑
// 自己绑定的事件,需要自己销毁!!!
window.removeEventListener('load', this.loadHandler)
}
}
</script>
事件修饰符
按键修饰符
6.表单
v-model
常见的表单项:textarea、checkbox、radio、select
修饰符:lazy、number、trim
<template>
<div id="app">
<p>输入框</p>
{{name1}}
<input type="text" v-model.trim="name">
<input type="text" v-model.lazy="name1"> <!-- 当改变输入框的值时,span中的值是不会变化的(注意光标还在输入框内)
而当输入框失去焦点时,span中的值才会改变(注意光标不在输入框内)
-->
<input type="text" v-model.number="age">
<p>多行文本:{{desc}}</p>
<textarea name="" id="" v-model="desc" cols="30" rows="10"></textarea>
<!-- 注意,<textarea>{{desc}}</textarea> 是不允许的!!! -->
<p>复选框</p>
<input type="checkbox" v-model="checked">
<p>多个复选框 {{checkedName}}</p>
<input type="checkbox" id="jack" value="Jack" v-model="checkedName">
<label for="jack">Jack</label>
<input type="checkbox" id="join" value="Join" v-model="checkedName">
<label for="join">join</label>
<input type="checkbox" id="milk" value="Milk" v-model="checkedName">
<label for="jack">Milk</label>
<p>单选 {{gender}}</p>
<input type="radio" id="male" value="male" v-model="gender"/>
<label for="male">男</label>
<input type="radio" id="female" value="female" v-model="gender"/>
<label for="female">女</label>
<p>下拉列表选择 {{selected}}</p>
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>下拉列表选择(多选){{selectedList}}</p>
<select name="" v-model="selectedList" id="" multiple>
<option>请选择</option>
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
<p>下拉列表选择(多选) {{selectedList}}</p>
<select v-model="selectedList" multiple>
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</div>
</template>
<script>
export default {
name: 'App',
data () {
return {
name: 'Ariel_Bo',
name1: 'Ariel_Bo',
age: 18,
desc: '自我介绍',
checked: true,
checkedName: [],
gender: 'male',
selected: '',
selectedList: []
}
},
methods: {
}
}
</script>
7.Vue组件使用
props和$emit
组件间通讯--自定义事件
组件生命周期
什么是Props呢?
Props是你可以在组件上注册一些自定义的attribute;
父组件给这些attribute赋值,子组件通过attribute的名称获取到对应的值;
Props有两种常见的用法:
方式一:字符串数组,数组中的字符串就是attribute的名称;
方式二:对象类型,对象类型我们可以在指定attribute名称的同时,指定它需要传递的类型是否是必须的、 默认值等等;
什么情况下子组件需要传递内容到父组件呢?
当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容;
子组件有一些内容想要传递给父组件的时候;
我们如何完成上面的操作呢?
首先,我们需要在子组件中定义好在某些情况下触发的事件名称;
其次,在父组件中以v-on的方式传入要监听的事件名称,并且绑定到对应的方法中;
最后,在子组件中发生某个事件的时候,根据事件名称触发对应的事件;
parent.vue
<template>
<div>
<Input @add="addHandler"/>
<List :list="list" @delete="deleteHandler"/>
</div>
</template>
<script>
import Input from './Input'
import List from './List'
export default {
components: {
Input,
List
},
data() {
return {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
}
]
}
},
methods: {
addHandler(title) {
this.list.push({
id: `id-${Date.now()}`,
title
})
},
deleteHandler(id) {
this.list = this.list.filter(item => item.id !== id)
}
},
created() {
// eslint-disable-next-line
console.log('index created')
},
mounted() {
// eslint-disable-next-line
console.log('index mounted')
},
beforeUpdate() {
// eslint-disable-next-line
console.log('index before update')
},
updated() {
// eslint-disable-next-line
console.log('index updated')
},
}
</script>
Input.vue
<template>
<div>
<input type="text" v-model="title"/>
<button @click="addTitle">add</button>
</div>
</template>
<script>
import event from './event'
export default {
data() {
return {
title: ''
}
},
methods: {
addTitle() {
// 调用父组件的事件
this.$emit('add', this.title)
// 调用自定义事件
event.$emit('onAddTitle', this.title)
this.title = ''
}
}
}
</script>
List.vue
<template>
<div>
<ul>
<li v-for="item in list" :key="item.id">
{{item.title}}
<button @click="deleteItem(item.id)">删除</button>
</li>
</ul>
</div>
</template>
<script>
import event from './event'
export default {
// props: ['list']
props: {
// prop 类型和默认值
list: {
type: Array,
default() {
return []
}
}
},
data() {
return {
}
},
methods: {
deleteItem(id) {
this.$emit('delete', id)
},
addTitleHandler(title) {
// eslint-disable-next-line
console.log('on add title', title)
}
},
created() {
// eslint-disable-next-line
console.log('list created')
},
mounted() {
// eslint-disable-next-line
console.log('list mounted')
// 绑定自定义事件
event.$on('onAddTitle', this.addTitleHandler)
},
beforeUpdate() {
// eslint-disable-next-line
console.log('list before update')
},
updated() {
// eslint-disable-next-line
console.log('list updated')
},
beforeDestroy() {
// 及时销毁自定义事件,否则可能造成内存泄露
event.$off('onAddTitle', this.addTitleHandler)
}
}
</script>
event.js event实例
import Vue from 'vue'
export default new Vue()
8.生命周期
- 挂载阶段
- 更新阶段
- 销毁阶段
父子组件之间的执行顺序
父beforeCreated ->父 created -> 父 beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
父beforeUpdate-> 子beforeUpdate -> 子updated -> 父updated
父子组件销毁顺序
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
二、vue高级特性
1.自定义V-model
官方有说到,v-model的原理其实是背后有两个操作:
v-bind绑定value属性的值;
v-on绑定input事件监听到函数中,函数会获取最新的值赋值到绑定的属性中;
2.$nextTick
- vue是异步渲染
- data改变之后,DOM不会立即渲染
- $nextTick会在DOM渲染之后被触发,以获取最新的DOM节点
如下:不加$nextTick,点击打印出来是3 6 9
加上$nextTick打印出来是 6 9 12,获取的是最新的dom节点个数
<template>
<div>
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{{ item }}
</li>
</ul>
<button @click="addItem">添加一条</button>
</div>
</template>
<script>
export default {
data () {
return {
list: ['a', 'b', 'c']
}
},
methods: {
addItem() {
this.list.push(`${Date.now()}`);
this.list.push(`${Date.now()}`);
this.list.push(`${Date.now()}`);
// 1. 异步渲染,$nextTick待DOM渲染完再回调
// 3. 页面渲染时,会将data的修改做整合,多次data修改只会渲染一次
this.$nextTick(() => {
const ulElem = this.$refs.ul1;
console.log(ulElem.children.length);
})
}
}
}
</script>
<style lang="scss" scoped>
</style>
3.slot
- 基本使用
- 作用域插槽
- 具名插槽
1.基本使用
//slotDome.vue
<template>
<a :href="url">
<slot>
默认内容,即父组件没设置内容时,这里显示
</slot>
</a>
</template>
<script>
export default {
props: ['url'],
data() {
return {}
}
}
</script>
父组件中使用
<SlotDemo :url="website.url">
{{website.title}}
</SlotDemo>
作用域插槽
<ScopedSlotDemo :url="website.url">
<template v-slot="slotProps">
{{slotProps.slotData.title}}
</template>
</ScopedSlotDemo>
具名插槽
4.动态、异步组件
动态组件
:is= “component-name” 用法
需要根据数据,动态渲染的场景。即组件类型不确定。
<!-- 动态组件 -->
<component :is="NextTickName"/>
异步组件
import()函数
按需加载,异步加载大组件
<!-- 异步组件 -->
<FormDemo v-if="showFormDemo"/>
<button @click="showFormDemo = true">show form demo</button>
export default {
components: {
FormDemo: () => import('../BaseUse/FormDemo'),
},}
5.keep-alive 缓存组件
Vue3组件化开发:切换组件、keep-alive_米儿的博客-CSDN博客_vue3 组件切换
缓存组件
频繁切换,不需要重复渲染
Vue常见性能优化
<template>
<div>
<button @click="changeState('A')">A</button>
<button @click="changeState('B')">B</button>
<button @click="changeState('C')">C</button>
<keep-alive> <!-- tab 切换 -->
<KeepAliveStageA v-if="state === 'A'"/> <!-- v-show -->
<KeepAliveStageB v-if="state === 'B'"/>
<KeepAliveStageC v-if="state === 'C'"/>
</keep-alive>
</div>
</template>
<script>
import KeepAliveStageA from './KeepAliveStateA'
import KeepAliveStageB from './KeepAliveStateB'
import KeepAliveStageC from './KeepAliveStateC'
export default {
components: {
KeepAliveStageA,
KeepAliveStageB,
KeepAliveStageC
},
data() {
return {
state: 'A'
}
},
methods: {
changeState(state) {
this.state = state
}
}
}
</script>
<template>
<p>state A</p>
</template>
<script>
export default {
mounted() {
// eslint-disable-next-line
console.log('A mounted')
},
destroyed() {
// eslint-disable-next-line
console.log('A destroyed')
}
}
</script>
不加keep-alive每次都会走钩子函数不停的销毁和重建,
加上keep-alive的话,就不会销毁
6.mixin
- 多个组件有相同代码逻辑,抽离出来
- mixin并不是完美解决方案,会有一些问题
- Vue3提出的Composition API旨在解决这些问题
问题:
变量来源不明确,不利于阅读
多个mixin可能会造成命名冲突
mixin和组件可能出现多对多的关系,复杂度较高
Mixin的合并规则
情况一:如果是data函数的返回值对象
返回值对象默认情况下会进行合并;
如果data返回值对象的属性发生了冲突,那么会保留组件自身的数据;
抽离出的逻辑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)
}
}
使用
<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: '双越',
major: 'web 前端'
}
},
methods: {
},
mounted() {
// eslint-disable-next-line
console.log('component mounted', this.name)
}
}
</script>
![](https://i-blog.csdnimg.cn/blog_migrate/9d4a554e722d7b0a4e4892e5453dd745.png)
三、Vuex和Vue-router使用
1.Vuex
知识点:state、getters、action、mutation
用于Vue组件
- dispatch
- commit
- mapState
- mapGetters
- mapActions
- mapMutations
异步操作必须在actions里
2.Vue-router使用
vue3:vue-router路由的使用_米儿的博客-CSDN博客
1.路由模式(hash,H5 history)
2.路由配置(动态路由、懒加载)
Vue-router路由模式
- hash模式(默认),如http://abc.com/#/user/10
- H5 history模式,如http://abc.com/user/20
- 后者需要server端支持,因此无特殊需求可选前者
H5 history模式配置
Vue-router路由配置 动态路由
Vue-router路由配置 懒加载
对应import()函数导入组件,异步加载