7

1.Vue生命周期:

Vue实例从创建到销毁的过程,就是声明周期。从创建实例、初始化数据、编译模板、挂载DOM(->渲染)、更新(->渲染)、卸载等一系列过程,我们称之为Vue的生命周期。
Vue 实例生命周期

共分为八个阶段,如下:
创建前/后,载入前/后,更新前/后、销毁前/后。

  • beforeCreate:可以在这加个loading事件,在加载实例时触发
  • created:初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用。
  • beforeMount
  • mounted:挂载元素,获取到DOM节点。
  • beforeUpdate
  • updated:如果对数据统一处理,在这里写上相应函数。
  • beforeDestroy:可以做一个确认停止事件的确认框。
  • destroyed

2.Vue的生命周期的作用:

它的生命周期中有很多事件钩子函数,让我们在控制整个Vue实例的过程时,更容易形成好的逻辑。

生命周期钩子函数(11个)类型详细
beforeCreateFunction实例初始化之后,数据观测(data observer)和event/watch事件配置之前被调用
createdFunction实例创建完成后被立即调用。在这一步,实例已经完成以下配置:数据观测(data observer),属性和方法的运算watch/event事件的回调。然而挂载阶段还没开始,$el属性目前不可见。
beforeMountFunction挂载开始之前被调用,相关的render函数首次被调用
mountedFunctionel被新创建的vm.$el替换,并挂载到实例上之后调用该钩子。如果root实例挂载了一个文档内元素,当mounted被调用时vm.$el也在文档内
beforeUpdateFunction数据更新时调用,发生在虚拟DOM打补丁之前。这是适合更新之前访问现有的DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染才会在服务器端运行
updatedFunction由于数据更新导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
activatedFunctionkeep-alive组件激活时调用该钩子在服务器端渲染期间不被调用
deactivatedFunctionkeep-alive组件停用时调用该钩子在服务器端渲染期间不被调用
beforeDestoryFunction实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用
destroyedFunction实例销毁后调用。调用后,实例指示的所有东西都会解绑定,所有事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用
errorCaptured(2.5.0+ 新增)(err: Error, vm: Component, info: string) => ?boolean当捕获一个来自子孙组件时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

注意:

  • mountedupdated不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图渲染完毕,可以用vm.$nextTick替换掉mountedupdated

    updated: function () {
      this.$nextTick(function () {
    // Code that will run only after the
    // entire view has been re-rendered
      })
    }
    
  • http请求建议在created生命周期内发出。需要页面加载完成之后的话就用mounted

3.生命周期钩子是如何实现的?

Vue的生命周期钩子就是回调函数而已,当创建组件实例的过程中会调用对应的钩子方法。

补充说明

内部主要是使用callHook方法来调用对应的方法。核心是一个发布订阅模式,将钩子订阅好(内部采用数组的方式存储),在对应的阶段进行发布。

4.Vue 的父组件和子组件生命周期钩子执行顺序?

核心答案:

第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子。

渲染过程:

父组件挂载完成一定是等子组件都挂载完成后,才算是父组件挂载完,所以父组件的mounted在子组件mouted之后

  • 父beforeCreate ->
  • 父created ->
  • 父beforeMount ->
  • 子beforeCreate ->
  • 子created ->
  • 子beforeMount ->
  • 子mounted ->
  • 父mounted

子组件更新过程:

影响到父组件:

  • 父beforeUpdate ->
  • 子beforeUpdate->
  • 子updated ->
  • 父updted

5.MVVM概念:Model-View-ViewModel

·MVVM的核心是ViewModel层,它就像是一个中转站,负责转换Model中的数据对象来让数据变得更容易管理和使用,该层向上与视图层进行双向数据绑定,向下与Model层通过接口请求进行数据交互,起承上启下作用
1.png

  • View层:View层是视图层,也就是用户界面。前端主要有HTML+CSS构建。
  • Model层:Model是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的API接口。
  • ViewModel层:ViewModel是前端开发人员组织 生成和维护的视图数据层。在这一层,前端开发者对从后端获取的Model数据进行转换处理,做二次封装,以生成符合View层使用与其视图数据模型。需要注意的是ViewModel所封装出来的视图数据模型包括视图的状态和行为两部分,而Model层的数据模型只包含状态,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。

    //具体实现
    //View层
    <div id="app">
    <p>{{message}}</p>
    <button v-on:click="showMessage()">Click me</button>
    </div>
    
    //ViewModel层
    var app = new Vue({
    el: '#app',
    data: {  // 用于描述视图状态   
        message: 'Hello Vue!', 
    },
    methods: {  // 用于描述视图行为  
        showMessage(){
            let vm = this;
            alert(vm.message);
        }
    },
    created(){
        let vm = this;
        // Ajax 获取 Model 层的数据
        ajax({
            url: '/your/server/data/api',
            success(res){
                vm.message = res;
            }
        });
    }
    })
    
    //Model层
    {
    "url": "/your/server/data/api",
    "res": {
        "success": true,
        "name": "IoveC",
        "domain": "www.cnblogs.com"
    }
    }

6.Vue是如何实现数据双向绑定的:

·Vue数据双向绑定主要是指:数据变化更新视图,视图变化更新数据。
1.png

即:

  • 输入框内容变化时,Data 中的数据同步变化。即 View => Data 的变化。
  • Data 中的数据变化时,文本节点的内容同步变化。即 Data => View 的变化。

其中,View变化更新Data,可以通过事件监听的方式来实现。所以Vue的数据双向绑定的工作主要是如何根据Data变化更新View。

Vue主要通过以下四个步骤来实现数据双向绑定

  • 实现一个监听器Observer:对数据对象进行遍历,包括子属性对象的属性,利用Object.defineProperty()对属性都加上 setter和getter。这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到数据的变化了。
  • 实现一个解析器Compile:解析Vue模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。
  • 实现一个订阅者Watcher:Watcher订阅者是监听器Observer和解析器Compile之间通信的桥梁,主要的任务是订阅Observer中的属性值变化的消息,当收到属性变化的消息时,触发解析器Compile中对应的更新函数。
  • 实现一个订阅器Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者Watcher,对监听器Observer和解析器Compile进行统一管理。
    1.png

7.虚拟Dom以及diff算法和patch函数**

1.虚拟Dom原理:利用JS创建HTML,解析成真实的Dom节点appendChild进去,直接给root层减少Dom操作。更新的时候也是先创建虚拟Dom,然后通过diff算法去比较新旧虚拟Dom的差异,再使用snabbdom的h函数和patch函数去修补差异。
2.diff算法:就是深度优先搜索。递归子节点,并且记录比较两个节点的差异。

