一、基础知识
1、内容渲染指令
1)v-text:覆盖元素中的默认内容
2){{}}:插值只能用在元素的内容节点,无法用在属性节点
3)v-html:把带有HTML标签的字符串,渲染为真正的DOM元素
2、属性绑定指令
1)v-bind,简写:" : "
3、事件绑定指令
1)v-on,简写:" @ "
2)使用:@click = "show"和@click = "show(传参)"
3)内置的变量$event:@click = "show(参数,$event)"
4)事件修饰符:.prevent .stop
5)按键修饰符(@keyup):.esc .enter
4、双向数据绑定指令
1)v-model:只能用在表单元素上
2)修饰符:.number .trim .lazy
5、循环渲染指令
1)v-for = "(item,index) in array" :key = "item.id"
2)官方建议:只要用到了v-for指令,一定要绑定一个:key属性,建议把id属性的值作为key的值
6、条件渲染指令
1)v-if v-else-if v-else:动态创建和移除元素
2)v-show:动态添加和移除display:none样式
7、过滤器
1) 过滤器常用于文本的格式化
2)定义
- 私有过滤器:定义到组件的filters节点之下,与data平级
- 全局过滤器:Vue.filter('名字', function (过滤器前面的值) { return 结果 } )
3)调用
- {{ message | dataFormat }}
8、watch
1)使用
- 方法格式:监听对象的名字(newVal, oldVal) { }
- 对象格式:
username: {
监听对象的名字(newVal, oldVal){
};
deep:true;
immediate:true;
}
- 单个属性:
watch:{
'info.username':{
handler(newVal){
};
};
}
9、computed
1)在定义的时候,要定义为function。而且,要return一个计算的结果。
2)在使用的时候,当作普通属性使用即可。在template模板结构中可以使用;在methods方法中也可以使用,this.计算属性的名字
3)计算属性的计算结果会被缓存,只要任何一个依赖的数据项发生了变化,计算属性就会重新计算
10、props
1)使用
- 数组格式:props:['init']
- 对象格式:
props:{
init:{
type:Number,
default:0, // 默认值
required:true
};
}
2)props是只读的,因此在项目开发中,不要直接修改它的值。把props的值转存到data中,就可以进行读写操作。props极大的提高了组件的复用性。
11、生命周期函数
1)创建阶段
- beforeCreate
- created:发起Ajax最早的时机,请求数据
- beforMount
- mounted:组件第一次被渲染到浏览器中,操作DOM的最早时机
2)运行阶段
- beforeUpdate
- updated:能够操作到最新的DOM元素
3)销毁阶段
- beforeDestory(vue3:beforeUnmount)
- destoryed(vue3:unmounted)
12、组件间的通信
1)父 -> 子:自定义属性
- 子组件中,通过props来自定义属性:props: [ 'init' ]
- 父组件中,负责把数据通过v-bind:绑定给子组件:<div :init="data"> </div>
2)子 -> 父:自定义事件
- 在子组件中调用this.$emit(自定义事件名称,数据)来触发自定义事件(vue3中要添加emits节点,即emits:['自定义事件名称']
- 在父组件中,通过v-on:来绑定自定义事件,并提供一个事件处理函数。通过事件处理函数的形参,接受到子组件传递过来的数据。
3)兄弟组件共享数据:EventBus
(1)数据发送方
- 导入eventBus.js:import bus from './eventBus.js'
- bus.$emit('要触发的事件的名字',要发送的数据)
(2)EventBus模块
vue2
- 创建vue实例对象:new Vue( )
- 向外导出Vue的实例对象:export default new Vue( )
vue3
- npm install mitt@2.1.0
- const bus = mitt( )
- export default bus
(3)数据接收方
- 导入eventBus.js:import bus from './eventBus.js'
- 在数据接收方的created生命周期函数中,调用bus.$on('要声明的自定义事件的名字',事件的处理函数)方法
4)(vue3)后代关系组件之间的数据共享
(1)父节点的组件通过provide方法,对其子孙组件共享数据
export default{
provide(){
return{
color:this.color
}
}
}
(2)子孙结点使用inject数组,接受父级节点向下共享数据
export default{
inject:['color'];
}
(3)父节点使用provide向下共享数据时,可以结合computed函数向下共享响应式的数据,子孙节点必须以.value的形式使用
export default{
provide(){
return{
color:computed(()=>this.color)
}
}
}
<h5> {{color.value}} </h5>
13、ref引用
1)不依赖于jQuery的情况下,获取DOM元素或组件的引用
2)使用方法
- 为对应的DOM元素添加ref属性,如:<h3 ref="myh3"></h3>
- 操作DOM元素,如:this.$refs.myh3.style.color='red'
3)this.$nextTick(cb)
- 等组件的DOM更新完成之后,再执行cb回调函数
14、动态组件
1)<component :is="comName"></component> :相当于组件的占位符
2)<keep-alive></keep-alive>
- 默认情况下,切换动态组件时无法保持组件的状态,使用keep-alive组件可以保持动态组件的状态。
- 当组件被缓存时,会自动触发组件的deactivated生命周期函数;当组件被激活时,会自动触发组件的activated生命周期函数
3)属性
- include:指定哪些组件被缓存
- exclude:指定哪些组件不被缓存
- 不能同时使用include和exclude属性
15、插槽
1)v-slot 简写:'#'
2)在组件或<template>元素上使用v-slot指令
3)定义
(1)具名插槽
- <slot name="名字"></slot>
- 没有指定name名称的插槽,会有隐含的名称叫做“default”
- 如果在封装组件时没有预留任何<slot>插槽,则用户提供的任何自定义内容都会被丢弃。
(2)作用域插槽
- <slot>插槽绑定的有props数据
- 接收作用域插槽对外提供的数据:<template v-slot:default="scope">或解构赋值:<template v-slot:default="{uesr}">
- 使用作用域插槽的数据:{{scope}}或{{user.id}}
16、自定义指令
1)私有自定义指令
directives:{
color:{
//当指令第一次被绑定到元素时被调用
bind(el,binding){
el.style.color=binding.value
}
},
//每次DOM更新时被调用
update(el,binding){
el.style.color=binding.value
}
},
}
//当bind和update函数中的逻辑完全相同
directives:{
color(el,binding){
el.style.color=binding.value
}
}
2)全局自定义指令
//放在main.js中
Vue.directive('color',function(el,binding){
el.style.color = binding.value
})
3)vue2 - vue3
- bind - mounted
- update - updated
17、v-router
1)使用
(1)安装
- npm i vue-router@3.5.2 -S
- (vue3) npm install vue-router@next -S
(2)在src源目录下,新建router/index.js路由模块,通过routes数组声明路由的匹配规则
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/Home.vue'
Vue.use(VueRouter)
const router=new VueRouter({
routes:[
{ path:'/home', component: Home }
]
})
export default router
//vue3
import { createRouter, createWebHashHistory} from 'vue-router'
import Home from './components/MyHome.vue'
const router=createRouter({
history:createWebHashHistory(),
routes:[
{path:'/home',component:Home},
]
})
export default router
(3)在src/main.js入口文件中,导入并挂载路由模块
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
new Vue({
render:h=>h(App),
router
}).$mount('#app')
//vue3
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
(4)在src/App.vue组件中,声明路由链接和占位符
<template>
<div class='app-container>
<router-link to='/home'>首页</router-link>
</hr>
<router-view></router-view>
</div>
</template>
2)常见用法
(1)路由重定向
{ path:'/', redirect: '/home' }
(2)嵌套路由
{ path:'/about',
component: About,
children:[
{ path:'tab', component: Tab }
]
}
如果children数组中,某个路由规则的path值为空字符串,则这条路由规则叫做默认子路由。
(3)动态路由
- 把Hash地址中可变的部分定义为参数项,在vue-router中使用:来定义
{ path:'/movie/:id', component: Movie }
- 可以使用this.$route.params对象访问到动态匹配的参数值
//Movie组件
{{this.$route.params.id}}
- 可以在路由规则中开启props传参
{ path:'/movie/:id', component: Movie, props:true }
//Movie组件
{{id}}
export default {
props:['id']
}
(4)编程式导航
- 跳转到指定hash地址,并增加一条历史记录:this.$router.push('hash地址')
- 跳转到指定hash地址,并替换掉当前的历史记录:this.$router.replace('hash地址')
- 实现导航历史前进、后退:this.$router.go(n)
在历史记录中,后退到上一个页面:this.$router.back()
在历史记录中,前进到上一个页面:this.$router.forward()
(5)全局前置守卫
const router = new VueRouter({...})
router.beforeEach((to,from,next)=>{
...
})
- to是将要访问的路由的信息对象
- from是将要离开的路由的信息对象
- 当前用户拥有后台主页的访问权限,直接放行:next()
当前用户拥有后台主页的访问权限,强制其跳转到登录页面:next('/login')
当前用户拥有后台主页的访问权限,不允许跳转到后台主页:next(false)
二、vuex
(一)基本使用
1、安装vuex依赖包
npm install --save vuex
2、导入vuex包
import Vuex from 'vuex'
Vue.use(Vuex)
3、创建store对象
const store = new Vuex.Store({
state: { count: 0 }
})
4、将store对象挂载到vue实例中
new Vue({
el: '#app',
render: h => h(app),
router,
store
})
(二)核心概念
1、state
1)State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储
2)组件访问State中的数据
//第一种方式
this.$store.state.全局数据名称
//第二种方式
//从vuex中按需导入mapState函数
import { mapState } from ‘vuex'
//将全局数据,映射为当前组件的计算属性
computed: {
...mapState(['count'])
}
2、Mutation
1)Mutation用于变更Store中的数据
2)使用方法
(1)定义
mutations: {
add(state) {
state.count++
}
}
(2)触发
//第一种方式
methods: {
handle1() {
this,$store.commit('add')
}
}
//第二种方式
//从vuex中按需导入mapMutations函数
import { mapMutations} from ‘vuex'
//将指定的mutations函数,映射为当前组件的methods函数
methods: {
...mapMutations(['add'])
btnHandler2() {
this.add()
}
}
3、Action
1)Action用于处理异步任务
2)使用方法
(1)定义
//...省略其他代码
mutations: {
add(state) {
state.count++
}
},
actions: {
addAsync(context) {
setTimeout( () => {
context.commit('add')
},1000)
}
}
(2)触发
//第一种方式
methods: {
handle() {
this.$store.dispatch('addAsync')
}
}
//第二种方式
//从vuex中按需导入mapActions函数
import { mapActions} from ‘vuex'
//将指定的actions函数,映射为当前组件的methods函数
methods: {
...mapActions(['add'])
btnHandler3() {
this.addAsync()
}
}
4、Getter
1)Getter用于对Store中的数据进行加工处理形成新的数据(Store中数据发生变化,Getter的数据也会跟着变化)
2)使用方法
(1)定义
getters: {
showNum(state) {
return ' 当前最新的数量是【' + state.count + '】'
(2)触发
//第一种方式
this.$store,getters.名称
//第二种方式
import { mapGetters} from ‘vuex'
computed: {
...mapGetters(['showNum'])
}
三、面试题
1、MVC和MVVM区别
1)MVC
MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,MVC 的思想:Controller 负责将 Model 的数据用 View 显示出来,换句话说就是在 Controller 里面把 Model 的数据赋值给 View。
2)MVVM
ViewModel 层:做了两件事达到了数据的双向绑定 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。
MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 DOM元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变(对应Vue数据驱动的思想)。
2、vue的单向数据流
数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。
3、为什么data是一个函数
一个组件被复用多次的话,会创建多个实例。本质上,这些实例用的都是同一个构造函数。如果data是对象的话,对象属于引用类型,其中一个组件的data改变就会影响到所有的实例。所以,为了保证每个组件的data都有自己的私有空间,data必须是个函数。
4、computed和watch区别
1、computed:计算属性,依赖其他属性计算值,并且computed的值有缓存,只有当计算值变化才会返回内容
2、watch:当监听到值的变化时就会执行回调函数
5、为什么v-for与v-if不建议一起使用
当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费
6、key的作用
vue采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言,map映射的速度更快。
7、父子组件生命周期函数执行顺序
1)加载渲染过程
父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted
2)子组件更新过程
父 beforeUpdate->子 beforeUpdate->子 updated->父 updated
3)父组件更新过程
父 beforeUpdate->父 updated
4)销毁过程
父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed
8、v-model原理
v-model在表单<input>、<textarea>及<select>元素上创建双向数据绑定,它会根据控件类型自动选取正确的方法来更新元素。v-model在本质上也就是语法糖,它负责监听用户的输入事件以更新数据。
v-model会忽略所有表单元素的value、checked、selected attribute的初始值,而总是将Vue实例的数据作为数据来源。 v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用 value 属性和 input 事件
- checkbox 和 radio 使用 checked 属性和 change 事件
- select 字段将 value 作为prop并将 change 作为事件