欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【前端知识体系-JS相关】深入理解MVVM和VUE

程序员文章站 2022-05-22 19:07:04
Vue 中三要素的是什么?Vue中如何解析模板?Vue中如何实现响应式的呢?说一下Vue的整体实现流程…… ......

1. v-bind和v-model的区别?

  1. v-bind用来绑定数据和属性以及表达式,缩写为':'
  2. v-model使用在表单中,实现双向数据绑定的,在表单元素外使用不起作用

2. vue 中三要素的是什么?

2.1 响应式

    // 如何让实现响应式的呢?
    let obj = {};
    let name = 'zhangsan';
    object.defineproperties(obj, name, {get : function() {
      console.log('name' , name)
    }, set : function() {
        console.log('name' , name)
    }})
    
    
    // 1. 关键是理解object.defineproperty
    // 2. 将data的属性代理到vm上面的
    let mv = {};
        let data = {
            price: 100,
            name: 'zhangsan'
        };
        for (let key in data) {
            (function (key) {
                object.defineproperty(mv, key, {
                    get: function () {
                        console.log('get val');
                        return data[key];
                    },
                    set: function (val) {
                        console.log('set val');
                        data[key] = val;
                    }
                })
            })(key);
        }

2.2 vue中如何解析模板?

2.2.1 模板是什么?

    <div id="app">
        <div>
            <input v-model="title">
            <button v-on:click="add">submit</button>
        </div>
        <ul>
            <li v-for="item in list">{{item}}</li>
        </ul>
    </div>
 // 1(*****). 模板实际上就是一个字符串………………(vue中的模板的本质)
 // 2. 模板有逻辑,如v-if, v-for
 // 3. 与html格式很像,但是有很大的区别
 // 4. 最终还是要转换为html来显示
 // 5(*****). 模板最终必须转换成js代码,因为:
 //   (1)有逻辑(v-if v-for):必须用js才能实现(图灵完备)
 //    (2) 转换成html来渲染页面,必须用js才能实现
 //    (3) 因此,模板最终要转换成为一个js函数(render函数)

2.3 render函数?

2.3.1 with的用法
var obj = {
    name: 'zhangsan',
    age: 20,
    getaddress(){
        alert('shanghai');
    }
}

// 不使用with
function fn() {
  alert(obj.name);
  alert(obj.age);
  obj.getaddress();
}

// 使用with(代码不易维护!!!)
function fn1() {
  with(obj){
      alert(name);
      alert(age);
      getaddress();
  }
}

fn();
fn1();
2.3.2 render函数的实现机制?
<div id='app'>
    <p>{{price}}</p>
</div>

// 使用with限制这个作用域里面的this
with(this) {
    return _c(                          // this._c
        'div',
        {
            attrs: {"id" : "app"}       // id=app
        },
        [
            _c('p', [_v(_s(price))])    // this._c('p', [_v(_s(price))])
        ]
    )
}


// 实现一个自己的render函数
var vm = new vue({
        el: '#app',
        data: {
            price: 100
        }
    });

    function render() {
        with (vm) {
            return _c(
                'div',
                {
                    attrs: {'id': 'app'}
                },
                [
                    _c('p', [_v(_s(price))])
                ]
            );
        }
    }

    function render() {
        return vm._c(
            'div',
            {
                attrs: {'id': 'app'}
            },
            [
                // vm._v 转换为一个文本节点
                // vm._s 转换为一个字符串
                // vm._c 转换为一个dom节点
                vm._c('p', [vm._v(vm._s(price))])
            ]

        );
    }

2.3.3 render函数与vdom?

<div id="app">
        <div>
            <input type="text" v-model="title">
            <button @click="add">submit</button>
        </div>
        <div>
            <ul>
                <li v-for="item in list">{{item}}</li>
            </ul>
        </div>
    </div>
