Vue.js常被提及的面试题

对于MVVM的理解

 

ModelViewViewModel 三部分构成,由MVC衍生。

 

Model: 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑,

 

View: 代表UI 组件,它负责将数据模型转化成UI 展现出来,

 

ViewModel: 是一个同步View 和 Model的对象。

 

在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。 (注意)

 

ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理

 

Vue.js双向数据原理

 

Vue.js 可以说是MVVM 架构的最佳实践,专注于 MVVM 中的 ViewModel,不仅做到了数据双向绑定,而且也是一款相对来比较轻量级的JS 库,API 简洁,很容易上手。

 

Vue.js 是采用 Object.defineProperty 的 getter 和 setter,并结合观察者模式来实现数据绑定的。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化注意:据悉vue3.0将采用Proxy替代Object.defineProperty

 

 

图的解析:

 

Observer :数据监听器,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用Object.defineProperty的getter和setter来实现 。

 

Compile  :指令解析器,它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数 。

 

Watcher  :订阅者,作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数 。

 

Dep :消息订阅器,内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法 。

 

执行过程:

 

当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行扫描和解析,初始化视图,并订阅 Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。

 

当数据发生变化时,Observer 中的 getter 方法被触发(注意这里触发什么),getter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。同理当表单输入内容发生变化时, 就会触发setter,watcher监听机制就会执行, watcher通知Vue生成新的VDOM树,再通过render函数进行渲染,生成真实DOM 。

 

通过 Object.defineProperty 实现见简单的双向数据绑定:

 

<body>
    <div id="demo"></div>
    <input type="text" id="inp">
</body>
<script>
    let obj = {};
    let demo = document.querySelector('#demo');
    let inp = document.querySelector('#inp');
    Object.defineProperty(obj,'name',{
        get : () => {
            return inp.value;
        },
        set : (newVal) => {//当该属性被赋值的时候触发
            inp.value  = newVal;
            demo.innerHTML = newVal;
        }
    });
    inp.addEventListener('input',(e) => {
        // 给obj的name属性赋值,进而触发该属性的set方法
        obj.name = e.target.value;
    })
    obj.name = 'huqinggui';//在给obj设置name属性的时候,触发了set这个方法
</script>

 

为什么vue3.0要用Proxy替代Object.defineProperty 实现双向数据绑定(待补充完善)

 

替换不是因为不好,是因为有更好的方法使用效率更高

 

那么到底为什么要用Proxy呢?既然想要替代了,说明一个有缺点,一个有优点的那么我们就来分析下他们的优缺点:

 

Object.defineProperty 的缺点

 

总体上说致命的缺点是:

 

  • 不能监听数组的变化
  • 必须遍历对象的每个属性
  • 必须深层遍历嵌套的对象

 

细分就是以下几点:

 

1.对IE11的兼容性(现在除了特殊的需求,基本上对IE都不考虑了)

 

2. 无法检测数组的变化

 

Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。 而且使用这些方法(push, pop, shift, unshift,splice, sort, reverse…)是不能触发set的,Vue中能监听是因为对这些方法进行了重写

 

var a = {},
bValue = 1;
Object.defineProperty(a,"b",{
  set: function(value){
    bValue = value;
    console.log("setted");
  },
  get: function(){
    return bValue;
  }
});

a.b = []; //setted
a.b = [1,2,3]; //setted
a.b[1] = 10; //无输出
a.b.push(4); //无输出
a.b.length = 5; //无输出

 

当a.b被设置为数组后,只要不是重新赋值一个新的数组对象,任何对数组内部的修改都不会触发setter方法的执行。所以要想实现实现数组的双向绑定,则必须通过Arr = newArr;这样的语句实现。同样常见的数组方法也不会触发,在框架中对这些方法进行了重写才能实现效果。

 

3.只能监听属性,而不是监听对象本身,需要对对象的每个属性进行遍历

 