8.vue优点及缺点?

优点:

  1. 轻量级框架:
    只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
  2. 数据绑定:
    这也就是vue.js最大的优点,通过MVVM思想实现数据的双向绑定,通过虚拟DOM让我们可以用数据来操作DOM,而不必去操作真实的DOM,提升了性能。且让开发者有更多的时间去思考业务逻辑
  3. 组件化:
    把一个单页应用中的各个模块拆分到一个个组件当中,或者把一些公共的部分抽离出来做成一个可复用的组件。所以组件化带来的好处就是,提高了开发效率,方便重复使用,使项目的可维护性更强。
  4. 虚拟DOM:
    dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
    运行速度更快:
    相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。

缺点:

  1. 不利于seo:
  2. 导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)。
  3. 初次加载时耗时多。
  4. 页面复杂度较高(单页面应用)

9.v-show和v-if的区别:

·编译过程:v-if 是 真正 的 条件渲染,因为它会确保在切换过程中条件块内的事件监听器子组件适当地被销毁重建v-show的元素始终会被渲染并保留在 DOM 中v-show只是简单地切换元素的 CSS 属性display

·编译条件:v-if 是惰性的:如果在初始渲染时条件为假,则什么也不做。直到条件第一次变为真时,才会开始渲染条件块。v-show不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

·性能消耗:v-if有更高的切换消耗。v-show有更高的初始渲染消耗

·应用场景:v-if适合运行时条件很少改变时使用。v-show适合频繁切换

10.v-for 与 v-if 的优先级

当它们处于同一节点时,v-for优先级高于v-if。
如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,应该替换成computed属性。

<li
  v-for="user in users"
  v-if="user.isActive"
  :key="user.id">
  {{ user.name }}
</li>

如上情况,即使100个user中之需要使用一个数据,也会循环整个数组。

omputed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
      return user.isActive
    })
  }
}
<ul>
  <li
  v-for="user in activeUsers" 
  :key="user.id">
  {{ user.name }}
  </li>
</ul>

11.绑定class的数组用法:

·对象方法:v-bind:class="{ active: isActive, 'text-danger': hasError }"
当isActive和hasError为true时,才会将active和text-danger两个样式添加到class中。

·数组方法:<div v-bind:class="[activeClass, errorClass]"></div>

·绑定内联样式:

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}

12.vue组件间怎么传递数据?

父子间通信:父亲提供数据通过属性 props传给儿子;儿子通过 $on 绑父亲的事件,再通过 $emit 触发自己的事件(发布订阅)
利用父子关系 $parent 、$children
父组件提供数据,子组件注入。 provide 、 inject ,插件用得多。
ref 获取组件实例,调用组件的属性、方法
vuex 状态管理实现通信
祖孙节点可以使用: $attrs/$listeners

方法一

props/$emit

父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。

1.父组件向子组件传值

接下来我们通过一个例子,说明父组件如何向子组件传递值:在子组件Users.vue中如何获取父组件App.vue中的数据

userList:["Henry","Bucky","Emily"]
//App.vue父组件
<template>
  <div id="app">
    <hook-users 
    :userList="userList"/>
    //前者自定义名称便于子组件调用,后者要传递数据名
  </div>
</template>
<script>
import AppUsers from "./Components/AppUsers"
export default {
  name: 'App',
  data(){
    return{
      userList:["Henry","Bucky","Emily"]
    }
  },
  components:{
    "app-users":AppUsers
  }
}
//users子组件
<template>
  <div class="hello">
    <ul>
      //遍历传递过来的值,然后呈现到页面
      <li v-for="user in userList">{{user}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'AppUsers',
  props:{
    userList:{ 
      type:Array,
      required:true
    }
  }
}
</script>

总结:

父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:datapropscomputed

2.子组件向父组件传值(通过事件形式)

// 子组件
<template>
  <header>
    <h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
  </header>
</template>
<script>
export default {
  name: 'AppHeader',
  data() {
    return {
      title:"Vue.js Demo"
    }
  },
  methods:{
    changeTitle() {
      this.$emit("titleChanged","子向父组件传值");
      //自定义事件  传递值“子向父组件传值”
    }
  }
}
</script>

// 父组件
<template>
  <div id="app">
    <app-header v-on:titleChanged="updateTitle" ></app-header>
    //与子组件titleChanged自定义事件保持一致
    // updateTitle($event)接受传递过来的文字
    <h2>{{title}}</h2>
  </div>
</template>
<script>
import Header from "./components/Header"
export default {
  name: 'App',
  data(){
    return{
      title:"传递的是一个值"
    }
  },
  methods:{
    updateTitle(e){   //声明这个函数
      this.title = e;
    }
  },
  components:{
   "app-header":Header,
  }
}
</script>

总结:

子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

方法二、$emit/$on

这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex

1.具体实现方式:

var App=new Vue();
App.$emit(事件名,数据);
App.$on(事件名,data => {});

或者自己实现一个

class MyEventEmitter {
  constructor() {
    this.event = {};
  }
  // 监听
  on(type, listener) {
    if (this.event[type]) {
      this.event[type].push(listener);
    } else {
      this.event[type] = [listener];
    }
  }
  //发送监听
  emit(type, ...rest) {
    if (this.event[type]) {
      this.event[type].map(fn => fn.apply(this, rest));
    }
  }
  //移除监听器
  removeListener(type) {
    if (this.event[type]) {
      delete this.event[type];
      console.log(this.event);
    }
  }
  //移除所有的监听器
  removeAllListener() {
    this.event = {};
  }
}


var MyEvent=new MyEventEmitter();
MyEvent.$emit(事件名,数据);
MyEvent.$on(事件名,data => {});
但是这种方式,记得在每次触发监听的时候,记得移除上一个监听器

方法三、Vuex与localStorage

vuex 是 vue 的状态管理器,存储的数据是响应式的。但是并不会保存起来,刷新之后就回到了初始状态,具体做法应该在vuex里数据改变的时候把数据拷贝一份保存到localStorage里面,刷新之后,如果localStorage里有保存的数据,取出来再替换store里的state。

const jsonToString=(json)=>{
  return JSON.stringify(json)
}

const stringToJson=(keyName)=>{
  //暂不验证数据格式
   return window.localStorage.getItem(keyName)?
   JSON.parse(window.localStorage.getItem(keyName))
   :{};
}
export default new Vuex.Store({
  state: {
    selectCity:stringToJson("selectCity")
  },
  mutations: {
    changeCity(state, selectCity) {
      state.selectCity = selectCity
      try {
        window.localStorage.setItem('selectCity',jsonToString(state.selectCity));
      // 数据改变的时候把数据拷贝一份保存到localStorage里面
      } catch (e) {}
    }
  }
})