with (this) {
    // this 就是vm
    return _c(
        'div',
        {attrs: {"id": "app"}},
        [
            _c('div',
                [
                    _c('input', {
                        directives: [{
                            name: "model",
                            rawname: "v-model",
                            value: (title),
                            expression: "title"
                        }],
                        attrs: {"type": "text"},
                        domprops: {"value": (title)},
                        on: {
                            "input": function ($event) {
                                if ($event.target.composing) return;
                                title = $event.target.value
                            }
                        }
                    }),
                    _v(" "),
                    _c('button',
                        {
                            on: {
                                "click": add
                            }
                        },
                        [_v("submit")]
                    )
                ]
            ),
            _v(" "),
            _c('div',
                [
                    _c(
                        'ul',
                        // 这里返回的是一个数组(li标签组成的数组)
                        _l((list), function (item) {
                            return _c('li', [_v(_s(item))])
                        }), 0
                    )
                ]
            )
        ]
    )
}

// view --->  data ---> 使用input的事件绑定 ---> 更新页面数据到data
// data --->  view ---> defineproperty --->  同步数据到页面

2.3.4 vm._c是什么,render函数返回了什么?

  1. vdom: 使用js模拟dom结构
  2. snabbdom: h函数和patch函数
    vue中的v_c:就是相当于snabbdom函数的h函数
    patch函数:
    vm._update(vnode) {
        const prevnode = vm._vnode;
        vm._vnode = vnode;
        if (!prevnode) {
            // 首次渲染的时候
            vm.$el = vm.__patch__(vm.$el, vnode);
        }
        else{
            vm.$el = vm.__patch__(prevnode, vnode);
        }
    }
    
    // 开始更新vue组件(修改data的属性的时候,object.defineproperty)
    function updatecomponent() {
      vm._update(vm._render());
    }

******(问题总结)

    1. vue模板:字符串,有逻辑,嵌入js变量……
    1. 模板必须转换为js代码(有逻辑的,渲染html,js变量)
    1. render函数是什么样子的
    1. render函数的执行结果是返回的vnode
    1. updatecomponent

3. vue的整个实现流程源码解读???(总结点)

3.1 解析模板成render函数

    <template></template> --->>> render 函数

[!note]

  • with函数的使用
  • 模板中的所有信息都被render函数包含
  • 模板中用到的data中的属性,都变成了js变量
  • 模板中的v-model v-for v-on都变成了js逻辑
  • render函数返回vnode

3.2 响应式开始监听数据

  • object.defineproperty
  • 将data的属性代理到vm上
    with(vm) {
        
    }

3.3 首次渲染,显示页面,且绑定依赖

[!note]

  • 初次渲染,执行updatecomponent, 执行vm._render()
  • 执行render函数,会访问到vm.list和vm.title属性
  • 会被响应式的get方法监听到(object.defineproperty)
    object.defineproperty(mv, key, {
        get: function() {
          return data[key];
        }
    })
  • 执行updatecomponent, 会执行vdom的patch方法
  • patch将vnode渲染成dom,初次渲染完成

3.4 为何要监听get, 直接监听set不行吗?

[!note]

  • data中有很多属性,有些会被用到,有些可能不被用到
  • 被用到的会走到get, 不被用到的不会走到get
  • 未走到get中的属性,set的时候我们也无需关心
  • 避免不必要的重复渲染(关键点)
    vm._update(vnode) {
        const prevnode = vm._vnode;
        vm._vnode = vnode;
        if (!prevnode) {
            // 首次渲染的时候
            vm.$el = vm.__patch__(vm.$el, vnode);
        }
        else{
            vm.$el = vm.__patch__(prevnode, vnode);
        }
    }
    
    // 开始更新vue组件(修改data的属性的时候,object.defineproperty)
    function updatecomponent() {
      vm._update(vm._render());
    }

3.5 data属性变化,触发rerender函数

    object.defineproperty(mv, key, {
        set: function(newval) {
          data[key] = newval;
          // 开始执行
          updatecomponnet()
        }
    })

