Vue的长处

  • 轻量级框架:只关注视图层,是一个构建数据的视图汇合,大小只有几十 kb
  • 简略易学:国人开发,中文文档,不存在语言障碍 ,易于了解和学习;
  • 双向数据绑定:保留了 angular 的特点,在数据操作方面更为简略;
  • 组件化:保留了 react 的长处,实现了 html 的封装和重用,在构建单页面利用方面有着独特的劣势;
  • 视图,数据,构造拆散:使数据的更改更为简略,不须要进行逻辑代码的批改,只须要操作数据就能实现相干操作;
  • 虚构DOM:dom 操作是十分消耗性能的,不再应用原生的 dom 操作节点,极大解放 dom 操作,但具体操作的还是 dom 不过是换了另一种形式;
  • 运行速度更快:相比拟于 react 而言,同样是操作虚构 dom,就性能而言, vue 存在很大的劣势。

Vue2.x 响应式数据原理

整体思路是数据劫持+观察者模式

对象外部通过 defineReactive 办法,应用 Object.defineProperty 来劫持各个属性的 settergetter(只会劫持曾经存在的属性),数组则是通过重写数组7个办法来实现。当页面应用对应属性时,每个属性都领有本人的 dep 属性,寄存他所依赖的 watcher(依赖收集),当属性变动后会告诉本人对应的 watcher 去更新(派发更新)

Object.defineProperty根本应用

