什么是单页面应用
说白就是无刷新,整个webapp就一个html文件,里面的各个功能页面是javascript通过hash,或者history api来进行路由,并通过ajax拉取数据来实现响应功能。因为整个webapp就一个html,所以叫单页面!
通俗点来讲,在应用整个使用流程里浏览器由始至终没有刷新,所有的数据交互由ajax完成。
但是用户体验起来和app一样,有明确的页面区分,即所谓的web app。
后端: 只是注重数据处理
前端: 负责逻辑交互,页面渲染。
数据模型
简述一下MVVM, 以及对比MVC模型有哪些优势?
先来看看下图:
这是一个MVVM模型
- view: 视图
- ViewModel 主要就是理解的话就是ViewModel把用户的状态和行为抽离出来成为一个抽象的对象
- mode 实现数据交互
与mvc相别的优势 - mvc 逻辑处理过多,会导致c(控制器)过于庞大,不好维护
- 控制器的大部分功能一直到viewModel上,瘦身了model
- 双向视图绑定,实时预览(view)
- 可以对View或ViewController的数据处理部分抽象出来一个函数处理model。这样它们专职页面布局和页面跳转,它们必然更一步的简化。
Virtual DOM与原生DOM谁更具有优势
- 将 Virtual DOM 作为一个兼容层,让我们还能对接非 Web 端的系统,实现跨端开发。
- 同样的,通过 Virtual DOM 我们可以渲染到其他的平台,比如实现 SSR、同构渲染等等。
- 实现组件的高度抽象化
Vue 生命周期
Vue 实例有一个完整的生命周期,生命周期也就是指一个实例从开始创建到销毁的这个过程。
- beforeCreated():在实例创建之间执行,数据未加载状态。
- created():在实例创建、数据加载后,能初始化数据,DOM 渲染之前执行。
- beforeMount():虚拟 DOM 已创建完成,在数据渲染前最后一次更改数据。
- mounted():页面、数据渲染完成,真实 DOM 挂载完成。
- beforeUpadate():重新渲染之前触发。
- updated():数据已经更改完成,DOM 也重新 render 完成,更改数据会陷入死循环。
- beforeDestory() 和 destoryed():前者是销毁前执行(实例仍然完全可用),后者则是销毁后执行。
Vue 的父组件和子组件生命周期钩子执行顺序是什么:
1、父组建: beforeCreate -> created -> beforeMount
2、子组件: -> beforeCreate -> created -> beforeMount -> mounted
3、父组件: -> mounted
总结:从外到内,再从内到外
前端路由
前端路由原理?两种实现方式有什么区别?
目前前端路由的原理就是通过监听路由的变化来改变视图的变化,并且无需刷新页面。目前实现主要有两种方式。
- hash模式
- History模式
两种模式的区别:
- hash模式,兼容性良好,无需后端配合,每次访问的域名不会改变,只是改变#后面的hash值
- history无需带#号,但是刷新后导致路劲错误,所以需要后端配置(nginx 指定跳转当前地址)
组件之间的通信
详情请点击这里vue通信
Vue 中怎么自定义指令
全局注册:
// 注册一个全局自定义指令 `v-focus`
Vue
.directive(
'focus'
, {
// 当被绑定的元素插入到 DOM 中时……
inserted:
function
(el) {
// 聚焦元素
el.focus()
}
})
局部注册:
directives: {
focus: {
// 指令的定义
inserted:
function
(el) {
el.focus()
}
}
}
常见知识点
Vue.extend 能做什么
extend主要是用于拓展组件,比如说拓展一些弹出组件,这个就比较好用
// 创建构造器
var Profile = Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
通常配合$mount来挂载到组件上去
$route
和$router
的区别
$router
为 VueRouter 实例,想要导航到不同 URL,则使用 $router.push 方法。
$route
为当前 router 跳转对象里面可以获取 name 、 path 、 query 、 params 等。
query和params区别
query:
parms:
mixin和 mixins 区别
- mixin: 全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。插件作者可以使用混入,向组件注入自定义的行为。不推荐在应用代码中使用。
// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
created: function () {
var myOption = this.$options.myOption
if (myOption) {
console.log(myOption)
}
}
})
new Vue({
myOption: 'hello!'
})
// => "hello!"
- mixins 相当于接受一个混入对象的数组
var mixin = {
created: function () { console.log(1) }
}
var vm = new Vue({
created: function () { console.log(2) },
mixins: [mixin]
})
// => 1
// => 2
keep-alive 组件有什么作用
如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。例如tab页面的切换 链接
当调用这个组件的时候,只有以下两个钩子函数被调用
- activated: keep-alive组件激活时调用
- deactivated: keep-alive组件停用时调用
vue中data为什么是函数
组件复用的情况,如果采用data为对象的话,更新了A组件更新了公用组件,结果就会导致其他公用这个共有组件的情况也会改变。
通过原型链看下具体情况
var MyComponent = function() {}
MyComponent.prototype.data = {
a: 1,
b: 2,
}
// 上面是一个虚拟的组件构造器,真实的组件构造器方法很多
var component1 = new MyComponent()
var component2 = new MyComponent()
// 上面实例化出来两个组件实例,也就是通过<my-component>调用,创建的两个实例
component1.data.a === component2.data.a // true
component1.data.b = 5
component2.data.b // 5
上面就证实了这一点,如果data是函数的话,就会形成一个自己的作用域,相互不影响
vue 原理实现
- 循环dom树,监听DOM的所有元素。(实现watcher)
- 运用了Object.defineProperty(建立订阅者) 中得set (通知变化), get(添加订阅者)方法来实现数据监听
- 当数据改变事,发送通知,响应dom
view响应数据的改变,是通过监听dom上的事件来实现的
当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty 将它们转为getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。
每个组件实例都有相应的watcher程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
这种方案得缺点:
- Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
- Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。
- Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
vue采用proxy重写后就不会出现这种情况了
NextTick 原理分析
nextTick 可以让我们在下次 DOM 更新循环结束之后执行延迟回调,用于获得更新后的 DOM。
写 React / Vue 项目时为什么要在列表组件中写 key,其作用是什么?
key的作用:
更准确
因为带key就不是就地复用了,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。更快
利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。(这个观点,就是我最初的那个观点。从这个角度看,map会比遍历更快。)
vue和react都是采用diff算法来对比新旧虚拟节点,从而更新节点。在vue的diff函数中(建议先了解一下diff算法过程)。
在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的key去对比旧节点数组中的key,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个map映射,另一种是遍历查找。相比而言。map映射的速度更快。
vue中 watch 和 computed的不同点
computed: 具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数
watch: 无缓存性,页面重新渲染时值不变化也会执行