[!note]

  • 修改属性,被响应式的set监听到
  • set中执行updatecomponnet
  • updatecomponent重新执行vm._render()
  • 生成的vnode和prevnode, 通过patch进行对比
  • 渲染到html中去

4. 说一下从使用jquery和使用框架的区别?

  1. 数据和视图的分离(代码解耦)---开房封闭原则
  2. 数据驱动视图,只关系数据变化,dom操作被封装

5. 说一下对mvvm的理解?

  1. mvc
  2. mvvm
  3. 关于viewmodel

6. vue中如何实现响应式的呢?

  1. 响应式
  2. 模板引擎
  3. 渲染(首次渲染,后面的渲染)
  • object.defineproperty
  • data的属性代理到vm上面(with)

7. vue中是如何解析模板的呢?

  1. 模板的本质就是字符串(有逻辑)
  2. 模板必须要转换为js代码
  3. render函数的实现(返回的是一个vnode)
  4. updatecomponnet(patch函数)

8. 说一下vue的整体实现流程?

  1. 解析模板成为render函数
  2. 响应式开始监听数据
  3. 首次渲染,显示页面,且绑定依赖
  4. data属性数据发生变化,重新触发惹人的人、函数

9. vue的数据劫持以及操作数组的坑?

  1. 给data添加新属性的时候vm.$set(vm.info,'newkey','newvalue')

  2. data上面属性值是数组的时候,需要用数组的方法操作数组,而不能通过index或者length属性去操作数组,因为监听不到属性操作的动作。

10. 谈一下对mvvm和mvc的理解?

  1. mvc其实是model view model传统所有逻辑在controller,难以维护。用户输入 => 控制器 => 数据改变,如果数据变了需要获取dom,操作属性,再渲染到视图上。
  2. mvvm其实是model view viewmodel数据变化驱动视图。数据变了,不需要你获取dom,然后改变dom的内容。这边数据变了,vm负责监听,视图那边自动发生变化。最明显的是不需要document.queryselector之类的了。

11. vm的实质?

[!note]
上面说了vm负责让数据变了,视图能自动发生变化。这么神奇魔术背后的原理是object.defineproperty。其实就是属性的读取和设置操作都进行了监听,当有这样的操作的时候,进行某种动作。来一个demo玩下。

    // 对obj上面的属性进行读取和设置监听
    let obj = {
        name:'huahua',
        age:18
    }
    function observer(obj){
        if(typeof obj === 'object'){
            for (const key in obj) {
                definereactive(obj,key,obj[key])
            }
        }
    }
    // get的return的值才是最终你读取到的值。所以设的值是为读取准备的。
    // set传的参数是设置的值,注意这里不要有obj.name = newval 这样又触发set监听,会死循环的。
    function definereactive(obj,key,value){
        object.defineproperty(obj,key,{
            get:function(){
                console.log('你在读取')
                // happy的话这边可以value++,这样你发现读取的值始终比设置的大一个,因为return就是读取到的值
                return value
            },
            set:function(newval){
                console.log('数据更新了')
                value = newval
            }

        })
    }
    observer(obj)
    obj.age = 2
    console.log(obj.age)

12. definereactive的实现(响应式手写实现)?

[!note]
在浏览器执行的时候,控制台随手也可以obj.name="hua1"类似的操作,发现都监听到了。但是如果更深一步,obj.name={firstname:'hua',lastname:'piaoliang'};obj.name.lastname='o'就不能监听到属性修改了。因为并没有将新的赋值对象监听其属性。所以函数需要改进。

需要在definereactive的第一行加上observer(value)。设置值的时候如果是对象的话,也需要将这个对象数据劫持。同理,set那边也需要加这行。

12.1 基础实现

    function definereactive(obj,key,value){
       // 注意这里!!!!!!!
        observer(value)
        object.defineproperty(obj,key,{
            get:function(){
                return value
            },
            set:function(newval){
                // 注意这里!!!!!!!
                observer(newval)
                console.log('数据更新了')
                value = newval
            }

        })
    }

