简单梳理 Vue 知识笔记(持续更新)

Vue

001、Vue-简介

一、Vue简介

  1. 一套用于构建用户界面的渐进式框架
  2. 核心思想是数据驱动:数据映射视图,数据的修改会自动引起视图的改变。
  3. 双向数据绑定:数据的改变会引起视图的变化,视图的变化也会引起数据的变化,这就是双向数据绑定。

二、如何理解Vue的渐进式框架

  • Vue框架本身是分层的设计
    1. 核心,视图渲染。
    2. 组件化
    3. 单页面路由
    4. 状态管理
    5. 构建系统

三、前端三大框架

框架名 出现时间 所属 一开始特色
Angular 2009年 谷歌 指令系统、双向数据绑定
React 2013年 Facebook 虚拟DOM、组件化
Vue 2015年 尤玉溪 指令系统、双向数据绑定、虚拟DOM、组件化

四、MVC、MVVM

  • MVC

    1. M:模型:(Model)

    2. V :视图 (view)

    3. C : 控制器 (Controller)

    4. 各部分之间的通信:

      Model—>View–>Controller—>Model

      1. View 传送指定到 Controller。
      2. Controller完成业务逻辑后要求Model改变状态。
      3. Model将新的数据发送到View,用户得到反馈。
    5. 所有通信都是单向的

  • MVVM

    1. M :模型:(Model)

    2. V :视图 (view)

    3. VM : 视图模型 (ViewModel)

    4. 各部分之间的通信:

      View<—>ViewModel<—>Model

      1. View 与 Model 不发生联系,都通过 ViewModel 传递
      2. View 与 ViewModel 之间双向绑定:View 的变动,自动反映在 ViewModel ,反之亦然。
      3. Angular、Vue 都采用了这种模式。
  • 虚拟DOM

    • 什么是MVP、MVC、MVVM模式?
      • M model数据
      • V view视图
      • C control控制器
    • 如何理解MVVM?
      • VM 即视图模型,你可以理解为虚拟DOM
    • 什么是虚拟DOM? 虚拟DOM就是一个json对象,它用于对真实DOM进行描述。
    • 什么是diff运算? 当Vue实例中的数据发生变化时,Vue会获得一份对虚拟DOM的拷贝,如此我们就有两份虚拟DOM,一份是数据变化前的虚拟DOM,一份是数据变化后的虚拟DOM。所谓的Diff运算,就是对这两份虚拟DOM进行差异比较,从而找出它们的最小差异。再把这份最小的差异渲染到真实DOM中去。 MVVM框架,基于这种虚拟DOM的Diff运算,大大地减少了DOM的频繁操作,减少DOM操作本身就是一种性能优化。所以MVVM框架有利于性能优化,非常适合数据化的产品应用开发。

五、hello Vue

  • Vue的简单使用步骤

    1. 页面中需要提供一个挂载点(用来承载Vue实例)。就提供一个div并设置一个id即可

    2. 引入Vue的核心js文件

      1. 开发版本 vue.js
      2. 生产版本 vue.min.js
    3. 实例化Vue ,并做相关设置

      1. 引入Vue核心js文件之后,就给window对象上加了一个Vue对象(Vue是一个类)

        /实例化Vue
        var vm = new Vue({
        //配置选项
            //el:配置挂载点,也就是说这里生成的Vue实例会应用到哪个页面位置上,1.值可以使一个css选择器写法'#app',2.可以是一个元素的Dom对象document.getElementById('app')
            el:"",
            //data:声明式变量,当前实例的数据源,data选项中自己定义的数据可以通过实例对象直接访问,设置。
            data:{
            //这块是自己写的
            }
            
        })
        

六、数据驱动

  • Vue 核心思想是数据驱动,数据驱动:数据的变化会 引起视图(页面)的变化。

002、Vue - 内置指令

一、指令是什么

  • Vue 中,以 v- 开头的标签属性被称为指令。不同的指令有不同的作用,指令的值是js表达式,""里的是变量。

二、双向数据绑定

  • 双向数据绑定;数据的变化会引起 页面的变化,反之亦然,主要用于INPUT框。

三、常用的Vue指令

1.文本类

  1. v-text

    1. 将数据绑定到元素的textContent上面

      <P v-text='name'></p>
      
  2. v-html

    1. v-html与v-text基本一样

    2. v-html会 解析html元素 ,v-text 不会解析html元素。

      <P v-html='name'></p>
      
  3. v-once它只能和文本插值进行使用,它只渲染一次

    1. <P v-once>{{name}}</p>
      
  4. {{}}

    1. 文本插值:渲染到页面的时候会有一闪而过的情况

      <p>{{name}}</p>
      

