vue基础

411 阅读13分钟

Vue基础

1. Vue和脚手架的介绍

1.1Vue概念

  1. Vue是一个渐进式的的JavaScript 的开发框架,是一套拥有自己规则的语法。

1.2 脚手架

1.2.1使用步骤:

  • @vue/cli 安装到全局
yarn global add @vue/cli  
# OR
npm install -g @vue/cli
  • 查看安装的版本 有版本号则安装成功,否则安装失败
vue -V
  • 创建项目 vuecli-demo为自定义的项目名,不能有大小字母及中文及特殊符号
vue create vuecli-demo
  • 选择模板 上下箭头选择

    1,选择Vue的版本

    1652441010192

    2,选择 包管理工具 NPM / Yarn

    1652441052177

    3,回车等待生成项目文件夹+文件+下载必须的第三方包们

    1652441117838

    4,进入脚手架项目下, 启动内置的热更新本地服务器

    1652441149150

    5,看到绿色的DONE即表示创建成功

    1652441366520

1.2.2脚手架项目结构

  • node_modules -> 第三方依赖包
  • public/index.html -> 浏览器运行的网页
  • src/ App.vue -> 页面根组件
  • src/ main.js -> 项目入口 /打包的入口
  • package.json -> 记录项目的依赖包
  • vue.config.js -> 脚手架项目的配置文件
  • 脚手架项目运行流程 -> src/main.js -> src/App.vue -> public/index.html

1.2.3 Vue单文件

  1. js作用域独立
  2. style配合scoped属性作用域独立 ,否则为全局样式
  3. template下面只能有一个根标签

2. Vue的指令

2.1 插值表达式

  1. 语法 : {{ 表达式 }}
  2. 作用:把表达式内容插入到插值所在的位置
  3. 变量要声明在data方法的返回对象里
  4. 表达式举例:变量 / 属性 / 三元运算符 / 方法调用 / 数字 / 字符串等
  5. 举例:
<template>
  <div>
  <h1>{{name}}</h1>
  <h2>{{obj.age}}</h2>
  <h3>{{obj.gender}}</h3>
  <h4>{{sayHi()}}</h4>
  <h4>{{obj.sayfn()}}</h4>
  <h5>{{obj.age>18? '成年':'未成年'}}</h5>
  </div>
</template>

<script>
export default {
  data() {
    return {
      name:'小青',
      obj:{
        age:18,
        gender:'男',
        sayfn(){
          return this.age
        }

      },
      sayHi(){
        return '成年与否'
      }
    }
  }
}
</script>

2.2 v-bind 为标签绑定属性

  1. 语法:v-bind:属性名="Vue变量" 或简写: :属性名="Vue变量"
  2. 作用:给标签绑定属性,使属性值能够使用 Vue变量。
  3. Vue变量声明在data方法的返回对象里
  4. 举例:
<template>
  <div>
    <a v-bind:href="baiduUrl">百度</a>
    <img :src="imgUrl" alt="">
  </div>
</template>

<script>
export default {
  data() {
    return {
      baiduUrl:'https://www.baidu.com/',
      imgUrl:'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
    }
  }
}
</script>

2.3 v-on 标签绑定事件

2.3.1 v-on 事件绑定

  1. 语法
    • v-on:事件名=“要执行的少量代码" 简写 @事件名=“要执行的少量代码"
    • v-on:事件名=“methods中的函数名" 简写 @事件名=“methods中的函数名"
    • v-on:事件名=“methods中的函数名(实参)" 简写 @事件名=“要methods中的函数名(实参)"
  2. 作用:给标签绑定事件
  3. 举例:
<template>
  <div>
    <h2>计数{{ count }}</h2>
    <button v-on:click="count++">加一</button>
    <button v-on:click="addFive()">加五</button>
    <button v-on:click="addfn(50)">加五十</button>
    <button @click="addfn(100)">加100</button>
    <a href="https://www.baidu.com/" @click.prevent="fn"></a>

  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    addFive() {
      this.count += 5;
    },
    addfn(num) {
      this.count += num;
    },
    fn(e){
      // e.preventDefault()
      console.log(e);
    }
  },
};
</script>

2.3.2 v-on修饰符

  1. 语法:@事件名.修饰符="方法"
  2. 分类:
    • .stop 阻止事件冒泡
    • .prevent 阻止默认行为
    • .once 程序运行期间,事件只触发一次
  3. 按键修饰符(针对 keydown / keyup 事件等)
    • .enter 回车键触发
    • .esc 按ESC触发
  4. 举例:按键修饰符
<template>
  <div>
    <p>
      用户名
      <input type="text" @keyup.enter="keyEvent" @keyup.esc="say" @keyup.d="sayd">
    </p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      
    };
  },
  methods: {
    keyEvent(){
      console.log('登录');
    },
    say(){
      console.log('esc');
    },
    sayd(){
      console.log('d');
    }
  },
};
</script>

2.3.3 Vue语法中获取事件对象

  1. 事件函数的第一个形参 参数名可自定义,如 e / event
  2. 如果第一个参数已有实参,则传入一个形参 $event ,只此一种写法

2.3.4 案例:点击翻转字符串

  1. 需求:点击按钮,文本的每个字符翻转排列
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="reverseworld">点击翻转</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "hello,world",
    };
  },
  methods: {
    reverseworld() {
      // 先用 .split('')方法将字符串转成数组
      const arr = this.message.split("");
      //   数组的翻转方法  会改变原有数组  .reverse()
      arr.reverse();
      //   将新数组重新赋值给 Vue变量
      this.message = arr.join("");
    },
  },
};
</script>

<style>
</style>

2.4 v-model 双向绑定

2.4.1 v-model基本介绍

  1. 语法: v-model="变量"
  2. 作用: value和vue变量双向绑定 ,实现同步改变
  3. 变量记得声明在data方法的返回对象里

2.4.2 v-model的修饰符

  1. 语法: v-model.修饰符="变量"
  2. 分类;
    • .number 转换为数字赋值给变量
    • .trim 去掉字符串首尾的空白符
    • .lazy input失去焦点才更新变量

2.4.3 v-model在表单中的使用

  1. 下拉选择框: v-model写在select上面 , 绑定的选中的option的value
  2. checkbox 多选框
  • v-model变量为非数组 :绑定的是布尔值,checked属性
  • v-model变量为数组 : 绑定的是选中项的value
  1. radio单选框: v-model绑定的是选中的value
  2. textarea 文本域: 绑定用户输入的value

2.4.4综合举例

<template>
  <div>
    <p>用户名:<input type="text" v-model="userName" /></p>
    <p>密码:<input type="text" v-model="password" /></p>
    <p>
      <!-- 多选 :变量声明为数组获取的是选中项的value,否则是布尔类型-->
      爱好:
      <input type="checkbox" v-model="hobby" value="音乐" />音乐
      <input type="checkbox" v-model="hobby" value="嘻哈" />嘻哈
      <input type="checkbox" v-model="hobby" value="打豆豆" />打豆豆
    </p>
    <p>
      <!-- 单选: 声明为普通变量即可,v-model获取到的是value-->
      <input type="radio" v-model="gender" value="男" />男
      <input type="radio" v-model="gender" value="女" />女
    </p>
    <p>
      <!-- 下拉列表:v-model 写在selected标签,获取的是选中项的value -->
      <select v-model="city">
        <option value="广州">广州</option>
        <option value="长沙">长沙</option>
        <option value="北京">北京</option>
        <option value="韶关">韶关</option>
        <option value="湛江">湛江</option>
      </select>
    </p>
    <button @click="login">登录</button>
    <textarea v-model.trim="txt" id="" cols="30" rows="10"></textarea>
  </div>