12.2 数组方法的劫持

如果obj.name=[1,2,3];obj.name.push(4)发现又没有通知了,这是因为object.defineproperty不支持监听数组变化。所以需要重写数组上面的方法。

        // 把数组上大部分方法重写了,这里不一一列举。但是如果你 [1,2].length--,这是捕捉不到的
        let arr = ['push','slice','split']
        arr.foreach(method=>{
            let oldpush = array.property[method]
            array.property[method] = function(value){
                console.log('数据更新')
                oldpush.call(this, value)
            }
        })

12.3 vue 的双向绑定的原理是什么(常考)

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

  1. 第一步:需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化

  2. 第二步:compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图

  3. 第三步:watcher 订阅者是 observer 和 compile 之间通信的桥梁,主要做的事情是:
    • 在自身实例化时往属性订阅器(dep)里面添加自己
    • 自身必须有一个 update()方法
    • 待属性变动 dep.notice()通知时,能调用自身的 update() 方法,并触发 compile 中绑定的回调,则功成身退。
  4. 第四步:mvvm 作为数据绑定的入口,整合 observer、compile 和 watcher 三者,通过 observer 来监听自己的 model 数据变化,通过 compile 来解析编译模板指令,最终利用 watcher 搭起 observer 和 compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。

13. vue如何监听数组数据变化?

13.1 vm.\(set方法 因为是一开始就数据劫持了。所以后来如果新绑定属性,是没有数据劫持的。如果需要调用 vm.\)set(vm.info,'newkey','newvalue'),vm是vue的实例。

13.2 使用数组的方法

当属性值是数组,数组变化的时候,跟踪不到变化。因为数组虽然是对象,但是object.defineproperty不支持数组,所以vue改写了数组的所有方法,当调用数组方法的时候,就调动变动事件。但是不能通过属性或者索引控制数组,比如length,index。

[!note]
总结:data上,绑定所有属性避免后期加新属性。如果是数组,只能通过数组方法修改数组。如下例子,控制台vm.arr--发现视图并不会变化,vm.arr.push(4)就能变化

    <div id="app">{{msg}}{{arr}}</div>
        <script src="node_modules/vue/dist/vue.js"></script>
        <script>
        let vm = new vue({
            el:'#app',
            // template加上之后会替换掉#app这个标签
            // template:'<h1>en</h1>',
            data:{msg:'msg',arr:[1,2,3]}
        })
        vm.msg = 'msg'
        </script>

14. vue 的优点和缺点是什么?

14.1 优点

  1. 低耦合。视图(view)可以独立于 model 变化和修改,一个 viewmodel 可以绑定到不同的"view"上,当 view 变化的时候 model 可以不变,当 model 变化的时候 view 也可以不变。
  2. 可重用性。你可以把一些视图逻辑放在一个 viewmodel 里面,让很多 view 重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(viewmodel),设计人员可以专注于页面设计,使用 expression blend 可以很容易设计界面并生成 xml 代码。
  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对 viewmodel 来写。

14.2 缺点(面试常考)

  1. 网站seo问题
  2. 浏览器兼容性问题
  3. 海量数据节点的渲染问题

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

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

  1. 创建前/后: 在 beforecreate 阶段,vue 实例的挂载元素 el 还没有, created阶段。
  2. 载入前/后:在 beforemount 阶段,vue 实例的$el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。
  3. 更新前/后:当 data 变化时,会触发beforeupdateupdated方法。
  4. 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在

16. vue组件之间的传值?

16.1. 父组件与子组件传值

//父组件通过标签上面定义传值
<template>
    <main :obj="data"></main>
</template>
<script>
    //引入子组件
    import main form "./main"

    exprot default{
        name:"parent",
        data(){
            return {
                data:"我要向子组件传递数据"
            }
        },
        //初始化组件
        components:{
            main
        }
    }
</script>


//子组件通过props方法接受数据
<template>
    <div>{{data}}</div>
