vue笔记日渐整理(每天更新)

6 篇文章 0 订阅

vue

一、什么是vue

是前端的一个框架
是一个渐进式框架
基于mvvm的框架

渐进式:不强求一次性使用和接受他的所有功能。如果你想用其中的一个或者多个再引用相对应的功能就好了。
mvvm
m:数据对象。
v:前端展示页面。
vm:双向绑定数据。vue的实例。数据改变视图,自动刷新。

二、vue开发特点

开发模式

  1. 声明式开发:程序告诉机器我要干什么,怎么我不管。 组件化开发 自定义开发 将标签成为组件 虚拟dom
  2. 命令式开发:应用程序 业务逻辑 开发将每一步实现步骤都需要代码实现。

三、vm

啥呀?答:初始化 Vue的实例
在这里插入图片描述

//  body中:
	<div>{{msg}}</div>
//  script中
	<script>
        const vm=new Vue({
            el:'#app',
            data:{
                msg:'这是一个msg'
            },
            methods:{
                changeMsg(){
                    this.msg='这是一个修改的msg'
                }
            }
        })
        console.log(vm)   //  打印的vm如上图所示
    </script>

四、vue模板

啥呀? 答:类似ejs 在视图上渲染vm上面的数据 {{ }}

输出表达式要加引号

{{msg}}
{{'这是一个字符串'}}

显示:
在这里插入图片描述
若是:

{{这是一个字符串}}

会报错!

必须是表达式

变量

js定义的变量不可以哦

data:{
	msg:'这是一个msg'
}

这里的这个msg会挂载到vm实例上
我们在模板中引用的变量去vm实例上去找

全局变量 白名单 白名单内部全局变量 全局变量可以在模板中使用

白名单:

‘Infinity,undefined,NaN,isFinite,isNaN,’ +
‘parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,’ +
‘Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,’ +
‘require’

全局使用后最终结果是一个值

五、抛弃原来操作dom语法 一切以数据驱动

指令

干哈用的:将视图上元素不同状态和不同m绑定 打通视图和数据之间的关联
怎么写:扩展了html标签属性的语法

<标签 v-指令名="值">

六、常用指令

v-text

将元素的文本内容与一个数据进行绑定

msg的值为:“这是一个值”
<div v-text="msg"></div>   //  msg的值会在div的内容上显示

v-html

渲染富文本

msg的值为:“<h1>这是一个值</h1>”
<div v-html="msg"></div>  //  div里面的内容为标题一的 “这是一个值”

v-show

将元素的显示隐藏状态和一个布尔值进行绑定

v-show的值为真,元素显示
v-show的值为假,元素隐藏

v-bind:属性

属性:可以是任何属性,html里面的属性,或者自定义的都可以。
万能指令:没有特定的功能,将一个普通属性变成一个动态属性。将属性的值和数据进行绑定。
简写:v-bind:属性名 ==》 :属性名

<img :src="imgArr"/>
//  将src属性值绑定到imgArr的值,简而言之imgArr值是什么src的值就是什么

v-model

将表单控件的值与vm上数据进行双向绑定

v-on:事件

将原生事件合成为vue事件(将事件函数变成vm上的方法)直接绑定事件,在事件中修改vm上的数据
简写:@事件名
事件绑定vm上的方法有两种方式:
1 绑定时不加括号

<button @click="fn"></button>
methods:{
  fn(){
  //  这是一个挂载在vm上面的方法
  }
}

用于:触发事件
在实例上找,没有找到就报错
2 绑定时加括号

<button @click="fn(参数一,参数二,......)"></button>
methods:{
  fn(参数一,参数二,......){
  //  这是一个挂载在vm上面的方法
  }
}

用于:事件触发时传递参数
在实例上找,没有找到就报错

七、vue事件绑定 (event)

绑定事件时,事件不加括号,那么第一个参数就是事件对象

<button @click="fn"></button>
methods:{
  fn(e){
  //  这里的e就是事件对象
  }
}

绑定事件时,事件加括号
定义一个全局变量$event传入调用即可

<button @click="fn(3, $event)">按钮</button>
methods: {
    fn(n, e){
      // e就是事件对象
    }
  }

八、事件修饰符

用于:修饰事件

.stop:取消事件冒泡

<div id="app">
    <div class="div1" @click="fn1">
      <div class="div2" @click="fn2">
        <div class="div3" @click="fn3">
          <div class="div4" @click="fn4">
            <div class="div5" @click="fn5">
              <div class="div6" @click="fn6">
                <div class="div7" @click="fn7">
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

如果这种嵌套形式(祖宗套爷爷,爷爷套父亲,父亲套儿子…),给每个div绑定一个事件,点击儿子的时候也会触发父亲、爷爷、祖宗的事件,执行的顺序为 7 6 5 4 3 2 1(从里面往外面触发)。
在这里插入图片描述

取消冒泡事件:
在某个div事件之后添加取消冒泡事件修饰符 @click.stop
那么点击div从里向外冒泡触发时遇到这个有修饰符的div时就停止
不再往上冒泡触发事件
案例:给上面div嵌套案例添加<div class="div3" @click.stop="fn3"></div>
输出:7 6 5 4 3

.prevent:阻止默认事件

阻止一些元素自带的默认事件。比如点击<a></a>会跳转链接。
点击表单的button会自动提交。
<a class="div3" @click.prevent="fn3"></a>

.capture:捕获阶段提前触发

从外向里捕获。
简单来说就是,拿上面的案例(七个嵌套div)来说,如果<div class="div4" @click.capture="fn4"></div>在div4添加捕获事件修饰符,输出变成4 7 6 5 3 2 1。先捕获,首先执行div4上面的事件,然后从最里层往外冒泡,当冒泡到对应的div4,它已经触发过了,所以不触发,继续向上触发上面的事件。
添加一个小案例:

<div class="div1" @click="fn1">
      <div class="div2" @click.capture="fn2">  //  给div2添加捕获事件修饰符
        <div class="div3" @click="fn3">
          <div class="div4" @click.capture="fn4">  //  给div4添加捕获事件修饰符
            <div class="div5" @click="fn5">
              <div class="div6" @click="fn6">
                <div class="div7" @click="fn7">
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
按照上面的解析,首先从外往内捕获,div2事件触发  div4事件触发 然后 从div7到div1开始冒泡触发事件。
已经触发过的不再触发。所以最后得到的输出结果是 2 4 7 6 5 3 1。

.self:事件只能由自己触发,后代元素无法触发

只能由自己触发,后代子元素无法触发。
什么场景使用:弹窗(点击按钮显示弹窗,点击弹窗以外的部分弹窗隐藏)。

<div id="app">
    <button @click="showModal">点击显示弹窗</button>
    <div class="mask" v-show="isShow" @click.self="hideModal">   //  点击父元素隐藏
      <div class="content">   //  父元素点击,子元素不受影响
        <p>这是内容1</p>
      </div>
    </div>
  </div>
const vm = new Vue({
      el: '#app',
      data: {
        isShow: false
      },
      methods: {
        showModal(){
          this.isShow = true;
        },
        hideModal(){
          this.isShow = false;
        }
      }
    })

.once:事件只绑定一次

绑定的事件只能执行一次。

九、v-model的三种修饰符

修饰v-model 用于绑定输入框

.trim

<input type="text" v-model.trim="msg">{{msg}}

去除输入框中 开头和结尾的空格

.lazy

<input type="text" v-model.lazy="msg">{{msg}}

.lazy的使用效果

双向绑定由input触发改为change触发

.number

过滤为数字,过滤规则同parseFloat
如果首字母就是非数字 直接停止工作 变成普通v-model

十、条件渲染 if else

单分支 v-if

v-if和v-show区别?
相同点:
都是绑定布尔值,值true元素显示。false元素隐藏
不同点:
当元素隐藏时,v-show控制 display:none v-if 直接在dom树上移除这个元素
使用场景?
v-show有更高切换性能,适用于 频繁切换状态场景
当初始不渲染时,v-if性能更高,适用于 初始不渲染,且 切换状态不频繁。因为一开始的dom会变得很少,加载快

双分支v-if v-else

v-else绑定元素,一定是 v-if绑定元素的下一个兄弟元素

多分支v-if v-else-if

必须是相邻的兄弟

十一、列表渲染 循环 v-for

普通循环

<ul>
      <li v-for="item in arr">
        {{ item }}
      </li>
    </ul>

循环时声明变量 包括下面下标,作用域 只能在 v-for绑定标签内部使用
循环得到下标

<ul>
      <li v-for="(item, index) in arr">
        {{ item }}
      </li>
    </ul>

循环对象

<ul>
     <li v-for="(value,key,index) in json">
       {{value}}
       ->
       {{key}}
       ->
       {{index}}
     </li>
   </ul>

复杂循环

使用场景:网站首页楼层
循环嵌套循环,需要注意的时,如果需要在内层 使用外层循环声明的变量或者下标,内外层命名一定不能冲突,否则 一定优先拿内层

<div v-for="(floor,i) in floors">
      <h2>{{floor.title}}</h2>
      <ul>
        <li v-for="(item, index) in floor.items">
          <h4>商品名字:{{item.name}}</h4>
          <p>商品价格:{{item.price}}</p>
          {{ i+1 }} 楼
        </li>
      </ul>
    </div>

十二、vue深入响应式原理 MVVM原理