方法四、$attrs/$listeners

场景

有些变态需求:比如说A父组件里面导入了B组件,可是B组件里面又导入了C组件,现在需要A父组件传值给C组件,或者是C组件需要传值给父组件,这时候就需要用到$attrs和$listeners了。

$attrs

包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。(父传孙专用)

$listener

包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件——在创建更高层次的组件时非常有用。(孙传父专用)

在父组件当中,最外层组件

<template>
    <div>
        <Child1 
        :child1Info="child1" 
        :child2Info="child2" 
        v-on:test1="onTest1" 
        v-on:test2="onTest2">
        </Child1>
    </div>
</template>
<script>
import Child1 from './child1';
export default {
    data() {
        return {
            child1:"hahha",
            child2:"asdsdasd"
        };
    },
    components: { Child1 },
    methods: {
        onTest1(msg) {
            console.log('test1 running...',msg);
        },
        onTest2(msg) {
            console.log('test2 running',msg);
        }
    }
};
</script>

//在子组件中

<template>
    <div class="child-1">
        <p>在子组件当中:</p>
        <p>props-child1Info: {{child1Info}}</p>
        <p>$attrs: {{$attrs}}</p>
        <hr>
        <!-- Child2组件中能直接触发test的原因在于 B组件调用C组件时 使用 v-on 绑定了$listeners 属性 -->
        <!-- 通过v-bind 绑定$attrs属性,Child2组件可以直接获取到A组件中传递下来的props(除了child1组件中props声明的) -->
        <Child2 v-bind="$attrs" v-on="$listeners"></Child2>
    </div>
</template>
<script>
import Child2 from './child2';
export default {
    props: ['child1Info'],
    data() {
        return {};
    },
    components: { Child2 },
    mounted() {
        this.$emit('test1','嘻嘻');
    }
};
</script>

//在孙子组件当中:

<template>
    <div class="child-2">
        <p>在最里层组件当中child2:</p>
        <p>props-child2Info: {{child2Info}}</p>
        <p> $attrs 的值: {{$attrs}}</p>
        <hr>
    </div>
</template>
<script>
export default {
    props: ['child2Info'],
    data() {
        return {};
    },
    mounted() {
        this.$emit('test2','哈哈');
    }
};
</script>

13.computedwatch 的区别和运用的场景?

<1>Computed:

·computed是计算属性,更多用于计算值的场景
·computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
·computed适用于计算比较消耗性能的计算场景

<2>Watch:

·更多的是观察的作用,类似于某些数据的监听回调,用于观察props、$emit或者本组件的值,当数据变化时来执行回调进行后续操作
·无缓存性,页面重新渲染时值不变化也会执行

<3>用法:

1. 当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
2. 如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化

14.单页面应用和多页面应用区别及优缺点?

单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。

多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新

单页面的优点:

1,用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小

2,前后端分离

3,页面效果会比较炫酷(比如切换页面内容时的专场动画)

单页面缺点:

1,不利于seo

2,导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)

3,初次加载时耗时多

4,页面复杂度提高很多

15.vue-cli工程中文件夹及文件的用处:


·build文件夹:用于存放webpack相关配置和脚本。例如经常需要用到webpack.dev.config.js来配置less、sass、stylus等css预处理器,或者用来配置UI库;
·config文件夹:主要存放配置文件,用于区分开发环境和生产环境的不同。常用到config.js配置开发环境的端口号、是否开启热加载或者设置生产环境的静态资源相对路径、是否开启gzip压缩、npm run build命令打包生成静态资源的名称和路径等。(webpack.*.config.js);
·dist文件夹:存放npm run build打包生成的静态资源文件,用于生产部署;
·node_modules:存放npm install下载的开发环境和生产环境的依赖包;
·src文件夹:存放项目源码及需要引用的资源文件;
·src/assets:存放项目中需要用到的资源文件,js、css、image等;
·src/components:存放vue开发中一些公共组件;
·src/emit:自己配置的vue集中式事件机制管理;
·src/router:vue-router vue路由的配置文件;
·src/service:自己配置的vue请求后台的接口方法;
·src/page:存放vue页面组件;
·src/util:存放公共js方法;
·src/vuex:存放vuex vue的状态管理器;
·src/app.vue:使用标签<router-view></router-view>渲染整个工程的.vue组件;
·src/main.js:vue-cli工程的入口文件;
·src/index.html:设置项目的一些meta头信息和提供<div id="app"></div>用于挂载vue节点;
·package.json:用于管理node_modules依赖包和启动、打包项目的npm命令。

16.config文件夹 下 index.js 的对于工程 开发环境 和 生产环境 的配置:

image.png
·index:配置打包后入口.html文件的名称以及文件夹名称;
·assetsRoot:配置打包后生成的文件名称和路径;
·assetsPublicPath:配置打包后.html引用静态资源的路径,一般要设置成"./";
·productionGzip:是否开启GZip压缩,以提升加载速度
image.png
·port:设置端口号;
·autoOpenBrowser:启动工程时,自动打开浏览器;
·proxyTable:vue设置的代理,以解决跨域问题

17.package.json配置:

image.png
·scripts:配置npm run xxx命令调用node执行的.js文件;
·dependencies:生产环境依赖包的名称和版本号,这些依赖包会被打包进生产环境的JS文件里面;
·devDependencies:开发环境依赖包的名称和版本号,这些依赖包只用于代码开发的时候,不会打包生产环境的JS文件里面。

18、reactvue的区别

相同点

  • 数据驱动页面提供响应式的试图组件
  • 都有virtual DOM,组件化的开发通过props参数进行父子之间组件传递数据都实现了webComponents规范
  • 数据流动单向都支持服务器的渲染SSR
  • 都有支持native的方法reactReact native vuewexx

不同点

  • 数据绑定Vue实现了双向的数据绑定react数据流动是单向的
  • 数据渲染大规模的数据渲染react更快
  • 使用场景React配合Redux架构适合大规模多人协作复杂项目Vue适合小快的项目
  • 开发风格react推荐做法jsx + inline stylehtmlcss都写在js
  • vue是采用webpack +vue-loader单文件组件格式html, js, css同一个文件

19.vue.js的两个核心:

·双向数据绑定,也叫数据驱动,数据的改变会驱动视图的自动更新。传统的做法是需要手动改变DOM来使得视图更新,而vue只需要改变数据。
·组件:组件化开发,优点很多,可以很好的降低数据之间的耦合度。将常用的代码封装成组件之后(vue组件封装方法),就能高度的复用,提高代码的可重用性。一个页面/模块可以由多个组件所组成。