</template>
<script>
    exprot default{
        name:"son",
        //接受父组件传值
        props:["data"]
    }
</script>

16.2 子组件向父组件传递数据

//子组件通过$emit方法传递参数
<template>
   <div v-on:click="events"></div>
</template>
<script>
    //引入子组件
    import main form "./main"

    exprot default{
        methods:{
            events:function(){

            }
        }
    }
</script>


//

<template>
    <div>{{data}}</div>
</template>
<script>
    exprot default{
        name:"son",
        //接受父组件传值
        props:["data"]
    }
</script>

17. vue路由相关问题

17.1 active-class 是哪个组件的属性?

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

17.2 嵌套路由怎么定义?

在实际项目中我们会碰到多层嵌套的组件组合而成,但是我们如何实现嵌套路由呢?因此我们需要在 vuerouter 的参数中使用 children 配置,这样就可以很好的实现路由嵌套。
index.html,只有一个路由出口

<div id="app">
    <!-- router-view 路由出口, 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>

main.js,路由的重定向,就会在页面一加载的时候,就会将 home 组件显示出来,因为重定向指向了 home 组件,redirect 的指向与 path 的必须一致。children 里面是子路由,当然子路由里面还可以继续嵌套子路由。

import vue from 'vue'
import vuerouter from 'vue-router'
vue.use(vuerouter)

//引入两个组件

import home from "./home.vue"
import game from "./game.vue"
//定义路由
const routes = [
    { path: "/", redirect: "/home" },//重定向,指向了home组件
    {
        path: "/home", component: home,
        children: [
            { path: "/home/game", component: game }
        ]
    }
]
//创建路由实例
const router = new vuerouter({routes})

new vue({
    el: '#app',
    data: {
    },
    methods: {
    },
    router
})

home.vue,点击显示就会将子路由显示在出来,子路由的出口必须在父路由里面,否则子路由无法显示。

17.3 路由之间跳转?

  • 声明式(标签跳转) <router-link :to="index">
  • 编程式( js 跳转) router.push('index')

17.4 懒加载(按需加载路由)(常考)

webpack 中提供了 require.ensure()来实现按需加载。以前引入路由是通过 import 这样的方式引入,改为 const 定义的方式进行引入。

17.4.1 不进行页面按需加载引入方式

import  home   from '../../common/home.vue'

17.4.2 进行页面按需加载的引入方式

const  home = r => require.ensure( [], () => r (require('../../common/home.vue')))

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

  1. 全局导航钩子
  • router.beforeeach(to, from, next),
  • router.beforeresolve(to, from, next),
  • router.aftereach(to, from ,next)
  1. 组件内钩子
  • beforerouteenter,
  • beforerouteupdate,
  • beforerouteleave
  1. 单独路由独享组件
  • beforeenter

18. vux相关问题

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

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

// 新建 store.js
import vue from 'vue'
import vuex form 'vuex'
vue.use(vuex)
export default new vuex.store({
    //...code
})

//main.js
import store from './store'
...

18.2 vuex 有哪几种属性?

有 5 种,分别是 state、getter、mutation、action、module

18.3 vuex 的 store 特性是什么

  • vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源存放地,对应于一般 vue 对象里面的 data
  • state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新
  • 它通过 mapstate 把全局的 state 和 getters 映射到当前组件的 computed 计算属性

18.4 vuex 的 getter 特性是什么

  • getter 可以对 state 进行计算操作,它就是 store 的计算属性
  • 虽然在组件内也可以做计算属性,但是 getters 可以在多给件之间复用
  • 如果一个状态只在一个组件内使用,是可以不用 getters

18.5 vuex 的 mutation 特性是什么

  • action 类似于 muation, 不同在于:action 提交的是 mutation,而不是直接变更状态
  • action 可以包含任意异步操作

18.6 vue 中 ajax 请求代码应该写在组件的 methods 中还是 vuex 的 action 中

如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex 的 state 里

如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回