mvvm底层api : Object.defineProperty();(这个api在IE8及以下不支持)
在这里插入图片描述

mvvm原理

当我们const vm=new Vue({})(new一个vm实例) 的时候,传入config对象,data属性也是对象,此时vue会立即遍历这个data对象,并利用Object.defineProperty();将data中所有属性转换成getter setter,同时每一个实例,定义一个观察者watcher(监听data里面的数据),当setter触发,setter会立即通知观察者,观察者在他的回调里会触发render函数,功能是生成新的虚拟DOM,和上一次保存在内存中的虚拟DOM进行比较(利用diff算法),得到代价最小的方案,来更新真实DOM。

虚拟DOM

产生的问题:如果vue数据修改直接更新DOM,会导致性能消耗太大,修改多少次就会导致DOM立即重建多少次。
什么是虚拟DOM:用js对象的结构来描述真实DOM结构

<div id="box" class="container">
    <p class="op">这是p</p>
    <span>这是span</span>
    这是文本
  </div>

转换成虚拟DOM:

{
    tag: 'div',
    attrs: {
      id: 'box',
      className: 'container'
    },
    children: [
      {
        tag: 'p',
        attrs: {
          className: 'op'
        },
        children: ['这是p']
      },
      {
        tag: 'span',
        attrs: {},
        children: ['这是span']
      },
      '这是文本'
    ]
  }

虚拟DOM是将真实的DOM节点用JavaScript模拟出来,将DOM变化的对比,放到 Js 层来做。
虚拟DOM在vue中做了什么:

vue中数据修改,视图刷新是异步的,
在vue中每一次修改都会生成新的虚拟DOM,和上一次保存在内存中未改变的虚拟DOM进行比较,比较过程中使用到diff算法

diff算法在虚拟DOM比较中做了什么优化:
实现同层比较
同层如果有相同key属性 同层同key进行比较

循环中为什么要加key属性

Vue可以通过key这种方式来观察数组或者对象中的数据哪些发生变化。v-for默认通过index索引值来追踪变化。但是尽可能使用数据唯一的ID来做每个元素的key。这样就更能准确的追踪。往下看,下面我们还介绍了为什么用唯一的ID做为key而不是用下标index。
key:独一无二

循环中用下标做key和用id做key有什么区别

使用下标做key

<!-- newVDom -->
<ul>
    <li>b</li>  // key==0 
    <li>c</li>  // key==1 
    <li>d</li>  // key==2 
    <li>e</li>  // key==3 
</ul>
<!--  oldVDom -->
<ul>
    <li>a</li>  // key==0 
    <li>b</li>  // key==1 
    <li>c</li>  // key==2 
    <li>d</li>  // key==3 
    <li>e</li>  // key==4 
</ul>

在这里插入图片描述
产生对比错位,从上图可以看到所有的li标签都发生了变化 ,diff认为每一个li都发生了改变,于是页面重新渲染了所有的列表项。
使用item做下标
这样每个li都有一个单独的key,进入diff算法时进行新旧对比,只会删除第一项,不会渲染其他li,大大提高性能。
在这里插入图片描述

在这里插入图片描述

十三、vue2.x 数据改变 视图不刷新

1 数组

数组[下标]="值"这样写的话,值变化了,但是视图上不会变化
原因:(ObjectdefineProperty) setter捕获不到这种操作
解决方法
this.$set(对象,属性,值)
this.$set(数组,下标,值)
$set修改数组时 自己主动去触发render让视图刷新
直接修改数组的长度
原因:(ObjectdefineProperty) setter捕获不到这种操作
解决方法:
使用splice取代修改length

2 初始化时 没有在 data中定义,后面通过代码 动态给实例添加属性

属性在修改时,视图不刷新
原因:初始化遍历给data添加getter setter时,data中没有,属性就没有setter getter
解决方案:在data中定义合理的初始值

3 模板闪烁问题

问题:
当我们在打开vue开发网页时,由于网页加载 顺序从上往下的,在实例未初始化完成时,html解析 vue, 模板变成了普通字符串来解析,等待vue实例初始化完成,模板挂载,并渲染,中间有时间差,产生闪烁
解决:
vue提供了一个指令v-cloak
v-cloak的功能:vue实例初始化完成会自动去除视图所有v-cloak指令。
具体实现:

//  CSS属性选择器
//  这是什么原理不言而喻了,就是使用选择器让当前元素消失隐藏,当vue实例初始化时,v-clock去除,这样让元素显示,同时data也被遍历,数据显示出来。这样就看不到{{msg}}的模板了。
[v-cloak]:{
  display:none
}
<div v-cloak>
{{msg}}
</div>

4 数据修改 视图 异步刷新

vue 数据修改后,视图更新是异步操作,当数据修改,立即获取最新dom是拿不到的,拿到是上一次未更新前的dom,
vue设计了观察者,监听视图异步更新,当每一次数据更新完成后触发回调,在回调中可以拿到最新的dom。
this.$nextTick

this.$nextTick(()=>{
//  在这里可以拿到最新的dom
})

十四、侦听器 watch

用于侦听实例上属性的变化 当数据改变时 侦听器会触发

侦听基础数据类型数据

config中新增watch属性 watch定义侦听同名方法即可

watch:{
  msg(newVal,oldVal){
  
 }
}

针对对象 数组 改变数组长度改变

使用深度监听

watch:{
  		obj:{
			handler(newVal){

			},deep:true    //  深度监听
		}
}

十五、计算属性

依赖已有一个或多个值经过计算得到一个新的值
使用场景:
某一个后端返回的数据,前端不能直接使用,需要转换一下才能用

新增config对象中computed属性
将属性定义成方法,在方法中基于依赖进行计算,返回(return)计算结果

computed: {
    reverseMsg(){
      return this.msg.split('').reverse().join('')
    },
    doneTodoLen(){
      return this.todos.filter(todo=>todo.done).length;
    }
  }

注意:计算属性最终编译成实例的属性
计算属性计算完成,基于依赖进行缓存,多次使用计算属性不会重新计算,拿缓存的值,只有依赖发生改变,重新计算。

计算属性可以赋值吗

答:可以,但是需要使用setter
计算属性不能直接修改值,但是可以直接赋值。
计算属性写成完整写法,增加setter getter 去修改依赖。

data: {
    num: 20
  },
  computed: {
    // 完整写法是 对象 有getter和setter(上面的写法是getter简写)
    doubleNum: {
      get(){
        // return结果就是 使用计算属性的值
        return this.num * 2
      },
      set(val){
        // 在给计算属性赋值时触发 this.doubleNum = 值
        // 在 setter中修改依赖
        this.num = val / 2;
      }
    }
  }

注:不建议直接赋值,如果计算属性依赖较多,计算过于复杂,不好在setter修改依赖
适用于简单的计算属性 加setter

十六、组件

什么是组件

  1. 组件组成网页的标签
  2. 组件是网页上不同组成部分

vue中组件语法

全局组件

可以在任意一个vm控制的视图范围内使用

//  这里相当于定义一个类继承了父类Vue 
Vue.component('CommonHead', {
        // 定义组件的视图结构
        template: `
          <div>
            <h3>这是公共头部</h3>
            ..
            ..
          </div>
        `,
        data(){
          return {

          }
        },
        ... 其他同vm的config
      })

在html中:

//  相当于定义好的  CommonHead   产生了组件实例
<common-head></common-head>

这这里在什么会将

data: {

}

变成了

data(){
          return {

          }
}

因为使用

data: {

}

对象会产生地址传递,如果组件进行复用的时候,后面使用的组件中data中的数据修改,前面使用的组件中data中的数据也会被修改。会导致数据混乱。

局部组件

局部和全局的不同之处:
1 全局在vue构造函数上挂载的,局部组件在另一个组件内部 通过components属性注册组件

2 使用范围不一样 局部组件只能在注册在父组件的template,无法在外部使用

// 定义局部组件的config
    const Logo = {
      template: `
        <div>
          <h3>这是公共logo组件</h3>
          {{ msg }}  
        </div>
      `,
      data(){
          return {
            msg: '我是logo数据'
        }
      }
    }
    // 全局组件
    Vue.component('CommonHead', {
        // 定义组件的视图结构
        template: `
          <div>
            <h3>这是公共头部</h3>
         	<login></login>    //  局部组件 (此处是局部组件在全局组件中作为子组件使用)
          </div>
        `,
        components: {
          Logo: Logo
        }
      })

注意:
1 组件和vm一样都是Vue实例(vm有的特性,组件都有)
2 组件有封闭作用域,内部属性方法不能在外部使用
3 template 只能有一个根元素
4 data必须是一个函数render对象,(防止引用传递。如果data是对象,多个组件实例使用同一个对象保存数据,那么任意一个组件实例修改数据,其他都会改变)
5 组件名:命名推荐使用大驼峰,比如:CommenHead或者commen-head
使用时,<commen-head></commen-head>
6 注意组件使用时单双标签都可以
但是如果一个组件中使用重复的子组件,那么子组件使用双标签,使用单标签时只会显示一个子组件

十七、组件通信

为什么需要组件通信

  1. 组件是封闭的
  2. 内部数据无法再外部使用
  3. 但是在某些场景下需组件通信

父组件向子组件传递数据

原理:在父组件中给子组件标签定义自定义属性传递父组件中的数据
子组件config中定义props来接受父组件传递的自定义属性的值
具体实现
父组件中:

const Father = {
  template:`
    <div>
      <h3>这是父组件</h3>
      //  使用自定义属性
      <child title="这是静态数据" :msg="msg"></child>
    </div>
  `,
  data(){
    return {
      msg: '父组件中的数据'
    }
  }
}

子组件中:

const Child = {
  template: `
    <div>
      <h4>这是子组件</h4>
      {{ title }}
      {{ msg }}
    </div>
  `,
  //  通过config中的props属性接受
  props: ['title', 'msg']
}

注意:注意命名
接受过来的props的数据会挂载到子组件实例上,所以命名不能和实例上的属性方法同名。

props验证

接受props时可以用数组也可以用对象
当使用对象来接受时 可以进行一些类型验证

props: {
    // 只验证类型
    a: String,
    // 只验证类型 类型可以是多个  用数组包裹
    b: [String, Number],
    // 验证类型且必须传值
    c: {
      type: String,
      required: true
    },
    // 默认值
    d: {
      type: String,
      default: '这是默认值'
    },
    // 默认值数据类型如果是数组或者对象 应该是函数return这个默认值(解决引用传递问题)
    e: {
      type: Array,
      default: () => [1,2,3,4]
    }
  }

类型举例:String Number Boolean Array Object Date Function Symbol

props是否能被修改问题

不能被修改哦!
只能由父组件传递给子元素,子组件无法被修改

单向数据流

只能由父组件传递给子组件,子组件无法被修改
这里就会产生一个概念叫做数据提升
所有子组件数据,统统提升到父组件的data管理,子组件通过props接受即可。

子组件向父组件传递数据

原理:通过触发子组件标签 自定义事件
父组件中通过v-on指令(简写就是@)在子组件标签上来监听这个自定义事件
子组件如何监听自定义事件:
所有vue实例都有俩方法 $emit$on,使用$emit或者$on
父组件中:

		<div>
         <h2>父组件</h2>
         {{ msg }}
          <hr/>
          //  通过 v-on:biubiubiu="fn"  监听biubiubiu这个自定义事件
          //  简写就是  @biubiubiu="fn"
          <common-title @biubiubiu="fn"></common-title>
        </div>

fn方法:

	fn(msg){   //  此处的msg参数就是子组件传递过来的数据
          this.msg = msg;
        }

子组件中:

		<div>
          <h3>这是子组件</h3>  
          <button @click="emit">点击传给父组件一个数据</button>
        </div>

emit方法:

	emit(){
          // 代码触发 事件触发一次
          this.$emit('biubiubiu', this.msg)   //  this.msg参数就是传递给父组件的数据
        }

注意:自定义事件名可以是系统事件 click也可以是任意的自定义事件名
父组件中监听组件实例自定义事件有两种方式:
第一种方式就是上文中在子组件中通过v-on接受监听:

<child @自定义事件名="fn"></child>

第二种方式:
通过子组件实例的$on方法来接受

child.$on('自定义事件名', () => {})

兄弟组件之间通信

原理(这个看不懂,看本质就好了,而且bus只是命名而已 ≡ω≡ ):
自定义事件 event bus 事件中心总线

本质(简而言之如下方 (๑•̀ㅂ•́)و✧ ):

在外部定义一个vue实例
命名为bus

组件一中bus.$emit触发bus的自定义事件
组件二中通过bus.$on来监听这个自定义事件

ref通信

在父组件中通过给子组件标签定义ref属性获取子组件实例
ref属性:用于获取子组件实例 或 dom对象
在需要获取子组件的标签上添加属性ref(如果是html标签,则是dom对象,如果是子组件标签,则是子组件实例)

const Father = {
      template: `
        <div>
          <h3 ref="title">标题</h3>  
          <button ref="btn">按钮</button>

          <child ref="child"/>
        </div>
      `
  }

每一个实例都有一个$refs属性
$refs:保存了ref转发的dom对象或者子组件实例

this.$refs.xxx // xxx 标签 获取ref属性的值

另外需要需要掌握的

$parent:返回是当前组件的唯一父组件实例
$children:返回当前组件所有子组件实例(数组)
provideinject 通信

十八、vue组件生命周期

一个组件从初始化到销毁的整个过程
中间有不同的阶段,不同的阶段触发实例上生命钩子函数
在这里插入图片描述

初始化挂载阶段(初始化实例到实例模板渲染成真实dom整个过程)

初始化组件事件(事件监听)以及其他生命周期钩子函数

触发:beforeCreated
触发时 实例还未创建完成 实例属性方法 数据都无法使用

初始化实例属性方法(包括原型链上) 以及将实例上数据变成响应式(遍历data getter setter watcher)

触发:created
代表实例已经初始化完成 实例上属性方法都可以调用 数据也可以修改和使用
但是template还未编译,dom还没有出来

编译分析template并且调用render函数生成虚拟dom

触发:beforeMount
注意:此时虚拟dom已经创建完成 但是真实dom还未构建

触发:mounted
组件初始化 真实dom已经构建完成
注意:所有初始化需要获取dom操作都应该在mounted中完成

更新阶段 数据改变 视图更新完成

数据改变立即触发beforeUpdate
最新虚拟dom都没有构建

立即重新render生成新的虚拟dom 然后patch(比较老旧的dom,然后更新真实dom)

触发:updated
可以获取最新的dom

卸载阶段
什么时候卸载:
条件渲染 值为false 组件停用
路由组件切出当前路由

为什么要卸载:从内存中移出组件实例 释放内存

beforeDestroy 组件卸载之前
destroyed组件实例已经卸载完成

十九、生命周期 钩子函数的使用场景

初始化阶段

created:
一般用在组件初始化时,调用接口请求函数(获取数据)
案例:在methods中定义此方法,在方法中发送请求,获取请求成功返回的数据;在组件初始化时调用方法。

data(){
    return {
      // 1 data中定义初始值
      cates: []
    }
  },
  methods: {
    // 2 methods中定义请求方法
    fetchCates(){
      axios.get('xxxx').then(res=> {
        if(res.data.code === 0) {
          this.cates = res.data.data
        }
      })
    }
  },
  created(){
    // 3 调用请求方法
    this.fetchCates();
  }

mounted
作用:初始化时只有在mounted中才能获取真实dom对象
使用场景:
初始化阶段,一切需要dom操作都需要在mounted中完成
注意:大部分情况下,都是初始化第三方插件时,需要传入dom作为容器(echarts,富文本,地图,swiper)

	<script>
    const CommonHead = {
      template: `
        <div>
          <h2 ref="title">mounted使用场景</h2> 
          <div class="swiper my-swiper">
            <div class="swiper-wrapper">
              <div class="swiper-slide" v-for="banner in banners" :key="banner.id">
                <img :src="banner.picUrl"/>  
              </div>
            </div>
          </div>
        </div>
      `,
      data(){
        return {
          swiper: null,
          banners: []
        }
      },
      methods: {
        initSwiper(){
          // 初始化swiper
          this.swiper = new Swiper('.swiper', {
           
          })
        },
        fetchBanners(){
          axios.get('https://api.it120.cc/conner/banner/list').then(res =>{
            if(res.data.code === 0) {
              this.banners = res.data.data
              // 在这里重新初始化
            }
          })
        }
      },
      watch: {
        banners(){
          // 在这里重新初始化swiper
          this.$nextTick(() => {
            this.swiper.update();
          })
        }
      },
      created(){
        this.fetchBanners();
      },
      mounted(){
        this.initSwiper();
      }
    }
    Vue.component('CommonHead', CommonHead);
    const vm = new Vue({
      el: '#app'
    })
  </script>
更新阶段使用场景

updated可以在这里获取最新dom
但是一般实际开发 不推荐在这里获取,因为会造成一个完成业务功能的代码割裂
推荐使用this.$nextTick()来获取修改后的最新dom

卸载阶段使用场景

问题:当我们初始化阶段,如果给全局添加 事件,特性(比如一些定时器 其他属性 window下面的属性 window.scroll window.mousemove),组件卸载的时候仅仅只是将实例从内存中移出,全局上的任意属性方法都还在哦!
希望组件卸载时,注销清除当前组件给全局定义的一些特性。
使用场景:在beforeDestroy中,注销全局一些特性 比如,清除定时器 注销全局事件绑定等。

	beforeDestroy () {
        clearInterval(this.timer);  // 清除定时器
        window.onscroll = null;  //  注销全局事件绑定
      }

二十、组件缓存

当一个组件停止使用时、条件渲染值为false、路由组件切出当前路由的情况,此时vue自动卸载当前组件实例,目的:释放内存
场景:如果我们希望一个组件停止使用时,不卸载(内存中的实例不移除)。例如:首页 点击进去详情页 返回首页时 首页不刷新 此时就需要将组件缓存
如何实现:
vue提供了keep-alive组件包裹需要状态切换的组件即可

	<keep-alive>
      <common-head v-if="isShow"></common-head>
    </keep-alive>

新增专门针对 被缓存组件的生命周期钩子函数

场景:首页或者列表被缓存 进入其他页面 再回来 由于被缓存 组件不刷新 但是希望局部数据刷新(广告区块)
问题?初始化 钩子都不会触发 没有钩子函数去调用局部请求函数
针对这个问题
vue专门给被缓存的组件定义两个钩子函数

activated 组件被调用时触发

被缓存的组件使用(包括初始化和再一次显示)时候触发

deactivated 组件停止使用时触发

被缓存的组件停止使用时触发