对于原本不在对象中的属性难以监听。在Vue 2.x里,是通过 “callback + 遍历 data 对象” 来实现对数据的监控的,如果属性值也是对象那么需要深度遍历,显然如果能劫持一个完整的对象是才是更好的选择,而Proxy就显示了这方面的优势

 

4.当对象增删的时候,是监控不到的

 

比如:data = {a:"a"},这个时候如果我们设置data.test = "test",这个时候是监控不到的。因为在observe data的时候,会遍历已有的每个属性(比如a),添加getter/setter,而后面设置的test属性并没有机会设置getter/setter,所以检测不到变化。同样的,删除对象属性的时候,getter/setter会跟着属性一起被删除掉,拦截不到变化。

 

ES6中的Proxy的优点

 

总的来说呢,Proxy是刚好解决了上述 Object.defineProperty 的缺点:

 

针对对象:针对整个对象,而不是对象的某个属性,所以也就不需要对 keys 进行遍历。

 

支持数组:Proxy 不需要对数组的方法进行重载,省去了众多 hack,减少代码量等于减少了维护成本,而且标准的就是最好的。

 

当然除此之外还有以下几个原因:

 

Proxy 的第二个参数可以有 13 种拦截方法,这比起 Object.defineProperty() 要更加丰富

 

Proxy 作为新标准受到浏览器厂商的重点关注和性能优化,相比之下 Object.defineProperty() 是一个已有的老方法。

 

请详细说下你对vue生命周期的理解

 

vue生命周期总共分为8个阶段: 创建前/后,载入前/后,更新前/后, 销毁前/后

 

beforeCreate (创建前)vue实例的挂载元素$el和数据对象 data都是undefined, 还未初始化

 

created (创建后) 完成了 data数据初始化, el还未初始化

 

beforeMount (载入前) vue实例的$eldata都初始化了, 相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。

 

mounted (载入后) 在el 被新创建的 vm.$el替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互

 