function observer(value) { // proxy reflect    if (typeof value === 'object' && typeof value !== null)    for (let key in value) {        defineReactive(value, key, value[key]);    }}function defineReactive(obj, key, value) {    observer(value);    Object.defineProperty(obj, key, {        get() { // 收集对应的key 在哪个办法(组件)中被应用            return value;        },        set(newValue) {            if (newValue !== value) {                observer(newValue);                value = newValue; // 让key对应的办法(组件从新渲染)从新执行            }        }    })}let obj1 = { school: { name: 'poetry', age: 20 } };observer(obj1);console.log(obj1)

源码剖析

class Observer {  // 观测值  constructor(value) {    this.walk(value);  }  walk(data) {    // 对象上的所有属性顺次进行观测    let keys = Object.keys(data);    for (let i = 0; i < keys.length; i++) {      let key = keys[i];      let value = data[key];      defineReactive(data, key, value);    }  }}// Object.defineProperty数据劫持外围 兼容性在ie9以及以上function defineReactive(data, key, value) {  observe(value); // 递归要害  // --如果value还是一个对象会持续走一遍odefineReactive 层层遍历始终到value不是对象才进行  //   思考?如果Vue数据嵌套层级过深 >>性能会受影响  Object.defineProperty(data, key, {    get() {      console.log("获取值");      //须要做依赖收集过程 这里代码没写进去      return value;    },    set(newValue) {      if (newValue === value) return;      console.log("设置值");      //须要做派发更新过程 这里代码没写进去      value = newValue;    },  });}export function observe(value) {  // 如果传过来的是对象或者数组 进行属性劫持  if (    Object.prototype.toString.call(value) === "[object Object]" ||    Array.isArray(value)  ) {    return new Observer(value);  }}

说一说你对vue响应式了解答复范例

  • 所谓数据响应式就是可能使数据变动能够被检测并对这种变动做出响应的机制
  • MVVM框架中要解决的一个外围问题是连贯数据层和视图层,通过数据驱动利用,数据变动,视图更新,要做到这点的就须要对数据做响应式解决,这样一旦数据发生变化就能够立刻做出更新解决
  • vue为例阐明,通过数据响应式加上虚构DOMpatch算法,开发人员只须要操作数据,关怀业务,齐全不必接触繁琐的DOM操作,从而大大晋升开发效率,升高开发难度
  • vue2中的数据响应式会依据数据类型来做不同解决,如果是 对象则采纳Object.defineProperty()的形式定义数据拦挡,当数据被拜访或发生变化时,咱们感知并作出响应;如果是数组则通过笼罩数组对象原型的7个变更办法 ,使这些办法能够额定的做更新告诉,从而作出响应。这种机制很好的解决了数据响应化的问题,但在理论应用中也存在一些毛病:比方初始化时的递归遍历会造成性能损失;新增或删除属性时须要用户应用Vue.set/delete这样非凡的api能力失效;对于es6中新产生的MapSet这些数据结构不反对等问题
  • 为了解决这些问题,vue3从新编写了这一部分的实现:利用ES6Proxy代理要响应化的数据,它有很多益处,编程体验是统一的,不须要应用非凡api,初始化性能和内存耗费都失去了大幅改善;另外因为响应化的实现代码抽取为独立的reactivity包,使得咱们能够更灵便的应用它,第三方的扩大开发起来更加灵便了

为什么要用 Vuex 或者 Redux

因为传参的办法对于多层嵌套的组件将会十分繁琐,并且对于兄弟组件间的状态传递无能为力。咱们常常会采纳父子组件间接援用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式十分软弱,通常会导致代码无奈保护。

所以须要把组件的共享状态抽取进去,以一个全局单例模式治理。在这种模式下,组件树形成了一个微小的"视图",不论在树的哪个地位,任何组件都能获取状态或者触发行为。

另外,通过定义和隔离状态治理中的各种概念并强制恪守肯定的规定,代码将会变得更结构化且易保护。

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]

vue-cli 工程罕用的 npm 命令有哪些

  • 下载 node_modules 资源包的命令:
npm install
  • 启动 vue-cli 开发环境的 npm命令:
npm run dev
  • vue-cli 生成 生产环境部署资源 的 npm命令:
npm run build
  • 用于查看 vue-cli 生产环境部署资源文件大小的 npm命令:
npm run build --report
在浏览器上自动弹出一个 展现 vue-cli 工程打包后 app.jsmanifest.jsvendor.js 文件外面所蕴含代码的页面。能够具此优化 vue-cli 生产环境部署的动态资源,晋升 页面 的加载速度

Vue我的项目中你是如何解决跨域的呢

一、跨域是什么

跨域实质是浏览器基于同源策略的一种平安伎俩

同源策略(Sameoriginpolicy),是一种约定,它是浏览器最外围也最根本的平安性能

所谓同源(即指在同一个域)具备以下三个相同点

  • 协定雷同(protocol)
  • 主机雷同(host)
  • 端口雷同(port)

反之非同源申请,也就是协定、端口、主机其中一项不雷同的时候,这时候就会产生跨域

肯定要留神跨域是浏览器的限度,你用抓包工具抓取接口数据,是能够看到接口曾经把数据返回回来了,只是浏览器的限度,你获取不到数据。用postman申请接口可能申请到数据。这些再次印证了跨域是浏览器的限度。

vue-loader是什么?它有什么作用?

答复范例

  1. vue-loader是用于解决单文件组件(SFCSingle-File Component)的webpack loader
  2. 因为有了vue-loader,咱们就能够在我的项目中编写SFC格局的Vue组件,咱们能够把代码宰割为<template><script><style>,代码会异样清晰。联合其余loader咱们还能够用Pug编写<template>,用SASS编写<style>,用TS编写<script>。咱们的<style>还能够独自作用以后组件
  3. webpack打包时,会以loader的形式调用vue-loader
  4. vue-loader被执行时,它会对SFC中的每个语言块用独自的loader链解决。最初将这些独自的块装配成最终的组件模块

原理

vue-loader会调用@vue/compiler-sfc模块解析SFC源码为一个描述符(Descriptor),而后为每个语言块生成import代码,返回的代码相似上面

// source.vue被vue-loader解决之后返回的代码// import the <template> blockimport render from 'source.vue?vue&type=template'// import the <script> blockimport script from 'source.vue?vue&type=script'export * from 'source.vue?vue&type=script'// import <style> blocksimport 'source.vue?vue&type=style&index=1'script.render = renderexport default script

咱们想要script块中的内容被作为js解决(当然如果是<script lang="ts">被作为ts理),这样咱们想要webpack把配置中跟.js匹配的规定都利用到形如source.vue?vue&type=script的这个申请上。例如咱们对所有*.js配置了babel-loader,这个规定将被克隆并利用到所在Vue SFC

import script from 'source.vue?vue&type=script

将被开展为:

import script from 'babel-loader!vue-loader!source.vue?vue&type=script'

相似的,如果咱们对.sass文件配置了style-loader + css-loader + sass-loader,对上面的代码

<style scoped lang="scss">

vue-loader将会返回给咱们上面后果:

import 'source.vue?vue&type=style&index=1&scoped&lang=scss'

而后webpack会开展如下:

import 'style-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'
  • 当解决开展申请时,vue-loader将被再次调用。这次,loader将会关注那些有查问串的申请,且仅针对特定块,它会选中特定块外部的内容并传递给前面匹配的loader
  • 对于<script>块,解决到这就能够了,然而<template><style>还有一些额定工作要做,比方

    • 须要用 Vue 模板编译器编译template,从而失去render函数
    • 须要对 <style scoped>中的CSS做后处理(post-process),该操作在css-loader之后但在style-loader之前

实现上这些附加的loader须要被注入到曾经开展的loader链上,最终的申请会像上面这样:

// <template lang="pug">import 'vue-loader/template-loader!pug-loader!source.vue?vue&type=template'// <style scoped lang="scss">import 'style-loader!vue-loader/style-post-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'

参考:前端vue面试题具体解答

vue 中应用了哪些设计模式

1.工厂模式 - 传入参数即可创立实例

虚构 DOM 依据参数的不同返回根底标签的 Vnode 和组件 Vnode

2.单例模式 - 整个程序有且仅有一个实例

vuex 和 vue-router 的插件注册办法 install 判断如果零碎存在实例就间接返回掉

3.公布-订阅模式 (vue 事件机制)

4.观察者模式 (响应式数据原理)

5.装璜模式: (@装璜器的用法)

6.策略模式 策略模式指对象有某个行为,然而在不同的场景中,该行为有不同的实现计划-比方选项的合并策略

如何了解Vue中模板编译原理

Vue 的编译过程就是将 template 转化为 render 函数的过程
  • 解析生成AST树template模板转化成AST语法树,应用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相干解决
  • 标记优化 对动态语法做动态标记 markup(动态节点如div下有p标签内容不会变动) diff来做优化 动态节点跳过diff操作

    • Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变动,对应的DOM也不会变动。那么优化过程就是深度遍历AST树,依照相干条件对树节点进行标记。这些被标记的节点(动态节点)咱们就能够跳过对它们的比对,对运行时的模板起到很大的优化作用
    • 期待后续节点更新,如果是动态的,不会在比拟children
  • 代码生成 编译的最初一步是将优化后的AST树转换为可执行的代码

答复范例

思路

  • 引入vue编译器概念
  • 阐明编译器的必要性
  • 论述编译器工作流程

答复范例

  1. Vue中有个独特的编译器模块,称为compiler,它的次要作用是将用户编写的template编译为js中可执行的render函数。
  2. 之所以须要这个编译过程是为了便于前端能高效的编写视图模板。相比而言,咱们还是更违心用HTML来编写视图,直观且高效。手写render函数不仅效率底下,而且失去了编译期的优化能力。
  3. Vue中编译器会先对template进行解析,这一步称为parse,完结之后会失去一个JS对象,咱们称为 形象语法树AST ,而后是对AST进行深加工的转换过程,这一步成为transform,最初将后面失去的AST生成为JS代码,也就是render函数

可能的诘问

  1. Vue中编译器何时执行?

new Vue()之后。 Vue 会调用 _init 函数进行初始化,也就是这里的 init 过程,它会初始化生命周期、事件、 propsmethodsdatacomputedwatch等。其中最重要的是通过 Object.defineProperty 设置 settergetter 函数,用来实现「响应式」以及「依赖收集」
  • 初始化之后调用 $mount 会挂载组件,如果是运行时编译,即不存在 render function 然而存在 template 的状况,须要进行「编译」步骤
  • compile编译能够分成 parseoptimizegenerate 三个阶段,最终须要失去 render function
  • React有没有编译器?

react 应用babelJSX语法解析

<div id="app"></div><script>    let vm = new Vue({        el: '#app',        template: `<div>            // <span>hello world</span> 是动态节点            <span>hello world</span>                // <p>{{name}}</p> 是动静节点            <p>{{name}}</p>        </div>`,        data() {          return { name: 'test' }        }    });</script>

源码剖析

export function compileToFunctions(template) {  // 咱们须要把html字符串变成render函数  // 1.把html代码转成ast语法树  ast用来形容代码自身造成树结构 不仅能够形容html 也能形容css以及js语法  // 很多库都使用到了ast 比方 webpack babel eslint等等  let ast = parse(template);  // 2.优化动态节点:对ast树进行标记,标记动态节点    if (options.optimize !== false) {      optimize(ast, options);    }  // 3.通过ast 从新生成代码  // 咱们最初生成的代码须要和render函数一样  // 相似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))  // _c代表创立元素 _v代表创立文本 _s代表文Json.stringify--把对象解析成文本  let code = generate(ast);  //   应用with语法扭转作用域为this  之后调用render函数能够应用call扭转this 不便code外面的变量取值  let renderFn = new Function(`with(this){return ${code}}`);  return renderFn;}

Vue中的过滤器理解吗?过滤器的利用场景有哪些?

过滤器本质不扭转原始数据,只是对数据进行加工解决后返回过滤后的数据再进行调用解决,咱们也能够了解其为一个纯函数

Vue 容许你自定义过滤器,可被用于一些常见的文本格式化

ps: Vue3中已废除filter

如何用

vue中的过滤器能够用在两个中央:双花括号插值和 v-bind 表达式,过滤器应该被增加在 JavaScript表达式的尾部,由“管道”符号批示:

<!-- 在双花括号中 -->{ message | capitalize }<!-- 在 `v-bind` 中 --><div v-bind:id="rawId | formatId"></div>

定义filter

在组件的选项中定义本地的过滤器

filters: {  capitalize: function (value) {    if (!value) return ''    value = value.toString()    return value.charAt(0).toUpperCase() + value.slice(1)  }}

定义全局过滤器:

Vue.filter('capitalize', function (value) {  if (!value) return ''  value = value.toString()  return value.charAt(0).toUpperCase() + value.slice(1)})new Vue({  // ...})

留神:当全局过滤器和部分过滤器重名时,会采纳部分过滤器

过滤器函数总接管表达式的值 (之前的操作链的后果) 作为第一个参数。在上述例子中,capitalize 过滤器函数将会收到 message 的值作为第一个参数

过滤器能够串联:

{ message | filterA | filterB }

在这个例子中,filterA 被定义为接管单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。而后持续调用同样被定义为接管单个参数的过滤器函数 filterB,将 filterA 的后果传递到 filterB 中。

过滤器是 JavaScript函数,因而能够接管参数:

{{ message | filterA('arg1', arg2) }}

这里,filterA 被定义为接管三个参数的过滤器函数。

其中 message 的值作为第一个参数,一般字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数

举个例子:

<div id="app">  <p>{{ msg | msgFormat('疯狂','--')}}</p></div><script>    // 定义一个 Vue 全局的过滤器,名字叫做  msgFormat    Vue.filter('msgFormat', function(msg, arg, arg2) {        // 字符串的  replace 办法,第一个参数,除了可写一个 字符串之外,还能够定义一个正则        return msg.replace(/单纯/g, arg+arg2)    })</script>

小结:

  • 部过滤器优先于全局过滤器被调用
  • 一个表达式能够应用多个过滤器。过滤器之间须要用管道符“|”隔开。其执行程序从左往右

利用场景

平时开发中,须要用到过滤器的中央有很多,比方单位转换数字打点文本格式化工夫格式化之类的等

比方咱们要实现将30000 => 30,000,这时候咱们就须要应用过滤器

Vue.filter('toThousandFilter', function (value) {  if (!value) return ''  value = value.toString()  return .replace(str.indexOf('.') > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(?:\d{3})+$)/g, '$1,')})

原理剖析

应用过滤器

{{ message | capitalize }}

在模板编译阶段过滤器表达式将会被编译为过滤器函数,次要是用过parseFilters,咱们放到最初讲

_s(_f('filterFormat')(message))

首先剖析一下_f

_f 函数全名是:resolveFilter,这个函数的作用是从this.$options.filters中找出注册的过滤器并返回

// 变为this.$options.filters['filterFormat'](message) // message为参数

对于resolveFilter

import { indentity,resolveAsset } from 'core/util/index' export function resolveFilter(id){  return resolveAsset(this.$options,'filters',id,true) || identity}

外部间接调用resolveAsset,将option对象,类型,过滤器id,以及一个触发正告的标记作为参数传递,如果找到,则返回过滤器;

resolveAsset的代码如下:

export function resolveAsset(options,type,id,warnMissing){ // 因为咱们找的是过滤器,所以在 resolveFilter函数中调用时 type 的值间接给的 'filters',理论这个函数还能够拿到其余很多货色  if(typeof id !== 'string'){ // 判断传递的过滤器id 是不是字符串,不是则间接返回      return   }  const assets = options[type]  // 将咱们注册的所有过滤器保留在变量中  // 接下来的逻辑便是判断id是否在assets中存在,即进行匹配  if(hasOwn(assets,id)) return assets[id] // 如找到,间接返回过滤器  // 没有找到,代码继续执行  const camelizedId  = camelize(id) // 万一你是驼峰的呢  if(hasOwn(assets,camelizedId)) return assets[camelizedId]  // 没找到,继续执行  const PascalCaseId = capitalize(camelizedId) // 万一你是首字母大写的驼峰呢  if(hasOwn(assets,PascalCaseId)) return assets[PascalCaseId]  // 如果还是没找到,则查看原型链(即拜访属性)  const result = assets[id] || assets[camelizedId] || assets[PascalCaseId]  // 如果仍然没找到,则在非生产环境的控制台打印正告  if(process.env.NODE_ENV !== 'production' && warnMissing && !result){    warn('Failed to resolve ' + type.slice(0,-1) + ': ' + id, options)  }  // 无论是否找到,都返回查找后果  return result}

上面再来剖析一下_s

_s 函数的全称是 toString,过滤器解决后的后果会当作参数传递给 toString函数,最终 toString函数执行后的后果会保留到Vnode中的text属性中,渲染到视图中

function toString(value){  return value == null  ? ''  : typeof value === 'object'    ? JSON.stringify(value,null,2)// JSON.stringify()第三个参数可用来管制字符串外面的间距    : String(value)}

最初,在剖析下parseFilters,在模板编译阶段应用该函数阶段将模板过滤器解析为过滤器函数调用表达式

function parseFilters (filter) {    let filters = filter.split('|')    let expression = filters.shift().trim() // shift()删除数组第一个元素并将其返回,该办法会更改原数组    let i    if (filters) {        for(i = 0;i < filters.length;i++){            experssion = warpFilter(expression,filters[i].trim()) // 这里传进去的expression实际上是管道符号后面的字符串,即过滤器的第一个参数        }    }    return expression}// warpFilter函数实现function warpFilter(exp,filter){    // 首先判断过滤器是否有其余参数    const i = filter.indexof('(')    if(i<0){ // 不含其余参数,间接进行过滤器表达式字符串的拼接        return `_f("${filter}")(${exp})`    }else{        const name = filter.slice(0,i) // 过滤器名称        const args = filter.slice(i+1) // 参数,但还多了 ‘)’        return `_f('${name}')(${exp},${args}` // 留神这一步少给了一个 ')'    }}

小结:

  • 在编译阶段通过parseFilters将过滤器编译成函数调用(串联过滤器则是一个嵌套的函数调用,前一个过滤器执行的后果是后一个过滤器函数的参数)
  • 编译后通过调用resolveFilter函数找到对应过滤器并返回后果
  • 执行后果作为参数传递给toString函数,而toString执行后,其后果会保留在Vnodetext属性中,渲染到视图

Vue我的项目中有封装过axios吗?次要是封装哪方面的?

一、axios是什么

axios 是一个轻量的 HTTP客户端

基于 XMLHttpRequest 服务来执行 HTTP 申请,反对丰盛的配置,反对 Promise,反对浏览器端和 Node.js 端。自Vue2.0起,尤大发表勾销对 vue-resource 的官网举荐,转而举荐 axios。当初 axios 曾经成为大部分 Vue 开发者的首选

个性

  • 从浏览器中创立 XMLHttpRequests
  • node.js 创立 http申请
  • 反对 Promise API
  • 拦挡申请和响应
  • 转换申请数据和响应数据
  • 勾销申请
  • 主动转换JSON 数据
  • 客户端反对进攻XSRF

根本应用

装置

// 我的项目中装置npm install axios --S// cdn 引入<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

导入

import axios from 'axios'

发送申请

axios({          url:'xxx',    // 设置申请的地址  method:"GET", // 设置申请办法  params:{      // get申请应用params进行参数凭借,如果是post申请用data    type: '',    page: 1  }}).then(res => {    // res为后端返回的数据  console.log(res);   })

并发申请axios.all([])

function getUserAccount() {    return axios.get('/user/12345');}function getUserPermissions() {    return axios.get('/user/12345/permissions');}axios.all([getUserAccount(), getUserPermissions()])    .then(axios.spread(function (res1, res2) {     // res1第一个申请的返回的内容,res2第二个申请返回的内容    // 两个申请都执行实现才会执行}));

二、为什么要封装

axios 的 API 很敌对,你齐全能够很轻松地在我的项目中间接应用。

不过随着我的项目规模增大,如果每发动一次HTTP申请,就要把这些比方设置超时工夫、设置申请头、依据我的项目环境判断应用哪个申请地址、错误处理等等操作,都须要写一遍

这种重复劳动不仅浪费时间,而且让代码变得冗余不堪,难以保护。为了进步咱们的代码品质,咱们应该在我的项目中二次封装一下 axios 再应用

举个例子:

axios('http://localhost:3000/data', {  // 配置代码  method: 'GET',  timeout: 1000,  withCredentials: true,  headers: {    'Content-Type': 'application/json',    Authorization: 'xxx',  },  transformRequest: [function (data, headers) {    return data;  }],  // 其余申请配置...}).then((data) => {  // todo: 真正业务逻辑代码  console.log(data);}, (err) => {  // 错误处理代码    if (err.response.status === 401) {  // handle authorization error  }  if (err.response.status === 403) {  // handle server forbidden error  }  // 其余错误处理.....  console.log(err);});

如果每个页面都发送相似的申请,都要写一堆的配置与错误处理,就显得过于繁琐了

这时候咱们就须要对axios进行二次封装,让应用更为便当

三、如何封装

  • 封装的同时,你须要和 后端协商好一些约定,申请头,状态码,申请超时工夫.......
  • 设置接口申请前缀:依据开发、测试、生产环境的不同,前缀须要加以辨别
  • 申请头 : 来实现一些具体的业务,必须携带一些参数才能够申请(例如:会员业务)
  • 状态码: 依据接口返回的不同status , 来执行不同的业务,这块须要和后端约定好
  • 申请办法:依据getpost等办法进行一个再次封装,应用起来更为不便
  • 申请拦截器: 依据申请的申请头设定,来决定哪些申请能够拜访
  • 响应拦截器: 这块就是依据 后端`返回来的状态码断定执行不同业务

设置接口申请前缀

利用node环境变量来作判断,用来辨别开发、测试、生产环境

if (process.env.NODE_ENV === 'development') {  axios.defaults.baseURL = 'http://dev.xxx.com'} else if (process.env.NODE_ENV === 'production') {  axios.defaults.baseURL = 'http://prod.xxx.com'}

在本地调试的时候,还须要在vue.config.js文件中配置devServer实现代理转发,从而实现跨域

devServer: {    proxy: {      '/proxyApi': {        target: 'http://dev.xxx.com',        changeOrigin: true,        pathRewrite: {          '/proxyApi': ''        }      }    }  }

设置申请头与超时工夫

大部分状况下,申请头都是固定的,只有少部分状况下,会须要一些非凡的申请头,这里将普适性的申请头作为根底配置。当须要非凡申请头时,将非凡申请头作为参数传入,笼罩根底配置

const service = axios.create({    ...    timeout: 30000,  // 申请 30s 超时      headers: {        get: {          'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'          // 在开发中,个别还须要单点登录或者其余性能的通用申请头,能够一并配置进来        },        post: {          'Content-Type': 'application/json;charset=utf-8'          // 在开发中,个别还须要单点登录或者其余性能的通用申请头,能够一并配置进来        }  },})

封装申请办法

先引入封装好的办法,在要调用的接口从新封装成一个办法裸露进来

// get 申请export function httpGet({  url,  params = {}}) {  return new Promise((resolve, reject) => {    axios.get(url, {      params    }).then((res) => {      resolve(res.data)    }).catch(err => {      reject(err)    })  })}// post// post申请export function httpPost({  url,  data = {},  params = {}}) {  return new Promise((resolve, reject) => {    axios({      url,      method: 'post',      transformRequest: [function (data) {        let ret = ''        for (let it in data) {          ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'        }        return ret      }],      // 发送的数据      data,      // url参数      params    }).then(res => {      resolve(res.data)    })  })}

把封装的办法放在一个api.js文件中

import { httpGet, httpPost } from './http'export const getorglist = (params = {}) => httpGet({ url: 'apps/api/org/list', params })

页面中就能间接调用

// .vueimport { getorglist } from '@/assets/js/api'getorglist({ id: 200 }).then(res => {  console.log(res)})

这样能够把api对立治理起来,当前保护批改只须要在api.js文件操作即可

申请拦截器

申请拦截器能够在每个申请里加上token,做了对立解决后保护起来也不便

// 申请拦截器axios.interceptors.request.use(  config => {    // 每次发送申请之前判断是否存在token    // 如果存在,则对立在http申请的header都加上token,这样后盾依据token判断你的登录状况,此处token个别是用户实现登录后贮存到localstorage里的    token && (config.headers.Authorization = token)    return config  },  error => {    return Promise.error(error)  })

响应拦截器

响应拦截器能够在接管到响应后先做一层操作,如依据状态码判断登录状态、受权

// 响应拦截器axios.interceptors.response.use(response => {  // 如果返回的状态码为200,阐明接口申请胜利,能够失常拿到数据  // 否则的话抛出谬误  if (response.status === 200) {    if (response.data.code === 511) {      // 未受权调取受权接口    } else if (response.data.code === 510) {      // 未登录跳转登录页    } else {      return Promise.resolve(response)    }  } else {    return Promise.reject(response)  }}, error => {  // 咱们能够在这里对异样状态作对立解决  if (error.response.status) {    // 解决申请失败的状况    // 对不同返回码对相应解决    return Promise.reject(error.response)  }})

小结

  • 封装是编程中很有意义的伎俩,简略的axios封装,就能够让咱们能够领略到它的魅力
  • 封装 axios 没有一个相对的规范,只有你的封装能够满足你的我的项目需要,并且用起来不便,那就是一个好的封装计划

MVVM的优缺点?

长处:

  • 拆散视图(View)和模型(Model),升高代码耦合,提⾼视图或者逻辑的重⽤性: ⽐如视图(View)能够独⽴于Model变动和批改,⼀个ViewModel能够绑定不同的"View"上,当View变动的时候Model不能够不变,当Model变动的时候View也能够不变。你能够把⼀些视图逻辑放在⼀个ViewModel⾥⾯,让很多view重⽤这段视图逻辑
  • 提⾼可测试性: ViewModel的存在能够帮忙开发者更好地编写测试代码
  • ⾃动更新dom: 利⽤双向绑定,数据更新后视图⾃动更新,让开发者从繁琐的⼿动dom中解放

毛病:

  • Bug很难被调试: 因为使⽤双向绑定的模式,当你看到界⾯异样了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得⼀个地位的Bug被疾速传递到别的地位,要定位原始出问题的地⽅就变得不那么容易了。另外,数据绑定的申明是指令式地写在View的模版当中的,这些内容是没方法去打断点debug的
  • ⼀个⼤的模块中model也会很⼤,尽管使⽤⽅便了也很容易保障了数据的⼀致性,过后⻓期持有,不开释内存就造成了破费更多的内存
  • 对于⼤型的图形应⽤程序,视图状态较多,ViewModel的构建和保护的老本都会⽐较⾼。

action 与 mutation 的区别

  • mutation 是同步更新, $watch 严格模式下会报错
  • action 是异步操作,能够获取数据后调用 mutation 提交最终数据

Vue模版编译原理晓得吗,能简略说一下吗?

简略说,Vue的编译过程就是将template转化为render函数的过程。会经验以下阶段:

  • 生成AST树
  • 优化
  • codegen

首先解析模版,生成AST语法树(一种用JavaScript对象的模式来形容整个模板)。 应用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相干解决。

Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变动,对应的DOM也不会变动。那么优化过程就是深度遍历AST树,依照相干条件对树节点进行标记。这些被标记的节点(动态节点)咱们就能够跳过对它们的比对,对运行时的模板起到很大的优化作用。

编译的最初一步是将优化后的AST树转换为可执行的代码

v-model 的原理?

咱们在 vue 我的项目中次要应用 v-model 指令在表单 input、textarea、select 等元素上创立双向数据绑定,咱们晓得 v-model 实质上不过是语法糖,v-model 在外部为不同的输出元素应用不同的属性并抛出不同的事件:

  • text 和 textarea 元素应用 value 属性和 input 事件;
  • checkbox 和 radio 应用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

以 input 表单元素为例:

<input v-model='something'>相当于<input v-bind:value="something" v-on:input="something = $event.target.value">

如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:

父组件:<ModelChild v-model="message"></ModelChild>子组件:<div>{{value}}</div>props:{    value: String},methods: {  test1(){     this.$emit('input', '小红')  },},

应用vue渲染大量数据时应该怎么优化?说下你的思路!

剖析

企业级我的项目中渲染大量数据的状况比拟常见,因而这是一道十分好的综合实际题目。

答复

  1. 在大型企业级我的项目中常常须要渲染大量数据,此时很容易呈现卡顿的状况。比方大数据量的表格、树
  2. 解决时要依据状况做不同解决:
  3. 能够采取分页的形式获取,防止渲染大量数据
  • vue-virtual-scroller (opens new window)等虚构滚动计划,只渲染视口范畴内的数据
  • 如果不须要更新,能够应用v-once形式只渲染一次
  • 通过v-memo (opens new window)能够缓存后果,联合v-for应用,防止数据变动时不必要的VNode创立
  • 能够采纳懒加载形式,在用户须要的时候再加载数据,比方tree组件子树的懒加载
  • 还是要看具体需要,首先从设计上防止大数据获取和渲染;切实须要这样做能够采纳虚表的形式优化渲染;最初优化更新,如果不须要更新能够v-once解决,须要更新能够v-memo进一步优化大数据更新性能。其余能够采纳的是交互方式优化,无线滚动、懒加载等计划

computed 的实现原理

computed 实质是一个惰性求值的观察者。

computed 外部实现了一个惰性的 watcher,也就是 computed watcher,computed watcher 不会立即求值,同时持有一个 dep 实例。

其外部通过 this.dirty 属性标记计算属性是否须要从新求值。

当 computed 的依赖状态产生扭转时,就会告诉这个惰性的 watcher,

computed watcher 通过 this.dep.subs.length 判断有没有订阅者,

有的话,会从新计算,而后比照新旧值,如果变动了,会从新渲染。 (Vue 想确保不仅仅是计算属性依赖的值发生变化,而是当计算属性最终计算的值发生变化时才会触发渲染 watcher 从新渲染,实质上是一种优化。)

没有的话,仅仅把 this.dirty = true。 (当计算属性依赖于其余数据时,属性并不会立刻从新计算,只有之后其余中央须要读取属性的时候,它才会真正计算,即具备 lazy(懒计算)个性。)

说下$attrs和$listeners的应用场景

API考查,但$attrs$listeners是比拟少用的边界常识,而且vue3有变动,$listeners曾经移除,还是有细节可说的

体验

一个蕴含组件透传属性的对象

<template>    <child-component v-bind="$attrs">        将非属性个性透传给外部的子组件    </child-component></template>

答复范例

  • 咱们可能会有一些属性和事件没有在props中定义,这类称为非属性个性,联合v-bind指令能够间接透传给外部的子组件。
  • 这类“属性透传”经常用于包装高阶组件时往外部传递属性,罕用于爷孙组件之间传参。比方我在扩大A组件时创立了组件B组件,而后在C组件中应用B,此时传递给C的属性中只有props外面申明的属性是给B应用的,其余的都是A须要的,此时就能够利用v-bind="$attrs"透传下去。
  • 最常见用法是联合v-bind做开展;$attrs自身不是响应式的,除非拜访的属性自身是响应式对象。
  • vue2中应用listeners获取事件,vue3中已移除,均合并到attrs中,应用起来更简略了

原理

查看透传属性foo和一般属性bar,发现vnode构造完全相同,这阐明vue3中将分辨两者工作由框架实现而非用户指定:

<template>  <h1>{{ msg }}</h1>  <comp foo="foo" bar="bar" /></template>
<template>  <div>    {{$attrs.foo}} {{bar}}  </div></template><script setup>defineProps({  bar: String})</script>
_createVNode(Comp, {    foo: "foo",    bar: "bar"})

keep-alive 中的生命周期哪些

keep-alive是 Vue 提供的一个内置组件,用来对组件进行缓存——在组件切换过程中将状态保留在内存中,避免反复渲染DOM。

如果为一个组件包裹了 keep-alive,那么它会多出两个生命周期:deactivated、activated。同时,beforeDestroy 和 destroyed 就不会再被触发了,因为组件不会被真正销毁。

当组件被换掉时,会被缓存到内存中、触发 deactivated 生命周期;当组件被切回来时,再去缓存里找这个组件、触发 activated钩子函数。

构建的 vue-cli 工程都到了哪些技术,它们的作用别离是什么

  • vue.jsvue-cli工程的外围,次要特点是 双向数据绑定 和 组件零碎。
  • vue-routervue官网举荐应用的路由框架。
  • vuex:专为 Vue.js 利用我的项目开发的状态管理器,次要用于保护vue组件间共用的一些 变量 和 办法。
  • axios( 或者 fetchajax ):用于发动 GET 、或 POSThttp申请,基于 Promise 设计。
  • vuex等:一个专为vue设计的挪动端UI组件库。
  • 创立一个emit.js文件,用于vue事件机制的治理。
  • webpack:模块加载和vue-cli工程打包器。