二十一、混入 minxin(混合)

场景:
多个组件都具有相同特性(属性 数据 方法 定时器 计算属性)
混入:
可以将多个组件 公共特性 单独定义在一个config对象中,通过vue注入语法,将公共config上特性注入任意一个组件中称为这个公共config就是混入

//  定义一个公共混入
	const mixin = {
      data(){
        return {
          msg: '我是混入上的数据'
        }
      },
      methods: {
        changeMsg(){
          this.msg = '值改变了';
        }
      },
      computed: {
        reverseMsg(){
          return this.msg.split('').reverse().join('')
        }
      }
    }

需要注入组件的config中新增mixins属性

Vue.component('组件名', {
  template: ``,
  mixins: [mixin]
})

注意:当前组件和注入混入中如果有相同特性,以组件自己的特性为准

二十二、插槽

功能:
可以在子组件template中通过slot组件定一个占位标签
在父组件中 使用子组件,子组件使用双标签,子组件双标签内嵌套内容最终会在子组件占位符slot所在位置处渲染

基础插槽

子组件内部定义slot占位

	const Child = {
      template: `
        <div>
          <h3>这是子组件头部</h3>  
          <slot></slot>
          <h3>子组件底部</h3>
        </div>
      `
    }

父组件中子组件标签内嵌套内容传入slot的位置
用嵌套内容’xxxxxx’取代slot

<child>
  <div>
    xxx
    xx
    xxx
  </div>
</child>

具名插槽

可以在子组件template定义多个slot 多个slot占据多个位置 (这样的话,会导致页面上slot和嵌套内容对应混乱,嗯,然后就芭比Q了 o(≧口≦)o)
所以 ! 在slot上面定义一个name属性,父组件中在传入的嵌套内容上添加slot属性,对应的属性值就是name的属性值 <( ̄︶ ̄)> 这样是不是一对一不会混乱了呢!
子组件中:

	const Child = {
      template: `
        <div>
          <slot name="a"></slot>  
          <h3>这是中间</h3>
          <slot name="b"></slot>  
        </div>
      `
    }

父组件中:

		<child>
            <div slot="b">
              <button>插槽嵌套内容</button>  
              <i>dhwidwid</i>
            </div>  
            <div slot="a">
              <strong>dwdw3feg4rhty</strong>  
            </div>
        </child>

作用域插槽

问题:插槽传入结构块(标签),是在父组件中定义的(最终渲染是在子组件slot位置),如果在模板中渲染数据,有时候会需要父组件中的数据,有时候需要子组件中的数据
解决方案
子组件中:通过slot标签添加自定义属性携带子组件中的数据
父组件中:在嵌套内容块的容器标签上定义一个slot-scope属性(这个属性的值会解析成对象,对象属性就是子组件中slot的自定义属性(作用域就是容器组件内部),属性值就是子组件的数据)
案例
子组件中通过slot组件的自定义属性携带子组件数据

	const Child = {
      template: `
        <div>
          <h3>这是子组件头部</h3>  
          <slot title="静态数据" :msg="msg" :arr="arr"></slot>
          <h3>子组件底部</h3>
        </div>
      `,
      data(){
        return {
          msg: '这是子组件数据',
          arr: ['a', 'b', 'c', 'd']
        }
      }
    }

父组件中通过slot传入嵌套内容块 容器定义slot-scope属性接收,data为对象,对象的属性的值就是子组件携带过来的数据

<child>
  <div slot-scope="data">
    {{ data.title }}
    {{ data.msg }}
    {{ data.arr }}
  </div>
</child>

二十三、过滤器 filter

在模板中简单过滤一些数据

全局过滤器

不传参

Vue.filter(‘过滤器名’,fn) // 参数1 过滤器名 参数2 过滤器函数 return的值就是最终输出的值

 Vue.filter('currenct', (v) => {
    // v使用时 过滤的原值
    return '¥'+v
  })
传参
Vue.filter('过滤器名', (v,...params) => {
  return xxx
})
//  使用时 传递多个参数,在过滤器第二个参数以及 往后来接收参与运算
//  {{ 10 | currency('a','b') }}

局部过滤器

定义在组件内部 在config中新增filters属性定义局部过滤器
注意:只能在注册组件内部使用

const Home = {
  template:``,

  filters: {
    currency: (v,...params) => {
      return xxx
    }
  }
}

过滤器使用

基础使用
{{ 值 | 过滤名 }}
传参
{{ 值 | 过滤器(...params) }}
过滤器串联
{{ 值 | 过滤器1 | 过滤器2 | 过滤器3.... }}

二十四、自定义指令 directive

全局指令

