1. 关于Vue 的生命周期
答:
beforeCreated —— 创建前
created —— 创建后
beforeMount —— 加载前
mounted —— 加载后
beforeUpdate —— 更新前
updated —— 更新后
beforeDestroy —— 销毁前
destroyed —— 销毁后
2. 谈谈你对keep-alive 的理解
答:
keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁.(概念)
keep-alive是系统自带的一个组件,主要用来缓存组件,避免多次加载相同的组件,减少性能消耗,提高用户体验(作用)
列表页进入详情页,如果在列表页点击的都是相同的 ,详情页就不用请求多次了,直接缓存起来就行了,如果点击的不同,则需要重新请求数据(使用场景)
使用方法一:在App.vue中使用keep-alive标签,表示缓存所有页面
<div id="app">
<keep-alive>
<tar-bar></tar-bar>
<div class="container">
<left-menu></left-menu>
<Main />
</div>
</keep-alive>
</div>
使用方法二:按条件缓存,使用include,exclude判断是否缓存
// 1. 将缓存 name 为 cc 的组件,如果有多个,可用逗号分
<keep-alive include='cc'>
<router-view/>
</keep-alive>
// 2. 将不缓存 name 为 cc 的组件
<keep-alive exclude='cc'>
<router-view/>
</keep-alive>
// 3. 还可使用属性绑定动态判断
<keep-alive :include='includedComs'>
<router-view/>
</keep-alive>
使用方法三:在router目录下的index.js中,
//使用meta:{keepAlive = true },表示需要缓存
const routes = [
{
path:'/',
component:Home,
meta:{
keepalive:true
}
},
{
path:'/login',
component:Login
},
{
path:'/edit',
component:Edit,
meta:{
istoken: true
}
},
{
path:'/home',
component:Home,
meta:{
keepalive:true
}
}
// 第二步 在App.vue中进行判断
<div id="app">
<!--keep-alive 表示页面不会被销毁,即保持页面状态-->
<keep-alive>
<router-view v-if="$route.meta.keepalive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepalive"></router-view>
</div>
Props:
include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
max - 数字。最多可以缓存多少组件实例
3.v-if与v-show 的区别
答:
v-show 是通过控制display属性来进行DOM的显示与隐藏,主要用于频繁操作
v-if 是直接对DOM节点的销毁与销毁,相对于v-show 更加损耗性能
- v-show不支持语法,即v-show="false"对template元素来说不起作用。但是此时的template元素支持v-if、v-else-if、v-else、v-for这些指令
- v-if切换时候会实时的销毁和重建内部的事件、钩子函数等,v-show只会初始化渲染时候执行,再切换时候不会执行生命后期的过程。
4.v-for 与v-if 的优先级
答:
1. vue2中,v-for的优先级高于v-if,把它们放在同一个标签上时,页面每次渲染的时候都会重复的进行判断是十分消耗性能的
2. 在vue3中,又恰好相反v-if的优先级是高于v-for的,同样vue3也不能把它们两者写在一起,正因为v-if优先于v-for,并且v-if又依赖v-for的数据源,在这个情况下将会出现报错的情况。
3. .一般我们在处理v-if和v-for连用的场景时我们可采取下列方式:
(1) .在循环渲染之前先用filter过滤下不需要的数据,也可以使用computed计算属性进行过滤
(2) .如果需要根据条件渲染单个项,可以将 v-if 放在内层元素上。
4. 将v-for和v-if写在同层级和v-for中嵌套v-if有什么区别呢?
- 将 v-if 和 v-for 写在同一层级时,Vue 会在渲染组件时,先根据 v-for 遍历出所有的子元素,并将它们都生成对应的 Virtual DOM,然后再根据 v-if 条件判断是否需要将这些元素渲染出来。如果元素不符合条件,则将它们对应的 Virtual DOM 从渲染队列中移除。
- 将 v-if 放在内层元素上时,Vue 可以先根据 v-for 遍历出所有的子元素,但只有符合 v-if 条件的子元素会被渲染,不符合条件的子元素不会生成对应的 Virtual DOM。这样可以避免不必要的性能消耗,提高页面渲染速度。
5. ref 是什么?
答:
ref是一个用于访问组件或DOM元素的属性。通过在元素上使用v-bind指令将ref绑定到组件实例或DOM元素上,就可以使用$refs对象访问它们。
// 主要获取节点的相关属性
<template>
<div>
<input type="text" ref="myInput">
</div>
</template>
6.nextTick 是什么
答:nextTick 是将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它
// 原本的情况
<template>
<div>
<button @click="testClick()" ref="a">{{msg}}</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg:"原始值",
}
},
methods:{
testClick(){
this.msg="修改值";
console.log(this.$refs.a.innerText); //this.$refs.a获取指定DOM,输出:原始值
}
}
}
</script>
//使用 nextTick 之后
methods:{
testClick(){
this.msg="修改值";
this.$nextTick(() => {
console.log(this.$refs.a.innerText); //输出:修改值
});
}
}
注意:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
7. scoped原理
-
什么是scoped?
答:1.在vue文件中的style标签上,有一个特殊的属性:scoped 2.当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,通过该属性,可以使得组件之间的样式不互相污染。
-
scoped 原理
答:1. 为组件实例生成一个唯一标识,给组件中的每个标签对应的dom元素添加一个标签属性,data-v-xxxx 2.给<style scoped>中的每个选择器的最后一个选择器添加一个属性选择器,原选择器[data-v-xxxx],如:原选择器为.container #id div,则更改后选择器为.container #id div[data-v-xxxx]
8.Vue 中如何做样式穿透
样式穿透的写法有三种:>>>、/deep/、::v-deep
' >>> ' 用于原生的css中
/* 用法: */
div >>> .cla {
color: red;
}
'/deep/' 项目中用到了预处理器 scss 、sass、less 操作符 >>> 可能会因为无法编译而报错 。可以使用 /deep/
注意:vue-cli3以上版本不可以
/* 用法: */
div /deep/ .cla {
color: red;
}
'::v-deep'
/* 用法: */
div ::v-deep .cla {
color: red;
}
9. Vue中组件传值
-
父子组件传值
- 父向子传递,通过props传值
在子组件里定义一个props,即props:[‘msg’],msg可以是对象也可以是基本数据类型
- 子向父传值
需要在子组件中使用自定义事件,子组件中使用this.$emit(‘myEvent’) 触发,然后在父组件中使用@myEvent监听
-
兄弟组件值
运用自定义事件emit的触发和监听能力,定义一个公共的事件总线eventBus,通过它作为中间桥梁,我们就可以传值给任意组件了。而且通过eventBus的使用,可以加深emit的理解
-
使用$ref传值
通过$ref的能力,给子组件定义一个ID,父组件通过这个ID可以直接访问子组件里面的方法和属性
-
使用依赖注入传给后代子孙曾孙
父组件通过provide 选项,是给后代组件的提供数据/方法
然后在任何后代组件里,我们都可以使用 inject 来给当前实例注入父组件的数据/方法 -
祖传孙 $attrs 以及孙传祖的 $listeners
$attrs–继承所有的父组件属性(除了 prop 传递的属性、class 和 style ),一般用在子组件的子元素上
$ listeners属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 v-on=“$listeners” 将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件)
-
$parent
通过parent可以获父组件实例,然后通过这个实例就可以访问父组件的属性和方法,它还有一个兄弟root,可以获取根组件实
-
sessionStorage传值
sessionStorage 是浏览器的全局对象,存在它里面的数据会在页面关闭时清除 。运用这个特性,我们可以在所有页面共享一份数据
-
vuex
状态管理器,详情见官方文档
10.computed、methods、watch 有什么区别
- computed
omputed计算属性顾名思义就是通过其他变量计算得来的另一个属性,计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要它们的响应式数据没有发生改变,多次访问 这个数据 计算属性会立即返回之前的计算结果,而不必再次执行函数。(需要有return,主要return中的依赖不会发生改变,不会二次执行computed,有缓存机制)
- watch
侦听一个特定的值,当该值变化时执行特定的函数。例如分页组件中,我们可以监听当前页码,当页码变化时执行对应的获取数据的函数。
- methods
定义函数,它需要手动调用才能执行,只要发生重新渲染,methods 调用总会执行该函数
watch更擅长一对多:就是主要监听一个可以影响多个数据的数据
computed擅长多对一:主要监听多个数据影响一个数据的数据,一定要return
-
什么情况下使用:
(1)数据量大,需要缓存的时候用computed;每次确实需要重新加载,不需要缓存时用methods。
(2)尽量用computed计算属性来监视数据的变化,因为它本身就这个特性,用watch没有computed“自动”,手动设置使代码变复杂。
(3)虽然计算属性在大多数情况下是非常适合的,但是在有些情况下我们需要自定义一个watcher,在数据变化时来执行异步操作,这时watch是非常有用的tips:
methods是通过事件驱动来执行函数的是被动的
watch、computed是当监听的数据发生变化时主动执行这个函数
watch,可监听同一属性值前后改变的值变化 (可监听数组,对象,字符串
11.props 和 data 的优先级谁更高
答:
props ==> methods ==> data ==> computed ==> watch
12.Vuex中有哪些属性
答:5个,分别是State、 Getter、Mutation 、Action、Module
-
state
state 为单一状态树,在 state 中需要定义我们所需要管理的数组、对象、字符串等等,只有在这里定义了,在 Vue.js 的组件中才能获取你定义的这个对象的状态。
类似于组件中的data,存放数据
-
getter
getter 有点类似 Vue.js 的计算属性,当我们需要从 store 的 state中派生出一些状态,那么我们就需要使用 getter,getter 会接收 state 作为第一个参数,而且 getter 的返回值会根据它的依赖被缓存起来,只有 getter 中的依赖值(state 中的某个需要派生状态的值)发生改变的时候才会被重新计算
类似于组件中的 computed
-
mutation
更改 store 中 state 状态的唯一方法就是提交 mutation,就很类似事件。每个 mutation 都有一个字符串类型的事件类型和一个回调函数,我们需要改变 state 的值就要在回调函数中改变。我们要执行这个回调函数,那么我们需要执行一个相应的调用方法:store.commit。
类似于组件中 methods,可以定义方法 【同步的一些处理方法】
-
action
action 可以提交 mutation,在 action 中可以执行 store.commit,而且 action 中可以有任何的异步操作。在页面中如果我们要用这个 action,则需要执行 store.dispatch。
提交mutations的,可以定义方法 【异步的一些处理方法】
-
module
module 其实只是解决了当 state 中很复杂臃肿的时候,module 可以将 store 分割成模块,每个模块中拥有自己的 state、mutation、action和 getter
把以上4个属性再细分,让仓库更好管理
13.Vuex是单向数据流还是双向数据流
答:单向数据流。组件可以使用vuex的值但不可以直接修改值,只能通过调用vuex中的mutations方法修改值
14.Vuex的mutaions和actions 区别
-
mutations
mutations是用于修改Vuex状态的唯一方法。它们是同步操作,意味着它们必须是纯函数,以确保状态的可预测性。这意味着mutations应该只用于同步操作,例如在响应用户事件时更新状态。它们不应该包含任何异步代码,如API调用
-
actions
actions用于执行异步操作或包含异步操作的操作序列。它们可以包含任何异步代码,例如API调用或其他异步操作,但不能直接修改状态。相反,它们通过commit触发mutations来间接修改状态
mutations是同步的,actions是异步的
15.Vuex 如何做持久化存储
答:Vuex本身不是持久化存储
- 使用localstorage
- 使用插件vuex-persist
16.Vue设置代理
-
在vue.config.js 文件中配置
module.exports = { devServer: { proxy: { '/api': { target: 'http://api.example.com', changeOrigin: true, pathRewrite: { '^/api': '' } } } }
-
搭建后端目录
全局命令: npm install express-generator -g
进入项目:express --view=ejs server -
设置跨域
-Origin *
16.Vue项目打包上线
- 在Vue项目根目录下,运行命令npm run build 或 yarn build 进行打包,打包后会生成一个dist 目录
- 将dist 目录的所有文件上传到服务器
- 在服务器安装Node.js环境,并安装http-server 或者nginx 等Web服务器
- 配置Web服务器,让它能够访问 dist 目录的文件
- 启动Web服务器,访问服务器IP地址或者域名即可查看Vue项目的网页
17.Vue的路由模式
路由的模式主要有两种:history和hash
- 区别:
- 表现形态的不同(hash会多一个#)
history:http://127.0.0.1:8000/about
hash:http://127.0.0.1:8000/#/about - 跳转请求:
hash:不会发送请求
history:http://127.0.0.1:8000/id ===> 会发送请求 - 打包后前端自测要使用 hash模式,如果使用 history 会出现空白页。
- 表现形态的不同(hash会多一个#)
18. 介绍下SPA以及SPA有什么缺点
-
SPA
SPA(single-page application) 是单页面Web应用
-
SPA优点:
- 无刷新界面,给用户体验原生的应用感觉
- 节省原生(android 和 ios)app 开发成本
- 提高发布效率,无需每次安装更新包
- 容易借助其他知名平台更有利于营销和推广
- 符合 web2.0 的趋势
-
SPA缺点:
- 效果和性能确实和原生的有较大差距(一个页面加载多个JS)
- 各个浏览器的版本兼容性不一样
- 业务随着代码量增加而增加,不利于首屏优化
- 某些平台对 hash 有偏见,有些甚至不支持 pushstate
- 不利于搜索引擎抓取
19. Vue的路径传值
-
显式 url可以传参
http://127.0.0.1:8000/about?a=1
传:
this.$router.push({path: '/about', query: {a:1})
接:this.$route.query.
-
隐式 url上不显示
http://127.0.0.1:8000/about?a=1
传:
this.$router.push({name: 'about', params: {a:1}) 通过路由的 name
接:this.$route.params.a
20. 路由导航守卫有哪些
路由守卫
> 路由守卫,也可以是路由拦截,我们可以通过路由拦截,来判断用户是否登录,该页面用户是否有权限浏览,需要结合meta来实现
- 路由守卫导航主要有3种,分别是
全局守卫
、独享守卫
、组件内守卫
- 全局守卫
beforeEach
router.beforeEach((to, from, next)=>{ if() { next() } else { router.push('/login'); next('/ogin') } })
- 独享守卫
beforeEnter
const routes = [ { name: 'user', component: User beforeEnter: (to, from) => { } } ] //beforeEnter: [removeQueryParams, removeHash], 可以接受函数数组,removeQueryParams,removeHash为自定义的函数
- 组件内守卫
beforeRouteEnter
、beforeRouteUpdate
、beforeRouteLeave
beforeRouteEnter: 组件对应路由被验证前调用,不能获取组件实例this
beforeRouteUpdate:在当前组件路由改变时,但是组件本身被复用时调用。可以访问this。
beforeRouteLeave:在导航离开当前组件路由时调用,可以访问this
都是有两个参数:
(to, from, next)=>{}
to:进入到哪个路由去,from:从哪个路由离开,next:函数,决定是否展示你要看到的路由页面。
使用场景:判断是否登录,如果登录就next否则就跳转到登录页面。
21. Vue的动态路由
答:
场景:详情页
比如:路由的路径上加一个 /:id 就把这个id作为一个动态的变化。
22.双向绑定原理
答:
vue数据的双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。其核心就是通过Object.defineProperty()方法设置set和get函数来实现数据的劫持,在数据变化时发布消息给订阅者,触发相应的监听回调。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
23. 什么是虚拟DOM
-
虚拟DOM
虚拟DOM的概念是通过状态生成一个虚拟节点树,然后使用虚拟节点树进行渲染。在渲染之前,会使用新生成的虚拟节点和上一次生成的虚拟节点进行对比,只渲染不同的部分
-
vue中的虚拟DOM
- vue中状态变化时,只能通知到组件,组件内部的变化需要通过虚拟DOM去进行比对与渲染
- 在vue中,我们使用模板来描述状态与DOM之间的映射关系。vue通过编译将
模板
转换成渲染函数
,执行渲染函数就可以得到一个虚拟节点树
,使用虚拟节点数就可以渲染页面 - 虚拟DOM在vue中主要提供与真实节点对应的虚拟节点vnode,然后需要将vnode和oldVnode进行比对,然后更新视图,对比两个虚拟节点的算法是patch算法
24. diff 算法
diff算法是一种通过
同层的树节点
进行比较的高效算法
diff算法的目的就是找出新旧不同虚拟DOM之间的差异,使最小化的更新视图,所以 diff 算法本质上就是比较两个js对象的差异
- 特点:
- 比较只会在同层级进行,不会跨层级比较
- 在diff比较的构成中,循环从两边向中间比较
25. 讲一下MVVM
- 什么是MVVM
MVVM从字面意思来理解就是划分为
View
、model
、VIewModel
三个部分,分别表达了View(视图)、Model(数据)、ViewModel(负责两者之间的数据处理操作)。这就类似于Mvc框架中的Model层、View层、congtroller层,MVVM的本质就是MVC的升级版,更好的应用于前端开发。