</template>

<script>
export default {
  data() {
    return {
      userName: "admin",
      password: "",
      hobby: [],
      gender: "",
      city: "",
      txt: "",
    };
  },
  methods: {
    login() {
      console.log("userName", this.userName);
      console.log("password", this.password);
      console.log("hobby", this.hobby);
      console.log("gender", this.gender);
      console.log("city", this.city);
      console.log("txt", this.txt);
    },
  },
};
</script>

2.5 v-for 数据的遍历

2.5.1 v-for的基本用法

  1. 语法: v-for="变量 in 目标数据" 或 v-for="(变量, 索引) in 目标数据"
  2. 作用:遍历列表,(数组,对象,字符串,数字)
  3. 扩展:如果是数字遍历,会从数字1递增到目标数字,目标数字必须大于或等于1
  4. 举例
<template>
  <div>
    <ul> <!-- 数组 -->
      <li v-for="(item,index) in arr">{{index+1}}:{{item}}</li>
    </ul>
    <ul> <!-- 对象 -->
      <li v-for="(value,key) in obj">{{key}}:{{value}}</li>
    </ul>
    <ul> <!-- 字符串 -->
      <li v-for="(value,index) in str">{{index+1}}:{{value}}</li>
    </ul>
    <ul>  <!-- 数字 -->
      <li v-for="(value,index) in num">{{index}}:{{value}}</li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      arr:['伏黑惠','旋涡玖辛奈','五条悟','夏油杰','利姆露','红丸'],
      obj:{
        userName:'哈利波特',
        parent:'莉莉.詹姆斯',
        ass:5000000,
      },
      str:'飞天小女警,我才不管你',
      num:8
    };
  },
  methods: {},
};
</script>

2.5.2 v-for的更新

2.5.2.1 触发更新的条件
  1. 对数组而言:
    • 当数组方法不会修改原始数组的方法不会导致v-for更新 ,如: array.filter / array.slice
    • 等当数组方法 修改原始数组的方法会导致v-for更新 ,如:array.push / array.reverse 等
    • 当通过数组下标 array[index] = value 这种方式 操作数组时,不会触发更新,而是要用 **$set(数组, 索引, 值)**该方法才会触发v-for的更新
    • 当数组里面的元素是一个对象时,通过 array[index].属性名=value 这种方式更新对象,也会触发v-for的更新
  2. 对对象而言,当对象的属性发生改变或新增了属性时就会触发更新
2.5.2.2 key对v-for更新的影响(设置 key属性)
  1. 设置key的方法:给v-for的标签添加 key="唯一标识"
  2. 设置与否的区别
    • 不设置key时的更新方式:就地更新,拿到原来的列表逐一对比更新,性能消耗较大
    • 设置key时的更新方式:比较后更新利用key为索引在上一次的列表寻找相同key的数据,对比后更新,性能消耗较小
  3. 建议:条件允许则尽量添加key属性

2.6 v-text与v-html

2.6.1 v-text

  1. 语法: v-text="变量"
  2. 作用: v-text把内容作为文本直接显示,不解析内容 , 变量声明在data方法返回对象里

2.6.2 v-html

  1. 语法: v-html="变量"
  2. v-html会解析内容中的html标签 , 变量声明在data方法返回对象里

2.6.3 代码演示

<template>
  <div>
    <h1 v-text="hello"></h1>
    <h1 v-text="helloHTML"></h1>
    <h1 v-html="helloHTML"></h1>
    <h1 v-html="hello"></h1>
  </div>
</template>

<script>
export default {
  data() {
    return {
      hello: "hello world",
      helloHTML: "<button>点我</button>"
    }
  }
}
</script>

2.7 v-show与v-if(显示与隐藏)

2.7.1 v-show

  1. 语法: v-show="表达式" ,表达式的布尔类型为 true 时,标签显示,反之则隐藏
  2. 作用: 控制标签显示和隐藏
  3. 原理: v-show通过控制display: none属性来控制显示隐藏
  4. 注意点:表达式一般声明在 data方法返回的对象里
<template>
  <div>
    <h1 v-show="age >= 18">成年人的快乐</h1>//会显示,
    <h1 v-show="age < 18">给你一瓶营养快线</h1>//会隐藏,但是html中还是会有该标签的结构
  </div>
</template>

<script>
export default {
  data() {
    return {
      age: 27,
    };
  },
};
</script>

2.7.2 v-if / v-else-if /v-else

  1. 语法:v-if="表达式",v-else-if="表达式",v-else 这三者可搭配使用,逻辑线类似 if /else if / else条件语句
  2. 作用:用于控制便签的显示和隐藏,
  3. 原理:通过验证表达式的布尔类型决定是否将该标签插入到html结构中,true则插入,反之则不。
  4. 注意点: v-if,v-else-if 和 v-else必须是相邻同级节点 ,否则报错
<template>
  <div>
    <input type="text" v-model.number="age">
    <h2 v-if="age < 18">甜甜圈</h2>
    <h2 v-else-if="age < 60">快乐水</h2>
    <h2 v-else-if="age < 100">脑白金</h2>
    <h2 v-else>冬虫夏草</h2>
  </div>
</template>

<script>
export default {
  data() {
    return {
      age: 8,
    };
  },
};
</script>

2.8 条件控制样式

2.8.1 :class

  1. 语法: :class="{ 类名: 布尔值 }"
  2. 作用:用于控制类名是否添加给标签 ,true为添加,否则除去
  3. 注意点:可控制多个类名, :class="{ 类名: 布尔值 ,类名: 布尔值}" ,当类名有横线是,用单引号包裹类名
<template>
  <div>
    <button :class="{ on: isOpen, off: !isOpen }" @click="fn">{{isOpen?'我开灯了':'关灯睡觉'}}</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isOpen:false,
    };
  },
  methods:{
    fn(){
      this.isOpen=!this.isOpen
    },
  }
};
</script>

<style>
.on{
  background-color: pink;
}
.off {
  background-color: orange;
}

</style>

2.8.2 :style

  1. 语法::style="{ 样式属性名: 合法的样式值 }"
  2. 作用:动态修改样式,使样式值能使用变量
  3. 注意点:样式名如果带横线,改为小驼峰,也可以使用引号
<template>
  <div>
    <button
      @click="fontColor = 'blue'"
      :style="{ color: fontColor, 'font-size': '60px' }"
      :class="{ 'text-center': true, textTop: true }"
    >
      变色
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      fontColor: "red",
    };
  },
};
</script>