Vue.directive('focus', {
  // 初始化阶段
  bind(el, binding){
    // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  },
  inserted(el, binding){
    // 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
  }
  // 初始化阶段
  // 更新阶段
  update(el, binding){
    /* 
      所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前
    */
  },
  componentUpdated(el, binding){
    /* 
      指令所在组件的 VNode 及其子 VNode 全部更新后调用。
    */
  },
  // 更新阶段
  // 解绑阶段
  unbind(){
    // 指令与 所在 组件 解绑时触发
    /* 
      啥时候解绑
      为啥解绑
      解绑干啥 
    */
  }
  // 解绑阶段
})

钩子函数参数:
el:当前指令绑定的dom对象
binding:指令相关参数 比如:binding.value使用指令时传入的值

<div 指令="值"/>

总结:
只需要记住三个钩子
初始化:inserted
更新:componentUpdated
解绑:unbind

局部指令

在一个组件内部config新增directives属性 定义局部指令
区别:局部指令只能在注册组件内部的template中使用

const Home = {
  directives: {
    '指令名': {},
    focus: {
      inserted(el,binding){},
      ....
    }
  }
}

二十五、父子组件生命周期钩子函数

初始化:

父 beforeCreate
父 created
父 beforeMounte
子 beforeCreate
子 created
子 beforeMounte
子 mounted
父 mounted

更新:

父 beforeUpdate
子 beforeUpdate
子 updated
父 updated

二十六、组件状态切换动效

条件渲染值为false 路由组件切除组件

transition 控制单组件动效

使用transition组件包裹需要状态切换的元素 并定义name

<transition name="ani1">
      <div class="box" v-if="isShow"></div>
</transition>

transition包裹状态切换元素 在状态切换过程中 加不同类

入场:元素从消失-显示的过程
name是指transition组件的name属性的值
.name-enter  入场动画  初始状态
.name-enter-to  入场最终状态(一般不使用,自己定义选择器中的样式就是最终状态)
.name-enter-active  入场初始状态到入场最终状态切换过程中加这个类
	.box{
      width: 200px;
      height: 200px;
      background-color: rgb(205, 205, 103);
      margin: 30px auto;
    }
    /* 定义入场初始状态 */
    .ani1-enter{
      transform: translateX(-500px) rotate(360deg) scale(0.1);
      opacity: 0;
    }
    /* active定义 初始状态到 最终状态中间 过渡状态 */
    .ani1-enter-active{
      transition: all 500ms;
    }
出场: 元素从显示-消失的过程
.name-leave 出场动画 初始状态(一般不使用,自己定义选择器中的样式就是出场初始状态)
.name-leave-to 出场最终状态
.name-leave-active 出场初始状态 到 出场最终状态 切换过程中 加这个类
	.box{
      width: 200px;
      height: 200px;
      background-color: rgb(205, 205, 103);
      margin: 30px auto;
    }
    /* 定义出场最终状态 */
    .ani1-leave-to{
      transform: translateY(500px) rotate(-360deg) scale(0.1);
      opacity: 0;
    }
    /* active定义 初始状态到 最终状态中间 过渡状态 */
    .ani1-enter-active, .ani1-leave-active{
      transition: all 500ms;
    }

注意:transition只能控制 单组件状态切换 不能同时控制多个状态切换。除非:使用条件渲染,保证同时入场和出场只有一个元素

transition 使用条件渲染 包裹多个元素情况

多一个属性,控制出场和入场的顺序
mode
both:默认值
in-out:先入场再出场
out-in:先出场再入场

列表动画 同时控制多个组件 状态切换

使用transition-group组件包裹即可,使用方法同transition一样

使用animation控制组件状态切换

动画自己有关键帧
不需要使用transition给我们提供类定义初始和结束状态
动画第一个关键帧就是初始状态
动画最后一个关键帧就是结束状态
总结:transition组件结合动画 需要记住两个过渡类

.name-enter-active{
  animation: 使用入场动画;
}
.name-leave-active{
  animation: 使用出场动画;
}

二十七、axios

发送get请求

axios.get('url?a=10&20')
//  get方法发送get请求  参数在地址上携带query即可
axios.get('url?a=10&b=20').then().catch()

axios.get 携带请求配置config

axios.get(url, {
  // config中params属性代表get请求的参数
  params: {
    a: 10,
    b: 20
  }
}).then().catch()

发送post请求

axios.post(url[, data,config])
axios.post('http://xxx/com/xxx', {page:1,pageSize:10}, {
  headers: {
    token: 'zzzzz'
  }
})

注意:post大部分场景下 都是需要携带参数的 axios为了便于开发者使用,第二个参数对象直接解析成post请求的参数
axios发送post请求携带参数格式默认是 application/json

axios方法

axios(config)

axios发送get请求

axios({
  url: 'xxx',
  method: 'get', // 默认是get
  params: { // params是get请求的参数

  },
  headers: {}
}).then().catch()

axios发送post请求

axios({
  url:'xxx',
  data: { // post请求参数

  },
  method: 'post',
  headers: {}
}).then().catch()

axios对于restful api支持

后端要求:

{
  code: 200, // 200 401 token  403 没有登录
  msg: '',
  data: []
}

前端要求:获取数据用get 提交用post 更新用put 删除用delete

axios.get()
axios.delete() // 用法同get
axios.post()
axios.put() // 用法同post

二十八、请求config有哪些配置属性

{
  // 请求地址
  url: '/user',
  // 请求方式
  method: 'get',
  // 基础url 最终请求地址是 baseURL+url
  baseURL: 'https://some-domain.com/api/',
  // 请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},
  // get请求参数
  params: {
    ID: 12345
  },
  // post请求参数
  data: {
    firstName: 'Fred'
  },
  // post请求参数 query格式 自动转换成 urlencoded格式
  data: 'Country=Brasil&City=Belo Horizonte',
  // 超时时间
  timeout: 6000
}

二十八、统一定义所有请求 默认配置项

通过创建axios实例 统一定义默认配置项

const request=axios.create({
	baseURL:'http://www.xxx.com',
	timeout:1000,
	headers:{
		xxx:'xxx'
})

注意:
1 实例request方法同axios一样

request.get()
request.post()
request({
	config
})

2 默认的配置是给实例定义,默认配置生效前提是一定要使用实例发送请求

通过defaults属性定义axios默认配置

axios.defaults.baseURL="......"
axios.defaults.timeout=6000
......

二十九、axios拦截器

请求拦截器

// 添加一个请求拦截器
axios.interceptors.request.use(function(config){
// 在请求发送之前可以在这里做点什么
// config就是我们请求的所有配置
// 在请求发送之前可以修改config
// 例如:config.headers.xxx="值"
	return config   //  这句话不执行  请求发送不出去
},function(error){
// 拦截请求失败
	return Promise.reject(error)   //  这句话不执行  请求catch无法触发
	}
)

响应拦截器

axios.interceptor.response.use(function(response){
// http状态码为2xx走请求成功拦截
// return response (axios包装返回数据  {data:{}})
// 不return则无法触发then  return 结果response就是then的参数
	return response
},function(error){
	return Promise.reject(error)
)

注意:如果使用axios发送请求 拦截器应该加给实例才能生效

const request = axios.create({
  ...
})

// 添加了一个请求拦截器
reqeust.interceptors.request.use(function (config) {
    return config;
  }, function (error) {
    // 拦截请求失败 return不执行 请求 catch无法触发
    return Promise.reject(error);
  });

  request.interceptors.response.use(function (response) {
    /* 
      http状态码 为 2xx 走 请求成功拦截
      return response(axios包装返回数据 {data: {}})
      不return 则 无法触发 then return结果response就是 
      then的参数
    */

    return response;
  }, function (error) {
    // 响应失败拦截 下面不return 不执行 catch
    return Promise.reject(error);
  });

三十、路由

vue路由是vue的一个插件
功能:创建基于vue前后端分离的spa应用(single page application)单页面应用
单页面应用:整个应用程序只有一个html,所有页面都是通过一个vue组件来实现

spa应用优缺点

优点:实现前后端分离 权责分明
单页面切换页面时,不需要重新加载页面,切换速度大大提升
缺点:不利于seo
首页加载速度慢(路由懒加载,提升首页打开速度)
vue-router3.x

路由基础使用

vue工程化环境使用(基于webpack构建)
npm i vue-router -S

引入:

import VueRouter from 'vue-router'
import Vue from 'vue'

工程化环境中使用路由插件需要显示Vue.ues(插件)

Vue.use(VueRouter)
传统环境

只需要通过script标签 在 vue.js后面引入vue-router vue自动注册 这个插件

<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
定义基础路由

准备路由组件 config对象

	const Home = {
      template: `
        <div>我是首页</div>
      `
    }
    const News = {
      template: `
        <div>我是新闻页</div>
      `
    }
    const About = {
      template: `
        <div>我是关于我们页</div>
      `
    }

定义路由实例 并挂载路由规则

    const routes = [
      // 单个路由规则 是一个对象 具有两个基础属性
      {
        path: '/home',
        component: Home
      },
      {
        path: '/news',
        component: News
      },
      {
        path: '/about',
        component: About
      }
    ]

     // 2 初始化 路由实例
    const router = new VueRouter({
      // 路由规则 数组
      routes
    });

将路由实例挂载vm上

    const vm = new Vue({
      el: '#app',
      router
    })

在 视图上定义 router-view组件作为 路由组件的出口

<router-view></router-view>
router-link 实现路由跳转

属性:
to:定义跳转path
tag:组件渲染标签
replace:跳转时是否覆盖当前历史记录
当路由匹配给对应router-link渲染标签 自动加上高亮类
.router-link-active
这个类只要地址栏地址以当前path开头即可匹配
地址栏地址:/news/nactive 当前:to /new
.router-link-exact-active
地址栏地址必须和当前router-link to属性一摸一样才能匹配

路由重定向

新增路由规则
使用redirect属性进行重定向

{
  path: '/',
  redirect: '/home'
}
404问题

vue路由提供了特殊path叫做 * 可以匹配任意路由地址 优先级别最低

{
  path: '*',
  component: NotFound
}

将这个规则放在最后(only建议)

动态路由

顾名思义:
路由地址是可变的
为了保证匹配一个组件 不可能路由地址所有部分都是可变
一个路由地址可由多个path组成 每个path必须是 / 开头

'/a'
'/a/b'

动态路由由两部分组成:
一部分是不可变path:建议用它开头
一部分是可变path:用来传参

如何定义动态路由

定义动态路由

{
path:'/detail/:id',   //  相当于定义一个动态参数id
component:Detail
}

跳转传参
注意:跳转path结构一定符合定义动态路由结构

 '/detail/3'

	// 特殊的
	{
	  path:"/a/:b/c/:d"
	}
	// 跳转时符合规则 /a/3/c/7

目录路由组件中 获取传递的参数

this.$route.params.xxx(动态参数名)
vue使用路由插件之后

路由会自动给所有的Vue实例添加两个属性,分别是:
$route 保存了 当前路由静态信息

命名路由

给每一个路由定义name属性

{
  path: '/home',
  name: 'home',
  component: Home
}
嵌套路由

嵌套在父级路由规则中

	{
        path: '/news',
        name: 'news',
        component: News,
        children: [
          {
            path: '/news',
            redirect: '/news/native'
          },
          {
            path: '/news/native',
            name: 'nativeNews',
            component: NativeNews
          },
          {
            path: '/news/abroad',
            name: 'abroadNews',
            component: AbroadNews
          }
        ]
      }

注意:
1 子级路由path最好携带一级路由path作为路由前缀
更为语义 有层级关系
2 子级路由重定向 定义在子级路由规则
子级路由组件出口嵌套 嵌套在父级路由组件的template

const News = {
  template:`
    <div>
      <h3>新闻页</h3>
      <router-view></router-view>
    </div>
  `
}
路由跳转 参数

以router-link to 举例
跳转参数直接给字符串(代表跳转path)

<router-link to="/news">到新闻</router-link>

跳转参数是对象
通过path跳转

<router-link :to="{path: '/news'}">到新闻</router-link>

通过name传参

<router-link :to="{name: 'news'}">到新闻</router-link>

三十一、编程式导航

导航分类:
声明式导航:通过组件或者标签进行路由切换
编程式导航:通过js api进跳转

vue路由给每个实例添加两个属性
$route:保存当前路由参数信息
$router:就是new VueRouter返回那个路由实例,它的原型上有一些常用操作路由的api

addRoute   动态添加路由
1 作为一级路由添加  this.$router.addRoute(routeConfig)
2 作为另一个已存在路由子路由  添加
this.$router.addRoute('父级路由name',routeConfig)

go(num)
-1  回退一步
1  前进一步
0  刷新
push()  普通路由跳转  参数同router-link的to属性
replace()  路由跳转  参数同router-link的to属性  跳转路由时 会覆盖当前历史记录

三十二、vue路由跳转传参

动态路由传参(如何定义动态参数 如何获取)

query传参

传参主要看跳转参数 不看跳转方式
跳转时 参数一定是对象
跳转参数

{
	path:'xxx',
	query:{
		id:10
	}
}

获取:

this.$route.query.xxx

query传递的参数会在地址上显示 刷新不会丢失

params传参

跳转参数

{
	name:'xxx',
	params:{
		a:10
	}
}

获取:

this.$route.params.xxx

不在地址栏显示 隐式的 必须通过命名路由 name 跳转否则参数无法传递

三十三、history模式

hash模式

路由默认是hash模式,以url后面hash值模拟路由切换
hash模拟路由
优点:
路由切换时 不会改变路径指向 不会改变404问题
缺点:url后面会多个#

history模式

优点:去掉#
看起来样式像正常的路由 /home /news
缺点:需要服务器做支持,否则产生404
配置原理:当我们在服务器访问时 产生了404 永远返回 单页面应用的index.html
扩展:
history模式原理:
利用html5标准下给js history对象新增的一个api pushState/replaceState
hash模式原理:
利用window.onhashchange事件,在事件中匹配路由渲染路由组件

三十四、路由守卫

是路由一些钩子函数 可以实现对于路由变化的拦截或者监听

全局路由守卫

全局前置守卫

功能:路由变化之前触发 可以对于路由变化进行拦截

router.beforeEach((to, from, next) => {
  /* 
    to 是即将跳转的目标路由 参数信息对象 同 this.$route
        from 将要离开的这个 路由参数信息对象

        next 路由放行函数,
          1 回调函数每一次执行,必须调用且只能调用一次 next(写分支结构注意)
          2 next 可以传参 决定 跳转的目标路由参数 同 router-link 的to属性
            字符串 直接写path  '/news'
            对象
              {
                path: '/news',
                query: {
                  a: 10
                }
              }
              {
                name: 'news',
                params: {
                  a: 10
                }
              }
  */
 next();
})
全局后置守卫
router.afterEach((to, from) => {

})

路由独享守卫

单独拦截某个路由

{
  path: '/news',
  component: News,
  beforeEnter: (to, from, next) => {

  }
}

组件内部守卫

beforeRouteEnter
不能通过this拿到实例
如果要拿的话:

beforeRouteEnter(to, from, next){
  next(vm => {
    // vm是这个实例
  })
}

三十五、路由元信息

在路由规则中新增meta属性(对象),在这个对象中可定义任何属性,meta对象可以在任意路由参数信息对象中拿到(this.$route from to)

{
  path:'',
  name: '',
  meta: {
    needLogin: true/ false
  },
  component: XX
}

三十六、如何监听动态路由参数的变化

利用组件内部路由守卫beforeRouteUpdate

{
  beforeRouteUpdate(to, from, next){

  }
}

使用watch

{
  watch: {
    $route(to, from){

    }
  }
}

路由滚动行为

const router = new VueRouter({
  routes,
  scrollBehavior (to, from, savedPosition) {
    /* 
      每一次路由组件切换都触发
      savedPosition 记录了 每次 历史记录中 当时的 滚动条位置也是对象{
        x:xxx,
        y:xxx
      }
      return的值 决定 每个路由组件 滚动条的位置
      {
        x:0, // 决定当前组件滚动条横向位置
        y: 0, // 决定当前组件垂直滚动条位置
      }
    */
    return {
      x: 0,
      y: 0
    }
  }
})
// 下面是常用的写法

const router = new VueRouter({
  routes,
  scrollBehavior (to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  }
})

三十七、vue-cli

脚手架工具
功能:自动搭建 基于webpack构建vue前端模块化工程化环境

1 使用过程

安装:

npm install -g @vue/cli

验证是否安装成功

vue --version // 显示版本号

启动项目:
在cmd中启动
可视化启动项目 不推荐 vue ui
创建项目:

vue create 项目目录名 // 项目名中不能出现大写字母

2 vueCli创建项目目录

public 开发时服务器根目录
index.html
src 源码目录所有代码都在这里
assets 开发时存储静态资源目录 比如字体图标 图片 js css
components存储公共组件
router路由配置文件
store vuex存储目录
views路由组件存储目录
App.vue 项目根组件
main.js 项目的入口文件
.browserslistrc postcss 配置文件 配置css浏览器兼容器
.eslintrc.js eslint配置文件
babel.config.js babel配置文件 es6转es5
package.json

3 vue-cli 项目运行架构

vue-cli是基于webpack构建vue模块化工程化环境
webpack:
模块化打包工具 可以将网页依赖变成webpack依赖
webpack构建项目会有一个入口文件(在这里是src/main.js),这个文件会自动编译在index.html上,项目依赖不再在index.html中引入,而是在main.js中(基于webpack。我们可以在js中引入各种其他类型资源)

总结:
想要代码运行 最终在main.js中(或者main.js中引入的其他资源文件中)

4 vue在vuecli运行架构

App.vue生成根组件config对象 最终在main.js中调用render函数 将App.vue中视图结构 在vm实例上渲染 并且最终mount(渲染并挂载) index.html上<div id="#app">位置处

5 es6 module模块导入导出

引入第三方包(安装在node_modules)中的包

直接引入包,导入内容

import 变量名 from '包名'
import Vue from 'vue'
import { a,b,c,d } from 'xxx'

注意:直接写包名,不需要加路径

引入一个未导出任何接口的自定义包
import '路径'
import './asserts/js/a.js'

注意:
1 如果是自定义的包,需要加路径,同一个目录要加./
2 如果是webpack环境(通过webpack解析器loader)可以引入其他类型的文件 比如css
3 引入后不需要变量来接收 相当于在引用位置运行

一个文件导出多个接口数据
导出

使用export关键字 后买你直接定义变量

		export const a = 10
		export const fn = () => {
			console.log(111)
		}
		export const c = [1, 2, 3, 4]

提前定义变量 统一export导出

		const a = 10
		const b = 20
		const c = () => {
			console.log(222)
		}
		export {
			a,
			b,
			c
		}

注意:导出不是对象 固定语法

导入

全部导入

import * as obj from './assets/a'

按需导入
es6对象的解构赋值

import { a, b as bb, c } from './assets/js/b'
如果一个接口名和导入文件存在文件冲突  可以使用as命名
导出默认值
只导出默认值
export default 值

引入

import 变量 from '路径'

注意:一个文件只能出现一个export default否则报错

导出默认值同时导出普通其他接口

导出

export default 值
const a = 10
const b = 20

export {
	a,
	b
}

导入
只导入默认值

import 变量 from '路径'

只导入普通接口

import { a, b as bb } from '路径'

导入默认也导入普通接口

import arr, {a, b as bb} from '路径'
import arr as arr2, {a, b as bb} from '路径'

注意:不管哪种形式 import都必须在代码顶部引入

eslint

js插件 用于检查代码格式
配置:
vueCli eslint配置文件在 .eslintrc.js

修改配置在.eslintrc.js rules中添加

https://blog.csdn.net/weixin_42681555/article/details/122333230

vueCli中standard模式常用eslint规则
1 每个结尾有空行
2 等号 = 两边必须有空格
3 =》 两边必须有空格
4 语句结束不需要;
5 ()两边需要有空格
6 if else 两边有空格
7 比较运算符两边要有空格
8 : 后面必须是空格
9 字符串必须是单引号
注意:所有项目只要修改了配置 需要重启才能生效

如何关闭eslint中同时保存提醒和修改

vue.config.js下管理eslint检查

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    host: 'localhost',
    port: 9527,
    open: true
  },
  //  关闭eslint中同时保存提醒和修改
  lintOnSave: false
})
vsCode 安装eslint插件