20.Vue性能优化方法

1)编码阶段

  • 尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher;
  • 如果需要使用v-for给每项元素绑定事件时使用事件代理;
  • SPA 页面采用keep-alive缓存组件;
  • 在更多的情况下,使用v-if替代v-show;
  • key保证唯一;
  • 使用路由懒加载、异步组件;
  • 防抖、节流;
  • 第三方模块按需导入;
  • 长列表滚动到可视区域动态加载;
  • 图片懒加载;

2)用户体验:

  • 骨架屏;
  • PWA;
  • 还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。

3)SEO优化

  • 预渲染;
  • 服务端渲染SSR;

4)打包优化

  • 压缩代码;
  • Tree Shaking/Scope Hoisting;
  • 使用cdn加载第三方模块;
  • 多线程打包happypack;
  • splitChunks抽离公共文件;
  • sourceMap优化;

21.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 作为事件。

例子

model: { 
  prop: 'checked', 
  event: 'change' 
}

如果想要更改 checked 这个 prop 可以在 Vueinstance 中用以下这行代码发送 change 这个 event,并将目标的变动值传给 checked 这个 prop

this.$emit('change', $event.target.value);

22.nextTick的实现原理是什么?

在下次 DOM 更新循环结束之后执行延迟回调。nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用

  • Promise
  • MutationObserver
  • setImmediate
  • 如果以上都不行则采用setTimeout

定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。

23.vue的常用指令:

·v-if:根据表达式的布尔值对元素进行渲染。在切换时,元素及它的数据绑定/组件被销毁或重建;
·v-show:根据表达式的布尔值,切换元素的display CSS属性值;
·v-for:循环指令,基于一个数组或对象渲染一个列表。vue2.0以上必须配合key值使用;
·v-bind:动态的绑定一个或多个特性,或一个组件prop到表达式;
·v-on:用于监听指定元素的DOM事件,比如点击事件。绑定事件监听器;
·v-model:实现表单输入和应用状态之间的双向绑定;
·v-pre:跳过这个元素和它的子元素编译的过程。可以用来显示原始Mustache标签。跳过大量没有指令的节点会加快编译;
·v-once:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点都被视为静态内容并跳过。这可以优化更新的性能。

24.如何理解自定义指令?

核心答案:

指令的实现原理,可以从编译原理 =>代码生成=> 指令钩子实现进行概述

1)在生成 ast 语法树时,遇到指令会给当前元素添加directives属性

2)通过 genDirectives 生成指令代码

3)在patch前将指令的钩子提取到 cbs中,在patch过程中调用对应的钩子。

4)当执行指令对应钩子函数时,调用对应指令定义的方法

25.vue常用的修饰符:

v-on 指令常用修饰符:

.stop - 调用 event.stopPropagation(),禁止事件冒泡。
.prevent - 调用 event.preventDefault(),阻止事件默认行为。
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.once - 只触发一次回调。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器
v-bind 指令常用修饰符:

.prop - 作为一个 DOM property 绑定而不是作为 attribute 绑定。
.camel - (2.1.0+) 将 kebab-case 特性名转换为 camelCase.
.sync (2.3.0+) 语法糖,会扩展成一个更新父组件绑定值的 v-on 侦听器。
v-model 指令常用修饰符:

.lazy - 取代 input 监听 change 事件
.number - 输入字符串转为数字
.trim - 输入首尾空格过滤

26.你的接口请求一般放在哪个生命周期中?

接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中。

  1. 如果不需要操作Dom元素或子组件, 那放到created或者mounted中都可以, 如果需要操作Dom元素, 则需要放到mounted中. 并且, 需要注意的是, 即便是放到created, Vue不会等待异步返回的数据再去执行下一步. 从体验的角度考虑, 放在mounted中更好.

27.vue中key值的作用:

key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。

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

28.vue事件中使用event对象:

<a href="#" data-id="12" @click="showEvent($event)">event click</a>

showEvent(event){
    console.log(event.target.dataset.id)
    event.preventDefault();
    event.stopPropagation();
}

29.Vue SSR渲染原理?

优点:

更利于SEO

不同爬虫工作原理类似,只会爬取源码,不会执行网站的任何脚本(Google除外,据说Googlebot可以运行javaScript)。使用了Vue或者其它MVVM框架之后,页面大多数DOM元素都是在客户端根据js动态生成,可供爬虫抓取分析的内容大大减少。另外,浏览器爬虫不会等待我们的数据完成之后再去抓取我们的页面数据。服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息。

更利于首屏渲染

首屏的渲染是node发送过来的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。

场景:

交互少,数据多,例如新闻,博客,论坛类等

原理:

相当于服务端前面加了一层url分配,可以假想为服务端的中间层,

当地址栏url改变或者直接刷新,其实直接从服务器返回内容,是一个包含内容部分的html模板,是服务端渲染

而在交互过程中则是ajax处理操作,局部刷新,首先是在history模式下,通过history. pushState方式进而url改变,然后请求后台数据服务,拿到真正的数据,做到局部刷新,这时候接收的是数据而不是模板

缺点:

服务端压力较大

本来是通过客户端完成渲染,现在统一到服务端node服务去做。尤其是高并发访问的情况,会大量占用服务端CPU资源;

开发条件受限

在服务端渲染中,created和beforeCreate之外的生命周期钩子不可用,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制;

安全问题

因为做了node服务,因此安全方面也需要考虑DDOS攻击和sql注入

30.new Vue() 发生了什么?

1)new Vue()是创建Vue实例,它内部执行了根实例的初始化过程。

2)具体包括以下操作:

  • 选项合并
  • $children$refs$slots$createElement等实例属性的方法初始化
  • 自定义事件处理
  • 数据响应式处理
  • 生命周期钩子调用 (beforecreate created
  • 可能的挂载

总结:

new Vue()创建了根实例并准备好数据和方法,未来执行挂载时,此过程还会递归的应用于它的子组件上,最终形成一个有紧密关系的组件实例树。

31.Vue.use是干什么的?原理是什么?

vue.use 是用来使用插件的,我们可以在插件中扩展全局组件、指令、原型方法等。

1、检查插件是否注册,若已注册,则直接跳出;

2、处理入参,将第一个参数之后的参数归集,并在首部塞入 this 上下文;

3、执行注册方法,调用定义好的 install 方法,传入处理的参数,若没有 install 方法并且插件本身为 function 则直接进行注册;

1) 插件不能重复的加载