beforeUpdate (更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。

 

updated (更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。

 

beforeDestroy (销毁前) 在实例销毁之前调用。实例仍然完全可用。

 

destroyed (销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

 

vue中 key 值的作用 (v-for)

 

key 的特殊属性主要用在 Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes。如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用key,它会基于key的变化重新排列元素顺序,并且会移除key不存在的元素。(有效的避免就地复用)

 

有相同父元素的子元素必须有独特的key。重复的key会造成渲染错误。

 

最常见的用例是结合 v-for:

 

<ul>
  <li v-for="item in items" :key="item.id">...</li>
</ul>

 

它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:

 

完整地触发组件的生命周期钩子

 

触发过渡

 

<transition>
  <span :key="text">{{ text }}</span>
</transition>

 

当 text 发生改变时,<span> 会随时被更新,因此会触发过渡。

 

vue中子组件调用父组件的方法

 

子组件调用父组件的方法可以使用this.$emit() 

 

v-showv-if指令的共同点和不同点?

 

v-show指令是通过修改元素的displayCSS属性让其显示或者隐藏。

 

v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果(注意:v-if 可以实现组件的重新渲染),因此我们要尽量减少v-if的使用,因为它消耗性能

 

如何让CSS只在当前组件中起作用?

 

将当前组件的<style>修改为<style scoped>,关键点在scoped,代表作用域,限制css的作用在当前组件。的作用是什么?

 

<keep-alive></keep-alive>的作用是什么?

 

<keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。

 

大白话: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>进行缓存,
这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染 。

 

 

Vue中引入组件的步骤?

 

 

引入:

 

 

1.采用ES6的import ... from ...语法

 

 

2.CommonJS的require()方法引入组件

 

 

注册:

 

 

Vue.component('my-component', 
{  template: '<div>A custom component!</div>'})

 

3.使用组件<my-component></my-component>

 

指令v-el的作用是什么?和v-ref有什么区别?

 

提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标.可以是 CSS 选择器,也可以是一个 HTMLElement 实例

 

通过v-el我们可以获取到DOM对象

 

通过v-ref获取到整个组件(component)的对象

 

<template id="demo">
        <h2>组件对象</h2>
    </template>
    <div id="app">
        <h2 v-el:myh2>DOM对象</h2>
        <button @click="getDom">获取DOM对象</button>
        <hr>
        <demo v-ref:mycom></demo>
        <button @click="getCom">获取组件对象</button>
    </div>
<script src="../../../js/vue/vue/1.0/vue.js"></script>
<script type="application/javascript">
    //创建Vue对象
    new Vue({
        el:'#app'
        ,methods:{
            getDom(){
                console.log(this.$els.myh2);
            }
            ,getCom(){
                console.log(this.$refs.mycom);
            }
        }
        ,components:{
            'demo':{
                template:'#demo'
            }
        }
    });
</script>

 

v-el和v-ref

 

在Vue中使用插件的步骤

 

1. 采用ES6的import ... from ...语法

 

2. 使用全局方法Vue.use( plugin )使用插件,可以传入一个选项对象 ,需要

 

Vue.use(MyPlugin, { someOption: true })

 

请列举出3个Vue中常用的生命周期钩子函数?

 

1.created: 实例已经创建完成之后调用,在这一步,实例已经完成数据观测, 属性和方法的运算, watch/event事件回调. 然而, 挂载阶段还没有开始, $el属性目前还不可见

 

2. mounted: el被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内

 

3. activated::keep-alive组件激活时调用

 

请简述下Vuex的原理和使用方法

 

 

数据单向流动

 

一个应用可以看作是由上面三部分组成: View, Actions,State,数据的流动也是从View => Actions => State =>View 以此达到数据的单向流动.

 

但是项目较大的, 组件嵌套过多的时候, 多组件共享同一个State会在数据传递时出现很多问题.Vuex就是为了解决这些问题而产生的.

 

Vuex可以被看作项目中所有组件的数据中心,我们将所有组件中共享的State抽离出来,任何组件都可以访问和操作我们的数据中心

 

Vuex的组成:一个实例化的Vuex.Store由state, mutations和actions三个属性组成:

 

1.state中保存着共有数据

 

2.改变state中的数据有且只有通过mutations中的方法,且mutations中的方法必须是同步的

 

3.如果要写异步的方法,需要些在actions中, 并通过commit到mutations中进行state中数据的更改.

 

vue watch的高级用法--监听对象的属性变化

 

1.监听对象需要深度监听 ,如下代码可以监听整个msg对象的变化

 

watch: {
  msg: {
    handler(newValue, oldValue) {
      console.log(newValue)
    },
    deep: true
  }
}

 

2. 监听对象里面某个属性的变化,通过computed做中间层实现

 

computed: {
  channel() {
    return this.msg.channel
  }
  },
  watch:{
    channel(newValue, oldValue) {
    console.log('new: %s, old: %s', newval, oldVal)
    //这里面可以执行一旦监听的值发生变化你想做的操作
  }
  }

 

vue等单页面应用及其优缺点

 

优点:

 

1、具有桌面应用的即时性、网站的可移植性和可访问性。

 

2、用户体验好、快,内容的改变不需要重新加载整个页面。

 

3、基于上面一点,SPA相对对服务器压力小。

 

4、良好的前后端分离。SPA和RESTful架构一起使用,后端不再负责模板渲染、输出页面工作,web前端和各种移动终端地位对等,后端API通用化。

 

5、同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端;

 

缺点:

 

1、不利于SEO。(如果你看中SEO,那就不应该在页面上使用JavaScript,你应该使用网站而不是Web应用,或者通过ssr渲染)

 

2、初次加载耗时相对增多。

 

3、导航不可用,如果一定要导航需要自行实现前进、后退。

 

vue prop不同数据类型设置默认值

 

vue prop 会接收不同的数据类型,这里列出了 常用的数据类型的设置默认值的写法,其中包含: Number, String, Boolean, Array,  Function, Object 

 

refAge: {
   type: Number,
   default: 0
},
refName: {
   type: String,
   default: ''
},
hotDataLoading: {
   type: Boolean,
   default: false
},
hotData: {
   type: Array,
   default: () => {
   return []
}
},
getParams: {
   type: Function,
   default: () => () => {}
},
meta: {
   type: Object,
   default: () => ({})
}

 

Vue实现组件props双向绑定解决方案

 

注意: 子组件不能直接修改prop过来的数据,会报错

 

方案一:

 

  1. 用data对象中创建一个props属性的副本
  2. watch props属性 赋予data副本 来同步组件外对props的修改
  3. watch data副本,emit一个函数 通知到组件外

 

子组件:

 

<template>
  <div class="hello">
    <h1 v-show="visible">测试显示隐藏</h1>
    <div @click="cancel">点我点我</div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
      value: {
        type: Boolean,
        default:false
      }
    },
  data () {
    return {
      visible: false
    }
  },
  watch:{
    value(val) {
      this.visible = val;
    },
    // 只有这一步 才触发父组件的方法 由父组件的 paretnVisibleChange 方法去改变父 
    组件的数据
  visible(val) { this.$emit("paretnVisibleChange",val); } },
  // 子组件修改的只能是子组件 本身的data数据
  methods:{
    cancel(){
      this.visible = !this.visible;
    }
  },
// 注意这段代码 为了同步父组件的数据
  mounted() {
    if (this.value) {
      this.visible = true;
    }
  }
}
</script>
<style scoped>

</style>

 

父组件:

 

<template>
  <div id="app">
    <HelloWorld :value = 'visible' @paretnVisibleChange="visibleChange" />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data () {
    return {
      visible: true
    }
  },
  methods:{
    // 父子组件就是靠的这个方法改变数据的
    visibleChange(val){
      this.visible = val;
    }
  }
}
</script>

 