功能:
插件 自动读取项目中eslint配置 编辑器替你检查
注意:当前项目在vscode以根目录形式打开

vue中的sfc(single file component)单文件组件

基于webpack vue-loader

<template>
	<div>
		<h2>你好</h2>
	</div>
</template>
<script>
	// 定义当前组件config对象 上面template内容会自动编译成虚拟dom挂载这个对象中template属性上
	export default {
		data(){},
		methods: {

		},
		computed: {}
	}
</script>
<style lang="scss" scoped>
	/* 
		定义当前组件 template中标签的样式
		lang 定义 使用css预处理器 值选择 你创建项目时选择css预处理器
		scoped 定义style标签中的选择器 只针对当前组件有效

		问题?面试题

		加上scoped情况下 如何修改子组件的样式
		1 深度选择器 有兼容性 不支持新的  sass(编译sass的包)
		deep穿透scoped
		/deep/ 子组件中的选择器
			eg:
				父组件选择器 /deep/ 子组件选择器{

				}
		2 deep在vue中兼容性写法
		::v-deep 取代 上面/deep/
	*/
</style>

vueCli配置文件

在项目根目录下 vue.config.js中修改项目默认配置
注意:修改完配置重启生效

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
	// 配置开发环境服务器
  devServer: {
    host: 'localhost',
    port: 9527,
    open: true,
		proxy: {
			// 反向代理 解决跨域
		}
  },
	// 关闭eslint保存代码就检查代码格式 codeReview
  lintOnSave: false,
	// 自定义路径别名
	chainWebpack: config => {
    config.resolve.alias
      .set('@', path.join(__dirname, 'src'))
      .set('_views', path.join(__dirname, 'src/views'))
  }
}
配置开发环境服务器
// 配置开发环境服务器
  devServer: {
    host: 'localhost',
    port: 9527,
    open: true,
		proxy: {
			// 反向代理 解决跨域
		}
  },
关闭eslint保存代码就检查代码格式 codeReview
 lintOnSave: false,
自定义路径别名
// 自定义路径别名
	chainWebpack: config => {
    config.resolve.alias
      .set('@', path.join(__dirname, 'src'))
      .set('_views', path.join(__dirname, 'src/views'))
  }

路由懒加载

原理:
将每个路由config文件切割成独立的js文件 在首次匹配路由时 只会引入匹配这个路径对应js文件 其他不引入 只有当对应路由组件匹配时才会引入对应组件的js文件
使用es6模块化:

{
	path: '/news',
	component: () => import('../views/Home.vue')
}

使用nodejs模块化

{
	path: '/news',
	component: resolve => require(['../views/Home.vue'],resolve)
}

路由组件的命名方法

路由组件命名符合一下规则

views
	Home
		index.vue
		components
	News
		index.vue
		components

要求:
1 每个路由组件在views下定义一个目录 目录名就是原来组件名 路由组件单文件组件在当前目录下index.vue
2 要求给每一个路由组件定义name属性 name属性同路由组件名
3 路由组件的path一般是路由组件名变小写 /路由组件名

