阅读 3009

【灵魂拷问】Vue高级知识

本文保证问题只增不减,使得知识面半径不断加长

webpack打包vue速度太慢怎么办?

  1. 配置externals
  2. 代码压缩用ParallelUglifyPlugin代替自带的 UglifyJsPlugin插件
  3. 不需要打包编译的插件库换成全局<script>标签引入的方式
  4. 使用CommonsChunkPlugin提取公共的模块,可以减少文件体积,也有助于浏览器层的文件缓存
// 提取公共模块文件
        new webpack.optimize.CommonsChunkPlugin({
            chunks: ['home', 'detail'],
            // 开发环境下需要使用热更新替换,而此时common用chunkhash会出错,可以直接不用hash
            filename: '[name].js' + (isProduction ? '?[chunkhash:8]' : ''),
            name: 'common'
        }),




// 切合公共模块的提取规则,有时后你需要明确指定默认放到公共文件的模块
// 文件入口配置
    entry: {
        home: './src/js/home',
        detail: './src/js/detail',
        // 提取jquery入公共文件
        common: ['jquery', 'react', 'react-dom']
    },
复制代码
  1. 使用HappyPack来加速构建

HappyPack会采用多进程去打包构建,使用方式还是蛮简单的,但并不是支持所有的loader 首先引入,定义一下这个插件所开启的线程,推荐是四个,其实也可以直接使用默认的就行了

HappyPack = require('happypack'),
    os = require('os'),
    happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
复制代码

然后在module的规则里改动一下,引入它,其中 id是一个标识符

{
    test: /\.jsx?$/,
    // 编译js或jsx文件,使用babel-loader转换es6为es5
    exclude: /node_modules/,
    loader: 'HappyPack/loader?id=js'
    // use: [{
    //     loader: 'babel-loader',
    //     options: {

    //     }
    // }]
}
复制代码

然后我们调用插件,设置匹配的id,然后相关的配置可以直接把use:的规则部分套在loaders上

new HappyPack({
    id: 'js',
    loaders: [{
        loader: 'babel-loader',
        options: {
            // cacheDirectory: true
        }
    }]
}),
复制代码

要注意的第一点是,它对file-loader和url-loader支持不好,所以这两个loader就不需要换成happypack了,其他loader可以类似地换一下

要注意的第二点是,使用ExtractTextWebpackPlugin提取css文件也不是完全就能转换过来,所以需要小小的改动一下,比如