2.9 过滤器

  1. 语法: {{ 表达式 | 过滤器1 | 过滤器2(参数1) }}
  2. 作用:转换内容格式
  3. 过滤器的方法注意点:
    • 记得返回数据 (return)
    • 方法第一个参数:表达式内容
    • 传给方法的参数从第二个开始
    • 过滤器能使用在插值表达式和v-bind属性里
  4. 全局与单文件过滤器:
    • 全局:声明在 main.js 文件,格式: Vue.filter('过滤器名', 方法)
    • 单文件:声明在当下vue文件的与data方法同级的 filters属性里
  5. 演示:
<template>
  <div>
    <!-- 过滤器作用:转换数据格式 -->
    商品价格:{{ price | priceFilter }}

    <h2>{{ "hello" | toUpperCase }}</h2>

    <!-- 过滤器能使用在插值表达式和v-bind属性里 -->
    <p :title="'hello vue' | toUpperCase">hello vue</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      price: 9,
    };
  },
  // 声明在data同级下的filters对象里
  // 声明在filters内的过滤器只能在当前vue文件使用
  filters: {
    // 过滤器要声明为方法
    // 表达式的值是过滤器的第一个参数
    priceFilter(num) {
      return `${num < 10 ? "0" + num : num}¥`;
    },
    toUpperCase(value) {
      return value.toUpperCase();
    },
  },
};
</script>

<style>
</style>

2.10 计算属性

  1. 是什么: 依赖一些变量运算出来的属性
  2. 语法:{{ 变量 }}
  3. 声明的完整写法: 计算属性: { get() {}, set(value) { } }
  4. 声明在 data同级的computed对象内 , 计算属性方法要返回值
  5. 好处:缓存计算结果,只有依赖项变化才会重新运算
<template>
  <div>
    <!-- 计算属性,作用:根据一些数据计算出来一个属性 -->
    <!-- 当计算属性依赖的数据变化的时候,计算属性会重新运算 -->
    <!-- 计算属性是作为变量使用的,不要使用括号的语法 -->
    <!-- 计算属性不能和data里的变量重名 -->
    <h1>{{sum}} = {{a}} + {{b}}</h1>
    a: <input type="text" v-model.number="a">
  </div>
</template>

<script>
export default {
  data() {
    return {
      a: 10,
      b: 20,
    };
  },
  // 计算属性声明在data同级的computed对象里
  computed: {
    // 计算属性声明为方法
    sum() {
      return this.a + this.b;
    }
  },
}
</script>

2.11 侦听器

  1. 作用:可以侦听data/computed属性值的改变,方便开发者做出应对

  2. 语法:写在与data方法同级的watch属性里面

  3. 注意点:如果需要监听复杂数据类型需加额外配置,deep: true, 深度监听immediate: true, 页面挂载后立即执行一次

  4. 分类:

    • 简单数据类型
    • 对象/数组等
    <template>
      <div>
        msg
        <input type="text" v-model="msg">
        <br>
        user.name
        <input type="text" v-model="user.name">
        user.age
        <input type="text" v-model="user.age">
        <br>
        <input type="text" v-model="num1">
        总数: {{num2}}
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          msg: '',
          user: {
            name: 'tom',
            age: 13
          },
          num1: 2,
          num2: 0
        }
      },
      watch: {
        // 以键值对的形式指定 被监听字段: 触发后回调函数
        msg(newVal, oldVal) {
          console.log(newVal, oldVal);
        },
        user: {
          deep: true, // deep 深度监听
          handler(newVal, oldVal) {
            console.log(newVal, oldVal);
          }
        },
        num1: {
          immediate: true, // immediate 页面挂载后立即执行一次
          handler (newVal) {
            this.num2 += newVal
          }
        }
      }
    }
    </script>
    

2.12 自定义vue指令

  1. 自定义指令是vue指令的拓展,当vue指令不能满足我们的一些需求时,就可使用自定义指令
  2. 自定义指令有两个钩子函数,
  • inserted( el,options ),在绑定自定义指令的dom元素被插入后触发,第一个参数为被绑定的dom元素本身,options是包含自定义指令及钩子函数的复杂对象,其中 options.value 等价于 自定义指令的属性值
  • update( el,options ),在绑定自定义指令的dom元素的值或模板更新时触发 ,参数含义与上述相同
<template>
  <div v-boxbg="'orange'">
      我的颜色是通过 v-boxbg指令设置的
  </div>
</template>
<script>
export default {
  directives:{
    boxbg:{
      inserted(el,options){
        // options.value 是自定义指令调用时 等号 右边的值
        el.style.backgroundColor=options.value
        console.log(options);
      }
    }
  }

}
</script>

<style>
  div {
    width: 300px;
    height: 300px;
  }
</style>

3. Vue组件

3.1 组件的基本介绍

  1. 概念:组件就是做单独封装的单个vue文件,用于封装可复用的代码,提升开发效率

  2. 组件的使用方法:封装 -> 引入 -> 注册 -> 使用标签渲染,注册后使用组件名以标签的形式使用:如:

    • 全局引入注册:在main.js文件,
    import Vue from 'vue' //如文件中已有实例vue,省略此步骤
    
    import 组件对象 from '组件vue文件路径' //引入
    Vue.component('组件名',组件对象)   //注册
    
    • 局部引入:单个vue 文件
    import 组件对象 from 'vue文件路径'
    export default {
    components:{  //注册写在components属性内
        "组件名":组件对象
    }
    }
    
  3. 完整演示

    <template>
      <div>
        <div class="list">
          <ProdItem/>
          <ProdItem/>  <!-- 使用组件名以标签的形式渲染 -->
        </div>
      </div>
    </template>
    
    <script>
    // 局部注册的演示
    import ProdItem from '@/components/ProdItem.vue'
    export default {
      components: {
        "ProdItem" :ProdItem 
      }
    }
    </script>
    

3.2 组件通信 父传给子

  1. 作用:将数据传递给子组件,

  2. 方法步骤:

    • 第一步,将要使用的数据变量,用属性方式写在子组件的单标签上 :prod="item"
    • 第二步,在子组件的文件中 export default {props:['prod']} props:['prod']属性,以数组的方式储存变量,变量名为第一步的自定义属性名
    • 第三步,直接使用储存在数组中的变量
  3. 单向数据流:父组件传递给子组件的数据,子组件为只读状态,无法修改原数据

  4. 注意点: ,组件中style标签加上 scoped 属性,可使样式只作用于属于本组件的标签

  5. 演示:

    • 父组件的代码
    <template>
        <div>
            <!-- :prod="item" 以属性的方式书写在标签上 -->
          <testProdItem :prod="item" v-for="(item,index) in list" :key="index" />
          <div class="item">我不属于组件</div>
        </div>
      
    </template>
    
    <script>
    import testProdItem from '@/components/子组件-testProdItem.vue'
    export default {
      components:{
        testProdItem,
      },
      data(){
        return {
          list:['周黑鸭','口水鸡','螺蛳粉','牛肉丸','四川火锅',]
        }
      }
    }
    </script>
    
    <style scoped>  //scoped属性 范围化样式作用域
    .item {
      width: 200px;
      height: 200px;
      background-color: pink;
      color: purple;
    }
    </style>
    
    • 子组件的代码
    <template>
      <div>
          <!-- 按照正常的vue指令使用 prod 即可 -->
          <div class="item">{{prod}}</div> 
      </div>
    </template>
    
    <script>
    export default {
        props:['prod'] //在props属性中以 以数组的元素方式接收,需加引号
    
    }
    </script>
    
    <style>
        .item {
            width: 100px;
            height: 100px;
            background-color: orange;
        }
    </style>
    
  6. 当子组件对父组件传递的数据有具体的格式要求时,可将 props定义成对象,每一个接收的数据变量为属性名

    <script>
    export default {
      // props: ['bg', 'color', 'title']
      // props 的数组写法, 其实可以换成对象
      // 可以设定要求的数据格式
      props: {
        bg: String, //要求传递的数据必须为字符串
        color: {
          type: String,
          default: '#fff' //设置默认值
        },
        title: {
          type: String,
          required: true  //数据的必填校验
        }
      }
    }
    </script>
    