vue-dev-tools

辅助 审查vue项目

keepAlive

路由组件缓存 需要使用 keep-alive 包裹 router-view
问题:
直接包裹造成所有的路由组件都缓存
怎么解决?
keep-alive有两个属性include exclude

include
当keep-alive同时包裹多个组件时(包裹router-view)定义哪些组件被缓存
值对应组件name属性
<!-- 组件name等a和b路由组件会被缓存 -->
<keep-alive include="a,b">
	<router-view/>
</keep-alive>
exclude
当keep-alive同时包裹多个组件时 定义哪些组件不被缓存
值对应组件name属性
<!-- 组件name等a和b路由组件不会被缓存 -->
<keep-alive exclude="a,b">
	<router-view/>
</keep-alive>
路由meta属性结合v-if定义两个router-view

问题:利用include 和 exclude 不适用于灵活组件缓存 且缓存组件较多时 写法较为繁琐

<template>
  <div id="app">
    <router-view v-if="!$route.meta.keepAlive"/>
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"/>
    </keep-alive>
  </div>
</template>

路由配置中新增meta中的keepAlive属性,值为true,则该路由组件缓存。值为false(或者不定义)则不缓存。

{
    path: '/home',
    meta: {
      keepAlive: false
    },
    component: Home
}

vueCli简易版本

1 找到App.vue全部删除只保留

<template>
  <div id="app">
    <router-view/> 
  </div>
</template>

2 定义路由组件在views中定义,在router/index.js注册路由组件
3 子组件在components/定义

vueCli中的环境变量

概念:

定义:跟运行环境相关全局变量 可以在项目js代码任意一处调用 值取决你的运行环境
development开发环境
production生产环境

系统环境变量

process.env.NODE_ENV
process:进程
env:环境变量
NODE_ENV:当前默认

打印 console.log(process.env.NODE_ENV),控制台显示 development,当前开发环境为开发

在开发环境值等于"development"
生产环境等于 “production”
用于判断开发环境

const request = axios.create({
	baseURL: process.env.NODE_ENV === 'development'? '开发源': '生产源'
})
自定义环境变量

用户可以自定义环境变量
在不同环境下 给这个变量定义不同的值

注意:自定义环境语法
变量名=值
变量名以VUE_APP开头

一个变量定义开发环境
根目录下创建.env.development文件 在这里定义

VUE_APP_BASEURL=开发源

一个变量定义生产环境
根目录下创建.env.production文件 在这里定义

VUE_APP_BASEURL=生产源

使用process.env.VUE_APP_BASEURL

const request = axios.create({
	baseURL: process.env.VUE_APP_BASEURL  //  获取到process.env.VUE_APP_BASEURL的值
})

vuex

vuex

vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

vuex是vue的一个插件,功能:用来集中式管理多个组件
注意:
什么时候使用vuex?
中大型项目 且用来管理多个组件公共的状态

vuex的基础使用
安装:

npm i vuex -S

创建 挂载:

在src目录下创建store目录(用来单独管理vuex)
目录下面建立index.js(创建仓库)

//  导入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

// 创建vuex仓库
const store = new Vuex.Store({
  // state 存储公共状态
  state: { },
  // 定义修改state的方法
  mutations: { },
  //  异步
  actions: {   }
  },
  //  经过state中不同数据各种计算  return计算结果
  getters: { }
})
//  导出
export default store

main.js文件中 store在vm上挂载

//  导入
import store from './store'
new Vue({
	store
})
state在组件中使用

使用vuex之后,每一个实例都会添加一个属性$store,就是这个vuex仓库实例
在这里插入图片描述

获取state中的数据:

this.$store.state.数据名
组件中提交mutation修改数据

语法

this.$store.commit('mutation名称',携带参数)

案例:点击视图上+按钮改变视图上 h2 的内容文字

<h2>{{$store.state.num}}</h2>
<button @click="addNum">+</button>

methods中定义方法

methods: {
    addNum () {
      this.$store.commit('ADD_NUM', 10)
    }
  },

vuex仓库中:

 mutations: {
    ADD_NUM (state, n) {
      state.num += n
    }
  },
action:

场景:很多存储vuex仓库中的公共的数据 需要请求后端接口才能拿到数据
在我们state定义完初始值之后 需要发送请求 拿到数据 调用mutation修改数据
问题:mutation中无法发送异步请求
解决方案1 :在组件中定义方法发送异步请求 拿到数据后 调用mutation修改数据
缺点:vuex公共数据的值获取没有在vuex中管理 后期如果请求数据有问题 审查较为复杂
解决方案2 :vuex提供action

action特点:Action类似于Mutation 也是vuex模块 里面定义的也是若干方法不同点在于:
1 只有mutation可以直接修改数据 action不可以直接修改数据
2 action提交的是mutation 而不是直接变更状态
3 action可以包含任意异步操作

const store = new Vuex.Store({
  state: {
		// cates数据需要请求接口拿到数据
		// 1 state定义初始值
    cates: []
  },
  // 定义修改state的方法
  mutations: {
		// 3定义mutation赋值
    INIT_CATES (state, cates) {
      state.cates = cates
    }
  },
  actions: {
		// action定义方法发送请求 提交mutation赋值
    FETCH_CATES (context, params) {
      /*
        参数1
        context就是store这个实例
        context.commit提交muatation
        参数2 可以省略
        action触发时携带参数,一般就是请求携带参数
      */
      //  发送异步请求
      axios.get('https://api.it120.cc/conner/shop/goods/category/all', {
        params
      }).then(res => {
        if (res.data.code === 0) {
          context.commit('INIT_CATES', res.data.data)
        }
      })
    }
  }
})

组件中使用dispatch 语法如下:

this.$store.dispatch('触发action的名字'[,参数])   // 参数一般是请求参数

案例:

created () {
    this.$store.dispatch('getData', { orderBy: 'priceUp' })
  }
getters

使用场景
1 vuex中有一个num 在a组件中使用原值 在b组件中希望使用是原值的两倍
2 vuex有一个todolist待办事项列表 在a组件中使用的是所有待办事项列表中已完成的长度
针对以上两种情况设置了getters
相当于vuex中的计算属性 计算结果是一个值 且它会根据依赖进行缓存

{
	state: {
		todos: [
			{},
			{},
			{}
		]
	},
	getters: {
		// 在某个组件中使用 使用所有待办事项列表中 已完成长度
		DONE_LEN (state) {
			return state.todos.filter(todo => todo.done).length
		}
	}
}

组件中使用:

this.$store.getters.getter名字
vuex的助手函数
mapState 简化vuex中获取state
//  先引入
import { mapState } from 'vuex'
mapState(['num', 'item'])
// 返回结果是
{
	num(){
		return store.state.num
	},
	item(){
		return store.state.item
	}
}

在组件中正确使用:

{
	 computed: {
    ...mapState(['cates', 'num'])
  }
}
mapMutations简化组件中提交mutation操作
import { mapMutations } from 'vuex'

mapMutations('ADD_NUM','REDUCE_NUM')
// 结果
{
	ADD_NUM(payload){
		store.commit('ADD_NUM', payload)
	},
	REDUCE_NUM(payload){
		store.commit('REDUCE_NUM', payload)
	}
}

正确使用:

methods: {
    ...mapMutations(['ADD_NUM', 'REDUCE_NUM'])
  }
mapActions用法同mapActions
methods: {
    ...mapActions(['FETCH_CATES'])
  },
mapGetters便于在组件中获取getters

用法同mapState

computed: {
    ...mapGetters(['DOUBLE_NUM'])
  }
vuex中的modules

vuex有两个原则
单一状态源(state只有一个)
state是只读(不能直接修改只能通过mutation修改)

由于单一状态源 当我们公共数据较多时 整个状态管理过于臃肿 难以维护

解决方案
在这个单一状态源 定义多个模块 在不同模块中单独管理不同功能的数据

将原来根对象中 定义state模块mutations模块actions getters模块 单独定义在另一个对象中
在vuex config对象定义modules来挂载模块
在这里插入图片描述
modulea中:

export default{
    namespaced: true,
    state:{
    },
    mutations:{
    },
    actions:{
    },
    getters:{
    }
}

moduleb中:

export default{
    namespaced: true,
    state:{
    },
    mutations:{
    },
    actions:{
    },
    getters:{
    }
}

index.js中:

// 在vuex中 挂载
const store = new Vuex.Store({
	modules: {
		modulea,
		moduleb
	}
})

在组件中获取state

this.$store.state.模块名.状态名

将state分层了
注意:模块化后的问题 只是将state分层了 mutation action并没有
解决方案:模块化同时定义每个模块的命名空间

// 这是某个模块 新增 namespaced属性为true
const modulea = {
	namespaced: true,
	state: {},
	actions: {},
	mutations: {
	
	}
}

加了命名空间后 mutation的使用为: this.$store.commit('modulea/ADD_NUM')

原理:加上命名空间后,该模块中的mutation或者action以及getter名字会自动变成 '模块名/mutation名'

加了命名空间后如何在组件中操作vuex
基础提交:
this.$store.commit('模块名/mutation名',参数)
使用助手函数

获取state数据

{
	computed: {
		// ...mapState('模块名', ['state名'])
		...mapState('cart', ['num']) // 使用时 this.num
		...mapState({
			cartNum: state => state.cart.num // 参数是对象好处重命名 使用this.cartNum
		})
	}
}