install 方法的第一个参数是vue的构造函数,其他参数是Vue.set中除了第一个参数的其他参数; 代码:args.unshift(this)

2) 调用插件的install 方法 代码:

typeof plugin.install === 'function'

3) 插件本身是一个函数,直接让函数执行。 代码:

plugin.apply(null, args)

4) 缓存插件。 代码:

installedPlugins.push(plugin)

32.Vue.set 方法是如何实现的?

核心答案

为什么$set可以触发更新,我们给对象和数组本身都增加了dep属性,当给对象新增不存在的属性则触发对象依赖的watcher去更新,当修改数组索引时我们调用数组本身的splice方法去更新数组。

补充答案

1) 如果是数组,调用重写的splice方法 (这样可以更新视图 )
代码:

target.splice(key, 1, val)

2) 如果不是响应式的也不需要将其定义成响应式属性。
3) 如果是对象,将属性定义成响应式的

defineReactive(ob.value, key, val)

33.Vue中模板编译原理?

核心答案

template转换成render函数

补充说明

这里要注意的是我们在开发时尽量不要使用template.

因为将template转化成render方法需要在运行时进行编译操作会有性能损耗,同时引用带有complier包的vue体积也会变大.

默认.vue文件中的 template处理是通过vue-loader 来进行处理的并不是通过运行时的编译。

流程如下

1) 将 template 模板转换成 ast 语法树 - parserHTML

2) 对静态语法做静态标记 - markUp

3) 重新生成代码 - codeGen

34.Vue Loader 的作用?

Vue Loader 是一个 webpack 的 loader,它允许你以一种名为单文件组件 (SFCs)的格式撰写 Vue 组件

作用:解析和转换 .vue 文件,提取出其中的逻辑代码 script、样式代码 style、以及 HTML 模版 template,再分别把它们交给对应的 Loader 去处理。

35.组件中写 name选项有哪些好处及作用?

1) 可以通过名字找到对应的组件 ( 递归组件 )
2) 可以通过name属性实现缓存功能 (keep-alive)
3) 可以通过name来识别组件 (跨级组件通信时非常重要)

36.keep-alive平时在哪里使用?原理是?

keep-alive 主要是组件缓存,采用的是LRU算法。最近最久未使用法。

常用的两个属性include/exclude,允许组件有条件的进行缓存。

两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态。

37.Vue.mixin的使用场景和原理?

核心答案:

Vue.mixin的作用就是抽离公共的业务逻辑,原理类似“对象的继承”,当组件初始化时会调用 mergeOptions方法进行合并,采用策略模式针对不同的属性进行合并,如果混入的数据和本身组件中的数据冲突,会采用“就近原则”以组件的数据为准。

补充回答:

mixin中有很多缺陷“命名冲突问题”,“依赖问题”,“数据来源问题”,这里强调一下mixin的数据是不会被共享的。

注意事项

如果只是提取公用的数据或者通用的方法,并且这些数据或者方法,不需要组件间进行维护,就可以使用mixins。(类似于js中封装的一些公用的方法)

38.mixinsvuex的区别?

vuex公共状态管理,在一个组件被引入后,如果该组件改变了vuex里面的数据状态,其他引入vuex数据的组件也会对应修改,所有的vue组件应用的都是同一份vuex数据。(在js中,有点类似于浅拷贝)

vue引入mixins数据,mixins数据或方法,在每一个组件中都是独立的,互不干扰的,都属于vue组件自身。(在js中,有点类似于深度拷贝)

39.mixins和公共组件的区别?

通用的数据和方法,确实可以提出一个通用的组件,由父子组件传参的形式进行分享公用。

公共组件

子组件通过props接收来自父组件(公共组件)的参数或者方法,但vue不建议,子组件直接修改props接收到的父组件的数据。需要在子组件的data中或者computed中定义一个字段来接收。(有点麻烦)
公共组件最主要的作用还是复用相同的vue组件(有视图,有方法,有状态
)。

mixins

如果只是提取公用的数据或者通用的方法,并且这些数据或者方法,不需要组件间进行维护,就可以使用mixins。(类似于js中封装的一些公用的方法)

40.$nextTick():

·作用:将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法Vue.nextTick一样,不同的是回调的this自动绑定到调用它的实例上。

Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})

41.Vue 组件中 data 为什么必须是函数?

<script>
    export default {
        data(){
        // 返回一个唯一的对象,不要和其他组件共用一个对象进行返回
            return{
                menu: MENU.data,
                poi:LIST.data
            }
        }
    }
</script>
Vue.component('my-component', {
  template: '<div>OK</div>',
  data() {
    return {} // 返回一个唯一的对象,不要和其他组件共用一个对象进行返回
  },
})

这个操作是一个简易操作,实际上,它

  • 首先需要创建一个组件构造器,
  • 然后注册组件。
  • 注册组件的本质其实就是建立一个组件构造器的引用。
  • 使用组件才是真正创建一个组件实例。
  • 所以,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式。

类比引用数据类型
Object是引用数据类型,如果不用function 返回,每个组件的data 都是内存的同一个地址,一个数据改变了其他也改变了;

javascipt只有函数构成作用域(注意理解作用域,只有函数的{}构成作用域,对象的{}以及 if(){}都不构成作用域),data是一个函数时,每个组件实例都有自己的作用域,每个实例相互独立,不会相互影响

const MyComponent = function() {};
MyComponent.prototype.data = {
    a: 1,
    b: 2,
}
const component1 = new MyComponent();
const component2 = new MyComponent();

component1.data.a === component2.data.a; // true;
component1.data.b = 5;
component2.data.b // 5

如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改;

两个实例应该有自己各自的域才对,需要通过下面的方法来进行处理

const MyComponent = function() {
    this.data = this.data();
};
MyComponent.prototype.data = function() {
    return {
        a: 1,
        b: 2,
    }
};

42.vue中 keep-alive 组件的作用:

·作用:主要用于保留组件状态或避免重新渲染;

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

·属性

  • include:字符串或正则表达式,只有匹配的组件会被缓存
  • exclude:字符串或正则表达式,任何匹配的组件都不会被缓存
    ·用法
    包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和<transition>相似,<keep-alive>是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。

    <!-- 基本 -->
    <keep-alive>
      <component :is="view"></component>
    </keep-alive>
    
    <!-- 多个条件判断的子组件 -->
    <keep-alive>
      <comp-a v-if="a > 1"></comp-a>
      <comp-b v-else></comp-b>
    </keep-alive>
    
    <!-- 和 `<transition>` 一起使用 -->
    <transition>
      <keep-alive>
    <component :is="view"></component>
      </keep-alive>
    </transition>

    注意:`<keep-alive>是用在其一个直属的子组件被开关的情形。如果你在其中有v-for则不会工作。如果有上述的多个条件性的子元素<keep-alive>要求同时只有一个`子元素被渲染。