3.3组件通信 子传父

  1. 作用:将子组件的数据传递给父组件

  2. 方法步骤:

    1. 子组件触发自定义事件, 传递参数(可选) this.$emit('自定义事件名', 传参1, 传参2)
  3. 父组件的子组件便签内 ,以@自定义事件名="函数fn",接收数据

    1. 父组件methods属性内定义 "函数fn" ,fn中 形参1=传参1,形参2=传参2
  4. 演示:

    子组件的代码

    <template>
      <div>
           <!-- 第二步:绑定合适的触发时机 -->
          <button @click.prevent="testClick">子传父</button>
      </div>
    </template>
    
    <script>
    export default {
        data(){
            return {
                dataTest:'传递给父组件的数据',
            }
        },
        methods:{
            testClick(){
                //第一步,将要发送的数据传递this.$emit('自定义事件名',要传递的数据1,要传递的数据2)
                //并封装在函数内
                this.$emit('getData',this.dataTest)
            }
        }
    }
    </script>
    
    <style>
    
    </style>
    

    父组件的代码

    <template>
      <div>
           <!--第三步:在子组件标签中,以那个自定义的事件名为事件,并绑定真正在父组件中要触发的函数 -->
          <ProdTest @getData="EvetFn"></ProdTest>
          <div>子组件的数据:</div>
          <div class="fromson">{{fromSonData}}</div>
      </div>
    </template>
    
    <script>
    import ProdTest from "./测试子组件.vue";
    
    export default {
        data(){
            return {
                fromSonData:''
            }
        },
        components:{
            ProdTest,
        },
        methods:{
            //第四步,触发函数的第一个参数就是子组件传递的数据1,以此类推
            EvetFn(val){
                this.fromSonData=val
            }
        }
    };
    </script>
    
    <style scoped>
    .fromson{
        color: orange;
        font-size: 30px;
    }
    </style>
    

4. Vue-生命周期

b623d5b14e5e445fa93cff3edc52fa48_tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.jpg

4.1 钩子函数

  1. 概念:Vue 框架内置函数,随着组件的生命周期阶段,自动执行
  2. 作用: 特定的时间点,执行特定的操作
  3. 场景: 组件创建完毕后,可以在created 生命周期函数中发起Ajax 请求,从而初始化 data 数据
  4. 注意点
  5. 分类: 4大阶段8个方法
阶段方法名方法名
初始化beforeCreatecreated
挂载beforeMountmounted
更新beforeUpdateupdated
销毁beforeDestroydestroyed

官网文档

贴上关于钩子函数的官方文档,按需查看。

df2dcf70b4e7476fa0fd15b741e7fd83_tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp

4.2 初始化阶段

初始化阶段2个钩子函数作用和执行时机

含义讲解:

1.new Vue() – Vue实例化(组件也是一个小的Vue实例)

2.Init Events & Lifecycle – 初始化事件和生命周期函数

3.beforeCreate – 生命周期钩子函数被执行

4.Init injections&reactivity – Vue内部添加data和methods等

5.created – 生命周期钩子函数被执行, 实例创建

6.接下来是编译模板阶段 –开始分析

7.Has el option? – 是否有el选项 – 检查要挂到哪里

​ 没有. 调用$mount()方法

​ 有, 继续检查template选项

9e67f2e7416642a2a2994adc8db60b63_tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp components/Life.vue - 创建一个文件

<script>
export default {
    data(){
        return {
            msg: "hello, Vue"
        }
    },
    // 一. 初始化
    // new Vue()以后, vue内部给实例对象添加了一些属性和方法, data和methods初始化"之前"
    beforeCreate(){
        console.log("beforeCreate -- 执行");
        console.log(this.msg); // undefined
    },
    // data和methods初始化以后
    // 场景: 网络请求, 注册全局事件
    created(){
        console.log("created -- 执行");
        console.log(this.msg); // hello, Vue

        this.timer = setInterval(() => {
            console.log("哈哈哈");
        }, 1000)
    }
}
</script>

App.vue - 引入使用

<template>
  <div>
    <h1>1. 生命周期</h1>
 	<Life></Life>
  </div>
</template>

<script>
import Life from './components/Life'
export default {
  components: {
    Life
  }
}
</script>

4.3 挂载阶段

挂载阶段2个钩子函数作用和执行时机

含义讲解:

1.template选项检查

​ 有 - 编译template返回render渲染函数

​ 无 – 编译el选项对应标签作为template(要渲染的模板)

2.虚拟DOM挂载成真实DOM之前

3.beforeMount – 生命周期钩子函数被执行

4.Create … – 把虚拟DOM和渲染的数据一并挂到真实DOM上

5.真实DOM挂载完毕

6.mounted – 生命周期钩子函数被执行

119614c0836447d08dcb4f4a953bb079_tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp

components/Life.vue - 创建一个文件

<template>
  <div>
      <p>学习生命周期 - 看控制台打印</p>
      <p id="myP">{{ msg }}</p>
  </div>
</template>

<script>
export default {
    // ...省略其他代码
    
    // 二. 挂载
    // 真实DOM挂载之前
    // 场景: 预处理data, 不会触发updated钩子函数
    beforeMount(){
        console.log("beforeMount -- 执行");
        console.log(document.getElementById("myP")); // null

        this.msg = "重新值"
    },
    // 真实DOM挂载以后
    // 场景: 挂载后真实DOM
    mounted(){
        console.log("mounted -- 执行");
        console.log(document.getElementById("myP")); // p
    }
}
</script>
复制代码

4.4 更新阶段

更新阶段2个钩子函数作用和执行时机

含义讲解:

1.当data里数据改变, 更新DOM之前

2.beforeUpdate – 生命周期钩子函数被执行

3.Virtual DOM…… – 虚拟DOM重新渲染, 打补丁到真实DOM

4.updated – 生命周期钩子函数被执行

5.当有data数据改变 – 重复这个循环

image-20210511154016777.png

components/Life.vue - 创建一个文件

准备ul+li循环, 按钮添加元素, 触发data改变->导致更新周期开始