方案一 的缺点就是 父组件必须有个 visibleChange 这样的方法,有点累赘。

 

这时候 想到了 v-model 

 

因为

 

<input v-model = 'someThing'>

 

是下面这段代码的语法糖

 

<input :value = 'someThing'  @input = 'someThing = $event.target.value'>

 

也就是说 v-mode 自带了 一个改变父组件的方法 类似方案一的   paretnVisibleChange

 

但是使用 v-model 的时候 需要注意两点:

 

1.子组件要接受  value  属性

 

2.value改变时 要触发input 事件

 

方案二:

 

子组件:

 

<template>
  <div class="hello">
    <h1 v-show="visible">测试显示隐藏</h1>
    <div @click="cancel">点我点我</div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
      value: {
        type: Boolean,
        default:true
      }
    },
  data () {
    return {
      visible: false
    }
  },
  watch:{
    value(val) {
      this.visible = val;
    },
    // 子组件 改变的就是这段代码
    visible(val) {
    this.$emit("input",val);
    }
  },
  methods:{
    cancel(){
      this.visible = !this.visible;
    }
  },
  mounted() {
    if (this.value) {
      this.visible = true;
    }
  }
}
</script>

 

父组件代码如下:(父组件省去了 paretnVisibleChange 方法)

 

<template>
  <div id="app">
    <HelloWorld v-mode = 'visible'/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data () {
    return {
      visible: true
    }
  }
}
</script>

 

方案三:

 

vue 2.3.0之后新增了 .sync 属性 使用方法跟 v-model  类似 具体 请参考 : https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-修饰符

 

下面我写了一个简单的sync 的使用实例:

 

父组件的代码如下:

 

<li
        is="DragCompent"
        v-for="(item, index) in layoutItem"
        :item="item"
        v-model="cloneLeftItemText"
        :leftDragItemIsDraged.sync = 'leftDragItemIsDraged'
        :key="index">
