Vue面试题 题目及答案(70道)
发布于 5 年前 作者 ZhaoHuan01 18197 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利
    • Vue面试题
      1. Vue.js介绍
    • Vue.js是一个轻巧、高性能、可组件化的MVVM库,同时拥有非常容易上手的API
    • Vue.js是一个构建数据驱动的Web界面的库。
    • Vue.js是一套构建用户界面的 渐进式框架。与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计。Vue的核心库只关注视图层,并且非常容易学习,非常容易与其它库或已有项目整合。另一方面,Vue 完全有能力驱动采用单文件组件和 Vue生态系统支持的库开发的复杂单页应用。数据驱动+组件化的前端开发。
    • 简而言之:Vue.js是一个构建数据驱动的 web 界面的渐进式框架。Vue.js 的目标是通过尽可能简单的 API实现响应的数据绑定和组合的视图组件。核心是一个响应的数据绑定系统。
    • 2.什么是 mvvm? MVC MVP MVVM
    • MVVM 是 Model-View-ViewModel 的缩写。mvvm 是一种设计思想。Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。
    • 3.简述Vue的响应式原理
    • 当一个Vue实例创建时,vue会遍历data选项的属性,用 Object.defineProperty poroupoti 将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 wocher watcher 重新计算,从而致使它关联的组件得以更新。
    • 4.Vue.js特点
    • 简洁:页面由HTML模板+Json数据+Vue实例组成
    • 数据驱动:自动计算属性和追踪依赖的模板表达式
    • 组件化:用可复用、解耦的组件来构造页面
    • 轻量:代码量小,不依赖其他库
    • 快速:精确有效批量DOM更新
    • 模板友好:可通过npm,bower等多种方式安装,很容易融入
    • 5.scss是什么?
    • 预处理css,把css当前函数编写,定义变量,嵌套.
    • 6.vue生命周期的理解?
    • 总共分为 8 个阶段创建前/后,载入前/后,更新前/后,销毁前/后。
    • 创建前/后: 在 beforeCreate 阶段,vue 实例的挂载元素 el 还没有。 载入前/后:在 beforeMount 阶段,vue 实例的$el 和 data 都初始化了,但还是挂载之前为虚拟的 dom 节点,data.message 还未替换。在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染。 更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated 方法。 销毁前/后:在执行 destroy 方法后,对 data 的改变不会再触发周期函数,说明此时 vue 实例已经解除了事件监听以及和 dom 的绑定,但是 dom 结构依然存在。
    • 7.为什么vue中data必须是一个函数?
    • 对象为引用类型,当重用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。
    • 8.active-class是哪个组件的属性?
    • vue-router模块的router-link组件。
    • 9.vue-router有哪几种导航钩子?
    • 三种。
    • 一种是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截。
    • 第二种:组件内的钩子;
    • 第三种:单独路由独享组件
    • 10.说出至少4种vue当中的指令和它的用法?
    • v-if:判断是否隐藏;v-for:数据循环出来;v-bind:class:绑定一个属性;v-model:实现双向绑定
    • 11.vue-loader是什么?使用它的用途有哪些?
    • 解析.vue文件的一个加载器,跟template/js/style转换成js模块。
    • 12.请说出vue.cli项目中src目录每个文件夹和文件的用法?
    • assets文件夹是放静态资源;
    • components是放组件;
    • router是定义路由相关的配置;
    • view视图;
    • app.vue是一个应用主组件;
    • main.js是入口文件
    • 13.计算属性和watch的区别
    • 在我们运用vue的时候一定少不了用计算属性computed和watch
    • computed计算属性是用来声明式的描述一个值依赖了其它的值。当你在模板里把数据绑定到一个计算属性上时,Vue 会在其依赖的任何值导致该计算属性改变时更新 DOM。这个功能非常强大,它可以让你的代码更加声明式、数据驱动并且易于维护。
    • watch监听的是你定义的变量,当你定义的变量的值发生变化时,调用对应的方法。就好在div写一个表达式name,data里写入num和lastname,firstname,在watch里当num的值发生变化时,就会调用num的方法,方法里面的形参对应的是num的新值和旧值,而计算属性computed,计算的是Name依赖的值,它不能计算在data中已经定义过的变量。
    • 14.prop 验证,和默认值
    • 我们在父组件给子组件传值得时候,为了避免不必要的错误,可以给prop的值进行类型设定,让父组件给子组件传值得时候,更加准确,prop可以传一个数字,一个布尔值,一个数组,一个对象,以及一个对象的所有属性。组件可以为 props 指定验证要求。如果未指定验证要求,Vue 会发出警告比如传一个number类型的数据,用defalt设置它的默认值,如果验证失败的话就会发出警告。
    • props: {
    • visible: {
      
    •     default: true,
      
    •     type: Boolean,
      
    •     required: true
      
    • },
      
    • },
      1. vue 组件通信
    • 父传递子
    • 父:自定义属性名 + 数据(要传递)=> :value=“数据”
    • 子:props ["父组件上的自定义属性名“] =>进行数据接收)
    • 子传递父
    • 在父组件中注册子组件并在子组件标签上绑定自定义事件的监听。
    • 子:this.$emit(‘自定义事件名称’, 数据) 子组件标签上绑定@自定义事件名称=‘回调函数’
    • 父:methods: {自定义事件() {//逻辑处理} }
    • 兄弟组件
    • 通过中央通信 let bus = new Vue()
    • A:methods :{ 函数{bus.$emit(‘自定义事件名’,数据)} 发送
    • B:created (){bus.$on(‘A发送过来的自定义事件名’,函数)} 进行数据接收
    • 16.vue路由传参数
    • 1.使用query方法传入的参数使用this.$route.query接受
    • 2.使用params方式传入的参数使用this.$route.params接受
    • 17.vuex 是什么? 有哪几种属性?
    • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
    • 有 5 种,分别是 state、getter、mutation、action、module
    • vuex 的 store 是什么?
    • vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源存放地,对应于一般 vue 对象里面的 datastate 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性
    • vuex 的 getter 是什么?
    • getter 可以对 state 进行计算操作,它就是 store 的计算属性虽然在组件内也可以做计算属性,但是 getters 可以在多给件之间复用如果一个状态只在一个组件内使用,是可以不用 getters
    • vuex 的 mutation 是什么?
    • 更改Vuex的store中的状态的唯一方法是提交mutation
    • vuex 的 action 是什么?
    • action 类似于 muation, 不同在于:action 提交的是 mutation,而不是直接变更状态action 可以包含任意异步操作 vue 中 ajax 请求代码应该写在组件的 methods 中还是 vuex 的 action 中
    • vuex 的 module 是什么?
    • 面对复杂的应用程序,当管理的状态比较多时;我们需要将vuex的store对象分割成模块(modules)。
    • 如果请求来的数据不是要被其他组件公用,仅仅在请求的组件内使用,就不需要放入 vuex 的 state 里如果被其他地方复用,请将请求放入 action 里,方便复用,并包装成 promise 返回
    • 18.v-show和v-if指令的共同点和不同点?
    • v-show指令是通过修改元素的displayCSS属性让其显示或者隐藏
    • v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果
    • 19.如何让CSS只在当前组件中起作用?
    • 将当前组件的<style>修改为<style scoped>
    • 20.<keep-alive></keep-alive>的作用是什么?
    • <keep-alive></keep-alive> 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。 大白话: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用<keep-alive></keep-alive>进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染
    • 21.delete和Vue.delete删除数组的区别?
    • delete只是被删除的元素变成了 empty/undefined 其他的元素的键值还是不变。 Vue.delete直接删除了数组 改变了数组的键值。
    • var a=[1,2,3,4]
    • var b=[1,2,3,4]
    • delete a[0]
    • console.log(a) //[empty,2,3,4]
    • this.$delete(b,0)
    • console.log(b) //[2,3,4]
    • 22.$nextTick是什么?
    • vue实现响应式并不是数据发生变化后dom立即变化,而是按照一定的策略来进行dom更新。
    • $nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
    • 23.v-on可以监听多个方法吗?
    • 可以。
    • <input type=“text” :value=“name” @input=“onInput” @focus=“onFocus” @blur=“onBlur” />
    • 复制代码
    • 24.v-on 常用修饰符
    • .stop 该修饰符将阻止事件向上冒泡。同理于调用 event.stopPropagation() 方法
    • .prevent 该修饰符会阻止当前事件的默认行为。同理于调用 event.preventDefault() 方法
    • .self 该指令只当事件是从事件绑定的元素本身触发时才触发回调
    • .once 该修饰符表示绑定的事件只会被触发一次
    • 25.v-for key的作用。
    • 当Vue用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue将不是移动DOM元素来匹配数据项的改变,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。 为了给Vue一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。key属性的类型只能为 string或者number类型。
    • key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
    • 26.v-for 与 v-if 的优先级
    • v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。
    • 27.Vue子组件调用父组件的方法
    • 第一种方法是直接在子组件中通过this.$parent.event来调用父组件的方法
    • 第二种方法是在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了。
    • 28.Promise对象是什么?
    • 1.Promise是异步编程的一种解决方案,它是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。promise对象是一个构造函数,用来生成Promise实例;
    • 2.promise的两个特点 对象状态不受外界影响 && 一旦状态改变,就不会再变,任何时候都可以得到结果(pending状态–>fulfilled || pending–>rejected)
    • 29.axios的特点有哪些?
    • 1、axios是一个基于promise的HTTP库,支持promise的所有API;
    • 2、它可以拦截请求和响应;
    • 3、它可以转换请求数据和响应数据,并对响应回来的内容自动转换为json类型的数据;
    • 4、它安全性更高,客户端支持防御XSRF;
    • 30.vue中的 ref 是什么?
    • ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
    • 31.Vue的路由模式,实现方式?
    • hash模式 和 history模式
    • 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 依赖的页面。”
    • 32.$route和$router的区别?
    • $route是“路由信息对象”,包括path,params,hash,query,fullPath,matched,name等路由信息参数。
    • $router是’路由实例’对象包括了路由的跳转方法,钩子函数等。
    • 33.vue.js的两个核心是什么?
    • 数据驱动、组件系统
    • 34.vue如何兼容ie的问题。
    • babel-polyfill插件
    • 35.页面刷新vuex被清空解决办法?
    • 1.localStorage 存储到本地再回去
    • 2.重新获取接口获取数据
    • 36.如何优化SPA应用的首屏加载速度慢的问题?
    • 1.将公用的JS库通过script标签外部引入,减小 app.bundel 的大小,让浏览器并行下载资源文件,提高下载速度;
    • 2.在配置 路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用 某个组件时再加载对应的js文件;
    • 3.加一个首屏loading图,提升用户体验;
    • 37.Vue 改变数组触发视图更新
    • 以下方法调用会改变原始数组:push(), pop(), shift(), unshift(), splice(), sort(), reverse(),Vue.set( target, key, value )
    • 调用方法:Vue.set( target, key, value )
    • target:要更改的数据源(可以是对象或者数组)
    • key:要更改的具体数据
    • value :重新赋的值
    • 38.DOM 渲染在哪个周期中就已经完成?
    • mounted
    • 注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted
    • mounted: function () {
    • this.$nextTick(function () {
    • // Code that will run only after the
      
    • // entire view has been rendered
      
    • })
    • }
    • 复制代码
    • 39.简述每个周期具体适合哪些场景
    • beforecreate : 可以在这加个loading事件,在加载实例时触发
    • created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
    • mounted : 挂载元素,获取到DOM节点 updated : 如果对数据统一处理,在这里写上相应函数
    • beforeDestroy : 可以做一个确认停止事件的确认框
    • 40.第一次加载会触发哪几个钩子?
    • 会触发beforeCreate , created ,beforeMount ,mounted
    • 41.动态绑定class
    • active classname, isActive 变量
    • 复制代码
    • 1、vue是一套渐进式框架的理解
    • 在我看来,渐进式代表的含义是:主张最少。
    • 每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。
    • 比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:
    • 必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
    • 所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
    • 比如React,它也有一定程度的主张,它的主张主要是函数式编程的理念,比如说,你需要知道什么是副作用,什么是纯函数,如何隔离副作用。它的侵入性看似没有Angular那么强,主要因为它是软性侵入。
    • Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。你可以在底层数据逻辑的地方用OO和设计模式的那套理念,也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。
    • 渐进式的含义,我的理解是:没有多做职责之外的事。
    • 2、Vue常用的指令
    • 3、v-if VS v-show区别
    • 4、Vue常用修饰符
    • 5、v-on可以监听多个方法吗
    • 可以,代码如下:
    • <input type=“text” :value=“name” @input=“onInput” @focus=“onFocus” @blur=“onBlur” />
    • 6、vue中key值的作用
    • 需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
    • 其实不只是vue,react中在执行列表渲染时也会要求给每个组件添加上key这个属性。
    • 要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了。
    • vue和react的虚拟DOM的Diff算法大致相同。
    • 虚拟DOM见“68题” ,diff算法见“69题”
    • 7、$nextTick的作用
    • 8、Vue组件中的data为什么必须是函数
    • 当我们定义一个 组件时( <button-counter>),你可能会发现它的 data 并不是像这样直接提供一个对象:
    • data: {
    • count: 0
    • }
    • 取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
    • data: function () {
    • return {
    • count: 0
      
    • }
    • }
    • 9、v-for和v-if的优先级
    • 当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用。
    • 10、详述组件通信
    • 11、keep-alive组件的作用
    • 当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。例如我们来展开说一说这个多标签界面:
    • 未使用keep-alive.gif
    • 你会注意到,如果你选择了一篇文章,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent 实例。
    • 重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。
    • <keep-alive>
    • <component v-bind:is="currentTabComponent"></component>
    • </keep-alive>
    • 使用了keep-alive.gif
    • 现在这个 Posts 标签保持了它的状态 (被选中的文章) 甚至当它未被渲染时也是如此
    • 12、Vue生命周期详解
    • 什么是Vue的生命周期
    • Vue实例有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂载DOM、渲染-更新-渲染、卸载等一系列过程,我们称为Vue实例的生命周期。钩子就是在某个阶段给你一个做某些处理的机会。
    • 生命周期的作用
    • 就是在某个阶段给你一个做某些处理的机会。
    • 生命周期总共有几个阶段
    • beforeCreate(创建前):在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,因此无法访问methods,data,computed等上的方法和数据。
    • created(创建后):实例已经创建完成之后被调用,在这一步,实例已完成了以下配置:数据观测、属性和方法的运算,watch/event事件回调。然而,挂载阶段还没有开始,$el属性目前不可见。created钩子可以获取Vue的data,调用Vue方法,获取原本HTML上的直接加载出来的DOM,但是无法获取到通过挂载模板生成的DOM。这是一个常用的生命周期,因为你可以调用methods中的方法、改变data中的数据,并且修改可以通过vue的响应式绑定体现在页面上、获取computed中的计算属性等等。通常我们可以在这里对实例进行预处理。也有一些童鞋喜欢在这里发ajax请求,值得注意的是,这个周期中是没有什么方法来对实例化过程进行拦截的。因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个页面发请求。
    • 建议在组件路由勾子beforeRouteEnter中来完成。
    • beforeMonut:挂载开始之前被调用:虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,相关的 render 函数首次被调用(虚拟DOM),实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
    • mounted[ˈmaʊntɪd]:挂载完成,也就是模板中的HTML渲染到了HTML页面中,此时一般可以做一些ajax操作,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了mounted只会执行一次。
    • beforeUpdate(更新前):在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染。
    • updated[ʌp’deɪtɪd](更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
    • beforeDestroy[dɪˈstrɔɪ](销毁前):在实例销毁之前调用。实例仍然完全可用。1.这一步还可以用this来获取实例。2.一般在这一步做一些重置的操作。比如清除掉组件中的 定时器 和 监听的dom事件。
    • destroyed[dɪs’trɔɪd](销毁后):在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
    • 第一次页面加载会触发哪几个钩子
    • beforeCreate
    • DOM 渲染在哪个周期中就已经完成
    • mounted
    • 生命周期使用场景举例
    • beforeCreate:可以在这里加一个loading
    • created:loading结束做一些初始化操作
    • mounted:ajax请求,配合路由钩子做一些事情
    • beforeDestory:你确认删除吗?
    • destoryed:当前组件已被删除,清空相关内容
    • 13、Vue如何监听键盘事件中的按键
    • 14、Vue中的过滤器有什么用?
    • Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示
    • 15、单页面应用和多页面应用区别及优缺点
    • 单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。
    • 多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新
    • 单页面的优点:
    • 用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
    • 前后端分离
    • 页面效果会比较炫酷(比如切换页面内容时的专场动画)
    • 单页面缺点:
    • 不利于seo
    • 导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)
    • 初次加载时耗时多
    • 页面复杂度提高很多
    • 姓名
    • 单页面应用(SinglePage Web Application,SPA)
    • 多页面应用(MultiPage Application,MPA)
    • 组成
    • 一个外壳页面和多个页面片段组成
    • 多个完整页面构成
    • 资源共用(css,js)
    • 共用,只需在外壳部分加载
    • 不共用,每个页面都需要加载
    • 刷新方式
    • 页面局部刷新或更改
    • 整页刷新
    • url 模式
    • 用户体验
    • 页面片段间的切换快,用户体验良好
    • 页面切换加载缓慢,流畅度不够,用户体验比较差
    • 转场动画
    • 容易实现
    • 无法实现
    • 数据传递
    • 容易
    • 依赖 url传参、或者cookie 、localStorage等
    • 搜索引擎优化(SEO)
    • 需要单独方案、实现较为困难、不利于SEO检索 可利用服务器端渲染(SSR)优化
    • 实现方法简易
    • 试用范围
    • 高要求的体验度、追求界面流畅的应用
    • 适用于追求高度支持搜索引擎的应用
    • 开发成本
    • 较高,常需借助专业的框架
    • 较低 ,但页面重复代码多
    • 维护成本
    • 相对容易
    • 相对复杂
    • 16、什么是计算属性?什么情况使用?
    • 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,例如:
    • {{ message.split(’’).reverse().join(’’) }}
    • 在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。
    • 所以,对于任何复杂逻辑,你都应当使用计算属性。
    • 17、vue-cli提供了几种脚手架模板
    • 六种
    • 18、computed、methods的区别
    • 两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。这就意味着只要值还没有发生改变,多次访问 定义的计算属性会立即返回之前的计算结果,而不必再次执行函数。
    • 相比之下,每当触发重新渲染时,调用方法(methods)将总会再次执行函数。
    • 我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
    • 19、什么是自定义指令,有哪些钩子函数及自定义指令的使用场景
    • 有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
    • 一个指令定义对象可以提供如下几个钩子函数 (均为可选):
    • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
    • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
    • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
    • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    • unbind:只调用一次,指令与元素解绑时调用。
    • 20、父组件获取异步动态数据传递给子组件
    • 在父组件中使用axios获取异步数据传给子组件,但是发现子组件在渲染的时候并没有数据,在created里面打印也是空的,结果发现一开始子组件绑定的数据是空的,在请求数据没有返回数据时,子组件就已经加载了,并且他绑定的值也是空的,问题找到了,怎么解决呢?
    • 开始的时候让子组件隐藏,然后等数据返回的时候,让子组件显示
    • 通过v-if,也就是判断数据是否为空,为空就不渲染,也能解决了
    • 为不能读取的属性添加一个默认值,就可以很好的解决了
    • 21、vue-router导航解析流程
    • 22、vue-router实现原理
    • 这里指的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。 换句话说,vue-router就是WebApp的链接路径管理系统。
    • vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。
    • 那与传统的页面跳转有什么区别呢?
    • 1.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。
    • 2.传统的页面应用,是用一些超链接来实现页面切换和跳转的。
    • 在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。
    • 至于为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,所以你写的标签是不起作用的,必须使用vue-router来进行管理。
    • SPA(single page application):单一页面应用程序,有且只有一个完整的页面;当它在加载页面的时候,不会加载整个页面的内容,而只更新某个指定的容器中内容。
    • 单页面应用(SPA)的核心之一是:
    • 更新视图而不重新请求页面;
    • vue-router在实现单页面前端路由时,提供了三种方式:Hash模式、History模式、abstract模式,根据mode参数来决定采用哪一种方式。
    • 路由模式
    • vue-router 提供了三种运行模式:
    • ● hash: 使用 URL hash 值来作路由。默认模式。
    • ● history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。
    • ● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。
    • Hash模式
    • vue-router 默认模式是 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会去重新加载。
    • hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分(/#/…),浏览器只会加载相应位置的内容,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。
    • JavaScript实现SPA路由hash模式详解
    • History模式
    • HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;
    • 由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: ‘history’",这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
    • const router = new VueRouter({
    • mode: ‘history’,
    • routes: […]
    • })
    • 当使用 history 模式时,URL 就像正常的 url,例如 yoursite.com/user/id,比较好… 不过这种模式有点问题,还需要后台配置支持。你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面,如果不这么做,直接访问页面空白
    • 配置Apache
    • 第一步:新建:.htaccess文件放在服务器根目录下 (命令type null>.htaccess)
    • <IfModule mod_rewrite.c>
    • RewriteEngine On
    • RewriteBase /
    • RewriteRule ^index.html$ - [L]
    • RewriteCond %{REQUEST_FILENAME} !-f
    • RewriteCond %{REQUEST_FILENAME} !-d
    • RewriteRule . /index.html [L]
    • </IfModule>
    • 除了 mod_rewrite,你也可以使用 FallbackResource。
    • 第二步: src/router/index.js
    • mode: ‘history’,
    • base: ‘/dist/’,
    • 第三步:访问:地址进行测试
    • abstract模式
    • abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。
    • 根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (当然,你也可以明确指定在所有情况下都使用 abstract 模式)
    • 23、vue-router有哪几种导航钩子
    • 24、vue-router参数传递方法详述及区别
    • 25、如何定义嵌套路由
    • 26、router-link是什么及其常用属性配置
    • 27、如何实现路由懒加载有什么好处
    • 28、vue-router共有几种模式,有什么区别?
    • 29、什么是Vuex及使用场景
    • 30、vuex的常用属性有哪几个,分别是做什么的
    • 31、简述vuex更新数据流程或机制
    • Vuex
    • 用户在组件中发起动作,然后从API中拿数据,就会牵扯到异步操作,所以我们通过dispatch来提交一个action,在action里面发起ajax请求,拿到数据以后我们只需要通过commit提交mutations改变我的state状态就可以了,状态改变后视图就会改变因为Vuex是响应式的,这就是Vuex的运作流程机制
    • 32、vuex中如何异步修改数据
    • Action 类似于 mutation,不同在于:
    • Action 提交的是 mutation,而不是直接变更状态。
    • Action 可以包含任意异步操作。
    • 33、axios、fetch和ajax有什么区别?
    • 34、axios有哪些特点
    • 35、组件样式中的scoped有什么用
    • 36、vue中常用的UI组件库有哪些?
    • 37、如何优化首屏加载速度
    • 路由懒加载
    • vue项目作为一个单页面应用,如果不对路由进行处理,在加载首页的时候,就会将所有组件全部加载,并向服务器请求数据,这必将拖慢加载速度;当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
    • 路由懒加载
    • 图片资源的压缩
    • 严格说来这一步不算在编码技术范围内,但是却对页面的加载速度影响很大,特别是对于移动端的项目来说。
    • 对于非logo的图片文件,让UI设计师提供jpg格式的,不要用png
    • 对于所有的图片文件,都可以在一个叫tinypng的网站上去压缩一下或采用webpack插件进行压缩
    • 使用cdn
    • 在Vue项目中,引入到工程中的所有js、css文件,编译时都会被打包进vendor.js,浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多,那么vendor.js文件体积将会相当的大,影响首开的体验。
    • 解决方法是,将引用的外部js、css文件剥离开来,不编译到vendor.js中,而是用资源的形式引用,这样浏览器可以使用多个线程异步将vendor.js、外部的js等加载下来,达到加速首开的目的。
    • 外部的库文件,可以使用CDN资源,或者别的服务器资源等。
    • 下面,以引入vue、vuex、vue-router为例,说明处理流程。
    • module.exports = {
    • context: path.resolve(__dirname, ‘…/’),
    • entry: {
    • app: './src/main.js'
      
    • },
    • externals:{
    • 'vue':'Vue',
      
    • 'vue-router':'VueRouter',
      
    • 'vuex':'Vuex'
      
    • },
    • // 格式为’aaa’:‘bbb’,其中,aaa表示要引入的资源的名字,bbb表示该模块提供给外部引用的名字,由对应的库自定。例如,vue为Vue,vue-router为VueRouter
    • 去掉原有的引用直接使用就可以了,否则还是会打包
    • 具体步骤为
    • 1、引入
    • 在bulid/webpack.base.conf.js文件中,增加externals,将引用的外部模块导入,如下:
    • module.exports = {
    • entry: {
    • app: './src/main.js'
      
    • },
    • externals:{
    • 'vue': 'Vue',
      
    • 'vue-router': 'VueRouter',
      
    • 'vuex':'Vuex'
      
    • }
    • 2、在index.html中引入cdn。推荐引入 百度静态资源库的
    • <body>
    • <div id="app"></div>
      
    • <script src="https://cdn.bootcss.com/vue/2.5.2/vue.min.js"></script>
      
    • <script src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
      
    • <script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
      
    • </body>
    • 注意一点:
    • 格式为 ‘aaa’ : ‘bbb’, 其中,aaa表示要引入的资源的名字,bbb表示该模块提供给外部引用的名字,由对应的库自定。例如,vue为Vue,vue-router为VueRouter.
    • 3、去掉原有的引用
    • main.js中
    • // import Vue from ‘vue’
    • // import Router from ‘vue-router’
    • 去掉Vue.use(XXX),如:
    • // Vue.use(Router)
    • 4、重新npm run build,会看到 vendor.js体积有所下降了
    • 通过开发者模式的Network工具,可以看到vue.js、vuex.js、vendor.js等文件会分别由一个线程进行加载。且因为使用了CDN,减轻了带宽压力。
    • 前流行的UI框架如iview,muse-ui,Element UI都支持按需加载,
    • gzip压缩
    • 38、打包命令是什么?
    • 39、打包后会生成哪些文件?
    • 40、如何配置打包后生成的文件路径错误问题
    • 41、简述MVVM、MVP、MVC模式及区别
    • MVVM
    • 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 来统一管理。
    • 42、MVVM模式的理解
    • MVVM 由 Model、View、ViewModel 三部分构成,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 来统一管理。
    • 43、Vue中的双向数据绑定是如何实现的
    • Vue双向数据绑定实现原理
    • 分成两个进程,一个进程是对挂载目标元素模板里的v-model和{{ }}这两个指令进行编译(绿色)。另一个进程是对传进去的data对象里面的数据进行监听(红色)。
    • 红色:
    • 当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部加上set和get访问器,这样在设置data的属性值的时候,会触发set方法,那么set方法主要有两个作用,一是改变data里面的属性值,二是发出数据变化的通知。Observer作为数据的观察者,让数据对象的读写操作都处于自己的监管之下,Dep作为Watcher(订阅器)的收集者,当数据发生变化set会发出通知,会被Observer观察到,然后由Dep通知到Watcher,最后更新视图。
    • 绿色:
    • 指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,同样由Dep进行收集,然后由Dep通知到Watcher,最后更新视图。
    • 节点介绍
    • 数据监听器观察者Observer,能够对数据对象的所有属性进行监听,让数据对象的读写操作都处于自己的监管之下,当数据发生变化set会发出通知,会被Observer观察到,然后由Dep通知到Watcher,最后更新视图。
    • 实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性
    • Watcher将数据监听器和指令解析器连接起来,数据的属性变动时,执行指令绑定的相应回调函数,
    • 1.如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。
    • 指令解析器Compile,
    • 对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher
    • Dep:因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的
    • 43、Object.defineProperty()方法做什么用
    • 44、vue-cli中常用的配置
    • 45、简述vue内部运作机制
    • 46、vuex内部运作机制
    • 47、axios内部运作机制
    • 48、vue-router内部运作机制
    • 49、在vue-cli中怎么使用scss
    • 50、vue和jquery有什么区别?
    • 51、vue的双向数据绑定原理是什么?
    • 52、你是怎么理解组件化开发的
    • 53、简述vue-cli每个目录的作用
    • 54、为什么选择vue?和其它框架对比的优劣势在哪?
    • 55、route和router的区别
    • 56、vue两个核心点是什么?
    • 数据驱动、组件系统
    • 57、用Vuex和不用vuex有什么区别?
    • 58、第一次页面加载会触发哪几个钩子
    • 59、v-model是什么?
    • 60、vue中的数组和原生js中的数组有什么区别?
    • 61、简述$set及使用场景
    • 62、ajax应该放在组件中还是视图中或是vuex中
    • 63、你觉得什么样的项目比较适合用vue框架
    • 64、列举vue中触发视图更新的方法
    • 65、Vue不能检测数组或对象变动问题的解决方法有哪些
    • 66、vue-router,history模式下打包后访问空白
    • 67、打包后访问某个视图,刷新404问题
    • 68、详述虚拟DOM
    • 第一种:
    • 1、state数据
    • 2、JSX模板
    • 3、 数据 + 模板 结合,生成真实的DOM -> 视图
    • 4、state发生了变化
    • 5、数据 + 模板 结合,生成真实的DOM,替换原始的DOM
    • 缺陷:
    • 1、第一次生成了完整的DOM片段
    • 2、第二次生成了完整的DOM片段
    • 3、第二次的DOM替换第一次的DOM,非常耗费性能
    • 第二种:
    • 1、state数据
    • 2、JSX模板
    • 3、数据 + 模板 结合, 生成真实的DOM -> 视图
    • 4、state发生变化
    • 5、数据 + 模板 结合,生成真实的DOM,并不直接替换原始的DOM
    • 6、新的DOM(DocumentFragment)和原始的DOM做比对,找差异
    • 7、找出input框发生了变化
    • 8、只用新的DOM中的input元素,替换掉老的DOM中input元素
    • 缺陷:
    • 虽然DOM只是局部替换,但是在比对时候的计算是比较耗费性能的,因此,性能的提升并不明显
    • 第三种:
    • 1、state数据
    • 2、JSX模板
    • 3、数据 + 模板 生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)(损耗一点性能)
    • 虚拟DOM:[‘div’, {id: ‘abc’}, [‘span’, ‘’, ‘hello world’]]
    • 4、用虚拟DOM的结构生成真实的DOM -> 视图显示
    • 真实DOM:
    • 5、state发生了变化
    • 6、数据 + 模板 生成新的虚拟DOM:[‘div’, {id: ‘abc’}, [‘span’, ‘’, ‘hi world’]](极大提升性能)
    • 7、比较原始虚拟DOM和新的虚拟DOM的区别,找到的区别是span中的内容发生了变化(极大提升了性能)
    • 8、直接操作DOM,改变span中的内容
    • 总结:
    • 减少了真实DOM的创建及对比,创建都是JS对象,对比的也都是JS的对象,在JS底层实现了极大的性能飞越
    • 组件生成流程:
    • JSX -> JS对象(虚拟DOM) -> 真实的DOM
    • 用React.createElement改写JSX模板:
    • JSX:return
      { item }
    • JSX -> JS对象(虚拟DOM) -> 真实的DOM
    • React.createElement(‘div’, {}, React.createElement(‘span’, {}, ‘item’))
    • JSX -> createElement -> JS对象(虚拟DOM) -> 真实的DOM
    • 虚拟DOM优点:
    • 1、性能提升了
    • 2、它使得跨端应用得以实现,Ract Native
    • React可以写原生应用了,得益于React中的虚拟DOM,如果没有虚拟DOM是不能写原生应用的。原生系统是不支持DOM不存在DOm这个概念的,但是支持虚拟DOM(虚拟DOM就是一个JS对象);虚拟DOM可以在浏览器端被解析为真实的DOM,在原生端可以被解析原生所支持的组件等格式
    • 69、详述虚拟DOM中的diff算法
    • 虚拟DOM对比时,会用到diff算法
    • 虚拟DOM什么时候会被比对?
    • 当数据发生变化的时候就会被比对
    • 那什么时候数据会发生改变呢?
    • 要么改变了state,要么改变了props(props的改变其实是他的父组件的state发生了改变)
    • setState方法,其实是异步的,为什么是异步的?实际为了提升React底层的性能,假设:调用三次setState变更三组数据,大家想页面会怎么做或者说React会怎么做?我们想的是React可能会做三次比对更新三次视图。又假设三次更新间隔非常小,这样会耗费性能,React可以把三次合并为一次,只去做一次虚拟DOM的比对,然后更新一次视图,这样的话就可以省去两次比对性能上的耗费。
    • 同学们听我说.png
    • 同层比对,如果一致,那么继续比对第二层,如果比对一样了,继续往下比对。
    • 如果比对到不一样了,React会这么做,它不会再继续往下比对了,而是从不一样的这一层开始直接用新的覆盖掉就得DOM节点,这样的话岂不是性能并未得到最大提升?这样的话会造成重复节点的浪费,。那这样比对会有什么好处呢?同层比对带来的好处就是比对的算法特别简单,虽然可能会造成DOM上的重新渲染的浪费,但是大大的减少了虚拟DOM之间比对的算法上的性能消耗,所以React中采用了同层比对的算法。
    • 遍历时候key的问题:
    • 同学们听我说
    • 假如:数组中有五条数据,渲染到页面,然后生成五个虚拟DOM树,接下来我往里面增加了一条数据于是数据发生变化会生成一个新的虚拟DOM树,然后我们会做两个虚拟DOM的比对也就是上下进行比对匹配关系,如果每一个虚拟DOM的节点没有一个key值,它就没有一个自己的名字,当我们在做两个虚拟DOM树的比对的时候节点和节点之间的关系就很难被确立,我们得做两层循环的比较,这样的话比较起来就很麻烦了,当然也是很耗费性能的。
    • 我们可以这样优化,假如我们在做DOM节点的循环的时候,我们可以给每个节点起个名字,A、B、C、D、E在第二次循环的时候我们有六个,以前的ABCDE还存在还是叫做ABCDE,我又增加了一个节点Z进来这个时候比对就很简单了,我们根据他们的名字进行比对,马上就能知道ABCDE都一致,可以继续复用,只有Z不同,我们快速的建立关联后把Z增加到这个DOM树上就可以了。所以极大的提升了虚拟DOM比对的性能。
    • 如果提升性能有个前提我们尽量不要用下标,因为大家看按照下标的话右图ABCDE,下面新的DOM树ABCDE和上面的其实不再是对应的关系了,对导致key值不稳定,key值是变化的,失去了存在的意义了。那用什么比较合适呢?唯一不变化的、稳定的值。
    • 70.ajax、axios、fetch之间的详细区别以及优缺点
    回到顶部