<template>
  <div>
      <p>学习生命周期 - 看控制台打印</p>
      <p id="myP">{{ msg }}</p>
      <ul id="myUL">
          <li v-for="(val, index) in arr" :key="index">
              {{ val }}
          </li>
      </ul>
      <button @click="arr.push(1000)">点击末尾加值</button>
  </div>
</template>

<script>
export default {
    data(){
        return {
            msg: "hello, Vue",
            arr: [5, 8, 2, 1]
        }
    },
    // ...省略其他代码

    // 三. 更新
    // 前提: data数据改变才执行
    // 更新之前
    beforeUpdate(){
        console.log("beforeUpdate -- 执行");
        console.log(document.querySelectorAll("#myUL>li")[4]); // undefined
    },
    // 更新之后
    // 场景: 获取更新后的真实DOM
    updated(){
        console.log("updated -- 执行");
        console.log(document.querySelectorAll("#myUL>li")[4]); // li
    }
}
</script>

4.5 销毁阶段

销毁阶段2个钩子函数作用和执行时机

含义讲解:

1.当$destroy()被调用 – 比如组件DOM被移除(例v-if)

2.beforeDestroy – 生命周期钩子函数被执行

3.拆卸数据监视器、子组件和事件侦听器

4.实例销毁后, 最后触发一个钩子函数

5.destroyed – 生命周期钩子函数被执行

d8a91b30df2b4fabaa42205fba4e4b66_tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp

components/Life.vue - 准备生命周期方法(Life组件即将要被删除)

<script>
export default {
    // ...省略其他代码
    
    // 四. 销毁
    // 前提: v-if="false" 销毁Vue实例
    // 场景: 移除全局事件, 移除当前组件, 计时器, 定时器, eventBus移除事件$off方法
    beforeDestroy(){
        // console.log('beforeDestroy -- 执行');
        clearInterval(this.timer)
    },
    destroyed(){
        // console.log("destroyed -- 执行");
    }
}
</script>
复制代码

主要: App.vue - 点击按钮让Life组件从DOM上移除 -> 导致Life组件进入销毁阶段

<Life v-if="show"></Life>
<button @click="show = false">销毁组件</button>

<script>
    data(){
        return {
            show: true
        }
    },
</script>

5. ref与nextTick

5.1 ref属性

  1. 作用:用于获取组件对象或者标签,还可以修改组件中的数据

  2. 语法:

    • 给标签或组件标签设置 ref属性

    1653046373904

    • 恰当时机, 通过ref属性 获取组件对象, 可调用组件对象里方法等

    1653046631658

  3. 演示

    • 父组件
    <template>
      <div>
        <ProdTest id="testId" ref="testRef"></ProdTest>
      </div>
    </template>
    
    <script>
    import ProdTest from "./components/02-组件-refs的测试.vue";
    export default {
      components: {
        ProdTest,
      },
      mounted(){
        console.log(document.querySelector('#testId'));//打印的是子组件的第一层根标签
        console.log(this.$refs.testRef);//打印的是子组件对象
        this.$refs.testRef.testData='我偏不' //修改子组件数据
        this.$refs.testRef.sayHi()  //调用子组件方法
      }
    };
    </script>
    
    • 子组件
    <template>
      <div>
          <ul>
              <li>{{testData}}</li>
          </ul>
      </div>
    </template>
    
    <script>
    export default {
        data (){
            return {
                testData:'生意嘛,不寒碜'
            }
        },
        methods:{
            sayHi(){
                alert('方法调用成功')
            }
        }
    }
    </script>
    

5.2 this.$nextTick

  1. 应用场景:由于dom元素的更新是异步的,this.$nextTick(函数体) 可以等DOM更新后, 触发此方法里函数体执行
  2. 语法:this.$nextTick(函数体)
  3. 代码演示:
<template>
  <div>
    <button @click="tooggleShow" v-if="isShow">切换</button>
    <input type="text" v-else placeholder="天真" ref="ipt">
  </div>
</template>

<script>
export default {
  data(){
    return {
      isShow:true
    }
  },
  methods:{
    tooggleShow(){
      this.isShow=!this.isShow  //值取反后dom元素并不会马上渲染到页面,
      this.$nextTick(()=>{  //因此要使用该方法,执行dom更新后要执行的函数代码
      this.$refs.ipt.focus()
      console.log(this.$refs.ipt);
      })
    }
  }
}
</script>

<style>
  input {
    animation: testAni 2s infinite alternate linear;
  }
  @keyframes testAni {
    from{
      margin-top: 0;
    }to{
      margin-top: 400px;
    }
  }
</style>

6.Vue组件进阶

6.1 动态组件

  1. 作用:用于组件间的切换

  2. 原理:设置挂载点, 使用is属性来设置要显示哪个组件,is的属性值为要切换的组件名

  3. 代码演示(先自行创建2个或以上的子组件,并完成引入与注册)

    <template>
      <div>
        <p>
            <!-- 通过按钮控制变量的取值 -->
          <button @click="comName = 'TestProid1'">组件1</button>
          <button @click="comName = 'TestProid2'">组件2</button>
        </p>
          <!-- 通过v-bind指令使 is属性值能使用变量,进而控制变量值即可完成组件切换 --> 
        <component :is="comName"></component>
      </div>
    </template>
    <script>
    import TestProid1 from "@/components/01-动态组件-子1.vue";
    import TestProid2 from "@/components/02-动态组件-子2.vue";
    export default {
      data() {
        return {
          comName: "TestProid1",
        };
      },
      components: {
        TestProid1,
        TestProid2,
      },
    };
    </script>
    
  4. 通过挂载点,切换组件的原理是反复创建于销毁组件,故建议使用keep-alive内置组件优化性能,此时切换的两个子组件会多两个钩子函数:

    • 钩子函数在子组件内部定义

    • activated – 激活时触发

    • deactivated – 失去激活状态时触发

<template>
  <div>
    <p>
      <button @click="comName = 'TestProid1'">组件1</button>
      <button @click="comName = 'TestProid2'">组件2</button>
    </p>
      <!-- keep-alive组件包裹component组件即可 -->
    <keep-alive>
        <component :is="comName"></component>
    </keep-alive>
  </div>
</template>
<script>
import TestProid1 from "@/components/01-动态组件-子1.vue";
import TestProid2 from "@/components/02-动态组件-子2.vue";
export default {
  data() {
    return {
      comName: "TestProid1",
    };
  },
  components: {
    TestProid1,
    TestProid2,
  },
};
</script>

6.2 slot 组件插槽

  1. 应用场景:一个组件内有需要外部传入标签的地方,
  2. 使用方法:在组件内用占位,使用组件时夹着的地方, 传入标签替换slot ,(Pannel为自定义的组件名)
  3. 中间包裹的内容为,默认显示内容,中间无内容时,子组件内的会替换成默认显示内容
  4. 代码演示
<!-- 子组件 -->
<template>
  <div>
      <!-- slot 用于占位,需要调用子组件的的人自定义标签内容 -->
      <slot>你调用子组件不传入内容我就默认显示这个</slot>
  </div>
</template>

<script>
export default {}
</script>

<style scoped>
    div {
        width: 100%;
        height: 80px;
        border: 2px dashed orange;
    }
