1:Vue解决了什么问题?
①虚拟dom:dom操作时非常耗性能的,不再使用原生的dom操作节点,极大的解放dom操作,但具体操作的还是dom,不过是换了一种方式。 ②视图、数据、结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作。 ③组件化:把一个单页应用中的各种模块拆分到一个一个单独的组件中,便于开发,以及后期的维护
2:MVVM的理解?
MVVM就是Model-View-ViewModel的缩写,MVVM将视图和业务逻辑分开。 View:视图层,Model数据模型,而ViewModel是把两者建立通信的桥梁。 在MVVM框架下,View和Model之间没有直接的联系,而是通过ViewModel进行交互。View和ViewModel之间以及Model和ViewModel之间的交互都是双向的,因此view数据的变化会同步到Model中,而Model数据的变化也会立即反映到View上。可以说它们两者是实时更新的,互相影响。 ViewModel通过双向数据绑定把View层和Model层连接了起来,而View和Model之间的同步工作完全是自动的,因此开发者只需要关注业务逻辑,不需要手动操作DOM,也不需要关注数据状态的同步问题,这些都由MVVM统一管理。
3:如何实现一个自定义组件?
创建子组件的文件,建立组件的模板,把架子搭起来,也就是在子组件中写好template视图层,script逻辑层style css样式层。然后定义好props里面的数据,实现子组件需要的逻辑代码后,也就封装好了,然后直接调用即可。调用import引入,同时在父组件script(逻辑层)中的components这个对象中写入组件名称,最后挂载到父组件的template中即可。
4:不同组件之间是如何通信的?
①props /
e
m
i
t
父
组
件
通
过
p
r
o
p
s
的
方
式
向
子
组
件
传
递
数
据
,
而
通
过
emit 父组件通过props的方式向子组件传递数据,而通过
emit父组件通过props的方式向子组件传递数据,而通过emit子组件可以向父组件通信。 ②$children /
p
a
r
e
n
t
t
h
i
s
.
parent this.
parentthis.children[0].msg = “hello world” //父组件修改子组件data中的数据 this.$parent.mag //子组件拿到父组件data中的数据
c
h
i
l
d
r
e
n
的
值
是
数
组
,
children的值是数组,
children的值是数组,parent的值是个对象 注意:
p
a
r
e
n
t
,
parent,
parent,children它们的目的是作为访问数组的应急方法,更推荐用props和events实现父子组件通信。
③provide / inject
这是vue2.2.0新增的api,简单来说就是父组件中通过provide来提供变量,然后再子组件中通过inject来注入变量。
//父组件
export default{
name:"A",
provide:{
for:"demo"
},
components:{
comB
}
}
//子组件
export default{
name:"B",
inject:["for"],
data(){
demo:this.for
}
}
④ref / refs
ref:如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子组件上,引用就指向组件实例,可通过实例直接调用组件的方法或访问数据。
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default{
mounted(){
const comA = this.$refs.comA;
console.log(comA.name)
comA.sayHello()
}
}
</script>
//子组件
export default{
data(){
return {
name:"Vue.js"
}
},
methods:{
sayHello(){
console.log("hello")
}
}
}
⑤eventBus(Bus总线):
//首先在src中创建一个Bus文件夹 => index.js
import Vue from "vue";
export default new Vue({
})
//子组件1(发送数据的组件)
<button @click="add()">点击</button>
import Bus from "@/Bus"
add(){
Bus.$emit("add",this.content);
}
//子组件2(接受数据的组件)
<p>{{tit}}</p>
import Bus from "@/Bus";
created(){
Bus.$on("add",(data) => {
this.tit = data;
})
}
⑥Vuex; ⑦LocalStorage; ⑧$attrs /
l
i
s
t
e
n
e
r
s
将
数
据
挂
在
到
子
组
件
的
标
签
上
去
后
,
在
子
组
件
中
使
用
t
h
i
s
.
listeners 将数据挂在到子组件的标签上去后,在子组件中使用this.
listeners将数据挂在到子组件的标签上去后,在子组件中使用this.attrs直接获取到所有挂载的数据,返回的是一个对象。
5:nextTick 的理解?
使用nextTick的原因:Vue是异步修改DOM的,并且不鼓励开发者直接接触DOM,但是有时候需要必须对数据更改后的DOM元素做相应的处理,但是获取到的DOM数据并不是更改后的数据,这时候就需要this.$nextTick(); 原理:Vue通过异步队列控制DOM更新和nextTick回调函数先后执行的方式。
使用:
<button @click="change()">按钮</button><h1 ref="gss">{{msg}}</h1>
//JS
export default{
name:"app",
data(){
return {
msg:"123"
}
},
methods:{
change(){
this.msg = "456";
console.log(this.refs["gss"].innerHTML)//123
this.$nextTick(function(){
console.log(this.refs["gss"].innerHTML)//456
})
}
}
}
6:Vue的生命周期(11个钩子函数)?
⑴beforeCreate(创建前):在此生命周期函数执行的时候,data和methods中的数据都还没有初始化。 ⑵created(创建后):在此生命周期函数中,data和methods都已经被初始化好了,如果要调用 methods中的方法,或者操作data中的数据,最早只能在created中操作。 ⑶beforeMount(载入前):在此生命周期函数执行的时候,模板已经在内存中编译好了,但是尚未挂载到页面中去,此时页面还是旧的。 ⑷mounted(载入后):此时页面和内存中都是最新的数据,这个钩子函数是最早可以操作dom节点的方法。 ⑸beforeUpdate(更新前):此时页面中显示的数据还是旧的,但是data中的数据是最新的,且页面并未和最新的数据同步。 ⑹Updated(更新后):此时页面显示数据和最新的data数据同步。 ⑺beforeDestroy(销毁前):当执行该生命周期函数的时候,实例身上所有的data,所有的methods以及过滤器…等都处于可用状态,并没有真正执行销毁。 ⑻destroyed(销毁后):此时组件以及被完全销毁,实例中的所有的数据、方法、属性、过滤器…等都已经不可用了。 //下面两个钩子函数一般配合使用 ⑼activated(组件激活时):和上面的beforeDestroy和destroyed用法差不多,但是如果我们需要一个实例,在销毁后再次出现的话,用beforeDestroy和destroyed的话,就太浪费性能了。实例被激活时使用,用于重复激活一个实例的时候 ⑽deactivated(组件未激活时):实例没有被激活时。 ⑾errorCaptured(错误调用):当捕获一个来自后代组件的错误时被调用
7: 虚拟DOM原理
虚拟DOM,其实就是用对象的方式取代真实的DOM操作,把真实的DOM操作放在内存当中,在内存中的对象里做模拟操作。当页面打开时浏览器会解析HTML元素,构建一颗DOM树,将状态全部保存起来,在内存当中模拟我们真实的DOM操作,操作完后又会生成一颗dom树,两颗DOM树进行比较,根据diff算法比较两颗DOM树不同的地方,只渲染一次不同的地方。
补充:
diff算法核心: ①如何用vnode生成一个dom的节点 patch方法patch(container, vnode)patch(vnode, newVnode) ②vnode和newVnode的对比 ③修改改变的dom节点 replacechildrencreateElement
8:双向绑定的原理?数据劫持?
mvvm 双向绑定,采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
几个要点:
1、实现一个数据监听器 Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者 2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 3、实现一个 Watcher,作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图 4、mvvm入口函数,整合以上三者
9:Proxy相比于defineProperty的优势?
Vue3.0摒弃了Object.defineProperty,改为基于Proxy的观察者机制探索。 首先说一下Object.defineProperty的缺点: ? ①Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实施响应。 ? ②Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue2.X里,是通过递归 + 遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择。 而要取代它的Proxy有以下两个优点: ? 可以劫持整个对象,并返回一个新对象。 ? 有多种劫持操作(13种)
补充:
? Proxy是ES6新增的一个属性,翻译过来的意思就是代理,用在这里表示由它来“代理”某些操作。Proxy让我们能够以简洁易懂的方式控制外部对象的访问,其功能非常类似于设计模式中的代理模式。 ? Proxy可以理解为,在目标对象之前设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。 ? 使用Proxy的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。从而可以让对象只需要关注核心逻辑,达到关注点分离,降低对象复杂度等目的。
10:watch、computed和methods的区别
? methods即是方法,封装的功能代码块,调用后执行。在重新渲染的时候每次都会被重新的调用; ? computed 是自动监听依赖值的变化,从而动态返回内容,主要目的是简化模板内的复杂运算。所以区别来源于用法,只是需要动态值,那就用 computed ;需要知道值的改变后执行业务逻辑,才用 watch。 ? watch也可以影响数据的变化,当绑定的数据方法变化时触发响应的函数,需要在数据变化时执行异步或开销较大的操作时使用watch。
11:virtual-dom原理实现(虚拟dom)?
virtual-dom(简称vdom)的概念大规模的推广还是得益于react的出现,virtual-dom也是react这个框架的非常重要的特性之一。相比于频繁的手动去操作dom而带来性能问题,vdom很好的将dom做了一层映射关系,进而将在我们本需要直接进行dom的一系列操作,映射到了操作vdom,而vdom上定义了关于真实dom进行的创建节点,删除节点,添加节点等一系列复杂的dom操作,并将这些操作放到vdom中进行,这样就通过操作vdom来提高直接操作的dom的效率和性能。 在vue的整个应用生命周期当中,每次需要更新视图的时候便会使用vdom,vdom算法是基于snabbdom算法所做的修改。
实现:
①用js对象构造一个虚拟的dom树,插入到文档中; ②状态变更时,记录新树和旧树的差异; ③把上面的差异构建到真正的dom中。
12:vue-router路由模式?
①hash(哈希默认)模式:使用 URL hash 值来作路由。默认模式。 ②history(mode:history)模式: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。 ③abstract模式(严格模式):支持所有JavaScript 运行环境,如 Node.js 服务器端。 根据mode参数来决定采用哪一种方式。 vue-router的实现原理(核心):更新视图但不重新请求页面。
13: vue-router登陆权限的判断?
vue-router的登陆权限判断主要是在全局钩子函数中进行的,我们在router.js文件中的定义路由里,将需要登陆权限的页面加上meta属性,值是对象的形式,然后在该对象中自定义一个属性,属性值就是一个Boolean值,这时候在main.js文件的全局钩子函数中进行判断,如果需要跳转的页面的自定义属性值为true,那么将进行判断其是否登录,如果没有登录,则告诉用户登录,如果有登录,那么进行页面跳转。
routes:[
{
path:"/home",
name:"Home",
components:Home
meta:{requireAuth:true}
}
]
router.beforeEach((to,from,next) => {
if(to.meta.requireAuth){//判断该路由是否需要登录权限
if(store.state.token){//通过vuex的state获取当前的token是否存在
next()
}else{
next({
path:"/one",
query:{redirect:to.fullPath}//将跳转的路由path作为参数,登陆成功后跳转到该路由
})
}
}else{
next();
}
})
14:vue-router路由嵌套?
routes:[
{
path:"/home",
name:"Home",
components:Home,
children:[
{
path:"child", name:"Child",
components:"Child"
}
]
} ]
15: Vuex的理解?Vuex的五大核心?
定义:Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式储存管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 使用场景:需要构建一个中大型单页应用,您很可能会考虑如何更好的在组件外部管理状态,Vuex将会成为自然而然的选择。 优点:当你在state中定义了一个数据之后,可以在所在项目中的任何一个组件里进行获取、进行修改、并且你的修改可以得到全局的响应变更。 ①state:定义初始数据。 ②mutations:更改Vuex的store中的状态的唯一方法是提交mutation ③getters:可以对 state 进行计算操作,它就是 store 的计算属性虽然在组件内也可以做计算属性,但是 getters 可以在多给件之间复用如果一个状态只在一个组件内使用,是可以不用 getters。 ④actions:异步操作初始数据,其实就是调用mutations里面的方法。 ⑤module:面对复杂的应用程序,当管理的状态比较多时;我们需要将vuex的store对象分割成模块(modules)。
16 : Vue.js的特点
? 简洁:页面由HTML模板+Json数据+Vue实例组成 ? 数据驱动:自动计算属性和追踪依赖的模板表达式 ? 组件化:用可复用、解耦的组件来构造页面 ? 轻量:代码量小,不依赖其他库 ? 快速:精确有效批量DOM更新 ? 模板友好:可通过npm,bower等多种方式安装,很容易融入
17:Vuex的运行机制?
Vuex提供数据(state)来驱动视图(components),通过dispath派发actions,在其中可以做一些异步的操作,然后通过commit来提交mutations,最后mutations来更改state。
18:Vuex的映射?
state(数据)、getters(计算属性)需要映射在computed实例中,而mutations(同步操作放),actions(异步操作方法)等需要放在methods实例中
computed:{
...mapState([
"list",
])
}
methods:{
...mapMutations([
"changes",
])
}
19:描述下vue从初始化页面–>修改数据–>刷新页面UI过程?
当Vue进入初始化阶段时,一方面Vue会遍历data中的属性,并用Object.defineProperty将它转化成getter/setterd的形式,实现数据劫持;另一方面,Vue的指令编译器Compiler对元素节点的各个指令进行解析,初始化视图,并订阅Watcher来更新视图,此时Watcher会将自己添加到消息订阅器Dep中,此时初始化完毕。 当数据发生变化时,触发Observer中setter方法,立即调用Dep.notify(),Dep这个数组开始遍历所有的订阅者,并调用其update方法,Vue内部再通过diff算法,patch相应的更新完成对订阅者视图的改变。
20:Vue的响应式原理?
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
21:插槽的理解?
插槽就是父组件往子组件中插入一些内容。 有三种方式,默认插槽,具名插槽,作用域插槽
- 默认插槽就是把父组件中的数据,显示在子组件中,子组件通过一个slot插槽标签显示父组件中的数据。
- 具名插槽是在父组件中通过slot属性,给插槽命名,在子组件中通过slot标签,根据定义好的名字填充到对应的位置。
- 作用域插槽是带数据的插槽,子组件提供给父组件的参数,父组件根据子组件传过来的插槽数据来进行不同的展现和填充内容。在标签中通过slot-scope来接受数据。
22:vue-router有哪几种导航钩子?
① 全局导航钩子:一般用来判断权限,以及页面丢失时需要执行的操作;
beforeEach()每次路由进入之前执行的函数。
afterEach()每次路由进入之后执行的函数。
beforeResolve()2.5新增
② 单个路由(实例钩子):某个指定路由跳转时需要执行的逻辑。
beforeEnter() beforeLeave()
③ 组件路由钩子:
beforeRouteEnter()
beforeRouteLeave()
beforeRouteUpdate()
23:vue组件中的data为什么是一个函数?
如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。Object是引用数据类型,里面保存的是内存地址,单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。 所以说vue组件的data必须是函数。
24:路由懒加载?
路由懒加载的应用场景:当项目很大的时候,为了提升用户体现,则必须使用懒加载。否则,首页会一次导入所有页面与组件。需要加载的内容过多,延时过长,不利于用户体验。但项目很小,则不推荐使用,项目小,分开后的懒加载会发多次请求,带来更多的性能缺陷; 路由懒加载的原理:vue 异步组件技术:异步加载,vue-router 配置路由 , 使用 vue 的异步组件技术 , 实现按需加载。
常用的懒加载方式有两种:即使用vue异步组件 和 ES中的import 1、vue异步组件实现路由懒加载
component:resolve=>([‘需要加载的路由的地址’,resolve])
2、es提出的import(推荐使用这种方式)
const HelloWorld = ()=>import(‘需要加载的模块地址’)
25:Vue.js介绍?
Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API;Vue.js是一套构建用户界面的渐进式框架。与其他重量级框架不同的是,Vue采用自底向上增量开发的设计。Vue的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。数据驱动+组件化的前端开发。通过尽可能简单的API实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
26:多环境变量
我一般使用多环境变量配置axios请求的baseURL,首先我在vue根目录下创建.env文件,用来写一些默认的配置信息;然后我会根据需要添加本地开发环境(.env.development)和生产环境的(.env.production)的配置信息,vue会根据配置信息自动获取变量值,然后在axios中使用process.env获取到相应的baseURL
27:对axios封装(url统一管理、axios请求拦截、响应拦截、函数封装)
首先要安装axios,一般我会在项目的src目录中,新建一个network文件夹,作为我们的网络请求模块,然后在里面新建一个http.js和一个api.js文件和一个reques.js。http.js文件用来封装我们的axios,api.js用来统一管理我们的接口url, 在request.js中添加请求拦截和响应拦截。在请求拦截中,会给请求头添加token字段,还有loading动画的开启。在响应拦截中,可以做一些loading动画的关闭,还有可以根据后端返回的状态码,做一些检验token是否有效或者过期的操作。接着就是做一些axios进行的api接口的封装,这里我用到了async,await封装请求接口函数,这样可以将异步操作同步化操作,代码更加友好,避免回调地域的出现。
28:Keep-alive
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated(组件激活时使用) 与 deactivated(组价离开时调用)
如果需要缓存整个项目,直接在app.vue中用keep-alive包裹router-view即可。要缓存部分页面,需要在路由地址配置中,在meta属性中添加一个状态,在app.vue中判断一下包裹的router-view即可
29:常见的指令,修饰符
常用指令
在vue中提供了一些对于页面 + 数据的更为方便的输出,这些操作就叫做指令,指令中封装了一些DOM行为, 结合属性作为一个暗号, 暗号有对应的值,根据不同的值,框架会进行相关DOM操作的绑定 vue中的指令有很多,我们平时做项目常用的有: v-if:是动态的向DOM树中添加或者删除元素; v-else是搭配v-if使用的,它必须紧跟在v-if或者v-else-if后面,否则不起作用 v-show:是通过标签的CSS样式display的值是不是none,控制显示隐藏 区别: 1、当条件为真的时候 没有区别 当条件为假的时候 v-if通过创建或删除DOM节点来实现元素的显示隐藏,v-show通过css中的display属性来控制 2、v-if更适合数据的筛选和初始渲染 v-show更适合元素的切换 v-for:v-for是根据遍历数据来进行渲染,要配合key使用,要注意的是当v-for和v-if同处于一个节点时,v-for的优先级比v-if更高。这意味着v-if将运行在每个v-for循环中 v-on:用来绑定一个事件或者方法,简写方式是@click="" v-bind: v-bind用来动态的绑定一个或者多个属性。没有参数时,可以绑定到一个包含键值的对象。常用于动态绑定class和style。以及href等。简写的方式是“:属性名=""”一个冒号 v-model 只能适用于在表单元素上,可以实现数据双向绑定
修饰符:
在Vue中,事件修饰符处理了许多DOM事件的细节,让我们不再需要花大量的时间去处理这些烦恼的事情,而能有更多的精力专注于程序的逻辑处理。在Vue中事件修饰符常用的主要有: ?.stop :阻止事件冒泡:由内而外,通俗的将就是阻止事件将向上级DOM元素传递 ?.capture:事件捕获:由外而内,在捕获阶段,事件从window开始,之后是document对象,一直到触发事件的元素。 ?.self:当事件作用在元素本身时才会触发 ? .once :只触发一次 ? .prevent: 阻止默认事件 ?passive:告诉浏览器你不想阻止事件的默认行为 ? ?trim:自动过滤用户输入的首尾空格 语法:@事件名.修饰符=“方法名”
30:v-for中key的作用?
key相当于给每一个虚拟dom节点的唯一id,也是diff的一种优化策略,可以根据key更快,更精准的找到相应的虚拟dom节点
|