</li>

 

子组件的代码如下:

 

props: {
    leftDragItemIsDraged: {
      type: Boolean,
      default: false
    }
  },
watch:{
    leftDragItemIsDraged(val) {
    this.thisLeftDragItemIsDraged = val;
},
thisLeftDragItemIsDraged(val){
    this.$emit('update:leftDragItemIsDraged', val)
}
}

 

效果图:

 

效果图

 

active-class是哪个组件的属性?嵌套路由怎么定义?

 

vue-router模块的router-link组件。

 

嵌套路由请参考:  https://blog.csdn.net/github_26672553/article/details/54861174

 

怎么定义vue-router的动态路由?怎么获取传过来的动态参数? 

 

在router目录下的index.js文件中,对path属性加上/:id。  使用router对象的params.id 例如 :  this.$route.params.id

 

params 和 query的区别

 

params代表路由参数

 

query代表页面跳转参数

 

vue-router有哪几种导航钩子?

 

三种,

 

一种是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。(常用)

 

第二种:组件内的钩子;

 

第三种:单独路由独享组件

 

具体参考:  https://www.jianshu.com/p/aae084e97452

 

scss是什么?安装使用的步骤是?

 

预处理css,把css当成函数编写,定义变量,嵌套。 先装css-loader、node-loader、sass-loader等加载器模块,在webpack-base.config.js配置文件中加多一个拓展:extenstion,再加多一个模块:module里面test、loader(现在的vue脚手架已经集成了)

 

scss是什么?在vue.cli中的安装使用步骤是?有哪几大特性?

 

css的预编译。

 

使用步骤:

 

第一步:用npm 下三个loader(sass-loader、css-loader、node-sass)

 

第二步:在build目录找到webpack.base.config.js,在那个extends属性中加一个拓展.scss

 

第三步:还是在同一个文件,配置一个module属性

 

第四步:然后在组件的style标签加上lang属性 ,例如:lang=”scss”

 

有哪几大特性:

 

1、可以用变量,例如($变量名称=值);

 

2、可以用混合器,例如:

 

定义了字体的混合器

 

@mixin font-dpr($font-size){
  $font:$font-size/2;
  font-size: $font;
  [data-dpr="2"] & { font-size: $font+2px;}
  [data-dpr="3"] & { font-size: $font+4px;}
}

使用方法如下
.div{

    @include font-dpr(24px);

}

 

3、可以嵌套

 

mint-ui是什么?怎么使用?说出至少三个组件使用方法?

 

基于vue的前端组件库。npm安装,然后import样式和js,vue.use(mintUi)全局引入。在单个组件局部引入:import {Toast} from ‘mint-ui’。组件一:Toast(‘登录成功’);组件二:mint-header;组件三:mint-swiper

 

v-model是什么?怎么使用? vue中标签怎么绑定事件?

 

可以实现双向绑定,在表单元素上使用,指令(v-class、v-for、v-if、v-show、v-on)。vue的model层的data属性。绑定事件:<input @click=doLog() />

 

axios是什么?怎么使用?描述使用它实现登录功能的流程?

 

请求后台资源的模块。npm install axios -S装好,然后发送的是跨域,需在配置文件中config/index.js进行设置。后台如果是Tp5则定义一个资源路由。js中使用import进来,然后.get或.post。返回在.then函数中如果成功,失败则是在.catch函数中

 

axios+tp5进阶中,调用axios.post(‘api/user’)是进行的什么操作?axios.put(‘api/user/8′)呢?

 

跨域,添加用户操作,更新操作。

 

什么是RESTful API?怎么使用?

 

是一个api的标准,无状态请求。请求的路由地址是固定的,如果是tp5则先路由配置中把资源路由配置好。标准有:.post .put .delete

 