·include 和 exclude 属性的使用

<!-- 逗号分隔字符串 -->
<keep-alive :include="a,b">
    <component :is="view"></component>
</keep-alive>

<!-- 正则表达式 (使用 `v-bind`) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive>

<!-- 数组 (使用 `v-bind`) -->
<keep-alive :include="['a', 'b']">
  <component :is="view"></component>
</keep-alive>

43.vue监听键盘事件中的按键

可以为v-on监听键盘事件的时候添加按键修饰符

//基本用法,直接使用keycode,但是难以记住
<input @keyup.13="submit"/>

常规按键修饰符:.enter、.tab、.delete、.esc、.space、.up、.down、.left、.right

//使用按键别名
<input @keyup.enter="submit" />

新增修饰符:.ctrl、.shift、.alt、.meta
仅在按下相应按键时才触发鼠标键盘事件的监听器。
请注意修饰键与常规按键不同,在和 keyup 事件一起用时,事件触发时修饰键必须处于按下状态。换句话说,只有在按住 ctrl 的情况下释放其它按键,才能触发 keyup.ctrl。而单单释放 ctrl 也不会触发事件。

//使用按键修饰符
//ALT+C
<input @keyup.alt.67="clear"/>

//Ctrl + Click
<div @click.ctrl="doSomething">doSomething</div>

.exact:修饰符允许你控制由精确的系统修饰符组合触发的事件。

<!-- 即使 Alt 或 Shift 和ctrl 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

自定义按键修饰符别名:config.keyCodes

// 可以使用 `v-on:keyup.f1`
Vue.config.keyCodes.f1 = 112

44.Vue.js 如何让CSS只在当前组件中起作⽤?

将当前组件的 <style> 修改为 <style scoped>

45.那为什么new Vue里data可以是一个对象?

new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: {App}
})
原因:因为JS里的对象是引用关系,而且new Vue是不会被复用的,所以不存在引用对象的问题。

46.Vue 中的diff原理?

核心答案:

vue的diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式 + 双指针的方式进行比较。

补充回答:

1) 先比较是否是相同节点

2) 相同节点比较属性,并复用老节点

3) 比较儿子节点,考虑老节点和新节点儿子的情况

4) 优化比较:头头、尾尾、头尾、尾头

5) 比对查找进行复用

Vue2 与 Vue3.x 的diff算法:

Vue2的核心Diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。

Vue3.x借鉴了ivi算法和 inferno算法,该算法中还运用了动态规划的思想求解最长递归子序列。(实际的实现可以结合Vue3.x源码看。)

47.vue更新数组时触发视图更新的方法:

·push()、pop()、sort()、reverse()、shift()、unshift()、splice()

·替换数组的方法:用新数组去覆盖旧的数组去触发视图更新
filter()、concat()、slice()这些方法都会返回一个新数组

items = items.filter((item)=>{
    return   item.message > 0;
})
//不会重新渲染整个数组,而是重新渲染修改的数组项(新增或删除的等等)

·不会触发视图更新的方法:

当你利用索引直接设置一个项时,vm.items[indexOfItem] = newValue
当你修改数组的长度时,vm.items.length = newLength
替换办法:
// 一下两种都可以解决第一个问题
Vue.set(vm.items, indexOfItem, newValue)
vm.items.splice(indexOfItem, 1, newValue)
//第二个修改数组长度问题
vm.items.splice(newLength)

48.vue中对象更改检测的注意事项

·Vue不能检测对象属性的添加删除,无法响应式的重新渲染视图。但是原属性的修改可以响应。

//解决办法:Vue.set(object, key, value)
var vm = new Vue({
  data: {
    user: {
      name: 'Anika'
    }
  }
})
Vue.set(user,'age',20);
//也可以使用 `vm.$set`实例方法,它只是全局`Vue.set`的别名
//vm是当前实例,一般组件中用this即可
//vm.$set(user,'age',20);
//添加多个属性可以利用Object.assign()方法覆盖原对象
vm.user = Object.assign({},vm.user,{
    age: 20,
    favor: 'apple'
})

49.过滤器

·调用过滤器

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

·创建过滤器

//在一个组件的选项中定义本地的过滤器
filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}


//在创建 Vue 实例之前全局定义过滤器
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
  // ...
})

·项目中常用的自定义文件创建方法

//src/filter/filter.js
function filterOne(n){
    return n + 10;
}
function filterTwo(n){
    return n + 5;
}
export{
    filterOne,
    filterTwo
}

//main.js
import * as filters from './filter/filter.js'
//遍历所有导出的过滤器并添加到全局过滤器
Object.keys(filters).forEach((key) => {
  Vue.filter(key, filters[key]);
})

50.组件以及JS中使用全局常量

·组件中使用全局常量:

//src/const/const.js
export default{
    install (Vue,options){
        vue.prototype.global = {
            title: '全局',
            isBack: true,
            isAdd: false
        };
    }
}
//main.js中全局引入
import constant from './const/const.js'
vue.user(constant);
//组件中调用
this.global.title
{{ gloabal.title }}

·JS中使用常量

//src/const/type.js
export const TEST_ADD = "TEST_ADD"
export const TEST_DEC = "TEST_DEC"
//js文件中引入
import * as types from '../types'
types.TEST_ADD

51.计算属性的缓存和方法调用的区别:

得到的结果是一样的
·计算属性是基于它们的依赖进行缓存的,只在相关依赖发生改变的时候才会重新求值。而每当触发重新渲染时,调用方法总会再次执行函数。
·使用计算属性还是methods取决于是否需要缓存,当遍历数组和做大量计算时,应当使用计算属性将结果缓存下来。
·计算属性是根据依赖自动执行的,methods需要事件调用。

52.解决跨域问题:

·vue-cli项目中通过node.js代理服务器来实现跨域请求