module: {
  rules: [{
      test: /\.css$/,
      // loader: 'HappyPack/loader?id=css'
      // 提取CSS文件
      use: cssExtractor.extract({
          // 如果配置成不提取,则此类文件使用style-loader插入到<head>标签中
          fallback: 'style-loader',
          use: 'HappyPack/loader?id=css'
          // use: [{
          //         loader: 'css-loader',
          //         options: {
          //             // url: false,
          //             minimize: true
          //         }
          //     },
          //     // 'postcss-loader'
          // ]
      })
  }, {
      test: /\.scss$/,
      // loader: 'HappyPack/loader?id=scss'
      // 编译Sass文件 提取CSS文件
      use: sassExtractor.extract({
          // 如果配置成不提取,则此类文件使用style-loader插入到<head>标签中
          fallback: 'style-loader',
          use: 'HappyPack/loader?id=scss'
          // use: [
          //     'css-loader',
          //     // 'postcss-loader',
          //     {
          //         loader: 'sass-loader',
          //         options: {
          //             sourceMap: true,
          //             outputStyle: 'compressed'
          //         }
          //     }
          // ]
      })
  }
复制代码

因为它是直接函数调用的,我们就放到里层的use规则就行了,然后配置插件即可

plugins: [
  new HappyPack({
      id: 'css',
      loaders: [{
          loader: 'css-loader',
          options: {
              // url: false,
              minimize: true
          }
      }]
  }),
  new HappyPack({
      id: 'scss',
      loaders: [{
          'loader': 'css-loader'
      }, {
          loader: 'fast-sass-loader',
          options: {
              sourceMap: true,
              outputStyle: 'compressed'
          }
      }]
  }),
复制代码
  1. 有些插件以模块化来引入,这样主要引入有用的代码
// 原来的引入方式
 import {debounce} from 'lodash';

//按模块化的引入方式
import debounce from 'lodash/debounce';
复制代码
  1. 使用异步的模块加载

这个算是可以减小模块的体积吧,在一定程度上也是为用户考虑的,使用require.ensure来设置哪些模块需要异步加载,webpack会将它打包到一个独立的chunk中,

在某个时刻(比如用户点击了查看)才异步地加载这个模块来执行

$('.bg-input').click(() => {
    console.log('clicked, loading async.js')

    require.ensure([], require => {

        require('./components/async2').log();
        require('./components/async1').log();
        console.log('loading async.js done');
    });
});
复制代码
  1. 有时不需要解析某些模块的依赖(这些模块并没有依赖,或者并根本就没有模块化),使用noParse,直接跳过解析
module: {
  noParse: /node_modules\/(jquey\.js)/
}
复制代码
  1. 优化构建时的搜索路径

在webpack打包时,会有各种各样的路径要去查询搜索,我们可以加上一些配置,让它搜索地更快

比如说,方便改成绝对路径的模块路径就改一下,以纯模块名来引入的可以加上一些目录路径

还可以善于用resolve alias别名 这个字段来配置

还有exclude等的配置,避免多余查找的文件,比如使用babel别忘了剔除不需要遍历的

{
    test: /\.jsx?$/,
    // 编译js或jsx文件,使用babel-loader转换es6为es5
    exclude: /node_modules/,
     use: [{
         loader: 'babel-loader',
         options: {

         }
     }]
}
复制代码
  1. 理一下打包构建涉及的模块,分析看有哪些包是不需要打包的,只打包需要的模块

webpack编译时加上参数 --json > stat.json 后,可以上传到 webpack-analyse 、webpack-visualizer 等分析站点上,看看打包的模块信息

  1. 提取公共代码

如何解决vue打包vendor过大的问题?

  1. 减少公共包大小:vendor打包过大的原因就是引用三方插件的js太大了 ,可以直接在index的script中引用镜像解决问题;

  2. 模板包不会太大:vue-router按需引入,这样会拆包

Vue首页白屏是什么问题引起的?如何解决呢?

首页白屏问题:

首页加载过慢,是因为它是一个单页应用,需要将所有需要的资源都下载到浏览器端并解析。所以页面越大,加载时间越长,而且js执行的时间也长,dcl发生的时间点就更晚,所以会白屏

解决办法:

  1. 优化 webpack 减少模块打包体积,code-split 按需加载
  2. 服务端渲染,在服务端事先拼装好首页所需的 html
  3. 首页加 loading 或 骨架屏 (仅仅是优化体验)
  4. 服务端开启gzip压缩
  5. 打包文件分包,提取公共文件包

如何优化首页的加载速度?

  1. 浏览器缓存
  2. gzip压缩
  3. 内容分发网络(CDN)
  4. 异步脚本
<script async src=""></script>
复制代码
  1. 优化JavaScript、HTML和CSS代码,删除所有不必要的空格和注释,从而减小文件大小
  2. JavaScript的延迟解析,通过延迟解析脚本,可以减少初始网站的加载时间
  3. 启用Keep Alive
  4. 不使用内联CSS
  5. 避免阻塞型的JavaScript和CSS

推迟加载那些不重要的JavaScript,或者采用异步加载的方式。另一种选择是将这些HTML代码内嵌到网站上,同时需要确保CSS的优化。

  1. 图像和文件格式

建议使用JPEG格式,而不是GIF和PNG图像,除非图像包含Alpha因子或者是透明的。

  1. 文件分离

网站的文件可以分为CSS、JavaScripts和图像。文件分离虽然并不能直接改善网站的加载时间。但是,这么做可以提高服务器的稳定性,特别是当网站流量突然出现了尖峰的时候。子域也可以用于托管文件,这样可以增加并行下载的数量。

  1. 尽量减少HTTP请求

当一个网站一下子收到太多的HTTP请求,它的访客就会有响应时间延迟的体验,这不仅增加了CPU使用率也增加了页面的加载时间。那么,又该如何减少HTTP请求?请见以下步骤。

  • 减少网站上的对象数量。
  • 最小化网站上的重定向数量。
  • 使用CSS Sprites技术(只要你需要的那部分图片内容)。
  • 结合JavaScripts和CSS。

分析下Vue项目本地开发完成后部署到服务器后报404是什么原因呢?

  1. 检查nginx配置,是否正确设置了资源映射条件

  2. 检查vue.config.js中是否配置了publicPath,若有则检查是否和项目资源文件在服务器摆放位置一致

vue部署上线前需要做哪些准备工作?

  1. 检查配置环境是否正确
  2. git版本是否正确
  3. 打包路劲是否正确
  4. ngnix配置
  5. 删除console

Vue开发规范有哪些?

  • 强制

    • 组件名为多个单词
    export default {
    
     name: 'TodoItem',
     // ...
    }
    反例:
    export default {
    name: 'Todo',
     // ...
    }
    复制代码
    • 组件的data必须是一个函数
    • prop定义尽量详细,包含类型、默认值、是否必须、validate
    • 为for设置key
    • 避免v-if和v-for同时使用
    • 为组件样式设置作用域
    • 用完整单词
  • 建议

    • 使用组件文件
    components/TodoItem.vue
    复制代码
    • 缩写指令
  • 谨慎使用 (有潜在危险的模式)

    • scoped使用元素选择器
    • 优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop
    • 没有在 v-if/v-if-else/v-else 中使用 key

    如果一组 v-if + v-else 的元素类型相同,最好使用 key

父组件中使用插槽时怎么访问子组件的数据

子组件

<template>
    <div>
        <h2>默认插槽</h2>
        <slot :toParent="childrenData" :toParent2="childrenData2">
            {{childrenData.data1}}
            {{childrenData2.data1}}
        </slot>
        <h2>头部插槽</h2>
        <slot name="header" :headerData="headerData">
            {{headerData.data1}}
        </slot>
    </div>
</template>
<script>
    export default{
        data(){
            return{
               childrenData:{
                   data1:'子组件数据一',
                   data2:'子组件数据二',
               },
               childrenData2:{
                   data1:'子组件数据三',
                   data2:'子组件数据四',
               },
               headerData:{
                   data1:'子组件头部数据一',
                   data2:'子组件头部数据二',
               },
            }
        },
    }
</script>

复制代码

slotProps是代表插槽prop的集合,其命名可以随便取的。此时slotProps.toParent的值就是childrenData的值

父组件

<template>
    <div>
        <myComponent>
        
        	// slotProps和headerProps是代表插槽prop的集合,其命名可以随便取的
            <template v-slot:default="slotProps">
                {{slotProps.toParent.data2}}
                {{slotProps.toParent2.data2}}
            </template>
            <template v-slot:header="headerProps">
                {{headerProps.headerData.data2}}
            </template>
            
            //当有子组件slot上有多个插槽prop时,父组件调用时候可以ES6对象解构的方法
            <template v-slot:default="{toParent,toParent2}">
                {{toParent.data2}}
                {{toParent2.data2}}
            </template>
            <template v-slot:header="{headerData}">
                {{headerData.data2}}
            </template>
            
            
            //也可以给子组件slot上绑定的值重新命名
            <template #default="{toParent:a,toParent2:b}">
                {{a.data2}}
                {{b.data2}}
            </template>
            <template #header="{headerData:c}">
                {{c.data2}}
            </template>


        </myComponent>
    </div>
</template>
复制代码

对DOM选项el、template、render的理解?

  • el:提供一个在页面上已存在的DOM元素作为Vue实例的挂载目标。可以是CSS选择器,也可以是一个HTMLElement实例。
    • 因为所有的挂载元素会被Vue生成的DOM替换。因此不推荐挂载Vue实例到html或者body上。
    • 如果在const vm = new Vue({})中存在这个选项,实例将立即进入编译过程,否则,需要显式调用vm.$mount()手动开启编译。

vm = new Vue({})中存在el

<script>
    const vm= new Vue({
        el:'#app',
        data:{
            age:17
        },
    }
</script>
复制代码

要显式调用vm.$mount()手动开启编译

<script>
    const vm= new Vue({
        data:{
            age:17
        },
    })
    vm.$mount('#app')
</script>
复制代码
  • template:一个字符串模板作为Vue实例的标识使用。如果el存在,模板将会替换挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发插槽。
    • 如果值以 # 开始,则它将被用作选择符,并使用匹配元素的 innerHTML 作为模板。
<script>
    const vm= new Vue({
        el:'#app',
        data:{
            age:17
        },
        template:'<div>我是template的内容:小明今年{{age}}岁了</div>',
    })
</script>

复制代码

<script type="x-template" id="mb">和 # 结合

<script type="x-template" id="mb">
    <div>我是template的内容:小明今年{{age}}岁了</div>
</script>
<script>
    const vm= new Vue({
        el:'#app',
        data:{
            age:17
        },
        template:'#mb',
    })
</script>

复制代码

template 、id和 # 结合

<body>
    <div id="app">
        我是el挂载的内容:小明今年{{age}}岁了
    </div>
    <template id="mb">
        <div>我是template的内容:小明今年{{age}}岁了</div>
    </template>
</body>
<script>
    const vm= new Vue({
        el:'#app',
        data:{
            age:17
        },
        template:'#mb',
    })
</script>
复制代码
  • render :Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数
<body>
    <div id="app">
        我是el挂载的内容:小明今年{{age}}岁了
    </div>
</body>
<script>
    const vm= new Vue({
        el:'#app',
        data:{
            age:17
        },
        template:'<div>我是template的内容:小明今年{{age}}岁了</div>',
        render(h){
            return h('div',`我是render的内容:小明今年${this.age}岁了`)
        }
    })
</script>
复制代码

自定义指令的生命周期(钩子函数)有哪些?如何手写一个自定义指令?

  • bind:只调用一次,在指令第一次绑定到元素时调用,可以在这个钩子函数中进行初始化设置;
  • inserted:被绑定元素插入父节点时调用,在bind后面调用;
  • update:所在绑定的组件的VNode更新时调用,但是可能发生在其子VNode更新之前。

调用时指令的值不一定发生改变,通过比较更新前后的值来忽略不必要的模板更新;

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用;
  • unbind:只调用一次,指令与元素解绑时调用。
Vue.directive('color', {
    bind:function(){
        alert('bind')
    },
    inserted: function (el,binding) {
        alert('inserted')
        el.style.color=binding.value;
    },
    update:function(el,binding){
        alert('update')
        el.style.color=binding.value;
    },
    componentUpdated:function(el,binding){
        alert('componentUpdated')
        el.style.color=binding.value;
    },
    unbind:function(){
       alert('v-color指令解绑')
    }
})
复制代码

render函数有什么好处?

template也会翻译成render,只有一点,template中元素的tag_name是静态的,不可变化,使用createEelment可以生成不同tag_name, 比如h1 ... h6, 可以通过一个number变量控制

vue挂载过程

看我的另一篇细讲:【Vue原理】$mounted源码分析

你了解什么是高阶组件吗?

高阶组件(HOC)的定义:接收一个组件作为参数,返回一个新的组件

高阶组件(HOC)的特点:

  1. 应该是无副作用的纯函数,且不应该修改原组件,即原组件不能有变动
  2. 不关心你传递的数据(props)是什么,并且新生成组件不关心数据来源
  3. 接收到的 props 应该透传给被包装组件即直接将原组件prop传给包装组件
  4. 完全可以添加、删除、修改 props

实例1

<template>
  <div>
    <p @click="Click">props: {{test}}</p>
  </div>
</template>
<script>
export default {
  name: 'Base',
  props: {
    test: Number
  },
  methods: {
    Click () {
      this.$emit('Base-click')
    }
  }
}
</script>
复制代码

实例2

export default function Console (BaseComponent) {
  return {
    template: '<wrapped v-on="$listeners" v-bind="$attrs"/>',
    components: {
      wrapped: BaseComponent
    },
    mounted () {
      console.log('haha')
    }
  }
}
复制代码

Vue.observable有了解过吗?

让一个对象可响应。可以作为最小化的跨组件状态存储器。有点类似于小型的Vuex

const store = Vue.observable({name:'hhy'});
const mutation = {
	updateName(name){
    	store.name = name
    }
}
store和mutaion可以分别在computed和methods中调用
复制代码

怎么解决Vue中动态设置img的src不生效的问题?

因为动态添加src被当做静态资源处理了,没有进行编译,所以要加上require

component 标签和 is 特殊特性在组件上的应用

  1. 动态组件
<component :is="componentName"></component>
复制代码

componentName可以是在本页面已经注册的局部组件名和全局组件名,也可以是一个组件的选项对象。

当控制componentName改变时就可以动态切换选择组件。

  1. is的用法

有些HTML元素,诸如 <ul>、<ol>、<table> 和 <select>,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如 <li>、<tr> 和 <option>,只能出现在其它某些特定的元素内部。

<ul>
    <name-list></name-list>
</ul>
复制代码

上面<name-list></name-list>会被作为无效的内容提升到外部,并导致最终渲染结果出错。应该这么写

<ul>
    <li is="nameList"></li>
</ul>
复制代码

删除对象delete和Vue.delete有什么区别?

  • delete:只是被删除对象成员变为' '或undefined,其他元素键值不变;
  • Vue.delete:直接删了对象成员,如果对象是响应式的,确保删除能触发更新视图,这个方法主要用于避开 Vue 不能检测到属性被删除的限制。

你有用过事件总线(EventBus)吗?说说你的理解

通过一个空的vue实例作为桥梁实现vue组件间的通信。它是实现非父子组件通信的一种解决方案。

  1. 创建一个vue实例,导出这个实例
import Vue from 'Vue'
export default new Vue
复制代码
  1. 在两个需要通信的两个组件中分别引入这个bus.js
import Bus from '这里是你引入bus.js的路径' // Bus可自由更换喜欢的名字
复制代码
  1. 传递数据的组件里通过vue实例方法$emit发送事件名称和需要传递的数据。(发送数据组件)
Bus.$emit('click',data) // 这个click是一个自定义的事件名称,data就是你要传递的数据。
复制代码
  1. 被传递数据的组件内通过vue实例方法$on监听到事件和接受到数据。
Bus.$on('click',target => {
 console.log(target)  // 注意:发送和监听的事件名称必须一致,target就是获取的数据,可以不写target。只要你喜欢叫什么都可以(当然了,这一定要符合形参变量的命名规范)
})
复制代码
  1. 在vue生命周期beforeDestroy或者destroyed中用vue实例的$off方法清除eventBus
beforeDestroy(){
    bus.$off('click')
}
复制代码

EventBus注册在全局上时,路由切换时会重复触发事件,如何解决呢?

有使用$on的组件中要在beforeDestroy钩子函数中用$off销毁。

使用Vue后怎么针对搜索引擎做SEO优化?

SEO:

  • 网站结构:网站结构要清晰、要扁平
  • 导航:页面应该要有简明的导航。导航可以让搜索引擎知道网站的结构,也可以让搜索引擎知道当前页面在网站结构所在的层次。
  • 规范的URL: url尽量简短,尽量减少动态url中包含的变量参数。
  • 提交Sitemap:Sitemap 可通知搜索引擎他们网站上有哪些可供抓取的网页,以便搜索引擎可以更加智能地抓取网站。
  • 合理的HTTP返回码:不同的返回码,搜索引擎的处理逻辑是不一样的
  • 合适的title:告诉搜索引擎网页的主要内容
  • 合适的description
  • HTML语义化

对于SEO支持比较好的项目方案是采用服务端渲染。所以如果项目有SEO需求,那么比较好的方案是服务端渲染。

Vue为什么要求组件模板只能有一个根元素?

let vm = new Vue({
	el:'#app'
})

<body>
  <div id='app1'></div>
  <div id='app2'></div>
</body>
复制代码

Vue其实并不知道哪一个才是我们的入口,因为对于一个入口来讲,这个入口就是一个‘Vue类',Vue需要把这个入口里面的所有东西拿来渲染,处理,最后再重新插入到dom中。

如果同时设置了多个入口,那么vue就不知道哪一个才是这个‘类'。

为什么template下必须有且只能有一个div呢?

template标签是HTML5出来的新标签,它有三个特性:

1.隐藏性:该标签不会显示在页面的任何地方,即便里面有多少内容,它永远都是隐藏的状态;

2.任意性:该标签可以写在页面的任何地方,甚至是head、body、sciprt标签内;

3.无效性:该标签里的任何HTML内容都是无效的,不会起任何作用;

但是呢,可以通过innerHTML来获取到里面的内容。

其实本质上,一个单文件组件,本质上会被各种各样的loader处理成为.js文件(因为当你import一个单文件组件并打印出来的时候,是一个vue实例),通过template的任意性我们知道,template包裹的HTML可以写在任何地方,那么对于一个.vue来讲,这个template里面的内容就是会被vue处理为虚拟dom并渲染的内容,导致结果又回到了开始 :既然一个.vue单文件组件是一个vue实例,那么这个实例的入口在哪里?

如果在template下有多个div,那么该如何指定这个vue实例的根入口?

为了让组件能够正常的生成一个vue实例,那么这个div会被自然的处理成程序的入口。

通过这个‘根节点',来递归遍历整个vue‘树'下的所有节点,并处理为vdom,最后再渲染成真正的HTML,插入在正确的位置

那么这个入口,就是这个树的‘根',各个子元素,子组件,就是这个树的‘枝叶',而自然而然地,这棵‘树',就是指一个vue实例了。

你知道nextTick的原理吗?

看我的另一篇文章:【Vue原理】$nextTick源码分析

说下你对Vue的template编译的理解?

简单的说法:先转化为AST树,在得到的render函数中返回VNode(vue的虚拟DOM节点)

  1. 通过compile编译器对template字符串进行解析,将标签、指令、属性等编译成AST语法树(即源代码的抽象语法结构的树状表现形式),compile是creatCompiler(createCompiler是用以创建编译器的)的返回值,compile负责合并option
AST:

Node {
  type: 'File',
  start: 0,
  end: 156,
  loc: 
   SourceLocation {
     start: Position { line: 1, column: 0 },
     end: Position { line: 10, column: 0 } },
  errors: [],
  program: 
   Node {
     type: 'Program',
     start: 0,
     end: 156,
     loc: SourceLocation { start: [Object], end: [Object] },
     sourceType: 'module',
     interpreter: null,
     body: [ [Object], [Object], [Object], [Object], [Object], [Object] ],
     directives: [] },
  comments: [] }
复制代码
  1. AST会经过generate(将AST语法树转化成render function字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面包含(标签名、子节点、文本等)

template预编译是什么?

预编译:在打包阶段预编译成render函数(也就是项目的构建过程中完成)

因为如果可以的话,就可以去掉vue源码中的compile部分,进一步减小体积;同时可以让实际组件在runtime时跳过模板渲染,进而提升性能

对于Vue组件来说,vue的template是在运行时转换为render函数的,模板编译只会在组件实例化时编译一次,生成render function不会再进行编译,因此,编译对组件的runtime是一种性能损耗。所以思考预编译。

Vue实例挂载的过程是怎样的?

实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-with-compiler.js & src/platforms/web/runtime/index.js 等文件中都有对Vue.prototype.$mount的定义

在$mount方法中,会先判断options中 el 是否存在,再判断 render (有template存在的条件下也需要有render函数),之后再是再判断template,会对template做一定的校验,最后使用 compileToFunctions 将template转化为render 和 staticRenderFns

为何官方推荐使用axios而不用vue-resource?

  1. vue-resources不再更新了,vue作者尤大推荐axios。
  2. axios更加强大
  3. axios就是一个基于ES6的Promise的网络请求库,其实说干净了就是一个打包好的XMLHttpRequests,也就是说,这个也是一个ajax库。
  4. axios 在浏览器里建立XHR,通过nodejs进行http请求,转换或者拦截请求数据或响应数据,支持Promise的API,可以取消请求,自动转换JSON,可以防御XSRF攻击!
  5. vue-resources 只提供了浏览器版本

vue中 axios怎么封装?

import axios from 'axios'
axios.defaults.baseURL = ...
axios.defaults.timeout = 10000;

axios.defaults.headers.post['Content-Type'] = 'application/json';
//请求拦截
axios.interceptors.request.use(config => {
   //可在此处登入
  return config;
}, error => {
  return Promise.reject(error);
});
//响应拦截
axios.interceptors.response.use(response => {
  // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
  if (response.status === 200) { 
    if (response.data.code === 900) {
      Message.error(response.data.msg)
      //可在此处登出
    }
    return Promise.resolve(response);
  } else {            
    return Promise.reject(response);
  }
}, error => {
  if (error.response && error.response.status === 401) {
    window.localStorage.removeItem('token');
  }
});

/**
 * axios封装get和post方法
 */
function get(url, params, config = {}) {    
  return new Promise((resolve, reject) => {
    axios.get(url, {
      params,
      ...config,
    }).then(res => {
      resolve(res.data);
    }).catch(err => {
      reject(err.data);
    })    
  });
}
function post(url, params, config = {}) {
  return new Promise((resolve, reject) => {
    axios.post(url, params, {
      ...config,
    })
      .then(res => {
        resolve(res.data);
      })
      .catch(err => {
        reject(err.data);
      })
  });
}
复制代码

Vue切换页面时碰见过中断axios请求的场景吗?如何中断?

场景:在Vue单页面开发过程中,遇到这样的情况,当我切换页面时,由于上一页面请求执行时间长,切换到该页面时,还未执行完,这时那个请求仍会继续执行直到请求结束,此时将会影响页面性能,并且可能对现在页面的数据显示造成一定影响。

所以我们应该,切换页面前中断前面所有请求

  1. 使用cancelToken.sourse工厂方法创建cancel token
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
     // 处理错误
  }
});
axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
复制代码
  1. 通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel toke
const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// cancel the request
cancel();
复制代码

如果将axios异步请求同步化处理?

使用 asyns/await 将 axios 异步请求同步化

async getData (params) {
  try {
    let res = await axios.get('url', {
      params
    })
    this.tableData = res.data.result
  } catch (err) {
    console.log(err)
  }
}
复制代码

也可以定义new Promise,再用asyns/await使用它

function getData (data) {
  return new Promise((resolve, reject) => {
    axios.get('url', {
      params: data
    }).then((res) => {
      resolve(res)
    }).catch((err) => {
      reject(err)
    })
  })
}
async function useFun(){
	try {
      let res = await this.getData()
      console.log(res)
      //拿到返回数据res后再进行处理
    } catch (err) {
      console.log(err)
    } 
}
复制代码

axios的原理和源码分析?

Axios源码深度剖析

www.cnblogs.com/imwtr/p/780…

juejin.cn/post/684490… (vue和react区别)

文章分类
前端
文章标签