答:vue实例从开始创建到销毁的过程,就是vue的生命周期。也就是从开始创建,初始化数据,编译模板,挂载dom,渲染,更新,渲染,销毁等一系列过程,称之vue的生命周期
答:vue生命周期共有8个阶段,创建前后,挂在前后,更新前后,卸载前后。
创建前/后:在beforeCreated阶段,vue实例的挂载元素和数据都为undefined,还为初始化。
在created阶段,vue实例的数据对象有了,dom元素还没有。
挂在前后:在beforeMount阶段,vue实例的挂载元素和数据对象初始化了,但都还是挂载之前的虚拟dom节点,数据对象中的数据还为替换。
在mounted阶段vue实例挂载完成。
更新前后:当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。
答:第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
>答:vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
具体步骤:
第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter和getter
这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化
第二步:compile解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。
第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
替换不是因为不好,是因为有更好的方法使用效率更高
Object.defineProperty的缺点:
1.在Vue中,Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。为了解决这个问题,经过vue内部处理后可以使用以下几种方法来监听数组。
有关于这个说明,可以看看这个文章 vue为什么不能检测数组变动
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
目前只针对以上方法做了hack处理,所以恰数组属性是检测不到的,有局限性。
2.Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。Vue里,是通过递归以及遍历data对象来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象,不管是对操作性还是性能都会有一个很大的提升。
而要取代它的Proxy有以下两个优点:
- 可以劫持整个对象,并返回一个新对象
- 有13种劫持操作
Proxy是 ES6 中新增的一个特性,翻译过来意思是"代理",用在这里表示由它来“代理”某些操作。 Proxy 让我们能够以简洁易懂的方式控制外部对对象的访问。其功能非常类似于设计模式中的代理模式。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
使用 Proxy 的核心优点是可以交由它来处理一些非核心逻辑(如:读取或设置对象的某些属性前记录日志;设置对象的某些属性值前,需要验证;某些属性的访问控制等)。 从而可以让对象只需关注于核心逻辑,达到关注点分离,降低对象复杂度等目的。
基本用法:
let p = new Proxy(target, handler);
参数:
target: 是用Proxy包装的被代理对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler: 是一个对象,其声明了代理target 的一些操作,其属性是当执行一个操作时定义代理的行为的函数。
p是Proxy对象,当其他操作对p进行更改的时候,会执行handler对象的方法。Proxy有13种数据劫持的操作,常用的handler处理方法:
get: 读取值,
set: 获取值,
has: 判断对象是否拥有该属性,
construct: 构造函数
给个例子:
let obj = {};
let handler = {
get(target, property) {
console.log(`${property} 被读取`);
return property in target ? target[property] : 3;
},
set(target, property, value) {
console.log(`${property} 被设置为 ${value}`);
target[property] = value;
}
}
let p = new Proxy(obj, handler);
p.name = 'tom' //name 被设置为 tom
p.age; //age 被读取 3
observe(data) {
const that = this;
let handler = {
get(target, property) {
return target[property];
},
set(target, key, value) {
let res = Reflect.set(target, key, value);
that.subscribe[key].map(item => {
item.update();
});
return res;
}
}
this.$data = new Proxy(data, handler);
}
这段代码里把代理器返回的对象代理到this. d a t a , 即 t h i s . data,即this. data,即this.data是代理后的对象,外部每次对this.$data进行操作时,实际上执行的是这段代码里handler对象上的方法。
一、Axios 是一个基于 promise 的 HTTP 库,支持promise所有的API
二、它可以拦截请求和响应
三、它可以转换请求数据和响应数据,并对响应回来的内容自动转换成 JSON类型的数据
四、安全性更高,客户端支持防御 XSRF
MVVM 是 Model-View-ViewModel 的缩写。
Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
MVC允许在不改变视图的情况下改变视图对用户输入的响应方式,用户把对View的操作交给了Controller处理,在Controller中响应View的事件调用Model的接口对数据进行操作,一旦Model发生变化便通知相关视图进行更新。
如果前端没有框架,只使用原生的html+js,MVC模式可以这样理解。将html看成view;js看成controller,负责处理用户与应用的交互,响应对view的操作(对事件的监听),调用Model对数据进行操作,完成model与view的同步(根据model的改变,通过选择器对view进行操作);将js的ajax当做Model,也就是数据层,通过ajax从服务器获取数据。
MVVM与MVC两者之间最大的区别就是:MVVM实现了对View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素来改变View的变化,而是改变其属性后,该属性对应的View层数据会自动改变。
以Vue为例:
<div id="vueDemo">
<p>{{ title }}</p>
<button v-on:click="clickEvent">hello word</button>
</div>
var vueDemo = new Vue({
el: '#vueDemo',
data: {
title: 'Hello Vue!'
},
methods: {
clickEvent: function () {
this.title = "hello word!"
}
}
})
复制代码这里的html => View层,可以看到这里的View通过模板语法来声明式的将数据渲染进DOM元素,当ViewModel对Model进行更新时,通过数据绑定更新到View。
Vue实例中的data相当于Model层,而ViewModel层的核心是Vue中的双向数据绑定,当Model发生变化时View也可以跟着实时更新,同理,View变化也能让Model发生变化。
总的看来,MVVM比MVC精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作DOM元素。因为在MVVM中,View不知道Model的存在,Model和ViewModel也观察不到View,这种低耦合模式提高代码的可重用性
父组件通过props传递数据给子组件,子组件通过emit发送事件传递给父组件。
// 父组件
<div>
<child :data="child" @send="getFromChild"></child>
</div>
data(){
return{
toChild: '大儿子',
fromChild: ''
}
},
methods: {
getFromChild(val){
this.fromChild=val
}
}
// 子组件
<div @click="toParent">{{data}}</div>
props:[data],
methods: {
toParent(){
this.$emit('send', '给父亲')
}
}
v-model其实是props,emit的语法糖,v-model默认会解析成名为value的prop和名为input的事件。
// 父组件
<children v-model="msg"></children>
<p>{{msg}}</p>
data(){
return{
msg:'model'
}
}
// 子组件
<input :value="value" @input="toInput" />
props: ['value'],
//默认是input事件 value默认值
model: {
prop: 'value',
event: 'input'
}
methods: {
toInput(e){
this.$emit('input', e.target.value)
}
}
// 父组件
<child />
data(){
return {
msg: '父组件数据'
}
},
methods: {
test(){
console.log('我是父组件的方法,被执行')
}
},
mounted(){
console.log(this.$children[0].child_msg); // 执行子组件方法
}
// 子组件
<div>{{$parent.msg}}</div>
data(){
return{
child_msg: '子组件数据'
}
},
mounted(){
// 子组件执行父组件方法
this.$parent.test();
}
在vue1.x中是对prop进行双向绑定,在vue2只允许单向数据流,也是一个语法糖
// 父组件
<child :count.sync="num" />
data(){
return {
num: 0
}
}
// 子组件
<div @click="handleAdd">add</div>
data(){
return {
counter: this.count
}
},
props: ["count"],
methods: {
handleAdd(){
this.$emit('update:count', ++this.counter)
}
}
可以通过查找父组件中的子组件实现,
this. p a r e n t . parent. parent.children
在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信跨多层次组件通信可以使用 provide/inject,虽然文档中不推荐直接使用在业务中。
假设有父组件A,然后有一个跨多层次的子组件B
// 父组件A
export default{
provide: {
data: 1
}
}
// 子组件B
export default{
inject: ['data'],
mounted(){
// 无论跨几层都能获取父组件的data属性
console.log(this.data); // 1
}
}
可以用Vuex或Event Bus解决
eventBus的使用
1.新建一个bus.js文件
import Vue from 'vue';
export default new Vue();
2.使用它
<div @click="addCart">添加</div>
import Bus from 'bus.js';
export default{
methods: {
addCart(event){
Bus.$emit('getTarget', event.target)
}
}
}
// 另一组件
export default{
created(){
Bus.$on('getTarget', target =>{
console.log(target)
})
}
}
hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
1.与AngularJS的区别
相同点:
都支持指令:内置指令和自定义指令;都支持过滤器:内置过滤器和自定义过滤器;都支持双向数据绑定;都不支持低端浏览器。
不同点:
AngularJS的学习成本高,比如增加了Dependency Injection特性,而Vue.js本身提供的API都比较简单、直观;在性能上,AngularJS依赖对数据做脏检查,所以Watcher越多越慢;Vue.js使用基于依赖追踪的观察并且使用异步队列更新,所有的数据都是独立触发的。
2.与React的区别
相同点:
React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置列数AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。
不同点:
React采用的Virtual DOM会对渲染出来的结果做脏检查;Vue.js在模板中提供了指令
首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。
beforeEach主要有3个参数to,from,next:
to:route即将进入的目标路由对象,
from:route当前导航正要离开的路由
next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。
只用来读取的状态集中放在store中;改变状态的方式是提交mutations,这是个同步的事物;异步逻辑应该封装在action中。
在main.js引入store,注入。新建了一个目录store,…… export 。
场景有:单页应用中,组件之间的状态、音乐播放、登录状态、加入购物车
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
getters
类似vue的计算属性,主要用来过滤一些数据。
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。key的作用主要是为了高效的更新虚拟DOM。
为什么使用v-for时必须添加唯一的key?
key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。
$route 是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。而 $router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。
答:在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。
好处:
①使得数据处理结构清晰;
②依赖于数据,数据更新,处理结果自动更新;
③计算属性内部this指向vm实例;
④在template调用时,直接写计算属性名即可;
⑤常用的是getter方法,获取数据,也可以使用set方法改变数据;
⑥相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
组件复用时所有组件实例都会共享data,如果data是对象就会造成一个组件修改data以后会影响到其他所有组件,所以需要将data写成函数,每次用到就调用一次函数获得新的数据
当我们使用new Vue()的方式的时候,无论我们将data设置为对象还是函数都是可以的,因为new Vue()的方式是生成一个根组件,该组件不会复用,也就不存在共享data的情况。