2.动态属性、动态样式

  1. v-bind(简写 :)用于动态绑定html标签属性

    语法:

    1. v-bind:attrname="数据"

    2. :attrname="数据"

    3. v-bind=对象

      1. v-bind="{name:'张三',age:18}"==>v-bind:name="张三" v-bind:age="18"
      2. v-bind="{name:name,age:age}"==>v-bind:name="name" v-bind:age="age"
      3. 此时对象的值在这里是变量,是data里的变量
    4. Vue中class 属性是一个特殊的属性 ,使用v-bind绑定时有很多不同的用法

      1. v-bind:class="'hello'"

        1. 此时 hello为字符串 ,代表给class加上hello这个类名
      2. v-bind:class="hello"

        1. 此时hello为vue实例中data的属性,代表给class加上hello代理属性对应的类名
      3. v-bind:class="{key1:value1,key2:value2}"

        1. 根据value1这个数据是否为真值,来控制是否要加上key1这个 类名

        2. 根据value2这个数据是否为真值,来控制是否要加上key2这个 类名

          v-bind:class="{ active: isActive, 'text-danger': hasError }"
          v-bind:class="[isActive ? activeClass : '', errorClass]"
          
      4. 数组 v-bind:class="[value1, 'value2',value3,{key:value}]"

        1. 使用value1这个数据的值来作为其中一个l类名
        2. 使用value2这个字符串来作为其中一个l类名
        3. 使用value3这个数据的值来作为其中一个l类名
        4. 根据value这个数据是否为真值,来控制是否要加上key这个 类名
    5. Vue中style 属性是一个特殊的属性 ,使用v-bind绑定时有很多不同的用法

      1. v-bind:style="color : red" 这种是错误的写法会报错

      2. v-bind:style="{key1:value1,key2:value2,key3:'value3'}"

        key1、key2、key3、都是css的属性名

        value1的值作为key1这个css属性名的值

        value2的值作为key2这个css属性名的值

        value3这个字符串会作为key3这个css属性名的值

          v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"
        
      3. v-bind:style="[value1, value2,{color:myColor}]"

        使用value1这个数据来控制,data中的value1必须为对象{font-size:‘20px’}

        使用value2这个数据来控制,data中的value2必须为对象{font-size:‘20px’}

        使用myColor这个数据来控制color属性的值

    6. v-bind 在组件时的sync修饰符

      sync—update:prop 搭配使用 prop传到子组件的变量

      <div id="app">
          <hello v-bind:value1.sync="msg1" v-bind:value2.sync="msg2"></hello>
      </div>
      Vue.component("hello", {
          props: {
              value1: {
                  type: String,
              },
              value2: {
                  type: String,
              },
          },
          template: `
              

      {{ value1 }}

      {{ value2 }}

      `
      , methods: { fn1() { // 不能直接去修改prop数据 // this.msg = "我的天"; // 通知父亲去修改 this.$emit("update:value1", "我的天"); }, fn2() { this.$emit("update:value2", "我的地"); }, }, }); var vm = new Vue({ el: "#app", data: { msg1: "hello", msg2: "world", }, });

3.条件渲染

  1. v-show

    1. 根据表达式的真假值,切换元素的display CSS显隐属性

      <div id= 'app'>
      <button v-show=' !islogined'>登录</button>
      <button v-show=' islogined'>退出登录</button>
      </div>
      <script>
          var vm = new Vue({
              el:'#app'
              data:{
              islogined:true
              }
          })
      </script>
      
  2. v-if

    1. v-if与v-show的效果基本一致

    2. v-if与v-show的区别

      1. v-if是真正的条件渲染 ,条件为true元素渲染出来,默认条件为flase时,不渲染,条件变为flase时会销毁,条件变成 true的时候渲染出来。

      2. v-show只是简单去控制CSS的display属性

      3. v-show有更高的初始渲染开销,(因为不管默认条件是true还是false,都会渲染出来)

        v-if有更高的切换开销(切换时做重建销毁的操作)

        如果需要频繁显示隐藏切换用v-show更好,其余情况使用v-if

      4. v-if有v-else和v-else-if配套使用,v-show没有

      5. v-if可以配合template元素一起使用,v-show不可以。

        因为v-show是要控制元素的css属性的,渲染页面的,template是不会被渲染出来的。

      <div id= 'app'>
      <button v-if=' !islogined'>登录</button>
      <button v-if=' islogined'>退出登录</button>
      </div>
      <script>
          var vm = new Vue({
              el:'#app'
              data:{
              islogined:true
              }
          })
      </script>
      
  3. v-elsev-else-if

    1. 这两个指令都需要与v-if搭配才可以使用

    2. v-else不需要表达式

      <div id= 'app'>
      <button v-if=' !islogined'>登录</button>
      <button v-else>退出登录</button>
      
      <p v-if='score>=90'>优秀</p>
      <p v-else-if='score>=80'>良好</p>
      <p v-else-if='score>=60'>及格</p>
      <p v-else>不及格</p>
      </div>
      <script>
          var vm = new Vue({
              el:'#app'
              data:{
              islogined:true
              sorce:100
              }
          })
      </script>
      
    3. v-else-if需要表达式

      <div id= 'app'>
      <button v-if=' !islogined'>登录</button>
      <button v-else>退出登录</button>
      
      <p v-if='score>=90'>优秀</p>
      <p v-else-if='score>=80'>良好</p>
      <p v-else-if='score>=60'>及格</p>
      <p v-else>不及格</p>
      </div>
      <script>
          var vm = new Vue({
              el:'#app'
              data:{
              islogined:true
              sorce:100
              }
          })
      </script>
      

4.列表渲染

  1. v-for列表循环

    • 循环数组

      1. v-for=“item in xxx”

      2. v-for=“(item,index) in xxx”

        <div id= 'app'>
        	<div v-for='item in arr'>
        		<span></span>
        		<span></span>
        		<span></span>
        	</div>
        </div>
        <script>
            var vm = new Vue({
                el:'#app'
                data:{
                	arr:[
                		{id:1,name:'yeshuai'},
                        {id:2,name:'xiezhaoyi'}
                	]
                }
           })
        </script>
        
    • 循环对象

      1. v-for=“item in xxx”
      2. v-for="(value,key,index) in xxx"
    • 循环字符串

      1. v-for=“item in "张三"
      2. v-for=“item in "10"
        • 相当于循环数组[1,2,3,4,5,6,7,8,9,10]
  2. template 元素

    1. Vue给我们提供了一个template 元素,他的作用是包裹元素,不会渲染到浏览器上。

      <div id= 'app'>
      <template v-if='isshow'>
          <p>优秀</p>
          <p>良好</p>
          <p>及格</p>
          <p>不及格</p>
      </template>
      </div>
      <script>
          var vm = new Vue({
              el:'#app'
              data:{
              isshow:true
              }
          })
      </script>
      

5.v-on 绑定事件

  1. v-on

    语法:

    1. v-on:xxxx= 'yyyy'
      • xxxx 事件名,keyup,click,mousemove,mouseenter,
      • yyyy 事件处理函数
    2. @xxxx = 'yyyy'
      • xxxx 事件名,keyup,click,mousemove,mouseenter,
      • yyyy 事件处理函数

6.v-model

  • v-model 将数据与表单相关元素 input、textarea、select绑定起来,实现双向数据绑定

  • input为输入框的时候,v-model就双向绑定了输入框的值和data里对应的代理属性的值

  • 当为单选框的时候设置v-model且data对应的代理属性一致,那么就 可以单选,且绑定值

    1. 多选框,定义一组数组类型的数据,然后通过v-model绑定到多选框上就可以了

      <label><input type="checkbox" value="apple" v-model="loveFruits" />apple</label>
      <label><input type="checkbox" value="banana" v-model="loveFruits" />banana</label>
      <label><input type="checkbox" value="orange" v-model="loveFruits" />orange</label>
      
    2. 下拉选择框,v-model要绑定到select上,myCity不是数组

      <select v-model="myCity">
          <option value="shenzhen">深圳</option>
          <option value="shanghai">上海</option>
          <option value="guangzhou">广州 </option>
      	<option value="beijing">北京</option>
      </select>
      

      多选下拉选择框,v-model要绑定到select上,loveCity是数组

      <select v-model="loveCity">
          <option value="shenzhen">深圳</option>
          <option value="shanghai">上海</option>
          <option value="guangzhou">广州 </option>
      	<option value="beijing">北京</option>
      </select>
      
    3. 多行文本输入框 ,v-model绑定到textarea

    4. v-mode有三个修饰符

      1. v-mode.lazy 相当于change事件,比如失去焦点
      2. v-mode.number 文本框的值变成number型
      3. v-mode.trim 给文本去除前后空格
    5. v-mode其实是一个语法糖(写起来方便,让我们甜甜的)

      <input type="text" v-model="msg" />
          ===
      <input type="text" v-bind:value="msg" v-on:input="msg=$event.target.value" />
      
    6. $event

      1. 原生事件时它是事件对象
      2. 自定义事件时它是触发事件时传递过来的payload参数
    7. 单个复选框v-model用法

      <input type="checkbox" v-model="checked" />我们的协议
      <button @click="handleRegister" :disabled="!checked">注册</button>
      //页面打开时,调用后台接口,通过接口返回的数据来控制这个勾选框是否勾上。这时发现后台接口返回的数据不是一个 boolean 类型。而是字符串的 Y  /  N
      <input
          type="checkbox"
          v-model="isChecked"
          true-value="Y"
          false-value="N"
      />
      var vm = new Vue({
          el: "#app",
          data: {
              checked: false,
              isChecked: "N",
          },
          methods: {
              handleRegister() {
                  alert("注册");
              },
      
              handleRegister2() {
                  if (this.checked) {
                      alert("注册2");
                  }
              },
          },
      });
      

7.v-pre

  • 有些时候我们需要在页面上 就直接输出{{}}这种格式的字符串,不让vue对其做解析的操作。这是只需要使用上v-pre就可以了

  • v-pre没有参数,也没有表达式

    <p v-pre>{{msg}}</p>
    

8.v-cloak

  • vue页面闪烁问题:

    1. 有时会在页面出现插值表达式的语句

    2. vue解析页面需要时间

      解决方案:

      1. 不适用插值表达式改用v-text
      2. 使用v-cloak
        • 将v-cloak写在挂载点元素上,他没有参数,也没有表达式
        • 当vue第一次解析成功以后会将v-locak属性主动删除掉
        • 设置一个全局的CSS样式。样式如下
[v-cloak]{
  display:none;
}

003、Vue - 数据更新检测

一、Vue的核心思想是数据驱动

  • Vue是数据驱动的,如果某个数据类型是数组类型,在一些数组的炒作下是可以更新页面的,还有一些数组的操作不会引起页面的更新。

二、可以引起页面更新的数组方法(怪异)

  1. unshift() 前面增加
  2. push() 后面增加
  3. shift() 前面删除
  4. pop() 后面删除
  5. splice() 高级方法删除、添加、删除
  6. sort() 排序
  7. reverse() 倒序

三、下面两种方式修改数组是不会引起页面的更新

  1. 直接通过下标的方式修改数组 vm.arr[10]=“halo”

  2. 通过length修改数组 vm.arr.length=num

    解决办法:

    1. 使用 Vue.set(target,index,value)的静态方法
    2. 使用实例对象 vm.$set(target,index,value) 方法
      • Vue.set(target,index,value)
      • target 要修改的数据源
      • index 要修改的下表
      • value 要修改成什么样

四、对象更新检测的问题

  1. 直接给对象新增一个属性是不会引起页面更新的

    解决办法:

    • 使用Vue.set(target,key,value)或者vm.$set(target,key,value)
    • Vue.set(target,key,value)
    • target 要添加的数据源
    • key 要添加的key
    • value 要修改成什么样

004、Vue - 属性/方法

  1. 类似这种 Vue.xxx() 静态方法
  2. 通过实例对象 .\$xxx() 实例方法
    1. vm.\$set()
    2. vm.\$destory()
  3. 通过实例对象.\$xxx 实例属性
    1. vm.\$data
    2. vm.\$sel
  4. 代理属性,一般就是定义在data选项中的数据,通过实例对象去访问。
  5. 代理方法,一般就是定义在methods选项中的方法,通过实例对象直接访问

005、Vue - 修饰符

1.修饰符

  1. stop 阻止事件传播
  2. prevent 阻止默认事件
  3. self 只点自己
  4. capture 事件流-----事件捕获
  5. {kecode}|{keyAlias} 特定键盘修饰符
    • keyAlias
      • enter
      • tab
      • delete
      • esc
      • space
      • up
      • down
      • left
      • right
      • Vue.config.keycodes.fn1=13
  6. once 只绑定一次
  7. left 鼠标左键
  8. right 鼠标右键
  9. middle 鼠标滚轮
  10. passive

2.系统修饰符

  • ctrl
  • shift
  • alt
  • meta windos键
  • exact 精准匹配
  • native 事件修饰符

    • 作用:将事件绑定到组件的根元素上

    • 在调用组件时去绑定一些原生事件,比如 click dblclick mousedown。他们是不生效的。因为会将他们任何是自定义事件的监听

    • 解决方法:

      1. 子组件触发这个事件

        <div id="app">
            <base-button @click="fn1">按钮1</base-button>
        </div>
        
        Vue.component("baseButton", {
            template: `
                
        	`,
        });
        var vm = new Vue({
            el: "#app",
            methods: {
                fn1() {
                    console.log("fn1");
                },
            },
        });
        
      2. 使用 native 事件修饰符

        <div id="app">
            <base-button @click.native="fn1">按钮1</base-button>
        </div>
        
        Vue.component("baseButton", {
            template: `
                
        	`,
        });
        var vm = new Vue({
            el: "#app",
            methods: {
                fn1() {
                    console.log("fn1");
                },
            },
        });
        

006、Vue - watch 配置选项

一、watch是做什么的?

  • 监听数据的变化,当数据发生变化的时候,可以去做一些额外的操作

    语法1:

    • watch:{

      ​ key1:value1,

      ​ key2:value2

      }

      key1,key2是要监听的数据表达式

      value1,value2是处理函数。string|function|object|Array

      ​ 处理函数有两个参数,newVal,oldVal

      watch:{
          a:function(newVal,oldVal){
              console.log(newVal,oldVal)
          }//不可以写箭头函数 
          
          a:'hello' //要在methods配置选项里写hello函数,不然会报错
          a:{ //一定要提供handler属性,属性值就是处理函数,
              handler:function(newVal,oldVal){
                  console.log(newVal,oldVal)
              }
          }
          
          	////写成对象的写法可以监听,做更多的事情。
          	//深度监听;对象的属性值更改默认是监听不到的
          	//immediate  默认触发一次监听
          obj:{
              handler:function(newVal,oldVal){
                  console.log(newVal,oldVal)
              },
              deep:true//深度监听
              immediate;true
          }
          
          
          //key的表达式的写法,直接监对象里的属性
          "obj.name":function(newVal,oldVal){
              console.log(newVal,oldVal)
          }
      }
      

007、Vue - computed 配置选项

  • computed 配置选项(计算属性,衍生属性,派生属性)

  • 其基于其余的数据算出来的一分新数据

  • computed 的依赖项可以是 data 数据 props 数据或者其余的 computed 数据

  • 语法

    • computed:{

      ​ key1:value1,

      ​ key2:value2

      }

      • key1/key2就是计算属性
      • value1、value2就是对应的计算属性的处理函数 (计算方法),且必须有返回值,返回值就是这个计算属性的值
      computed:{
         key1:function(){
      	   return   计算属性+计算属性
         },
         key2:function(){
             return   计算属性+计算属性
         },
      }
      
    • computed:{

      ​ key1:{

      ​ get:function(){

      ​ return 计算属性+计算属性

      ​ },

      ​ set:function(value){

      ​ return 计算属性+计算属性

      ​ }

      ​ }

      }

    • 计算属性的特点:

      • 计算属性也可以像data中的数据那样去使用
      • 计算属性的值不允许手动修改,他的值基于依赖项
      • 计算属性有缓存,默认计算出来之后,如果他的依赖项不发生变化,那么后续将一直使用缓存的数据

008、Vue - 生命周期/钩子函数

一、什么是生命周期?

  • vue实例或组件(组件本质上就是一个具有预定义选项的实例)创建到销毁的一系列过程,就叫生命周期

二、什么是钩子函数?

  • 在生命周期不同的阶段中会自动执行的函数,就叫做生命周期的钩子函数

三、钩子函数有哪些?

beforeCreate 实例创建之前

created 实例创建之后

beforeMount 实例挂载之前

mount 实例挂载之后

beforeUpdate 数据更新之前

updated 数据更新之后

beforeDestroy 实例销毁之前

destroyed 实例销毁之后

  • swiper 引入接口的图片会有小问题,图片是异步回调,

    • 解决方案:

        1. 使用updated钩子函数,等图片都回来渲染到视图上,在this.swiper.update()

        2. 在ajax请求成功的回调函数内

          this.$nextTick(()=>{
              this.swiper.update()
          })
          

009、Vue - props选项

一、props选项

  • 将组件看成是一个函数,props 就是这个函数接收到的参数集合
  • prop 就是 props 中的具体一个参数
function hello(props) {

}
hello({name: 张三, age: 18, sex: '男'})
props => {name: 张三, age: 18, sex: '男'}  形参
name - prop
age  - prop
sex  - prop

//这里就是举一个例子

二、组件定义时如何设置形参,就是设置 props 选项

Vue.component('hello', {

//最简单的方式,写成数组形式,数组中每一项就是一个 prop

//注意:调用时不传递是没问题的

//定义的每一个 prop 。可以直接通过实例对象去访问到,就向访问 data 数据和 computed 数据那样

props: ['name', 'age', 'sex'],
template: `
     
Hello {{ name }} - {{ age }}
`
, methods: { hi() { console.log(this.name) } } })

三、调用组件时,如何传递参数,就是在组件的标签上写属性即可

​ hello 组件中的 name 得到的是 “张三”

​ hello 组件中的 age 得到的是 “18”

​ hello 组件中的 sex 得到的是 undefined

  • 如果调用组件时,没有传递某个 porp 下来,那么组件中的这个 prop 的值是 undefined

    我们希望能够设置 prop 的默认值,也就是说调用时如果没有传递的化,我就使用默认值

    function hello(name = ‘张三’) {

    }

    hello()

    组件中如何给 prop 设置默认值呢?关键点将 props 选项从数组格式修改成 对象格式

    Vue.component("hello", {
    props: {
    key1: { type: String, default: 'xxx' },
    key2: { type: Number, default: 'yyy' }
    },
    template: `
        
    Hello. {{ name }}
    `
    , }) //key1、key2 就是 prop 的名字。 //type: String, type: Number, 代表对应 prop 希望接收到的数据类型 //default: 'xxx', 默认值设置。

四、希望在调用组件时传递过来的prop是有效的。

  • 比如说:

    我希望你给我传 String 类型, 调用时就要传 String 类型过来。

props: {
   // value 直接写要的类型
   key1: String
   // value 写成对象,在对象中再通过 type 属性去控制类型
   key2: {
   type: Number
   }
}

五、单向数据流

  • 单向数据流:数据的流向是从高往地流。不能反过来

    root ->prop-> hello √

    hello ->prop-> root ×

六、非 prop 的 attribute (非 prop 的特性)

  • 在调用组件时,设置的属性(attribute)如果这个组件中没有定义相应的prop。那么这个属性就叫做 非prop的attribute
Vue.component('hello', {
        props: {
          name: String,
          age: Number
        },
        template: `
hello
`
}) <hello name="张三" age="18" id="box" title="我的天" class="box1" style="color: red"></hello>
  1. 这里 hello 组件调用时。设置的属性有:name、age、id、title、class、style
  2. hello 组件中将 name、age 是规定为 prop 数据的
  3. 那么 这里 id 和 title 这两个属性就叫做非prop的特性

注意:class 与 style 不是非prop的特性。他们两个是非常非常特殊的一个属性

  • 非prop的attribute 的特点

    1. 最主要的一个特点就是会继承到组件的根元素上
    2. 并且是一种替换的操作
  • class与style是非常特殊的

    1. 他们有 非prop的attribute 的继承的特点
    2. 但是他们不是替换的操作,而是拼接操作

    注意:用处。调用别人的组件的时候,这个组件的源代码我不能修改,这时如果要修改样式,就可以在调用时

    ​ 设置自己的 class 或 style

七、组件的 inheritAttrs 配置选项

  • 组件接收到的 非prop特性 会自动继承到组件的 根元素上。如果不希望继承,就可以配置 inheritAttrs 为 false 。
Vue.component("hello", {
    inheritAttrs: false,
    props: {
        name: String,
        age: Number,
    },
    template: `
		
hello
`
, });

八、 $attrs 和 inheritAttrs来实现一些基础组件

Vue.component("baseInput", {
    inheritAttrs: false,
    props: {
        label: {
            type: String,
            required: true,
        },
    },
    // 不使用prop数据,而是使用非prop特性数据
    // template: `
    //   
// // {{ label }} // // :type="$attrs.type" // :placeholder="$attrs.placeholder" // :disabled="$attrs.disabled" // :readonly="$attrs.readonly" /> // //
// `, // 一个一个的去绑定 $attrs 也挺费劲。这时可以使用 v-bind 直接绑定一个对象 // v-bind="{key1: value1, key2: value2}" => v-bind:key1="value1" v-bind:key2="value2" template: `
`
, created() { console.log(this.$attrs); }, });

010、Vue - 组件之间的关系

一、父—>子通信

  • 就是使用prop传递数据给子组件即可

二、子—>父通信

  • 子组件通过$emit() 去出发一个自定义事件

    
    //子组件的$emit()去触发一个自定义事件
    methods:{
        fn1(){
            //触发自定义事件
            this.$emit('abc')
            //'abc'是父组件自定义的监控子组件的事件
        }
    }
    //同时父组件在调用子组件的时候也要去监听这个自定义事件 
    <son @abc='父组件methods里的事件处理函数'></son>
    

三、兄弟通信

  • 中央事件管理器(中央事件总线),适用于: 亲兄弟之间或者关系特别复杂的组件之间

    1. 先创建一个空的vue实例对象。也就是不需要额外配置什么选项。

      const bus = new Vue()

    2. A B 两个组件,A 通信 B

      1. 先在 B 组件的 created 中去通过 bus 来监听一个自定义事件

        bus.$on(eventName, callback)

            -	eventName 要监听的事件的名字
            -	callback 事件触发时的一个回调函数
        
        1. 在 A 组件的某个时候去触发自定义事件

          bus.$emit(eventName, payload)

          • eventName 要触发的事件的名字
          • payload 触发事件时传递过去的参数
// 创建一个事件总线
const bus = new Vue();

Vue.component("hello", {
    data() {
        return {
            name: "hello",
        };
    },
    template: `
		
hello - {{ name }}
`
, created() { // 监听 bus.$on("abc", () => { console.log("abc事件被触发了"); // this === hello 组件实例 this.name = "hellohello"; }); }, }); Vue.component("world", { data() { return { name: "world", }; }, template: `
world - {{ name }}
`
, methods: { fn1() { // 触发abc bus.$emit("abc"); }, }, }); var vm = new Vue({ el: "#app", });
  • vuex 全局状态管理器

011、Vue - slot插槽

  • 调用组件时,写在组件标签内的内容,默认是不会被渲染的。有时,我们需要在写在组件标签内的内容能够被渲染出来,这时就需要使用 slot 插槽这个功能点

一、slot相关的一些话术

  1. slot内容、插槽内容、插槽模板内容

    调用组件时写在组件标签内的内容就叫做 slot 内容

  2. slot标签、slot组件、slot插槽、插槽 (坑、茅坑)

    Vue 内置的一个组件

二、slot 的基本使用

  1. 定义组件时想好要在哪个位置去渲染 slot 内容

  2. 在想好的位置哪里,放置一个 slot 标签 即可

  3. slot 内容 就会自动去替换 slot 标签

<div id="app">
    <hello>
    <p>我的天,我不帅吗?</p>
</hello>
</div>

Vue.component("hello", {
    template: `
        

Hello

`
, });

三、slot 默认内容 (slot 后备内容)

  • 写在 slot 标签 内的内容就是这个 slot 标签 的默认内容

四、具名 slot (具名插槽)

  • 给 slot 标签 设置一个 name 属性。这时 这个 slot 标签就叫做 具名slot便签
  1. slot 在一个组件内是可以使用多次的
  2. 给 slot 取了名字之后,slot 内容想要渲染在哪个 slot 里面的话,就需要设置 slot 属性。属性的值为某个 slot 的名字。
  3. slot 有一个默认的名字,就叫做 default
<div id="app">
    <hello>
    <p slot="bottom">我的天,我不帅吗?</p>
	<button>点我</button>
	</hello>
</div>

Vue.component("hello", {
    template: `
        

Hello


`
, });

五、插槽的编译作用域

  1. 编译作用域:
    • 父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
  2. 插槽的编译作用域:
    • 注意:虽然说插槽内容最终渲染在子组件内。但是他的编译作用域还是要看插槽内容写在哪个组件的template里面。而不是看插槽内容最终渲染在哪个组件的template里面的slot标签。
    • 写在组件标签内的插槽内容是不属于组件的template的。只是通过slot可以让他渲染在组件里面去。

六、作用域插槽

<!-- 老语法的作用域插槽使用 -->
<world>
    <p slot="top" slot-scope="{ msg, age }">
        我的天-{{ msg }} - {{ age }}
    </p>
</world>

<!-- 新语法的作用域插槽使用 -->
<world>
     <template #top="obj">
         <p>
         我的天-{{ obj.msg }} - {{ obj.age }}
         </p>
</template>
</world>

<!-- 新语法的作用域插槽使用 解构赋值-->
<world>
    <template #top="{ msg, age }">
        <p>
            我的天-{{ msg }} - {{ age }}
        </p>
</template>
</world>


Vue.component("world", {
    data() {
        return {
            msg: "张三",
            age: 18,
        };
    },
    template: `
        

world

`
, });

七、插槽的新语法

  • slot 插槽 在 2.6.0 这个版本的时候,做了更新。提供了一个新的指令叫做 v-slot。后续实现具名插槽与作用域插槽都使用 v-slot 来实现

  • 2.6.0 版本在什么时候发布的:2019-02-04号(农历2018年过年那天)

  • v-slot语法:

    • v-slot:xxxx=yyyy

      ​ - xxxx 插槽名字

      ​ - yyyy 作用域插槽数据

      1. v-slot 必须用在 template 元素上。也就是说 插槽内容 需要通过 template 元素进行包裹

      2. 如果插槽没有设置 name 名字的化,v-slot === v-slot:default

      3. v-bind 指令有个简写 :

        v-on 指令有个简写 @

        v-slot 指令有个简写 #

      注意,使用 简写时必须携带名字。 默认的名字需要写成 #default

      1. 插槽只有一个的情况下,可以不使用 template 去包裹插槽内容。而是直接将 v-slot 写在组件标签上。

012、Vue - component/keep-alive

一、什么是动态组件

  • Vue 中提供有一个内置的组件叫做 component 。这个组件就是动态组件。它需要设置 is 属性。is 属性的值就是需要渲染的组件的名字
  • component 不具备缓存能力,是一直在创建,销毁
<component is="hello" ></component>     hello 组件替换这个内容
<component is="world" ></component>     world 组件替换这个内容
  1. 如果组件不存在就会报错
  2. 将is属性设置为一个可以变化的数据,就能实现组件的动态切换

二、keep-live

  • Vue 中提供了一个 keep-alive 内置组件。可以实现组件的缓存。
  • 将要缓存的组件使用 keep-alive 包裹住即可。
  • 被 keep-alive 缓存了的组件的特点:
    • 切换组件时,当前组件不会触发销毁的生命周期钩子。也就是说不会销毁了。
    • 切换回来时,也不会重新创建。(既然都没有被销毁,哪里来的重新创建呢)
    • 会多出两个生命周期的钩子函数
      • activated 缓存激活 第一次会触发、组件能被看到
        • 一般根 created 做一样的事情:请求数据
      • deactivated 缓存失活 组件不能被看到
        • 一般根 beforeDestroy 做一样的事情: 清除定时器、移除全局事件监听
 <keep-alive>
     <component :is="curPage"></component>
</keep-alive>
  • keep-alive 的更多属性设置:

    1. include 包含

      1. include=“组件1,组件2” 注意 逗号前后不要有空格
      2. :include="[组件1, 组件2]"
      3. :include="/^hello/"
    2. exclude 排除

      1. exclude=“组件1,组件2” 注意 逗号前后不要有空格
      2. :exclude="[组件1, 组件2]"
      3. :exclude="/^hello/"
    3. max 规定最大能缓存组件的数量,默认是没有限制的

    4. max=x2 规定最大能缓存组件的数量为2

      ​ 假定缓存队列是 [home, list]

      ​ 现在进入about的时候 about 也会被缓存上,这时会将之前的第一个给排出去 [home, list, about] => [list, about]

      先进先出原则

013、Vue - 脚手架/@vue/cli

一、安装

  1. 安装脚手架指令
$ npm install  @vue/cli -g
  1. 查看vue脚手架版本号
$ vue --version

二、创建项目

  1. 找到创建目录的文件夹 运行git bash here
$ vue create 项目名称

三、启动项目

  1. 启动项目,生成服务器
$ npm run serve

四、解释项目构成

  1. node_modules 项目依赖包
  2. public 静态资源文件,里面有一个默认的index.html和favicon.ico小图标
    1. index.html 整个项目的入口文件,它只提供了一个挂载点元素,js内容都是构建工具去处理的
    2. favicon.ico 当前项目文的浏览器小图标
  3. src 项目的源代码文件(放在这个文件夹里的东西我们认为都是需要构建的)
    1. assets 静态资源文件
    2. components 存放公用vue组件的文件
    3. .gitignore git忽略文件的设置文件
    4. babel.config.js babel相关的配置文件(es5以上语法转es5语法)
    5. package.json 项目描述文件
    6. README.MD 读我文件。接收一个项目先看读我文件
    7. 项目文件夹下有一个 APP.vue 文件
      1. 这是根容器,就是new Vue()
      2. 一个 .vue 的文件就是一个组件,浏览器不能识别
      3. template 是一个视图,他的里面只能有一个子节点
      4. 视图容器
      5. script标签内写的是脚本 ,如果需要外部的东西,就引进来
//外部引入的组件不能直接用,要先转换成局部组件
import xxx  from './components/HelloWorld.vue'
xxx  是组件名
''   是组件的地址

​ 6. exports default {} //当前组件的选项

export default {
  //
  name: 'App',
  //从外部引入的组件变成局部组件
  components: {
    HelloWorld
  }
  //数据要使用工厂函数
  data(){
    return {
      msg:'hello app'
    }
  }
}

​ 4.项目文件夹下有一个 main.js 文件

  • 是整个应用程序的入口文件
//main.js是整个应用程序的入口文件
import Vue from 'vue'        //引入Vue这个类
import App from './App.vue'  //引入App这个根组件

Vue.config.productionTip = false    //生产环境不提示

new Vue({
  render: h => h(App),
}).$mount('#app')           //创建Vue的实例  并挂载在#app上
  1. style 样式 存放当前组件的样式 scoped 表示当前样式只对自己起作用

014、Vue - 路由

一、要区分后端路由与前端路由

  • 后端路由:应用程序如何去处理不同的url地址发送过来的请求。

    POST localhost:3000/api/login
    GET  localhost:3000/list 
    
  • 前端路由:不同的url地址要渲染不同的页面效果出来

    1.1 GET localhost:8080/index.html 
    1.2 GET localhost:8080/list.html 
    1.3 GET localhost:8080/about.html  //多页面应用
    
    2.1 GET localhost:8080/index
    2.2 GET localhost:8080/list
    2.3 GET localhost:8080/about  //单页面应用
    
    3.1 GET localhost:8080/#/index
    3.2 GET localhost:8080/#/list
    3.3 GET localhost:8080/#/about  //单页面应用
    
    1.x 的是以往看到的最多的
    2.x 与 1.x 的很类似,     history 模式路由
    3.x 修改hash的操作        hash 模式路由
    
  • 后端路由与前端路由最本质的一个区别:

    前端路由只处理 GET 请求

  • Vue 中 路由有两种不同的模式

    1. hash 模式 (默认模式)
    2. hisotry 模式
  • hash模式与history模式的区别

    1. url地址表现不同,hash模式有#号,history模式没有
    2. 实现原理不同
      1. hash模式是通过改变hash并监听hashchange事件
      2. history模式是基于最新的HTML5里面history相关的api进行的一些操作
    3. 兼容性不同
      1. hash模式都可以兼容
      2. history模式只能兼容到IE10及以上
      3. ps: vue的兼容性,是只能兼容到ie8
    4. history模式会出现404找不到页面这种问题。需要后端配置相关的处理才行。而hash模式没有这个问题
    5. history模式时,重定向会有些问题,需要页面的名字叫index.html才行
  • 如何切换模式呢

    • new VueRouter() 的时候,配置 mode 选项就可以切换模式

二、单页面应用

  • SPA single page application
  • 一个应用只有一个html页面,它就是 SPA.
  • 页面的切换其实是组件的切换,并修改了url地址。

三、多页面应用

  • MPA multe page application
  • 一个应用是有多个html页面组成的,页面之间的跳转是通过a标签的方式跳转的

四、路由

一、安装

  • 一个局部的路由的包
$ npm install vue-router

二、根目录下创建 router.js

//模块引入
import Vue from 'vue'        //引入Vue这个类
import VueRouter from 'vue-router'  //引入App这个根组件
//注册使用路由
Vue.use(VueRouter)
//引入组件
import Todolist from './views/Todolist.vue'
const router = new VueRouter({
  //定义路由匹配规则
  routes:[
    {path:'todo',
     component:Todolist},
  ]
})
//  把路由实例抛出去
exports default router
//  把路由实例抛出去以后,要在main.js里面接收一下
import router from './router'
//main.js接收完以后,还要在vue的实例中挂载一下
new Vue({
	router:router
  render: h => h(APP)
}).$mount('#app')

三、router-link

  • 与 router-view 一样,都是引入了 vue-router 之后,提供的全局组件

  • router-link的作用就是实现路由的跳转(url的跳转)它本质上就是一个a标签。我们推荐使用它,而不是去用a标签来跳转

  • 语法:

    • home
    • to 属性是必须的。用来设置点击时跳转到哪里。
  • 使用router-link的好处?

    1. 路径不需要去写 # 号了。它会根据当前路由的模式来决定最终渲染出去的a标签的href属性有没有# 号。

    2. 能够很方便的去实现高亮效果

      ​ 默认情况下,当前的路由地址与某个router-link的to属性匹配时,会给渲染出去的a元素添加上两个classs

      • router-link-exact-active

      • router-link-active

      • 这两个类可以添加高亮效果

        方案一、只需要去设置一下这两个类中的某个类的样式就可以了

        方案二、设置 router-link 组件的 active-class 属性,属性值为某个class名字

        1. 希望默认的 router-link-active => active

        ​ active-class=“active”

        1. 希望默认的 router-link-exact-active => exact-active

        ​ exact-active-class=“exact-active”

四、重定向

{
  path: "/",
    // 重定向,值设置为一个要重定向到的路径
  redirect: "/home",
},

五、路由别名

//路由规则中配置 alias 配置项
{
{
  path: "/home",
  component: home,
  alias: "/a",
},
  {
    path: "/list",
    component: list,
    alias: "/b",
  },
    {
      path: "/about",
      component: about,
      alias: "/c",
    },
}

六、路由

  • 路由规则中path选项的值形如 /home/:xxxx/:yyyy 这种的就是动态路由
  • 动态路由的使用场景:一般是在详情页面的时候

七、路由与传参

在router.js里配置路由,代码如下:
​```
{ path: '/detail/:abc/:title', component: Detail }
​```
在编程式路由跳转,代码如下:
​```
this.$router.push('/detail/'+id)
​```
在详情页面中,如何接收动态参数?代码:
​```
this.$route.params.id

八、懒加载

  • 语法:const Home = ()=>import('@/views/Home.vue')
  • 作用:应用程序中组件进行异步加载,节省应用程序初始化的性能。
  • 结合 Vue的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

九、跳转

声明式路由跳转 ,方便实现高亮样式

编程式路由跳转 this.$router.push() / replace() / back()

  • $router

    • 使用 vue-router 之后,在 vue 的原型上提供了一个 $router 对象

    • 使用这个对象的一些方法来进行路由的跳转

      • this.$router.push()

        1. 就相当于普通的 router-link
        2. 它的参数可以是:
          1. 字符串,要跳转到路由的path
          2. 对象,对象中配置 path 属性。

        push效果:[空白页, home,] => push(’/list’) => [空白页, home, list]

      // 使用编程式导航的方式来跳转到 /home 这个路由
      //1. 传字符串的形式
      this.$router.push("/home");
      //2. 传对象的形式
      this.$router.push({
        name: "abc", //先给路由取个名字
        params: {
          id: 1,
        },//params不能和path一起使用,要配合name一起使用
      });
      //3.传对象错误的演示
      //this.$router.push({
      //path: "/detail",
      //  query: {
      //    name: "张三",
      //   age: 18,
      //  },
      //  params: {
      //		   id: 1,
      //  }, ///params不能和path一起使用,要配合name一起使用
      //});
      
      • this.$router.replace()

        1. 就相当于带有 replace 属性 的 router-link
        2. 它的参数可以是:
          1. 字符串,要跳转到路由的path
          2. 对象,对象中配置 path 属性。

        replace效果:[空白页, home,] => replace(’/list’) => [空白页, list ]

      • this.$router.back() 后退 history.back()

      • this.$router.go(num) 前进或后退,根据传递参数是正数还是负数来决定 history.go()

      • this.$router.forward() 前进 history.forward()

    • $route 与 $router 的区别

      1.$route 是当前匹配的路由信息对象,可以通过他获取一些当前路由的一些信息。比如 params

      ​ +动态参数。vue 将 $route 挂到了每个组件的 data 数据里面。

      2.$router 就是 new VueRouter() 生成的那个实例对象,vue 将他挂载到了 Vue 的原型上面

      ​ +所以每个组件都通过访问,然后使用来完成编程式导航的一些操作

十、嵌套路由

  • 嵌套路由,一般情况工作中,到两级足够了。尽量不要超过两级。

    router-link的to永远写的都是完整路径(端口号后端的全部路径)

    {
      path: "/home",
      component: home,
      children:[
        {	path: "cn",
      		component: Cn,
        }
      ]
    },
    

十一、导航守卫

一、什么叫导航
  • 导航就是路由正在发生变化
二、导航守卫、路由守卫、路由的钩子
  • 路由在发生变化的时候会触发的一些函数
三、守卫有哪些

一、全局守卫(通过 new VueRouter() 生成的这个实例对象去使用)

  1. 全局前置守卫 beforeEach

    router.beforeEach((to, from, next) => {
    //to 代表着将要去的路由的信息对象
    //from	当前导航正要离开的路由
    //next	是一个函数,必须要调用  //1. next()	进入下一个
                                //2. next('/')  next({ path: '/list' })   重定向到某个路由
    })
    
  2. 全局后置守卫 afterEach

    router.afterEach((to, from) => {
      //to 代表着将要去的路由的信息对象
      //from	当前导航正要离开的路由
      // afterEach 是在导航已经完成了触发。这时有 next 也没用。
    })
    
  3. 全局解析守卫 beforeResolve

    router.beforeResolve((to, from, next) => {
    //to 代表着将要去的路由的信息对象
    //from	当前导航正要离开的路由
    //next	是一个函数,必须要调用  //1. next()	进入下一个
                                //2. next('/')  next({ path: '/list' })   重定向到某个路由
    })//用的比较少
    

二、路由独享 (路由规则中的一个配置项)

  1. beforeEnter

    new VueRouter({
      routes: [
        {
          path: '/hello',
          component: hello,
          beforeEnter: (to, from, next) => {
            next()//next执行才可进入/hello页面
          }
        }
      ]
    })
    

三、路由组件中 (在组件的配置选项中定义的,也就是与那个 data 、props 一起的那个)

  1. beforeRouteLeave
  2. beforeRouteUpdate
  3. beforeRouteEnter
export default {
  name: "Cinemas",

  beforeRouteLeave(to, from, next) {
    console.log("beforeRouteLeave");
    next();
  },

  beforeRouteUpdate(to, from, next) {
    console.log("beforeRouteUpdate");
    next(); 
  },

  beforeRouteEnter(to, from, next) {
    console.log("beforeRouteEnter");
    next();
  }
};
四、详细介绍
  1. beforeEach
    1. 全局前置
  2. afterEach
    1. 全局后置
  3. beforeEnter
    1. 路由独享
    2. 当进入到这个路由的时候,这个函数会触发,触发时机在 beforeEach 之后。在 beforeRouteEnter 之前
  4. beforeRouteEnter
    1. 组件内配置的。触发在 beforeEnter 之后
  5. befoerRouteUpdate
    1. 在动态路由的时候,参数发生变化时,触发
  6. beforeRouteLeave
    1. 当离开时第一个触发,在 beforeEach 之前
五、思考如下的导航,导航守卫触发的顺序

一、/home -> /list

  1. /home 组件中的 beforeRouteLeave
  2. 全局前置 beforeEach
  3. /list 的配置中触发 beforeEnter
  4. /list 组件中的 beforeRouteEnter
  5. 全局后置 afterEach

二、/detail/1 -> /detail/2

  1. 全局前置 beforeEach
  2. detail 组件中的 beforeRouteUpdate
  3. 全局后置 afterEach
六、需求1: 任何页面跳转时,需要在页面上出现滚动条显示
  1. 在全局前置的 beforeEach 中让进度条开始滚动
  2. 在全局后置的 afterEach 中让进度条完成
  1. 借助 nprogress 插件实现

    安装指令

    $ npm install --save nprogress 
    引入他的核心
    import nprogress from 'nprogress '
    import 'nprogress /nprogress .css'
    //配置不转圈
    nprogress.configure({
    	showSpinner:false,
    })
    nprogress.start()  //开始
    nprogress.done()   //结束
    
七、需求2: 离开个人中心页面时,需要二次确认
  1. 在个人中心页面的 beforeRouteLeave 中处理即可
八、需求3:必须要有登录状态才能进入个人中心页面。身份认证

方案一、

​ 对个人中心页面配置路由独享守卫 beforeEnter ,在里面判断是否有登录

new VueRouter({
  routes: [
    {
      path: '/hello',
      component: hello,
      beforeEnter: (to, from, next) => {
        if(window.isLogined){
          next()
        }else{
          next("/login")
        }
      }
    }
  ]
})
  1. 需求增加,登录成功之后,浏览器后退不要进入登录页面
    • 在登录成功跳转的时候,不要使用 push 而是使用 replace 就ok了。
  2. 需求增加,登录成功之后要跳转回之前想要去的页面
    1. 在身份验证不通过,打回到登录页面时,将当前的地址携带在url地址上,通过?号传递过去
    2. 登录页面登录成功之后,通过url地址获取到之前要去的页面地址。然后跳转即可
  3. 推荐使用 to.fullPath 而不是 to.path 好在哪里?
    1. 比如我们去 /my 的时候,url 路由携带有 ?号参数。/my?type=1&hello=2.
    2. 这时使用 to.path 只能得到 /my
    3. 而用 to.fullPath 能得到完整的 /my?type=1&hello=2

方案二、

​ 给需要身份认证的页面配置路由元信息 (meta),然后在全局前置守卫 beforeE ach 中处理即可。

​ 元信息是给某个路由配置一些额外的信息

  1. 当前只有一个页面需要身份验证。如果多个的时候,每一个都去写 beforeEnter 是不是不合理?

这时请使用推荐的方案二

015、Vue - vuex

一、vuex简介

  • vue官方提供的状态管理器。用于处理全局的数据共享问题的。
  • 采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

二、为什么要有vuex

  • 项目越写越大,功能越写越复杂,当 Vue.js 应用程序遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏

三、vuex的五大核心

state: 状态

getters: 对 state 的派生,可以理解为 store 的计算属性

mutations: 修改 state 的唯一操作就是提交 mutation 。

actions: 类似于 mutations , 用来处理异步。

modules: 对仓库 分割成 模块

四、如何使用vuex

  1. 项目中安装vuex
$ npm install vuex 
  1. 创建 src/store.js 文件。这个文件用来生效仓库的实例

    // 1. 引入 Vue
    import Vue from "vue";
    
    // 2. 引入 Vuex
    //      vuex 完整的取出赋值给 Vuex
    //      vuex.Store 赋值给    Store
    import Vuex, { Store } from "vuex";
    
    // 3. !!进行调用!!
    Vue.use(Vuex);
    
    // 4. 实例化 Vuex 的一个 仓库实例对象
    const store = new Vuex.Store({
      // 配置选项
      // 1. state
      state: {},
    	// 2. getters 里面 设置 getter
      getters: {},
    	// 3. mutations 里面 设置 mutation
      mutations: {},
      // 4. actions 里面 设置 action
      actions: {},
      // 5. modules 里面 设置 module
      modules: {},
    });
    
    // end. 不要忘了暴露 store
    export default store;
    
    • getters

      getters里是以key:value形式存在

      key 一个getter的名字

      value 是一个函数,进行getter计算函数,与computed一样,必须有返回值,并且会自动接收到一个state这个参数,参数的内容就是当前仓库的数据

  2. 需要在 src/main.js 文件的 new Vue() 的位置去配置 store 选项,选项值是上一个步骤中暴露出来的 store 的实例

    import Vue from "vue";
    import App from "./App.vue";
    //引入store
    import store from "./store";
    
    Vue.config.productionTip = false;
    
    new Vue({
      store,//挂载在Vue实例上
      render: (h) => h(App),
    }).$mount("#app");
    

五、组件中如何使用state与getter

  1. 方案一、使用挂载到 Vue原型上的 $store 对象。这个 $store 就是 new Vuex.Store() 生成的仓库实例对象
$store.state
$store.getters
  1. 方案二 (推荐)、使用辅助函数

    1. 组件中从 vuex 中引入你需要的辅助函数

    2. 调用辅助函数,并将其返回的值赋给组件的 computed 选项

      <script>
        // 引入 辅助函数 。采用类似 解构赋值的方式
        import { mapState } from 'vuex'
        export default {
          // mapState 返回值是一个 对象
          computed: mapState(['curCity', 'cart'])
        } 
      script>
      

    当本组件中也需要computed计算属性时?

    • 使用…扩展运算符赋给computed
    <script>
      // 引入 辅助函数 。采用类似 解构赋值的方式
      import { mapState } from 'vuex'
      export default {
        // mapState 返回值是一个 对象
        computed: {
          ...mapState(['curCity', 'cart']),
          a:function(){
            
          },
        }
      } 
    script>
    

    mapState的语法和mapGetters的语法的区别

    // mapState 接收一个数组做为参数,参数中的每一项,就是在仓库中的 state 数据
    mapState([state1, state2, state3])
    
    // mapGetters  接收一个数组做为参数,参数中的每一项,就是在仓库中的 getter 数据
    mapGetters([getter1, getter2])
    

六、组件中如何修改仓库中的state和getter数据呢?

  • state可以修改,getter不能修改

    步骤:

    1. 在仓库中提供对应的state修改的mutation函数
    2. 在组件中调用mutation
  • 调用mutation的方案:

    • 方案一:直接使用 vuex 绑定到 vue 原型上的 $store 这个对象的 commit() 方法

      在store.js文件中

      mutations:{
        SETCURCITY(state,payload){
          state.curCity=payload
        }
      }
      

      在组建中

      methods:{
        fn1(){
          this.$store.commit("SETCURCITY",'修改的内容')
        }
      }
      
    • 方案二、使用 mapMutations 辅助函数

      // mapMutations的语法
      // 接收一个数组作为参数,数组中的每一项是一个 mutation 的名字
      mapMutations([mutation1, mutation2, mutation3])
      

      实例代码:

      {
      	methods: mapMutations(["SETCURCITY", "ADDCART"])
      }
      

      转换代码:

      {
        methods: {
          SETCURCITY(payload) {
      			this.$store.commit('SETCURCITY', payload)
          },
          ADDCART(payload) {
      			this.$store.commit('ADDCART', payload)
      		}
        }
      }
      

七、使用 action 异步的修改 state 数据

  • 首先需要知道,mutation 里面只允许同步的去修改 state 数据。(虽然在mutation中可以异步的去修改state数据不会报错,但是会导致时间旅行等机制没有效果)

  • 如果异步的修改的化,有两个大方案

    1. 不涉及action。在组件上异步代码走完之后再去调用 mutation

    2. 使用 action,使用 action。首先需要在 actions 选项中定义 action 函数

      注意:action 中不能直接去修改 state,要修改是通过 context.commit() 去执行某个 mutation 来修改

    方案一、直接使用 vuex 绑定到 vue 原型上的 $store 这个对象的 dispatch() 方法

    在store.js文件中

    actions:{
      SYNCSETCURCITY({commit},payload){
        setTimeout(()=>{
          commit("SETCURCITY",payload)
        },2000)
      }
    }
    

    在组件中

    methods:{
      fn1(payload){
        this.$store.dispatch(actionName, payload)
      }
    }
    

    方案二、使用 mapActions 这个辅助函数

    // mapActions 的语法
    // 接收一个数组作为参数,数组中的每一项是一个 action 的名字
    mapActions([action1, action2, action3])
    

    示例代码

    {
      methods: mapActions(["SYNCSETCURCITY"])
    }
    

    转换之后如下所示

    {
      methods: {
        SYNCSETCURCITY(payload) {
          this.$store.dispatch('SYNCSETCURCITY', payload)
        }
      }
    }
    

八、vuex的module

一、什么时候需要在 vuex 中使用 module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成****模块(module)****。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

项目越做越大,功能点越写越多。需要使用 vuex 共享的数据越来越庞大时,就需要使用 module 来进行仓库模块拆分啦。

二、示例代码

// 拆分的仓库子模块A
const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... },
  // 仓库子模块也可以继续去做拆分,但是没必要搞这么复杂
  modules: {
    aa,
    ab, 
  }
}
            
// 拆分的仓库子模块B
const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

// 仓库根模块
const store = new Vuex.Store({
	// 通过 modules 选项配置子模块
	modules: {
		// key: value
    //    key - 仓库子模块的名字
    //    value - 对应的仓库子模块的对象
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

三、仓库拆分子模块之后,没有设置命名空间。有一些问题的

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间

  1. 多个仓库子模块中的 getter 不能同名,否则会报错
  2. 多个仓库子模块中的 mutation 如果同名的化,组件调用这个 mutation 时。都会被触发, 会有污染的风险
  3. 多个仓库子模块中的 action 如果同名的化,组件调用这个 action 时。都会被触发, 会有污染的风险

四、上面的这种问题存在,所以推荐仓库子模块都设置上命名空间。

1. 如何设置呢?

给 仓库子模块的 那个对象配置一个 namespaced 属性。属性值为 true

2. 设置之后的改变是什么?

模块内部的 action、mutation 和 getter 是注册在自己命名空间

3. 设置了命名空间之后如何在组件中使用呢

基本使用步骤不变,只是要处理处理命名空间

下面的 xx 代表着某个仓库子模块的名字

1. 获取某个仓库子模块中的 state
// 1. 直接通过 $store
this.$store.state.xx
// 2. computed
computed: {
  name () {
		return this.$store.state.xx.name
  }
}
// 3. mapState
computed: {
  ...mapState('xx', [state1, state2, state3])
}
// 4. mapState 的转换
computed: {
 	state1() {
  	return this.$store.state.xx.state1
  }
}

// !如果要在组件中同时拿到多个仓库子模块的同名state数据,不要使用 mapState 请使用方案二 !
2. 获取 某个仓库子模块中的 getter
// 1. 直接通过 $store
this.$store.getters['ma/firstName']
// 2. computed
computed: {
  firstName() {
    return this.$store.getters['ma/firstName']
  }
}
// 3. mapGetters
computed: {
  ...mapGetters('xx', [getter1, getter2])
}
// 4. mapGetters 的转换
compouted: {
  getter1() {
		return this.$store.getters['xx/getter1']
  }
}
3. 提交 某个仓库子模块中的 mutation
// 1. 直接通过 $store
this.$store.commit('ma/SET_NAME', payload)
// 2. methods
methods: {
  SET_NAME(payload) {
    this.$store.commit('ma/SET_NAME', payload)
  }
}
// 3. mapMutations
methods: {
  ...mapMutations('xx', [mutation1, mutation2])
}
// 4. mapMutations 的转换
methods: {
	mutation1(payload) {
    this.$store.commit('xx/mutation1', payload)
  }
}
4. 派发 某个仓库子模块中共的 action
// 1. 直接通过 $store
this.$store.dispatch('ma/SYNC_SET_NAME', payload)
// 2. methods
methods: {
  SYNC_SET_NAME(payload) {
		this.$store.dispatch('ma/SYNC_SET_NAME', payload)
  }
}
// 3. mapActions
methods: {
  ...mapActions('xx', [action1, action2])
}
// 4. mapActions 的转换
methods: {
	action1(payload) {
    this.$store.dispatch('xx/action1', payload)
  }
}

五、仓库模块的局部状态

  • 首先要知道,做了仓库模块的拆分之后,getter与mutation中的第一个参数state是当前模块的局部state。
  • action中的第一个参数context。context中的state也是当前模块的局部state
  • 有时候我们需要在getter中action中去获取到其余的模块的state数据

getter语法

getters:{
  //state - 当前模块的state
  //getters - 当前模块的getters
  //rootState - 跟模块的State数据,根据他可以去获取到其余模块的state数据
  getter1(state,getters,rootState){
    
  }
}

action语法

action:{
  //context是一个对象,这个对象中有一些属性
  //   state - 当前模块的局部state
  //   getters - 当前模块的getters
  //   commit - 提交mutation的方法
  //   dispatch- 派发action的方法
  //   rootState- 跟模块的State数据,根据他可以去获取到其余模块的state数据
  action(context,payload){
    
  }
}

你可能感兴趣的:(JavaScript)