</style>
<!-- 父组件 -->
<template>
  <div>
    <!-- 子组件双标签之间夹住的内容,实际上会代替 slot的位置 -->
    <TestSlot>
      <button>李白</button>
      <button>杜甫</button>
    </TestSlot>
    <TestSlot>
      <button>白居易</button>
    </TestSlot>
      
      <!--组件标签内无内容时,会显示 slot的默认值 -->
    <TestSlot> </TestSlot>
  </div>
</template>

<script>
import TestSlot from "@/components/04-slot 插槽标签.vue";
export default {
  components: {
    TestSlot,
  },
};
</script>

6.3 v-slot 具名插槽

  1. 应用场景:一个组件内有2处以上需要外部传入标签的地方

  2. 语法:

    • v-slot:可以简化成 #
  • slot使用name属性区分名字
    • template配合v-slot:名字来分发对应标签
    • 在v-slot属性上可以绑定一个变量名,这个变量是一个对象,这个对象包含子组件传过来的所有数据
  1. 代码演示
<!-- 子组件 -->
<template>
  <div>
      <!-- slot上的name属性值,绑定具名 -->
      <slot name="left"></slot>
      <slot name="right"></slot>
  </div>
</template>

<script>
export default {

}
</script>

<style scoped>
    div {
        width: 100%;
        height: 80px;
        border: 2px dashed orange;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0 25px;
    }
</style>
<!-- 父组件 -->
<template>
  <div>
    <TestSlot>
        <!-- 组件标签的template标签 v-slot  简写 # 绑定使用 -->
      <template #left>
        <button>显示在左边</button>
      </template>
      <template #right>
        <button>显示在右边</button>
      </template>
    </TestSlot>
    <TestSlot>
      <template #left> 我只是一个小标题 </template>
      <template #right>
        <span> 我很听话地待在右边 </span>
      </template>
    </TestSlot>
	
  </div>
</template>

