1、new Vue() 发生了什么
New Vue() 是实例化一个Vue 对象
在构造函数中执行_init(options),随后导入Mixin,进行实例化的初始化过程
1、initMixin(Vue) //options初始化
2、StateMixin(Vue) //状态props、state、computed、watch
3、eventMixin(Vue) //事件
4、lifeCycleMixin(Vue) //生命周期
5、renderMixin(Vue) //页面渲染
initLifeCycle : 初始化生命周期
initEvents:初始化事件
initRender:渲染页面
callHook(vm,’beforeCreate’) beforeCreate钩子函数
initState: 初始化状态 props data computed watch methods
callHook(vm,’created’) created 钩子函数
双向绑定:initState 里的initData
Proxy和 Observe:
Proxy 将key做代理,间接调用
Observe 做一个数据监听,订阅者Object.defineProperty 在存取值可以添加依赖进行操作,在复制是通知订阅者进行依赖更新
2、对 SPA 单页面应用的理解,优缺点是什么?
SPA 即( single-page application )仅在Web页面初始化加载相应的HTML、CSS和JavaScript。
一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转;
而是通过路由机制来实现HTML内容的变换,UI与用户的交互,避免页面的重新加载。
优点:1. 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
2. SPA相对服务器压力小;
3. 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责处理数据;
缺点:1. 首屏(初次)加载慢:为实现单页面Web应用功能和显示效果,需要在加载资源的时候将CSS和JavaScript统一加载,部分页面按需加载;
2. 不利于SEO:由于所有的内容都在一个页面中动态替换显示,所以在SEO上有着天然的弱势。
3、 Vue.use是干什么的?原理是什么?
Vue.use()是用来使用插件的,需要在new Vue()之前完成
插件的作用:插件通常用来为Vue添加全局功能,插件的功能没有严格的限制,一般有以下几种:
添加全局方法或property,如:vue-custom-element
添加全局资源:指令、过滤器、过度等,如:vue-touch
通过全局混入来添加一些组件选项,如: vue-router
添加Vue实例方法,通过把它们添加到Vue.prototype上实现
一个库,提供自己的API,同时提供上边的一种或几种功能,如: vue-router
Vue.use的参数必须是一个Object对象或者function函数,如果是对象的话,必须要提供install方法,之后会将Vue作为参数传入
Vue.use的参数为函数时,这个函数的参数是 Vue对象
Vue.use的参数为对象时,install方法的参数是Vue对象
4、vue set方法的实现原理
Object.defineProperty()这个方法对对象的属性方法的添加或者删除不能做到实时的监听,数组通过索引去 修改数组都是不能被检测?所以vue实现了set方法,那么实现的set方法的原理是什么呢?
用法:向响应式对象中添加一个属性,并确保这个新属性同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,因为 Vue 无法探测普通的新增属性。
5、Vue中模板编译原理?
第一步是将 模板字符串 转换成 element ASTs(解析器)
第二步是对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
第三步是 使用 element ASTs 生成 render 函数代码字符串(代码生成器)
6、ES6 Proxy 与 Object.defineProperty 的优劣对比?
Proxy 的优势如下:
Proxy 可以直接监听数组的变化
Proxy 可以直接监听对象而非属性
Proxy 有 13 种拦截方法,比 Object.defineProperty 要更加丰富的多
兼容性不好
Object.defineProperty 的优势如下:
兼容性好
Object.defineProperty (obj, prop, descriptor) 的问题主要有三个
无法监听数组的变化
必须遍历对象的每个属性
必须深层遍历嵌套的对象
7、 Vue3.x响应式数据原理
Vue2.0 object.defineProperty
Vue3.0 proxy
8、vue生命周期钩子是怎么实现的?
处理生命周期的lifecycle.js中定义了一个callHook函数
export function callHook (vm: Component, hook: string) {
const handlers = vm.$options[hook]
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
try {
handlers[i].call(vm)
} catch (e) {
handleError(e, vm, `${hook} hook`)
}
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
}
vue实例在各个生命周期阶段,都会去调用钩子callHook,当options中有对应的内容时,就会去运行相关的方法。
9、Vue 的父组件和子组件生命周期钩子执行顺序
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
10、 Vue中的组件的data 为什么是一个函数?
组件是可以被复用的,那么注册了一个组件本质上就是创建了一个组件构造器的引用,而真正当我们使用组件的时候才会去将组件实例化
当我们使用组件的时候,虽然data是在构造器的原型链上被创建的,但是实例化的component1和component2确是共享同样的data对象,当你修改一个属性的时候,data也会发生改变
11、Vue 组件间通信有哪几种方式?
父子通信:父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;v-bind=$attrs/v-on=$listeners
兄弟通信:Bus;Vuex
跨级通信:Bus;Vuex;provide / inject API、$attrs/$listeners
12、[vue] 组件中写name选项有什么作用?
项目使用keep-alive时,可搭配组件name进行缓存过滤
DOM做递归组件时需要调用自身name
vue-devtools调试工具里显示的组件名称是由vue中组件name决定的
13、keep-alive平时在哪里使用?原理是?
keep-alive用于保存组件的渲染状态,有两个钩子函数activated deactivated
Include:这里定义的缓存
Exclude:这里定义的不缓存
Max:定义最多缓存的数量
14、vue的mixin的理解和应用场景
mixin作为功能模块的使用,在需要该功能时“混入”,有利于代码复用又避免了多继承的复杂。
mixin(混入),提供了一种非常灵活的方式,来分发vue组件中的可复用功能。
其本质就是一个js对象,可以包含我们组件想要复用的任意功能和生命周期,如:data、created、methods、computed等等。
局部混入:
let myMixin = {
created: function() {
this.hello()
},
methods: {
hello: function() {
console.log('hello myMixin')
}
}
}
import myMixin from '@/mixin'
export default {
mixins: [myMixin],
data () {
return {}
}
}
全局混入:
let myMixin = {}
new Vue({
i18n,
router,
mixins: [myMixin],
render: h => h(App)
}).$mount('#app')
当组件存在与mixin对象相同的选项的时候,混入的时候组件的选项会覆盖mixin的选项
如果相同选项为生命周期钩子的时候,混入的时候会合并成一个数组,先执行mixin的钩子,再执行组件的钩子
15、Vue-router有几种钩子函数?具体是什么及执行流程是怎样的?
全局路由:
beforeEach(to,from,next):前置守卫
afterEach(to,from):后置钩子
路由独享的钩子:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
组件内的导航钩子:
beforeRouteEnter(to,from,next):在渲染该组件的对应路由被 confirm 前调用
beforeRouteUpdate(to,from,next):在当前路由改变,但是该组件被复用时调用
beforeRouteLeave(to,from,next):导航离开该组件的对应路由时调用
16、nextTick在哪里使用?原理是?
场景:下次DOM更新循环结束之后执行的延迟回调,需要在视图更新之后,基于新的视图进行操作,获取更新后的DOM
原理:下次DOM更新循环结束之后执行的延迟回调异步方法(promise, mutationObserver, setImmediate, setTimeout)
事件循环:Vue在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
17、Vue 为什么需要虚拟DOM? 虚拟DOM的优劣如何?
用js对象来描述真实DOM,是对真实DOM的抽象,直接操作真实DOM效率低,用DOM操作转化为对象操作,最终通过diff算法比对差异更新DOM,虚拟DOM不依赖真实平台从而实现跨平台
18、Vue中key的作用和工作原理,说说你对它的理解
作用:为了高效的更新虚拟DOM
原理:通过key可以判断两个节点是否是同一个,避免更新不同的元素,提高效率,减少DOM操作量
19、Vue 中的diff原理
vue的diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式 + 双指针的方式进行比较。
20、computed 和 watch 的区别和运用的场景?
computed: 计算属性。依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
watch: 监听数据的变化。更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
场景:1)当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
2)当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
21、如何理解自定义指令?
指令的实现原理,可以从编译原理 =>代码生成=> 指令钩子实现进行概述
1、在生成 ast 语法树时,遇到指令会给当前元素添加directives属性
2、通过 genDirectives 生成指令代码
3、在patch前将指令的钩子提取到 cbs中,在patch过程中调用对应的钩子。
4、当执行指令对应钩子函数时,调用对应指令定义的方法
22、V-model的原理是什么?
v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性。
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
1)text 和 textarea 元素使用 value 属性和 input 事件;
2)checkbox 和 radio 使用 checked 属性和 change 事件;
3)select 字段将 value 作为 prop 并将 change 作为事件。
23、Vue性能优化
23.1 编码阶段
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher;
如果需要使用v-for给每项元素绑定事件时使用事件代理;
SPA 页面采用keep-alive缓存组件;
在更多的情况下,使用v-if替代v-show;
key保证唯一;
使用路由懒加载、异步组件;
防抖、节流;
第三方模块按需导入;
长列表滚动到可视区域动态加载;
图片懒加载;
23.2 用户体验
骨架屏;
PWA; progressive web app(渐进式web App)
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
23.3 SEO优化
预渲染;prerender-spa-plugin
服务端渲染SSR;
23.4 打包优化
压缩代码;
Tree Shaking/Scope Hoisting;
使用cdn加载第三方模块;
多线程打包happypack;
splitChunks抽离公共文件;
sourceMap优化;
24、修饰符 .prevent .passive .native的区别以及用在哪里
.prevent:阻止冒泡事件发生,某些标签有自己的默认事件,如a[href=”#”] button[type=’submit’]
.passive:会执行默认方法,如@scroll @touchmove
.native: 给自定义的组件添加原生事件,可以理解为该修饰符的作用就是把一个vue组件转化为一个普通的HTML标签,并且该修饰符对普通HTML标签是没有任何作用的。
25、== 和 === 的区别,什么情况下用相等==?
:运算符称作相等,用来检测两个操作数是否相等,这里的相等定义的非常宽松,可以允许进行类型转换
===:用来检测两个操作数是否严格相等 1、对于string,number等基础类型, == 和 === 是有区别的 不同类型间比较,==之比较“转化成同一类型后的值”看“值”是否相等,===如果类型不同,其结果就是不等
同类型比较,直接进行“值”比较,两者结果一样 2、对于Array,Object等高级类型, == 和 ===是没有区别的
26、typeof 和 instanceof的区别
typeof(x) a instanceof array
27、this的各种情况?
1、以函数形式调用时,this永远是window
2、以方法形式调用时,this是调用方法的对象
3、以构造函数的形式调用时,this是被创建的那个对象
4、使用call和apply调用时,this是指定的那个对象
5、箭头函数:看外层是否有函数,有则是内部函数的this,没有则是window
6、特殊情况:this指向最后调用它的对象
28、