//在config/index.js文件里配置dev对象的proxyTable对象
//仅适用于开发环境,生产环境没解决
dev:{
    env: require('./dev.env'),
    port: 8080,
    autoOpenBrowser: false,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable:{
      '/api': {   //请求服务器接口文件夹名称
      target: 'http://www.abc.com',  //目标接口域名
      changeOrigin: true,  //是否跨域
      pathRewrite: {
        '^/api': '/api'   //重写接口
      }
    },
    cssSourceMap: false
}

//'http://localhost:8080/api' ===> 'http://www.abc.com/api'

·在服务器响应客户端的时候,带上Access-Control-Allow-Origin:* 头信息 (推荐)
·通过jQuery的jsonp形式解决跨域问题

53.Vuehash模式和history模式的区别?

最明显的是在显示上,hash模式的URL中会夹杂着#号,而history没有。

Vue底层对它们的实现方式不同。hash模式是依靠onhashchange事件(监听location.hash的改变),而history模式是主要是依靠的HTML5 history中新增的两个方法,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改。

当真正需要通过URL向后端发送HTTP请求的时候,比如常见的用户手动输入URL后回车,或者是刷新(重启)浏览器,这时候history模式需要后端的支持。

因为history模式下,前端的URL必须和实际向后端发送请求的URL一致,例如有一个URL是带有路径path的(例如www.lindaidai.wang/blogs/id),如果后端没有对这个路径做处理的话,就会返回404错误。所以需要后端增加一个覆盖所有情况的候选资源,一般会配合前端给出的一个404页面。

hash:

window.onhashchange = function(event){
  // location.hash获取到的是包括#号的,如"#heading-3"
  // 所以可以截取一下
  let hash = location.hash.slice(1);
}

核心答案:

vue-router 有 3 种路由模式:hashhistoryabstract

1) hash模式:hash + hashChange

特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。通过监听 hash(#)的变化来执行js代码 从而实现 页面的改变。

核心代码:

window.addEventListener(‘hashchange‘,function(){
    self.urlChange()
})

2) history模式:historyApi + popState

HTML5推出的history API,由pushState()记录操作历史,监听popstate事件来监听到状态变更;
因为 只要刷新 这个url(www.ff.ff/jjkj/fdfd/fdf/fd)就会请求服务器,然而服务器上根本没有这个资源,所以就会报404,解决方案就 配置一下服务器端。

说明:

1)hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;

2)history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;

3)abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

54.vue-router响应 路由参数 的变化:用watch监听

//例如/user?id=1到/user?id=2
watch:{
    '$route'(to,from){
        //if(to.path !== from.path)
        //if(to.params.id !== from.params.id)
        if(to.query.id !== from.query.id){
            this.id = to.query.id;
            this.init(); //重新加载数据,自定义函数
        }
    }
}

//使用beforRouteUpdate() vue-router的内置钩子函数
beforeRouteUpdate(to,from,next){
    if(to.fullPath !== from.fullPath){
        next();
        this.changeUser();
    }
},
methods:{
    changeUser:()=>{
        getUserInfo({userId:this.$route.params.id}).then(res => {
            if(res.success){
                this.userInfo = res.data;
            }else{
                ....
            }
        })
    }
}

//使用v-bind:key,阻止组件复用,只要路由变化就重新创建这个组件
<router-view :key="$route.fullPath"></router-view>

55.vue-router的导航守卫(导航钩子):导航守卫主要用于路由正在发生改变时,通过跳转或者取消的方式守卫导航。

参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用beforeRouteUpdate的组件内守卫

·router.beforeEach(to,from,next)(全局守卫):
·router.beforeResolve(to,from,next)(全局解析守卫):
·router.afterEach(to,from)(全局后置守卫):
·beforeEnter(to,from,next)(路由独享的守卫):
·beforeRouteEnter(to,from,next)、beforeRouteUpdate(to,from,next)、 beforeRouteLeave(to,from,next)(组件内的守卫):

注意确保要调用next方法,否则钩子就不会被 resolved

56.参数传递的方式:params、query、meta三种

// 字符串,不带参数
this.$router.push('home')

// 对象,不带参数
this.$router.push({ path: 'home' })

// params(推荐):命名的路由,params 必须和 name 搭配使用
this.$router.push({ name:'user',params: { userId: 123 }})

// 这里的 params 不生效
this.$router.push({ path:'/user',params: { userId: 123 }})

// query:带查询参数,变成 /register?plan=private
this.$router.push({ path: 'register', query: { plan: 'private' }})

//meta方式:路由元信息
export default new Router({
    routes: [
        {
            path: '/user',
            name: 'user',
            component: user,
            meta:{
                title:'个人中心'
            }
        }
    ]
})
//通过 $route 对象获取,注意是route
this.$route.params.userId

this.$route.query.userId

this.$route.meta.title

57.vue-router的几种实例方法以及参数传递:

vue-router的实例方法

  1. this.$router.push(location, onComplete?, onAbort?)
    这个方法向history添加一个新的记录,所以当用户点击浏览器后退按钮时,则回退到之 前的URL;点击<router-link>相当于调用this.$router.push()
  2. this.$router.replace(location, onComplete?, onAbort?)
    这个方法不会向history添加一个新的记录,而是替换掉当前的history记录,所以点击后退按钮时不会回退
  3. this.$router.go(n)
    参数是一个整数,作用是在history记录中向前或者向后后退几步
  • onComplete以及onAbort回调会在导航成功完成(在所有的异步钩子被解析之后)或者导航终止(导航的相同的路由、或在当前导航完成之前导航到另一个路由)的时候进行调用;
  • 如果目标路由和当前路由只是参数不同(/user/1 -> /user/2),则需要使用组件内的导航守卫beforeRouteUpdate来响应这个变化(抓取渲染页面数据信息)

58.$router对象有哪些属性?

  • $router.app: 配置了router的Vue根实例。
  • $router.mode: 路由使用的模式。
  • $router.currentRoute: 当前路由对应的路由信息对象。

59.$route对象有哪些属性?

  • $route.path:
    返回字符串,对应当前路由的路径,总是解析为绝对路径。
  • $route.params:
    返回一个key-value对象,包含了动态片段和全匹配片段,如果没有路由参数,就是一个空对象。
  • $route.query:
    返回一个key-value对象,表示URL查询参数。
  • $route.hash:
    返回当前路由的带#的hash值,如果没有hash值,则为空字符串。
  • $route.fullPath:
    返回完成解析后的URL,包含查询参数和hash的完整路径。
  • $route.matched:
    返回一个数组,包含当前路由的所有嵌套路径片段的路由记录,路由记录就是routes配置数组中的对象副本。
  • $route.name:
    如果存在当前路由名称则返回当前路由的名称。
  • $route.redirectedFrom:
    如果存在重定向,即为重定向来源的路由的名字。

60.$route和$router的区别:

$route是“路由信息对象”,包含path、fullPath、params、query、name、hash、matched等路由信息参数;
$router是“路由实例对象”,包括了路由的跳转方法和钩子函数等。

61. 动态路由的匹配及使用:

const router = new Router({
    routes:[
        {
            path: '/user/:id',  //匹配/user/bar、/user/foo等等
            component: User
        }
    ]
})

//template中获取
this.$route.params.id

62.嵌套路由:

const router = new Router({
    routes:[{
        path: '/user/:id',
        component: User,
        children: [
        //不设置这个子路由将无法匹配/user/:id
        {
            path:'',  //匹配/user/:id,渲染UserHome组件
            component: UserHome
        },
        {
            path: 'profile',  //匹配/user/:id/profile
            component: UserProfile
        }
        ]
    }]
})

63.动态加载路由组件(懒加载)

·把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件
·结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

//第一步,在/src/router/index.js里面异步引入组件.vue
const index = () => import('../page/list/index.vue')

//第二步,配置路由
const router = new Router({
    routes: [
        {
            path: '/index',
            component: index,
            name: 'index'
        }
    ]
})

//第三步,配置webpack.base.config.js的output属性,添加chunkFileName
output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    //新增chunFilename属性
    chunkFilename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath
  },

64.Vuex概念:

·vuex是一个专门为vue.js应用程序开发的状态管理器。它采用 集中式存储 管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
·vuex就是专门解决页面与页面之间需要的共享变量的创建、维护和变更的问题
·有了vue-router,可以将一个组件视为一个页面来使用。由于组件只维护自身的状态(data),组件创建时或者说进入路由时它们被初始化,切换至其他的组件页时当前页自然要被销毁,从而导致data也随之销毁。
页面与页面之间总会产生各种需要的共享变量,如果通过$router.param或者$router.meta来传递是远远不够的,所以就需要vuex来实现这个需求。

65.vuex的核心概念:

·每一个vuex应用的核心就是store(仓库)。store基本上就是一个容器,它包含着你的应用中的大部分状态state。

·vuex和单纯的全局对象的不同:

  • Vuex的状态存储是响应式的。当Vue组件从store中读取状态的时候,如果store中的状态发生变化,那么相应的组件也会得到高效的更新;
  • 你不能直接改变store中的状态。改变store中的状态的唯一途径就是显示的提交(commit)mutation。这样可以使我们方便的追踪每一个状态的变化。
  • state:Vuex store实例的根状态对象。用于定义共享的状态变量。
  • Action:动作,向store发出调用通知,执行本地或者远端的某一操作(可以理解为store的methods)。
  • Mutations:修改器,它只用于修改state中定义的状态变量。
  • getter:读取器,外部程序通过它获取状态变量的具体值,或者在取值前做一些计算(可以认为是store的计算属性)。

66.VueXactionsmutations的区别?

actions

1、用于通过提交mutation改变数据

2、会默认将自身封装为一个Promise

3、可以包含任意的异步操作

mutations

1、通过提交commit改变数据

2、只是一个单纯的函数

3、不要使用异步操作,异步操作会导致变量不能追踪

67.如何在vuex中使用异步修改?

在调用vuex中的方法action的时候,用promise实现异步修改

const actions = {
    asyncLogin({ commit }, n){
        return new Promise(resolve => {
            setTimeout(() => {
                commit(types.UserLogin, n);
                resolve();
            },3000)
        })
    }
}

68.Vuex在vue-cli中的应用:

·下载Vuex依赖包:npm install vuex --save
·在src/main.js中引入:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

·创建src/store/store.js:导出Vuex.Store实例

import Vue from 'vue'
import Vuex from 'Vuex'
//index.js存放action、mutations、getter  也可以把他们拆分成单独文件再引入
import index from './index.js' 

Vue.use(Vuex)
export default new Vuex.Store({
    index
})

·创建src/store/index.js:存放action、mutations、getter

import * as types from './types'

const state = {
    //vuex初始化值
    count: 0
}
const actions = {
    increment({ commit },n){
        commit(types.TEST_INCREMENT,n)
    },
    decrement({ commit },state){
        commit(types.TEST_DECREMENT,n)
    }
}
const getters = {
    count: state => state.count
}
const mutations = {
    [types.TEST_INCREMENT](state, n){
        console.log(n)
        state.count = state.count + n + 5
    },
    [types.TEST_DECREMENT](state, status){
        state.count = state.count - 3
    }
}

export default {
    state,
    actions,
    getters,
    mutations
}

·创建 src/store/types.js:存放常量

export const TEST_INCREMENT = 'TEST_INCREMENT'
export const TEST_DECREMENT = 'TEST_DECREMENT'

69.Vuex的5个核心属性是什么?

分别是 state、getters、mutations、actions、modules 。

70.在Vuex中使用mutationvuex要注意什么?

mutation 必须是同步函数

71.Vuex中action和mutation有什么相同点?

第二参数都可以接收外部提交时传来的参数。

this.$store.dispatch('ACTION_NAME',data)
和
this.$store.commit('SET_NUMBER',10)

在组件中多次提交同一个action,怎么写使用更方便。

72.组件中使用vuex的值和修改值

·直接获取、修改

//获取:state\getter
this.$store.state.count
this.$store.getters.count
//修改
this.$store.actions('increment')
this.$store.actions('increment',{value:123})

·通过辅助函数获取、修改:mapState、mapGetters、mapActions
也就是将vuex的变量或者方法映射到vue组件this指针上,通过map获取到的变量或者方法,可通过 this 直接使用或者调用。

computed:{
    mapState([
        'count'
    ])
}

computed:{
    mapGetters([
        'count'
    ])
}

methods:{
    mapActions({
        increment:'increment',
        decrement:'decrement'
    })
}

73.vuex中使用异步修改:

·在调用Vuex的Action中的方法时,用promise实现异步修改
const actions = {

asyncIncrement({ commit }, n){
    return new Promise(resolve => {
        setTimeout(()=>{
            commit(types.TEST_INCREMENT,n);
            resolve();
        },3000)
    })
}

}

74.axios的基本写法及安装

·npm install axios --save

//post请求
axios.post('/user',{name: 'abc'}).then( res => {
    console.log(res.updateName);
});

axios({
    method: 'POST',
    url: '/user',
    data:{
        name: 'abc'
    }
});

//get请求
axios.get('/user?ID=1234').then( res =>{
   console.log(res.data); 
});

axios('/user/1234').then( res =>{
    consoe.log(res.status);
})

75.axios的特点:

  • Axios是一个基于Promise的HTTP库,支持Promise的所有API
  • 它可以拦截请求和响应
  • 它可以转换请求数据和响应数据,并对响应回来的内容自动转换为JSON类型的数据
  • 安全性更高,客户端支持防御CSRF跨域请求伪造

rirmk
178 声望18 粉丝

目标资深web前端工程师!!