<script>
import TestSlot from "@/components/06-  v-slot具名插槽.vue";
export default {
  components: {
    TestSlot,
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
</style>
  1. 具名插槽 - 子组件传递数据给父组件
<!-- 子组件 -->
<template>
  <div>
      <!-- 第一步:用属性绑定变量传递 -->
      <slot :data="str" name="left"></slot>
      <slot name="right"></slot>
  </div>
</template>

<script>
export default {
    data(){
        return {
            str:'君不见黄河之水天上来'
        }
    }

}
</script>

<style scoped>
    div {
        width: 100%;
        height: 80px;
        border: 2px dashed orange;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0 25px;
    }
</style>
<!-- 父组件 -->
<template>
  <div>
    
    <TestSlot>
      <!-- 第二步:通过 #具名="自定义变量名"  自定义变量名(对象)会一次接收所有子组件通过 v-bind属性名 传递的数据,-->
      <template #left="obj">
          <!-- 通过 obj.属性 的方式调用 -->
        <button>{{ obj.data }}</button>
      </template>
      <template #right>
        <button>显示在右边</button>
      </template>
    </TestSlot>
  </div>
</template>

<script>
import TestSlot from "@/components/08-通过v-slot 子传父-子组件.vue";
export default {
  components: {
    TestSlot,
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
</style>

7. 路由

7.1 简单理解

  1. 概念:路由是一种标记到资源的映射关系,在vue中就是url路径和组件的映射关系,即 特定的url路径指向对应的组件
  2. 作用:在没有发生html跳转的情况下,完成页面的切换
  3. 从语义及作用出发将组件分类:/src/views (pages) 页面组件 / /src/components 复用组件

7.2 路由系统的引入

  1. 方式一:引入和配置对象都写在main.js文件中,不单独封装

    // 如果main.js没有引入vue则要先 
    //import Vue from 'vue'
    // 1. 引入库
    import VueRouter from 'vue-router'
    
    // 2.注册全局组件
    Vue.use(VueRouter)
    
    // 3. routes 创建路由规则 路由配置 详见7.3
    
    // 4. 创建一个实例
    const router = new VueRouter({
      // 放入 routes, 属性名必须叫 routes
      //mode: 'history' 去掉地址栏上的 #  (默认值为 hash )
    })
    new Vue({
      // 4. 将实例挂载到 new Vue 根实例上
      router,
      render: h => h(App),
    }).$mount('#app')
    
  2. 方式二:在 /src/router (index.js) 下单独封装,导出并挂载到main.js中

    //在 index.js文件中
    
    //1. 引入路由系统及 vue
    import VueRouter from "vue-router";
    import Vue from "vue";
    
    // 2. 注册全局组件
    Vue.use(VueRouter)
    
    // 3. routes 创建路由规则 路由配置 详见7.3
    
    // 4. 将路由配置放入实例化的路由对象中,
    const router=new VueRouter({
        // 放入 routes, 属性名必须叫 routes
    })
    
    // 5. 导出路由对象
    export default router
    
    
    
    //在main.js文件中
    
    // 6. 引入封装的路由对象
    import router from './router/index.js';
    
    new Vue({
      router, // 7. 将实例挂载到 new Vue 根实例上
      render: h => h(App),
    
    }).$mount('#app')
    

7.3 路由配置

  1. 路由配置是指路由对象里的一个属性,用于指定url与路由组件的映射关系

  2. 路由配置有两种写法:逐一引入 / 按需引入

    • 逐一先引入路由组件后配置
    // 1 引入组件对象 2 指定对应路径
    import Login from '@/views/Login.vue'
    import Find from '@/views/Find.vue'
    const routes = [
        {
            path: '/',
            // 重定向,当用户直接访问localhost:8080时强制跳转到登录页
            redirect: '/login'
        },
        {
            // 1. path 路径 / 开头
            path: '/find',
            // 2. component 组件
            component: Find
        },
        {
            // 1. path 路径 / 开头
            path: '/login',
            // 2. component 组件
            component: Login
        },
    ]
    //注:定义好的路由配置最终要放在 router 实例中
    
    • 按需引入组件,当用户访问到对应页面是才会加载组件
    const routes=[
        {
            path:'/',
            // 重定向,当用户直接访问localhost:8080时强制跳转到登录页
            redirect: '/login'
        },
        {
            path:'/login',
            // 按需引入组件,当用户访问到login页面才加载login.vue组件
            component:()=>import('@/views/login.vue')
        },
        {
            path:'/register',
            component:()=>import('@/views/register.vue')
        },
    ]
    //注:定义好的路由配置最终要放在 router 实例中
    
  3. 路由配置嵌套 =>直接在一级路由配置里面嵌套即可

    const routes=[
        {
        // 1. path 路径 / 开头
        path: '/find',
        // 2. component 组件
        component: Find,
        redirect: '/find/ranking', //子路由的重定向
            
        // 嵌套子路由
        children: [
          // 依旧是 path 跟 component 的配对
          // 不要加斜杠, 因为需要改上一层拼接在一起
          // 如果加了, 就需要在根路径进行访问
          {
            path: 'ranking',
            component: ()=>import('@/views/ranking.vue')
          },
          {
            path: 'recommend',
            component: ()=>import('@/views/recommend.vue')
          },
          {
            path: 'songlist',
            component: ()=>import('@/views/songlist.vue')
          },
        ]
      },
    ]
    
    //注:二级路由的挂载点(<router-view></router-view>)放在对应的一级路由组件中
    

7.4 挂载点和路由链接

7.4.1 简单介绍

  1. 路由第三方库给我们提供了两个组件, router-view/挂载点, router-link/路由链接
  2. 挂载点:router-view, 决定了路由组件最终摆放的位置
  3. 路由链接:router-link ,最终会编译成 a 标签,to属性的值接收了要跳转的路由组件的url
  4. 演示:
<template>
  <div>
      <!-- 点击不同的 router-link 时,下方的挂载点会显示对应的组件-->
      <router-link to="/find/ranking">排行榜</router-link>
      <router-link to="/find/recommend">推荐</router-link>
      <router-link to="/find/songList">列表</router-link>
      
      <!-- 最终对应的路由组件摆放的位置 -->
      <router-view></router-view>
  </div>
</template>

7.4.2路由链接传值给路由组件

  1. 在 router-link的 to 属性上传值(两种方式)
    1. /path?参数名=值
    2. /path/值 该方法需在路由配表中对应的配置对象提前配置 path:"/path/:参数名"
  2. 在对应路由组件接收传递的值(分别对应)
    1. 方法1.1的接收方式: $route.query.参数名
    2. 方法1.2的接收方式: $route.params.参数名
<!-- 调用者文件 -->
<template>
  <div>
    <router-link to="/find?name=方方方"> 跳转到发现页</router-link>
    <router-view></router-view>
  </div>
</template>


<!-- 被调用的路由组件 -->
<template>
  <div>
      发现音乐<br>
      {{$route.query.name}}
  </div>
</template>

7.5 编程式导航与声明式导航

7.5.1编程式导航

编程式导航是用在js处理逻辑后需要页面跳转,比如点击button按钮跳转

export default {
   methods: {
      //点击按钮跳转路由
      /*方式一: 直接 path 作为字符串传入 
      handleClick() {
       this.$router.push('/my')
     } */
      /*方式二: 传入一个对象, 其中带有 path 属性
      handleClick() {
      this.$router.push({
        path: '/my'
      })
    } */
      // 方式三:传入一个对象, 其中带有 name 属性, 需要在路由中预先配置
      handleClick() {
         this.$router.push({
            // 需要 params 传参的话
            // path 跟 params 两个属性会冲突
            // path: 'part/8888',
             
            //name 属性, 需要在路由中预先配置 同一个属性值
            name: 'partPage',
            //路由组件同样可以用 $route.params.参数名 接收参数值
            params: {
               id: 66666
            }
         })
      }
   }
}

7.5.2声明式导航

声明式导航是直接渲染到页面的,比如a链接 与 router-link组件

<router-link to="/find"> 跳转到发现页</router-link>
<router-link to="/my">跳转到我的音乐</router-link>
<a href="#/login">跳转到登录页</a> <!-- a标签跳转路由需加上 # 号 -->
<router-view></router-view>

7.5.3 $router 方法小扩展

  1. this.$router.push() //会进行页面跳转,同时会在历史记录上留下记录
  2. this.$router.replce() //和push功能相同,但是会替换当前页出现在历史记录中
  3. this.$router.go(num) //表示距离当前页的在历史记录上的页数
  4. this.$router.back() 返回到上一页
  5. this.$router.forward() 前进到下一页

7.6 路由守卫

  1. 简单理解为所有路由跳转前都会执行的函数,router路由实例上添加,一般定义在main.js文件中

    //创建的路由实例叫什么,路由守卫函数的前缀就得叫什么
    const router = new Router({
      // 这里可以有配置对象
    })
    router.beforeEach((to,from,next)=>{
      // 拦住了必须放行, 默认这个函数可以接收到三个参数
      // to 目的地, from 来源, next 放行回调函数
      if (to.path==='/my/75668' && !localStorage.getItem('token')) {
        next('/find')
      }else{
        next()
      }
    })
    

8. vant组件库

8.1使用方法

  1. 利用npm命令下载第三方包:npm i vant@2

  2. 官方文档网址

  3. 三种引入方式

    • 引入全部组件
    import Vant from 'vant'; //引入第三方包
    import 'vant/lib/index.css'; //引入样式
    
    Vue.use(Vant); //全局注册组件
    
    • 手动按需引入
    import { Button } from 'vant'; //解构引入具体组件
    import 'vant/lib/button/style'; //具体组件对应的样式
    Vue.use(Button); //注册该组件
    
    • 半自动按需引入, 需先安装插件:npm i babel-plugin-import -D
    // 按需引入 ,解构的方式引入
    import { Button,Field,} from 'vant';
    
    //省去了对应样式的引入步骤,插件会自动引入样式
    
    // 注册对应组件,注册不能合并成一行写
    Vue.use(Button);
    Vue.use(Field);
    

    vuex

1. 简单介绍

  1. 概念:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
  2. 大白话:集中管理系列全局的数据变量等的一个对象,通过内部封装的各个属性方法对全局数据修改更新。
  3. 特点:对于数据的更新和修改,只能使用内置属性,不再是传统方法。
  4. 使用方法:
npm i vuex@3
注意:如果用的是vue2这个框架,vuex必须下载3这个版本

1654344053649

2. state 共享状态数据

  1. 理解:用来声明全局数据的属性

  2. 声明方法:

    const store=new Vuex.Store({
        state:{
          str:'肥仔快乐水',
          num:111,
          list:[1,2,3,4,5,6,7,8,9,110]
        },
    })
    
  3. 组件如何调用

    <template>
      <div>
          <!--第一种 $store.state.变量名 -->
        <h3>{{$store.state.num}}</h3>
          <!--第二种 computed属性里的函数返回值 -->
        <h3>写在计算属性:{{myNum}}</h3>
          <!--第三种 内置辅助函数解构形同正常变量使用 -->
        <h3>通过mapState辅助函数解构的state变量str:{{str}}</h3>
        <h3>通过mapState辅助函数解构的state变量num:{{num}}</h3>
    
    </template>
    
    <script>
    // 辅助函数第1步.  引入
    import {mapState} from 'vuex'
    export default {
      
      computed:{
        // state优化使用,写在computed属性中,通过函数返回值
        myNum(){
          return this.$store.state.num
        },
        // 辅助函数第2步. 以方法解构的方式 ...mapState(['state变量名1','state变量名2'])
        ...mapState(['str','num'])
      },
    }
    </script>
    

3. mutations 同步更新修改

  1. 理解:state数据的修改只能通过mutations,并且必须是同步更新(也就是里面不能有异步代码),目的是形成数据快照。(mutations是专门用来修改state中的变量的对象(属性))

  2. 声明函数的方法(main.js文件中, mutations 属性内):

    const store=new Vuex.Store({
        state:{
          num:111,
        },
        
        mutations:{
          // 通过mutations里面的方法修改state里面的数据,除state外,只能有一个形参,可通过以数组为形参的方式扩参
          addfn(state,data){
            //state 为上述定义的数据变量
            state.num+=data
          }
        },
    })
    
  3. 组件如何调用:

    <template>
      <div>
        <h3>观察num的改变:{{$store.state.num}}</h3>
        <!--1. $store.commit('要调用的函数名',实参) -->
        <button @click="$store.commit('addfn',5)">直接调用:加5</button>
    	<!--2. 在methods属性中声明函数,函数内进行$store.commit,详见addFnTest函数 -->
        <button @click="addFnTest(100)">函数封装:加100</button>
         <!--3. 通过mapMutations辅助函数将方法解构出来,当正常函数使用 -->
        <button @click="addfn(200)">mapMutations解构的: 点我加200</button>
    
      </div>
    </template>
    
    <script>
        // 辅助函数第1步 引入
    import {mapMutations} from 'vuex'
    export default {
      methods:{
        // 优化,函数调用函数
        addFnTest(num){
          this.$store.commit('addfn',num)
        },
        // 辅助函数第2步,解构: ...mapMutations(['要解构的函数名'])
        ...mapMutations(['addfn'])
      }
    }
    </script>
    

4.actions异步更新

  1. actions就是可以通过异步操作,调用mutations中的函数,从而修改state中的变量。

  2. 声明函数的办法(main.js文件中, actions属性内):

    const store=new Vuex.Store({
        state:{
          num:111,
        },
        
    actions:{
          // 第一个参数是,store实例对象, store.state才是上述的数据属性
          getAsyncNum(store,data){
            setTimeout(() => {
              store.state.num+=data
            }, 2000);
          }
        },
    })
    
  3. 组件如何调用:

    <template>
      <div>
        <h3>观察num的改变:{{$store.state.num}}</h3>
        <!--1. $store.dispatch('要调用的函数名',实参) -->
        <button @click="$store.dispatch('getAsyncNum',10)">直接调用:延迟2秒后加10</button>
    	<!--2. 在methods属性中声明函数,函数内进行$store.dispatch,详见testAsyncNum函数 -->
        <button @click="testAsyncNum(20)">函数封装:延迟2秒后加20</button>
          
         <!--3. 通过mapMutations辅助函数将方法解构出来,当正常函数使用 -->
        <button @click="getAsyncNum(30)">mapActions解构的: 延迟2秒后加30</button>
    
      </div>
    </template>
    
    <script>
    // 辅助函数第1步 引入
    import {mapActions} from 'vuex'
    export default {
      methods:{
        // 优化,函数调用函数
        testAsyncNum(num){
          this.$store.dispatch('addfn',num)
        },
        // 辅助函数第2步,解构: ...mapActions(['要解构的函数名'])
        ...mapActions(['getAsyncNum'])
      }
    }
    </script>
    

5.getters 变量派生

  1. 理解:除了state可以声明全局变量之外,还可以利用getters声明全局变量,只不过getters声明的变量是从state派生出来的,getters就相当于vue中的computed计算属性。

  2. 声明办法:

    const store=new Vuex.Store({
        state:{
          list:[1,2,3,4,5,6,7,8,9,110]
        },
        // getters类似于计算属性中的变量
        getters:{
          // 内置参数 state 即state属性
          filterList:(state)=>{
           return state.list.filter(item=>item>5)
          }
        }
    })
    
  3. 组件调用

    <template>
      <div>
          <!--1. $store.getters.变量名 -->
        <h2>$store.getters.mapList直接调用:{{$store.getters.mapList}}</h2>
          <!--2. 在computed属性中声明函数,函数内return this.$store.getters.变量名 -->
        <h2>函数封装返回值{{testmapList}}</h2>
          <!--3. 通过mapGetters辅助函数将方法解构出来,当正常计算属性变量使用 -->
        <h2>辅助函数解构:{{mapList}}</h2>
      </div>
    </template>
    <script>
        // 辅助函数第1步 引入
    import { mapGetters,} from 'vuex'
    export default {
     computed:{
        // 辅助函数第2步,解构: ...mapGetters(['要解构的变量名'])
        ...mapGetters(['mapList']),
        testmapList(){
          return this.$store.getters.mapList
        }
      }
    }
    </script>
    

6.module模块化

  1. 理解:分模块管理我们的全局变量,这样有利于项目的后期维护

  2. 声明方法

    const store=new Vuex.Store({
        state:{
          str:'肥仔快乐水',
          num:111,
          list:[1,2,3,4,5,6,7,8,9,110]
        },
        // 通过mutations里面的方法修改state里面的数据
        mutations:{
          addfn(state,[data,abc]){
            state.num+=data
            console.log(typeof data);
            console.log(abc);
          }
        },
        actions:{
          // 第一个参数是,store实例对象
          getAsyncNum(store,num){
            setTimeout(() => {
              store.state.num=store.state.num*num
            }, 2000);
          }
        },
        // getters类似于计算属性中的变量
        getters:{
        // 内置参数 state 即state属性
        mapList:(state)=>{
         return state.list.filter(item=>item>5)
        },
        // 利用一层getters简化模块内的数据
        userName:state=>state.user.userName,
        my_like:state=>state.setting.my_like
      },
        modules:{
          /* 模块内的mutations,actions,getters 都是挂载到一层的,调用方法与一层一致 */
          user:{
        // 加锁,模块封闭化
        namespaced:true,
        state:{
          userName:'黄昏'
        },
        mutations:{
          changeUN(state){
            state.userName='约尔'
          }
        },
        actions:{
          asyncChangeUN(store){
            setTimeout(() => {
              store.commit('changeUN')
            }, 2000);
          }
        }
      },
          setting:{
            state:{
              my_like:'间谍过家家'
            }
          }
        }
    })
    
  3. 组件调用

    <template>
      <div>
        <h2>$store.state.user.变量名,直接调用:{{$store.state.user.userName}}</h2>
        <h2>$store.state.user.变量名,直接调用:{{$store.state.setting.my_like}}</h2>
        <h3>getters简化,解构后调用:{{userName}}</h3>
        <h3>getters简化,解构后调用:{{my_like}}</h3>
        <!-- 
          模块加namespaced:true 后不能全局直接调用,加多一层模块名才可
        <button @click="$store.commit('changeUN')">mutations修改变量userName</button>
        <button @click="$store.dispatch('asyncChangeUN')">actions延迟修改变量userName</button>
         -->
         <!-- <h3>当设置模块内属性namespaced:true,加模块名后才能调用方法</h3>
        <button @click="$store.commit('user/changeUN')">mutations修改变量userName</button>
        <button @click="$store.dispatch('user/asyncChangeUN')">actions延迟修改变量userName</button> -->
         <h3>当设置模块内属性namespaced:true,辅助函数接解构调用也有所不同</h3>
        <button @click="changeUN">mutations修改变量userName</button>
        <button @click="asyncChangeUN">actions延迟修改变量userName</button>
      </div>
    </template>
    
    <script>
    import {mapGetters,mapMutations,mapActions} from 'vuex'
    export default {
      computed:{
        ...mapGetters(['userName','my_like'])
      },
      methods:{
        // 模块上锁后,辅助函数解构,方式有所不同
        ...mapMutations('user',['changeUN']),
        ...mapActions('user',['asyncChangeUN'])
      }
    }
    </script>
    
    <style>
    
    </style>