提交mutation

{
	methods: {
		// 第二个参数是数组
		...mapMutations('cart', ['ADD_NUN'])
		// 使用时 this.ADD_NUN(参数)
		// 第二个参数是对象 好处 重命名
		...mapMutations('cart', {
			addNum: 'ADD_NUN'
		})
		// 调用 this.addNum(参数)
	}
}

触发action

{
	methods: {
		// 第二个参数是数组
		...mapActions('cart', ['FETCH_CATES'])
		// 使用时 this.FETCH_CATES(参数)
		// 第二个参数是对象 好处 重命名
		...mapActions('cart', {
			 fetch_cates: 'FETCH_CATES'
		})
		// 调用 this.fetch_cates(参数)  使用
	}
}

mapGetters

{
	computed: {
		// 第二个参数是数组
		...mapGetters('cart', ['DOUBLE_NUM'])// 使用 this.DOUBLE_NUM
		
		// 第二个参数是对象 好处 重命名
		...mapGetters('cart', {
			doubleNum: 'DOUBLE_NUM'
		})
		
		// 使用时 调用 this.doubleNum
	}
}
vuex如何状态持久化

原理:将改变后状态在缓存中备份一下 当刷新时 取得缓存中备份数据即可

手动在数据改变时备份 拿数据时 判断缓存有无备份 有就取备份数据赋值 否则就初始值

利用vuex持久化插件 vuex-persist
安装npm i 'vuex-persist'
引入import VuexPersistence from 'vuex-persist'

// 创建缓存插件
/* 
// 所有的状态都缓存
const vuexLocal = new VuexPersistence({
  storage: window.localStorage
}) 
*/

// 增加reducer属性决定state下哪些数据缓存
const vuexLocal = new VuexPersistence({
  storage: window.localStorage,
  reducer: state => {
    return {
    // cart属性名必须是 state下对应状态同名!!!!!!!!!!!!!!!!!!
      cart: state.cart
    }
  }
})

// 使用插件
const store = new Vuex.Store({
  modules: {
    user,
    cart
  },
  plugins: [vuexLocal.plugin]
})

这样 state.cart 中的数据就存入了 window.localStorage 中:

在这里插入图片描述

如何在vueCli中 开发环境下做反向代理

在vue.config.js中设置proxy

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    proxy: {
    //  开发环境下所有的请求必须以/api开头  才能触发反向代理
      '/api': {
      //  代理的源   开发源和生产源一般不一样
        target: 'https://xxx.com',
        //  是否切换源
        changeOrigin: true,
        //  路径重写  将请求的前缀  /api 重写为一个值(最终请求完整地址中没有/api   会被替换为路径重写的值)
        pathRewrite: {
        //  所有请求中前缀/conner      真实发送请求时会被替换成 /conner
          '^api': '/conner'
        }
      }
    }
  }
})

例如发送一个请求:

axios.get('/api/shop/goods/category/all').then((res) => {
        console.log(res)
      })

那么反向代理真实地址是:

'https://xxx.com/conner/shop/goods/category/all

注意:

'/api'可以写成[process.env.VUE_APP_BASEURL]
即:

proxy: {
      [process.env.VUE_APP_BASEURL]: {
        target: 'https://xxxxxx',
        changeOrigin: true,
        pathRewrite: {
          '^api': '/api'
        }
      }
    }

为啥??? [process.env.VUE_APP_BASEURL]是横么???
原因如下:
上文中我们提到“自定义环境变量”,这里使用变量VUE_APP_BASEURL定义生产环境和开发环境,
使用的时候使用process.env.VUE_APP_BASEURL获取。
在开发阶段中设置变量值为 ‘/api’ ,通过process.env.VUE_APP_BASEURL获取到的值为 ‘/api’ ,所以可以使用process.env.VUE_APP_BASEURL替换’/api’。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

扩展:对象中使用变量值作为键名

定义一个变量test,值为name:

let test='name'

定义一个对象:

let obj={
	test:'happy'
} 
//  此处表示obj里面对象的属性名为 'test'  值为happy

再举个例子,定义一个对象:

let obj={
[test]:'happy'
}
//  此处表示obj里面的对象的属性名为 name  值为 happy

所以!
如果变量值要做属性名,就要在变量外面加 [ ]
这也是上文中 [process.env.VUE_APP_BASEURL]为什么加 [ ]

mock 接口

前后端分离开发,很多情况下,前端后端开发是同步的,会出现前端在开发时后端接口还没有完成的情况,前端可以自己mock接口

(定义接口)规范:
mock所有的接口 path一定要和真实后端接口保持一致
所有接口返回的数据格式一定要和真实接口保持一致

代码本地使用mock包进行mock接口

安装:
npm install mockjs -D
在目录下新建mock文件夹,新建一个js文件,写入:

//  引入
const Mock = require('mockjs')
//  三个参数:模拟接口的路径   模拟接口请求类型   模拟接口返回数据
Mock.mock('/api/getItemLists', 'post', {
   //  返回状态码
  code: 200,
  //  提示信息
  msg: 'success',
  //  返回的data参数   具体的书写规则往下文看
  'data|10-20': [
    {
      'id|+1': 1,
      name: '@ctitle',
      'price|0-1000': 0,
      'cateId|1-100': 1,
      'sale|0-1000': 0,
      'store|0-500': 0,
      onOff: '@boolean',
      createAt: '@datetime',
      thumb: '@image("100x100","@color","@cname")'
    }
  ]
})
mockjs产生随机数的语法

量词 (修饰 数组 字符串 number(就是大小)id)
注意:需要定义在 字段名中 加引号

{
  code: 200,
  msg: 'success',
  'data|10': [] // 数组长度就是确定10
  'data|10-20': [] // 数组长度就是确定10-20
}
// 针对number
{
  'num|10-100':10 // 生成10-100随机整数
}
// 针对字符串
{
  'str|5': '☆' // 产生'☆☆☆☆☆'
}
{
  'str|5-10': '☆' // 产生5到10五角星
}
// 针对id
{
  'id|+1': 1 //自增1 
}

随机数据 占位符
注意:用在 字段值中 需要加上引号 语法 ‘@占位符’

{
  isShow: '@boolean'
}

Basic 基础占位符
boolean, natural, integer, float, character, string, range, date, time, datetime,now
Image 图片相关
image, dataImage
使用时,可以定义 生成图片的大小 颜色,以及 图片中的文本
"@image('100x100','@color', '文本内容')"
Color 随机颜色
color
Text 随机文本
paragraph, sentence, word, title, cparagraph, csentence, cword, ctitle
Name 随机名字
first, last, name, cfirst, clast, cname
Web 随机网络地址
url, domain, email, ip, tld
Address 随机地址
area, region
Helper
capitalize, upper, lower, pick, shuffle
Miscellaneous
guid, id

案例:

Mock.mock('/api/getItemLists', 'post', {
  code: 200,
  msg: 'success',
  'data|10-20': [
    {
      'id|+1': 1,
      name: '@ctitle',
      'price|0-1000': 0,
      'cateId|1-100': 1,
      'sale|0-1000': 0,
      'store|0-500': 0,
      onOff: '@boolean',
      createAt: '@datetime',
      thumb: '@image("100x100","@color","@cname")'
    }
  ]
})

在线mock平台

有啥

  1. easyMock
  2. fastMock
  3. rap2.taobao.org
  4. (软件:apiPost)

在线mock平台
利用mockjs语法生成随机数,真实线上接口,请求真的可以发送出去
打开 fastmock 网站
新建一个项目:
在这里插入图片描述

新建接口:
在这里插入图片描述
填写接口路径 参数 等信息保存即可就可以得到一个可以请求的/itemList接口:
在这里插入图片描述

webpack中支持模块化

支持es6nodejs
区别
import:必须在代码编译前提前引入
require:一般在开发中使 用require引入开发时用到的包(上线手动删除)

二次封装axios

新建一个utils的工具包,在里面新建request.js,
在里面干什么? 新建实例request

//  引入axios
import axios from 'axios'

// 创建实例
const request = axios.create({
  // 定义baseURL
  baseURL: process.env.VUE_APP_BASEURL,
  // 定义过期时间
  timeout: 6000
})
// 请求拦截
request.interceptors.request.use(function (config) {
  return config
}, function (error) {
  return Promise.reject(error)
})
// 响应拦截
request.interceptors.response.use(function (response) {
  return response
}, function (error) {
  return Promise.reject(error)
})
// 导出
export default request

新建一个api文件在里面建立index.js文件

// 导入
import request from '_utils/request'
// request.post('/itemList', params)   返回一个promise对象
// params = {}   传入的参数时params接收   如果不传参数  自定义为空  如果不手动设置为空  会出现undefined
const getItemList = (params = {}) => request.post('/itemList', params)

// 导出
export {
  getItemList
}

这样我们就可以在组件中使用了
首先在组件中引入

//  需要使用es6的解构赋值引入
import { getItemList } from '_api'

如果直接import getItemList from '_api'
会出错,报错显示:
在这里插入图片描述
为什么?
import getItemList from '_api'用来接收导出默认值
例如:export default request
导入多个时,
例如:export { getItemList }
使用import getItemList from '_api'接收

组件中导入getItemList以后,使用 :

	getFetch () {
      getItemList().then((res) => {
        console.log(res)  //  得到的res就是成功返回的数据
      })
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值