18.7 不用 vuex 会带来什么问题

  • 可维护性会下降,你要修改数据,你得维护 3 个地方
  • 可读性下降,因为一个组件里的数据,你根本就看不出来是从哪里来的
  • 增加耦合,大量的上传派发,会让耦合性大大的增加,本来 vue 用 component 就是为了减少耦合,现在这么用,和组件化的初衷相背

18.8 vuex 原理

vuex 仅仅是作为 vue 的一个插件而存在,不像 redux,mobx 等库可以应用于所有框架,vuex 只能使用在 vue 上,很大的程度是因为其高度依赖于 vue 的 computed 依赖检测系统以及其插件系统,

vuex 整体思想诞生于 flux,可其的实现方式完完全全的使用了 vue 自身的响应式设计,依赖监听、依赖收集都属于 vue 对对象 property set get 方法的代理劫持。最后一句话结束 vuex 工作原理,vuex 中的 store 本质就是没有 template 的隐藏着的 vue 组件;

18.9 扩展问题

18.9.1 使用 vuex 只需执行 vue.use(vuex),并在 vue 的配置中传入一个 store 对象的示例,store 是如何实现注入的?

vue.use(vuex) 方法执行的是 install 方法,它实现了 vue 实例对象的 init 方法封装和注入,使传入的 store 对象被设置到 vue 上下文环境的\(store 中。因此在 vue component 任意地方都能够通过 this.\)store 访问到该 store。

18.9.2 state 内部支持模块配置和模块嵌套,如何实现的?

在 store 构造方法中有 makelocalcontext 方法,所有 module 都会有一个 local context,根据配置时的 path 进行匹配。所以执行如 dispatch('submitorder', payload)这类 action 时,默认的拿到都是 module 的 local state,如果要访问最外层或者是其他 module 的 state,只能从 rootstate 按照 path 路径逐步进行访问。

18.9.3 在执行 dispatch 触发 action(commit 同理)的时候,只需传入(type, payload),action 执行函数中第一个参数 store 从哪里获取的?

store 初始化时,所有配置的 action 和 mutation 以及 getters 均被封装过。在执行如 dispatch('submitorder', payload)的时候,actions 中 type 为 submitorder 的所有处理方法都是被封装后的,其第一个参数为当前的 store 对象,所以能够获取到 { dispatch, commit, state, rootstate } 等数据。

18.9.4 vuex 如何区分 state 是外部直接修改,还是通过 mutation 方法修改的?

vuex 中修改 state 的唯一渠道就是执行 commit('xx', payload) 方法,其底层通过执行 this._withcommit(fn) 设置_committing 标志变量为 true,然后才能修改 state,修改完毕还需要还原_committing 变量。外部修改虽然能够直接修改 state,但是并没有修改_committing 标志位,所以只要 watch 一下 state,state change 时判断是否_committing 值为 true,即可判断修改的合法性。

18.9.5 调试时的"时空穿梭"功能是如何实现的?

devtoolplugin 中提供了此功能。因为 dev 模式下所有的 state change 都会被记录下来,'时空穿梭' 功能其实就是将当前的 state 替换为记录中某个时刻的 state 状态,利用 store.replacestate(targetstate) 方法将执行 this._vm.state = state 实现。

19. 指令相关

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

  • 全局定义指令:在 vue 对象的 directive 方法里面有两个参数, 一个是指令名称, 另一个是函数。
  • 组件内定义指令:directives
  • 钩子函数: bind(绑定事件出发)、inserted(节点插入时候触发)、update(组件内相关更新)
  • 钩子函数参数: el、binding

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

v-if(判断是否隐藏)、v-for(把数据遍历出来)、v-bind(绑定属性)、v-model(实现双向绑定)

20. axios

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

[!note]
思路:使用vue的router.beforeeach钩子函数结合axios的拦截器功能来实现。

20.2 axios与ajax, fetch的区别和优缺点?

  • 参考文章:https://blog.csdn.net/qq_36407875/article/details/84642060