vuex是什么?怎么使用?哪种功能场景使用它?

 

vue框架中状态管理。在main.js引入store,注入。新建了一个目录store,….. export 。场景有:单页应用中,组件之间的状态。音乐播放、登录状态、加入购物车

 

mvvm框架是什么?它和其它框架(jquery)的区别是什么?哪些场景适合?

 

一个model+view+viewModel框架,数据模型model,viewModel连接两个

 

区别:vue数据驱动,通过数据来显示视图层而不是节点操作

 

场景:数据操作比较多的场景,更加便捷。

 

自定义指令(v-check、v-focus)的方法有哪些?它有哪些钩子函数?还有哪些钩子函数参数?

 

全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。组件内定义指令

 

钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)

 

钩子函数参数:el、binding

 

说出至少4种vue当中的指令和它的用法?

 

v-if:判断是否隐藏;v-for:数据循环出来;v-bind:class:绑定一个属性;v-model:实现双向绑定 。

 

vue-router是什么?它有哪些组件?

 

vue用来写路由一个插件。router-link、router-view 。

 

导航钩子有哪些?它们有哪些参数?

 

导航钩子有:a/全局钩子和组件内独享的钩子。b/beforeRouteEnter、afterEnter、beforeRouterUpdate、beforeRouteLeave。

 

参数:有to(去的那个路由)、from(离开的路由)、next(一定要用这个函数才能去到下一个路由,如果不用就拦截)最常用就这几种。

 

Vue的双向数据绑定原理是什么?vue data是怎么实现的

 

vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调。

 

详细请参考:  http://www.cnblogs.com/libin-1/p/6893712.html

 

具体步骤:

 

第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上 settergetter
这样的话,给这个对象的某个值赋值,就会触发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变更的双向绑定效果。

 

 

请详细说下你对vue生命周期的理解?

 

 

总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

 

 

创建前/后: 在beforeCreated阶段,vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,$el还没有。

 

 

载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。

 

 

更新前/后:当data变化时,会触发beforeUpdate和updated方法。

 

 

销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在

 

 

请说下封装 vue 组件的过程?

 

 

首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低难维护复用性等问题。

 

 

然后,使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。

 

 

你是怎么认识vuex的?

 

 

vuex可以理解为一种开发模式或框架。比如PHP有thinkphp,java有spring等。
通过状态(数据源)集中管理驱动组件的变化(好比spring的IOC容器对bean进行集中管理)。

 

 

应用级的状态集中放在store中; 改变状态的方式是提交mutations,这是个同步的事物; 异步逻辑应该封装在action中。

 

 

vue-loader是什么?使用它的用途有哪些?

 

 

解析.vue文件的一个加载器,跟template/js/style转换成js模块。

 

 

用途:js可以写es6、style样式可以scss或less、template可以加jade等

 

 

请说出vue.cli项目中src目录每个文件夹和文件的用法?

 

 

assets文件夹是放静态资源;components是放组件;router是定义路由相关的配置;view视图;app.vue是一个应用主组件;main.js是入口文件

 

 

vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?

 

 

第一步:在components目录新建你的组件文件(smithButton.vue),script一定要export default

 

 

第二步:在需要用的页面(组件)中导入:import smithButton from ‘../components/smithButton.vue’

 

 

第三步:注入到vue的子组件的components属性上面,components:{smithButton}

 

 

第四步:在template视图view中使用,<smith-button>  </smith-button>
问题有:smithButton命名,使用的时候则smith-button。

 

 

聊聊你对Vue.js的template编译的理解?

 

 

简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)

 

 

详情步骤:

 

 

首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。

 

 

然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)

 

 

Vue的路由实现:hash模式 和 history模式

 

 

hash模式:

 

 

在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取; 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xiaogangzai.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 依赖的页面。”

 

posted @ 2019-07-23 22:01  前端纸飞机  阅读(739)  评论(0编辑  收藏  举报