Vue2+Vue3

文章目录

Vue快速上手

Vue是什么

概念:Vue是一个用于构建用户界面的渐进式框架

  • 优点:大大提升开发效率
  • 缺点:需要理解记忆规则
    在这里插入图片描述

Vue的两种使用方式:

  • Vue核心包开发
    场景:局部模块改造
  • Vue核心包 & Vue插件工程化开发
    场景:整站开发

第一个Vue程序

穿件Vue实例,初始化渲染的核心步骤:

  • 准备容器
  • 引包(官网)—— 开发版本 / 生产版本
  • 创建Vue实例 new Vue()
  • 指定配置项 el data => 渲染数据
    1. el指定挂载点,选择器指定控制饿时哪个盒子
    2. data提供数据

代码演示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    创建Vue实例,初始化渲染
    1. 准备容器(Vue所管理的范围)
    2. 引包(开发版本包 / 生产版本包) 官网
    3. 创建实例
    4. 添加配置项 => 完成渲染
-->
<div id="app">
    <!-- 这里将来会编写一些用于渲染的代码逻辑 -->
    {{msg}}
</div>

<!--引入的是开发版本包(包含完整的注释和警告)-->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>
    // 一但引入了Vue.js核心包,在全局环境,就有了Vue构造函数
    const app = new Vue({
        // 通过el配置选择器,指定Vue管理的是哪个盒子
        el: '#app',
        // 通过data提供数据
        data:{
            msg: '小吴在敲Bug'
        }
    })
</script>
</body>
</html>

插值表达式

插值表达式是一种Vue的模版语法

  • 作用: 利用表达式进行插值,渲染到页面中
    表达式:是可以被求值的代码,JS引擎会将其计算出一个结果
  • 语法: {{ 表达式 }}
    1. 使用的数据必须存在(data)
    2. 支持的是表达式,而非语句
    3. 不能再标签属性中使用{{}}插值

代码演示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--
    插值表达式:Vue的一种模板语法
    作用:利用 表达式进行插值渲染
    语法:{{表达式}}
-->
<div id="app">

    <p>{{msg}}</p>
    <p>{{msg.toUpperCase()}}</p>
    <p>{{msg + '你好'}}</p>
    <p>{{age>18? '成年':'未成年'}}</p>

</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>

    const app = new Vue({

        el: '#app',

        data:{
            msg: '小吴在敲Bug',
            age: 21
        }
    })
</script>
</body>
</html>

Vue核心特性:响应式

我们已经掌握了寄出的模版渲染,其实除了基本的模版渲染,Vue背后还做了大量工作。
比如:数据的响应式处理 —— 数据变化,视图自动更新
聚焦于数据——>数据驱动视图
使用Vue开发,关注 业务的核心逻辑,根据业务 修改数据即可
在这里插入图片描述


代码演示

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <p>{{msg}}</p>

</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>

  const app = new Vue({

    el: '#app',

    data:{
      // 响应式
      msg: '小吴在敲Bug'
    }
  })
  // 1. 访问数据 实例.属性名
  // 2. 修改数据 实例.属性名=新值
</script>
</body>
</html>

测试
在这里插入图片描述

Vue指令

Vue会根据不同的指令,针对不同标签实现不同的功能
指令:带有 v-前缀 的特殊 标签属性

v-html

v-html指令:

  • 作用:向指定节点中渲染包含html结构的内容。

  • 与插值语法的区别:

    • v-html会替换掉节点中所有的内容,{{xx}}则不会。

    • v-html可以识别html结构。

  • 严重注意:v-html有安全性问题!!!!

    • 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。

    • 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!


代码演示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <div v-html="msg"></div>

</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>

    const app = new Vue({

        el: '#app',

        data:{
            msg:`
            <a href="www.4399.com">
            4399小游戏
            </a>
            `
        }
    })
</script>
</body>
</html>

v-show 与 v-if

v-show

  • 作用:控制元素显示和隐藏
  • 语法:v-show=“表达式” | 表达式值true显示,false隐藏
  • 原理:切换display:none 控制显示隐藏
  • 场景:频繁切换显示隐藏的场景

v-if

  • 作用:控制元素显示隐藏(条件渲染
  • 语法:v-if=“表达式” | 表达式值true显示,false隐藏
  • 原理:基于 条件判断,是否创建或移除元素节点
  • 场景:要么显示,要么隐藏,不频繁切换的场景

v-else 与 v-else-if

  • 作用:辅助v-if进行判断渲染
  • 语法:v-else | v-else-if=“表达式”
  • 注意:需要紧挨着v-if一起使用

代码演示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

<div id="app">
    <div >
        <p v-if="gender===1">性别:♂ 男</p>
        <p v-else>性别:♀ 女</p>
        <hr>
        <p v-if="score==='A'">成绩评定A:奖励电脑一台</p>
        <p v-else-if="score==='B'">成绩评定B:奖励周末郊游</p>
        <p v-else-if="score==='C'">成绩评定C:奖励零食礼包</p>
        <p v-else>成绩评定D:惩罚一周不能玩手机</p>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
<script>

    const app = new Vue({
        el: '#app',
        data: {
            gender: 1,
            score: 'B'

        }
    })
</script>

</body>
</html>

v-on

  • 作用:注册时间 = 添加监听 + 提供处理逻辑
  • 语法:
    • v-on:事件名 = “内联语句”
    • v-on:事件名 = “methods中的函数名”
  • 简写:@事件名

v-on调用传参

  • 语法:@click=函数名(参数1,参数2……)
  • 接收参数使用函数的形参,函数名(a,b){}

代码演示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <button @click="count--">-</button>
    <span>{{count}}</span>
    <button v-on:click="count++">+</button>
</div>


<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>

<script>

    const app = new Vue({

        el: '#app',

        data:{
            count: 0
        }
    })
</script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <button @click="fn()">切换显示隐藏</button>
    <h1 v-show="isShow">小吴在敲Bug</h1>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        isShow:true
        
      },
      methods:{
        fn(){
          this.isShow=!this.isShow
        }
      }
    })
  </script>
</body>
</html>

v-bind

  • 作用:动态的设置html的标签属性
  • 语法:v-bind:属性名=“表达式”
  • 注意:简写形式 :属性名=“表达式”

代码演示

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <img v-bind:src="imgUrl" v-bind:title="msg" alt="">
    <img :src="imgUrl" :title="msg" alt="">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        msg:'波仔喝奶茶',
        imgUrl:'./imgs/10-02.png'
      }
    })

  </script>
</body>
</html>

v-bind对于样式控制的增强
为了方便开发者进行样式控制,Vue扩展了v-bind的语法,可以针对class类名和style行内样式进行控制。
语法 :class="对象/数组"

  • 对象 ——> 键就是类名,值是布尔值。如果值为true,有这个类,否则没有这个类

    <div class="box" :class="{类名1:布尔值,类名2:布尔值}"></div>
    
  • 数组 ——> 数组中所有的类,都会添加到盒子上,本质就是一个class列表

    <div class="box" :class="[类名1,类名2,类名3]"></div>
    

v-for

  • 作用:基于数据循环,多次渲染整个元素
  • 语法:v-for = “(item,index) in 数组”
    • item每一项,index下标

代码演示

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <div id="app">
    <h3>小黑水果店</h3>
    <ul>
      <li v-for="(item, index) in list">
        {{ item }} - {{ index }}
      </li>
    </ul>

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

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        list: ['西瓜', '苹果', '鸭梨', '榴莲']
      }
    })
  </script>
</body>
</html>

v-for中的key

  • 语法::key属性 = “唯一标识”
  • 作用:给列表项添加的唯一标识。便于Vue进行列表项的正确排序复用
  • 注意点:
    • key的值只能是字符串或数字类型
    • key的值必须具有唯一性
    • 推荐使用id作为key(唯一),不推荐使用index作为key(会变化,不对应)

v-model

  • 作用:给表单元素使用,双向数据绑定 ——> 可以快速获取或设置表单元素内容
    • 数据变化 —— 视图自动更新
    • 视图变化 —— 数据自动更新
  • 语法:v-model=“变量”

代码演示

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>

  <div id="app">
    <!-- 
      v-model 可以让数据和视图,形成双向数据绑定
      (1) 数据变化,视图自动更新
      (2) 视图变化,数据自动更新
      可以快速[获取][设置]表单元素的内容
     -->
    账户:<input type="text" v-model="username"> <br><br>
    密码:<input type="password" v-model="password"> <br><br>
    <button @click="login">登录</button>
    <button @click="reset">重置</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '',
        password: ''
      },
      methods: {
        login () {
          console.log(this.username, this.password)
        },
        reset () {
          this.username = ''
          this.password = ''
        }
      }
    })
  </script>
</body>
</html>

指令修饰符

通过“.”指明一些指令后缀,不同后缀封装了不同的处理操作(简化代码)

  • 按键修饰符
    • keyup.enter —— 键盘回车监听
  • v-model修饰符
    • v-model.trim —— 去除首尾空格
    • v-model.number —— 转数字
  • 事件修饰符
    • @事件名.stop —— 阻止冒泡
    • 事件名.prevent —— 阻止默认行为

计算属性

概念:基于现有的数据,计算出来的新属性依赖的数据变化,自动重新计算
语法:

  • 声明在 computed配置项 中,一个计算属性对应一个函数
  • 使用起来和普通属性一样使用{ { 计算属性名 } }
computed: {
    计算属性名() {
        基于现有的数据,编写求值逻辑
       
        return 结果
    }
}

computed计算属性 VS methods方法

  • computed计算属性:
    • 作用:封装了一段对于数据的处理,求得一个结果
    • 语法:
      • 写在 computed 配置中
      • 作为属性,直接使用 —— this.计算属性 | { { 计算属性 } }
    • 缓存特性(提升性能):
      • 计算属性会对计算出来的 结果缓存 ,再次使用直接读取缓存,依赖项变化了,会 自动 重新计算,并 再次缓存
  • methods方法:
    • 作用:给实例提供一个方法,调用以处理业务逻辑
    • 语法:
      • 写在 methods 配置项中
      • 作为方法,需要调用 —— this.方法名() | @事件名=“方法名

计算属性完整写法
计算属性默认的简写,只能读取访问, 不能"修改"
如果要 修改 ,需要写计算属性的 完整写法

computed: {
    计算属性名: {
        get(){
        	一段逻辑代码
        	return 结果
        },
        set(修改的值){
        	一段逻辑代码
        }
    }
}

watch侦听器(监视器)

作用:监视数据变化,执行一些业务逻辑异步操作

watch——简写

data:{
	words: '苹果'
},
watch: {
	// 该方法会在数据变化时,触发执行
	words —— 数据属性名(newValue,oldValue){
		一些业务逻辑 或 异步操作
	},
	'对象.属性名'(newValue,oldValue){
		一些业务逻辑 或 异步操作
	}
}

watch——完整写法

  • deep: true 对复杂类型深度监视
  • immediate: true 初始化立刻执行一次 handler方法
watch: {
	数据属性名:{			//数据属性是对象
	deep: true,   //深度监视,对对象里的所有子属性进行监视
	immediate: true		// 立刻执行,打开页面就马上执行一次
	handler(newValue,oldValue){
		一些业务逻辑 或 异步操作
	}
}

Vue生命周期 和 生命周期的四个阶段

Vue生命周期:一个Vue实例从创建销毁的整个过程
生命周期四个阶段:

  • 创建:响应式数据
  • 挂载:渲染模版
  • 更新:数据修改,跟新视图
  • 销毁:关闭浏览器

Vue生命周期函数(钩子函数)

Vue生命周期过程中,会自动运行一些函数,被称为生命周期钩子,让开发者可以在特定阶段运行自己的代码
在这里插入图片描述

// 1.创建阶段(准备数据)
beforeCreate(){
  console.log("beforeCreate 响应式数据准备好之前")
},
created(){
  console.log("create 响应式数据准备好之后")
},
// 2.挂载阶段(渲染模板)
beforeMount(){
  console.log("beforeMount 模板渲染之前")
},
mounted(){
  console.log("mounted 模板渲染之后")
},
// 3.更新阶段
beforeUpdate(){
  console.log("beforeUpdate 数据修改了,视图还没更新")
},
updated(){
  console.log("updated 数据修改了,视图已经更新" )
},
// 4.卸载阶段
beforeDestroy(){
  console.log("beforeDestroy")
},
destroyed(){
  console.log("destroyed")
}

工程化开发

开发Vue的两种方式:

  • 核心包传统开发模式:基于html/css/js文件,直接引入核心包,开发Vue
  • 工程化开发模式:基于构建工具(webpack)的环境中开发Vue
    在这里插入图片描述

脚手架 Vue cli

基本介绍:

  • Vue cli 是 Vue 官方提供的一个全局命令工具
  • 可以帮助我们快速创建一个开发 Vue 项目的标准化基础架子(集成了web怕吃苦配置)

好处:

  • 开箱既用,零配置
  • 内置babel等工具
  • 标准化

使用步骤:

  • 全局安装(一次): npm i @vue/cli -g
  • 查看 Vue 版本: vue --version
  • 创建项目架子: vue create 项目名称 (项目名不能用中文)
  • 启动项目: npm run serve (找package.json)

脚手架目录文件介绍

在这里插入图片描述

组件化开发

  • 组件化: 一个页面可以拆分成 一个个组件 ,每个组件有着自己独立的 结构样式行为
  • 好处:便于维护,利于复用 —— 提升开发效率
  • 组件分类:普通组件、根组件

根组件: 整个应用 最上层 的组件,包裹了所有普通小组件
- template 结构
- style 样式(可以支持less,需要安装less和less-loader)
- script 行为

普通组件的注册使用

组件注册的两种方式

  • 局部注册:只能在注册的组件内使用
    • 创建.vue文件
    • 在使用的组件内导入并注册

在这里插入图片描述

  • 全局注册:所有组件内部都能使用
    • 创建.vue文件
    • main.js 中进行全局注册

在这里插入图片描述


使用: 当成html标签使用 <组件名></组件名>
注意: 组件命名规范 —— 大驼峰命名法

scoped样式冲突

默认情况: 写在组件中的样式会 全局生效 ,因此很容易造成多个组件之间的样式冲突问题

  • 全局样式: 默认组件中的样式会作用到全局

  • 局部样式: 可以给组件加上 scoped 属性, 可以让样式只作用于当前组件

    <style scoped>
    
    </style>
    

组件通信

组件通信,就是指 组件与组件 之间的数据传递

  • 组件的数据是 独立 的,无法直接访问其他组件的数据
  • 想用其他组件的数据 —— 组件通信

组件关系分类:

  • 父子关系
  • 非父子关系
    在这里插入图片描述

组件通信解决方案:
在这里插入图片描述


父子通信流程图

  • 父组件通过 props 将数据传递给子组件
    在这里插入图片描述

  • 子组件利用 $emit 通知父组件修改更新
    在这里插入图片描述

在这里插入图片描述

prop

prop定义:组件上 注册的一些 自定义属性
prop作用:向子组件传递数据
特点:

  • 可以传递 任意数量 的prop
  • 可以传递 任意类型 的prop

props校验

作用: 为组件的prop指定 验证要求,不符合要求,控制台就会有 错误提示
语法:

  • 类型校验

    props:{
      校验的属性名:类型		// 类型有 Number String Boolean Array Object Function 
    }
    
  • 非空校验

  • 默认值

  • 自定义校验

    props:{
      校验的属性名:{
      	type: 类型,		// 类型有 Number String Boolean Array 
      	required: true,		// 是否必填
      	default: 默认值,	// 默认值
      	validator (value) {		// 形参可以接受到传过来的值
      		// 自定义校验逻辑
      		return 是否通过校验		// true 通过校验		false 不通过校验
      	}
      }
    }
    

非父子通信

作用: 非父子组件之间,进行简易消息传递
在这里插入图片描述


事件总线

  • 创建一个都能访问到的事件总线(空Vue实例)

    import Vue from 'vue'
    const Bus = new Vue()
    export default Bus
    
  • A 组件(接收方),监听 Bus 实例 的事件

    created(){
    	Bus.$on('sendMsg',(msg) =>{
    		this.msg = msg
    	})
    }
    
  • B组件(发送方),触发 Bus 实例 的事件

    Bus.$emit('sendMsg','这是一个消息')
    

provide & inject
provide & inject作用:跨层级 共享数据
在这里插入图片描述

  • 父组件provide提供数据

    export default {
      provide() {
        return {
          // 普通类型【非响应式】
          color: this.color,
          // 复杂类型【响应式】
          userInfo: this.userInfo
        }
      }
    }
    
  • 子/孙组件inject取值使用

    export default {
      inject: ['color','userInfo']
      created(){
      	console.log(this.color,this.userInfo)
      }
    }
    

表单类组件封装

表单类组件封装
在这里插入图片描述

  • 父传子:数据应该是在父组件 props 传递过来的,v-model 拆解 绑定数据
  • 子传父:监听输入,子传父传值给父组件修改

在这里插入图片描述


v-model简化代码

父组件v-model 简化代码,实现子组件与父组件数据 双向绑定

  • 子组件中:props通过 value 接收,事件触发 input
  • 父组件中: v-model 给组件直接绑定数据

.sync修饰符

作用:可以实现子组件父组件数据双向绑定
特点:prop属性名,可以自定义,非固定为value
本质:就是:属性名@update:属性名和写

<BaseDialog :visble.sync="isShow" />

在这里插入图片描述

ref和$refs

作用:利用ref和$refs可以用于获取dom元素,或组件实例
特点:查找范围 —— 当前组件内(更精确稳定)

  • 获取dom:

    • 目标标签 —— 添加 ref 属性
      <div ref="chartRef">我是渲染图表的容器</div>
      
    • 恰当时机,通过this.$refs.xxx,获取目标标签
      mounte(){
      	console.log(this.$refs.chartRef)
      }
      
  • 获取组件:

    • 目标组件 —— 添加ref属性
      <BaseForm ref="baseForm"></BaseForm>
      
    • 恰当时机,通过this.$refs.xxx,获取目标组件,就可以 调用组件对象里面的方法
      this.$refs.baseForm.组件方法()
      

Vue异步更新、$nextTick

$nextTick:等DOM更新后,才会触发执行此方法的函数体
语法: this.$nextTick(函数体)

this.$nextTick(() => {
	// 业务逻辑
})

自定义指令

自定义指令:自己定义的指令,可以封装一些DOM操作,扩展额外功能

  • 全局注册 —— 语法(main.js中编写语句)

    Vue.directive('指令名',{
    	// inserted 会在指令所在的元素,被插入到页面中时触发
    	inserted (el){
    		// 可以对 el 标签,扩展额外功能
    		el.focus()
    	}
    })
    
  • 局部注册 —— 语法

    directives:{
    	指令名:{
    		inserted(el){
    			// 可以对 el 标签,扩展额外功能
    			el.focus()
    		}
    	}
    }
    
  • 使用

    <input v-指令名 type="text">
    

插槽

作用:让组件内部的一些结构支持自定义

默认插槽

插槽基本语法:

  • 组件内需要定制的结构部分,改用<slot></slot>占位
  • 使用组件时,<组件名></组件名>标签内部,传入结构替换slot

在这里插入图片描述

插槽后备内容(默认值)

插槽后备内容:封装组件时,可以为预留的<slot>插槽提供后备内容(默认值)

  • 语法:在<slot>标签内,放置内容,作为默认值
    在这里插入图片描述
  • 效果:
    • 外部使用组件时,不传东西,则slot会显示默认值
      <组件名></组件名>
      
    • 外部使用组件时,传东西了,则slot整体会换掉
      <组件名>传的新内容</组件名>
      

具名插槽

具名插槽语法

  • 多个slot使用name属性区分名字
    在这里插入图片描述
  • template配合v-slot:名字来分发对应标签
    在这里插入图片描述
  • v-slot:插槽名可以简化成#插槽名

作用于插槽

基本使用步骤:

  • 给slot标签,以添加属性的方式传值

    <slot :id="item.id" msg="测试文本"></slot>
    
  • 所有添加的属性,都会被收集到一个对象中

    {id: 3, msg: '测试文本'}
    
  • 咋提template中,通过#插槽名="obj"接收,默认插槽名为default

    <MyTable :list="list">
    	<template #default="obj">
    		<button @click="del(obj.id)">删除</button>
    	</template>
    </MyTable>
    

路由

VueRouter的介绍

作用:修改地址栏路径时,切换显示匹配的组件
说明:Vue官方的一个路由插件,是一个第三方包
官网

VueRouter的使用(5+2)

五个基本步骤(main.js

  • 下载:下载VueRouter模板到当前工程

    npm install vue-router@3.6.5
    
  • 引入

    import VueRouter from 'vue-router'
    
  • 安装注册

    Vue.use(VueRouter)
    
  • 创建路由对象

    const router = new VueRouter()
    
  • 注入,将路由对象注入到new Vue实例中,建立关联

    new Vue({
    	render: h => h(App),
      	router
    }).$mount('#app')
    

2个核心步骤

  • 创建需要的组件(views目录),配置路由规则

    const router = new VueRouter({
        // routers 路由规则们
        // router 一条路由规则{path: 路径, component: 组件}
        routes: [
            {path: '/find', component: Find
    	        children:[{ //子路由
        			path: '路由路径',
       				component: 组件名
    			}]
            },
            {path: '/my',component: My},
            {path: '/friend',component: Friend}
        ]
    })
    
  • 配置导航:配置路由出口(路径匹配的组件显示的位置)

    <template>
      <div>
        <div class="footer_wrap">
          <a href="#/find">发现音乐</a>
          <a href="#/my">我的音乐</a>
          <a href="#/friend">朋友</a>
        </div>
        <div class="top">
          <!-- 路由出口 → 匹配的组件所展示的位置 -->
          <router-view></router-view>
        </div>
      </div>
    </template>
    

路由的封装抽离

问题:所有的路由配置都堆在main.js中不合适
目标:将路由模块抽离出来
好处:拆分模块利于维护

  • 创建router\index.js目录与文件
    在这里插入图片描述

  • 在index.js文件中编写路由规则

    import Find from "@/views/Find";
    import My from "@/views/My";
    import Friend from "@/views/Friend";
    
    import Vue from "vue";
    import VueRouter from "vue-router";
    Vue.use(VueRouter)      //VueRouter插件初始化
    
    const router = new VueRouter({
        // routers 路由规则们
        // router 一条路由规则{path: 路径, component: 组件}
        routes: [
            {path: '/find', component: Find},
            {path: '/my',component: My},
            {path: '/friend',component: Friend}
        ]
    })
    
    // 导出router
    export default router
    
  • 在main.js文件中引用
    在这里插入图片描述

使用router-link(声明式导航)

vue-router提供了一个全局组件router-link(取代了a标签)

  • 能跳转,配置to属性指定路径(必须),本质还是a标签,to无需#
  • 能高亮,默认就会提供高亮类名,可以直接设置高亮样式

在这里插入图片描述


router-link —— 两个类名

在这里插入图片描述

  • router-link-active 模糊匹配(常用)
    to=“/my” 可以匹配 /my /my/a /my/b ……
  • router-link-exact-active 精确匹配
    to=“/my” 仅可以匹配 /my

自定义匹配类名

说明:router-link的两个类名太长了,我们可以自定义匹配类名

const router = new VueRouter({
    // routers 路由规则们
    // router 一条路由规则{path: 路径, component: 组件}
    routes: [ …… ],
    林肯ActiveClass: '精确匹配的类名',
    linkExactActiveClass: '模糊匹配的类名'
})

声明式导航——跳转传参

目标:在跳转路由时,进行传值

  • 查询参数传参
    • 语法:to="/path?参数名=值(&参数名n=值n)
    • 对应页面组件接收传递过来的值:$router.query.参数名
  • 动态路由传参
    • 配置动态路由:path:"/path/:参数名
      /path/:参数名,必须要传参数,如果不传参数,也希望匹配,可以加个可选符 ?(/path/:参数名?)
      const router = new VueRouter({
        routes: [
          { path: '/search/:words', component: Search }
        ]
      })
      
    • 配置导航链接:to="/path/参数值
    • 对应页面组件接收传递过滤的值:$route.params.参数名

重定向

问题:网页打开,url默认是/路径,未匹配到组件时,会出现空白
重定向:匹配到path后,强制跳转path路径
语法 {path: 匹配路径,redirect: 重定向到的路径}

const router = new VueRouter({
    routes: [
        {path: '/',redirect: '/home'},
        {path: '/home', component: Home},
        {path: '/search', component: Search}
    ]
})

404页面设置

作用: 当路径找不到匹配时,给个提示页面
位置: 配在路由最后
语法:path:“*”(任意路径)—— 前面的路由不匹配就匹配最后一个

const router = new VueRouter({
    routes: [
        {path: '/',redirect: '/home'},
        {path: '/home', component: Home},
        {path: '/search', component: Search},
        {path: "*",component: NotFind}
    ]
})

路由模式

问题:路由的路径看起来不自然,有#,能否切换成真正的路径形式?

  • hash路由(默认):例如:http://localhost:8080/#/home
  • history路由(常用):例如:http:/localhost:8080/home
const router = new VueRouter({
    routes: [ …… ],
    mode: "history"
})

编程式导航

问题:点击按钮跳转如何实现?
编程式导航:用JS代码来进行跳转

基本跳转

  • path路径跳转(简易方便)

    this.$router.push('路由路径')
    
    this.$router.push({
    	path: '路由路径'
    })
    
  • name命名路由跳转(适合path路径长的场景)

    this.$router.push({
    	name: '路由名'
    })
    
    {name: '路由名',path: '/path',component: 组件名}
    

路由传参

两种传参方式:查询参数+动态路由传参
两种跳转方式,对于两种传参方式都支持

  • path路径跳转传参(query传参)

    this.$router.push('路由路径?参数名1=参数值1&参数名2=参数值2')
    
    this.$router.push({
    	path: '路由路径',
    	query:{
    		参数名1: '参数值1',
    		参数名2: '参数值2'
    	}
    })
    
  • path路径跳转传参(动态路由传参)

    this.$router.push('路由路径/参数值')
    
    this.$router.push({
    	path: '路由路径/参数值'
    })
    
  • name命名路由跳转传参(query传参)

    this.$router.push({
    	path: '路由名',
    	query:{
    		参数名1: '参数值1',
    		参数名2: '参数值2'
    	}
    })
    
  • name命令路由跳转传参(动态路由传参)

    this.$router.push({
    	path: '路由名',
    	params:{
    		参数名: '参数值'
    	}
    })
    

组件缓存(keep-alive)

  • 使用keep-alive包裹路由出口,可以使得切换组件时上一个组件不会被销毁

    <keep-alive>
    	<router-view></router-view>
    </keep-alive>
    
  • keep-alive的三个属性

    • include:组件名数组,只有匹配的组件会被缓存
    • exclude:组件名数组,任何匹配的组件都不会被缓存
    • max:最多可以缓存多少组件实例
    <keep-alive :include="['组件名1','组件名2']">
    <router-view></router-view>
    </keep-alive>
    
  • 两个钩子

    • activated:进入页面钩子
    • deactivated:离开页面钩子

ESlint代码规范

代码规范:一套写代码的约定规则
老话说:“没有规矩不成方圆” —— 正规的团队需要统一的编码风格
如果你的代码不符合standard的要求,ESlint会跳出来刀子嘴,豆腐心提示你

JavaScript Standard Style 规范说明

  • 字符串使用单引号:‘abc’
  • 无分号:const name = ‘xw’
  • 关键字后加空格:if (name = ‘ls’) { ···}
  • 函数名后加空格: function name (arg) { ··· }
  • 坚持使用全等 === 摒弃 ==
    ……

代码规范错误解决方案

  • 手动修正
    • 更具错误提示一项一项手动修改纠正
    • 如果不认识命令行中的语法报错是什么意思,根据错误代码去ESlint规则表中查找其具体含义
  • 自动修正
    • 快捷修复,右键点击唤出快捷菜单,点击“Fix ESLint Problems"。
      在这里插入图片描述

vuex

概述

vuex是一个vue的状态管理工具(状态就是数据)
大白话:vuex是一个插件,可以帮我们管理vue通用的数据(多组件共享的数据)


场景:

  • 某个状态在很多个组件来使用(个人信息)
  • 多个组件共同维护一份数据(购物车)

优势:

  • 共同维护一份数据,数据集中化管理
  • 响应式变化
  • 操作简洁(vuex提供了一些辅助函数)

创建一个空仓库

多组件共享的数据环境

目标:安装vuex插件,初始化一个空仓库
在这里插入图片描述

  • npm install vuex@3
    在这里插入图片描述

  • 新建 store/index.js专门存放vuex
    在这里插入图片描述

  • Vue.use(Vuex) 创建创库 new Vuex.Store()

    // 这里存放的就是 vuex的相关核心代码
    import Vue from 'vue'
    import Vuex from 'vuex'
    
    // 插件安装
    Vue.use(Vuex)
    
    // 创建仓库(空仓库)
    const store = new Vuex.Store()
    
    // 导出给main.js使用
    export default store
    
  • main.js中导入挂载到Vue实例上
    在这里插入图片描述

提供与访问vuex的数据

目标:明确如何给仓库提供数据,如何使用仓库数据

  • 提供数据: State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储。在State对象中可以添加我们要共享的数据

    // 创建仓库
    const store = new Vuex.Store({
      // state状态:即数据,类似于Vue组件中的data
      /*
          区别:
            1.data是自己的数据
            2.State是所有组件共享的数据
       */
      state: {
        count: 100
      }
    })
    
  • 使用数据:

    • 通过store直接访问
      获取 store:
      (1this.$store
      (2import 导入 store
      
      模板中:{{$store.state.xxx}}
      组件逻辑中:this.$store.state.xxx
      JS模块中:store.state.xxx
      
    • 通过辅助函数(简化): mapState是辅助函数,帮助我们把store中的数据自动映射到组件的计算属性中
      • import { mapState } from 'vuex'
      • mapState(['count'])
      • computed: { ...mapState(['count']) }

在这里插入图片描述

修改vuex的数据

目标:明确vuex同样是遵循单向数据流,组件中不能直接修改仓库的数据

通过strict: true 可以开启严格模式(项目上线移除掉,会吃性能)

// 创建仓库(空仓库)
const store = new Vuex.Store({
  // 开启严格模式
  strict: true,
  // state状态:即数据,类似于Vue组件中的data
  /*
      区别:
        1.data是自己的数据
        2.State是所有组件共享的数据
   */
  state: {
    title: '大标题',
    count: 100
  }
})

mutations

目标掌握mutations的操作流程,来修改state数据(state数据的修改只能通过mutations)

  • 定义mutations对象,对象中存放修改state的方法

    const store = new Vuex.Store({
      state: {
        title: '大标题',
        count: 100
      }// 定义mutations
      mutations: {
      	// 第一个参数是当前store的state属性
      	addCount (state){
      		state.count += 1
      	}
      }
    })
    
  • 组件中提交调用mutations

    this.$store.commit('addCount')
    

mutations传参语法

  • 提供 mutation 函数(带参数 —— 提交载荷 payload)

    mutations: {
    	...
    	addCount (state, n) {
    		state.count += n
    	}
    }
    
  • 页面中调用 mutation

    this.$store.commit('addCount', 10)
    

辅助函数(mapMutations):mapMutations 和 mapState 很像,它是把位于mutations中的方法提取出来,映射到组件methods

mutations: {
	subCount (state, n) {
		state.count -= n
	}
}
import { mapMutations } from 'vuex'

methods: {
	...mapMutations(['subCount'])
}
this.subCount(10) //JS中调用

actions

目标:明确 actions 的基本语法,处理异步操作
说明:mutations 必须是同步的(便于检测数据变化,记录调试)

  • 提供actions方法

    actions: {
    	setAsyncCount (context, num) {
    		// 一秒钟后,给一个数,去修改 num
    		setTimeout(() => {
    			context.commit('changeCount', num)
    		}, 1000)
    	}
    }
    
  • 页面中 dispatch 调用

    this.$store.dispatch('setAsyncCount', 200)
    

辅助函数mapActions

目标:掌握辅助函数 mapActions,映射方法
mapAction 是把位于actions中的方法提取出来,映射到组件methods

actions: {
	setAsyncCount (context, num) {
		// 一秒钟后,给一个数,去修改 num
		setTimeout(() => {
			context.commit('changeCount', num)
		}, 1000)
	}
}
import { mapActions } from 'vuex'

methods: {
	...mapActions(['setAsyncCoun'])
}
this.setAsyncCoun(666)		//调用

getters

目标:掌握核心概念 getters 的基本语法(类似于计算属性)
说明:除了state之外,有时我们还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters

例如:state 中定义了list,为1-10的数组,组件中,需要显示所有大于5的数据

state: {
	list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
  • 定义 getters

    getters: {
    	/*
    		注意:
    			(1) getters函数的第一个参数是 state
    			(2)getters函数必须要有返回值
    	*/
    	filterList (state) {
    		return state.list.filter(item => item > 5)
    	}
    }
    
  • 访问getters

    • 通过store访问getters

      {{ $store.getters.filterList }}
      
    • 通过辅助函数 mapGetterrs映射

      import { mapGetters } from 'vuex'
      
      computed: {
      	...mapGetters(['filterList'])
      }
      
      {{ filterList }} 		//使用
      

模块module(进阶语法)

由于vuex使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿(当项目变得越来越大的时候,vuex会变得越来越难以维护)
在这里插入图片描述

模块拆分:
在这里插入图片描述

user模块:store/modules/user.js
在这里插入图片描述


  • 创建文件
    在这里插入图片描述

  • 编写模块

    // user模块
    const state = {
      userInfo: {
        name: 'zhangsan',
        age: 18
      },
      score: 80
    }
    const mutations = {}
    const actions = {}
    const getters = {}
    
    // 导出模块
    export default {
      state,
      mutations,
      actions,
      getters
    }
    
  • 导入和挂载

    import user from '@/store/modules/user'
    const store = new Vuex.Store({
      modules: {
        user
      }
    })
    

访问模块中的state

尽管已经分模块了,但其实子模块的状态,还是会挂到根级别的state中,属性名就是模块名

使用模块中的数据:

  • 直接通过模块名访问 $store.state.模块名.xxx
  • 通过mapState映射
    • 默认根级别的映射 mapState(['xxx'])
    • 子模块的映射 mapState('模块名',['xxx']) 需要开启命名空间
      export default {
        namespaced: true,   // 开启命名空间
        state,
        mutations,
        actions,
        getters
      }
      

使用模块中getters中的数据

  • 直接通过模块名访问**$store.getters['模块名/xxx']**
  • 通过mapGetters映射
    • 默认根级别的映射 mapGetters(['xxx'])
    • 子模块的映射 mapGetters('模块名',['xxx'] 需要开启命名空间
      export default {
        namespaced: true,   // 开启命名空间
        state,
        mutations,
        actions,
        getters
      }
      

调用子模块中mutation

注意:默认模块中的 mutation 和 actions 会被挂载到全局,需要开启命名空间,才会挂载到子模块

  • 直接通过 store 调用 $store.commit('模块名/xxx',额外参数)
  • 通过 mapMutations 映射
    • 默认根级别的映射 mapMutations(['xxx'])
    • 子模块的映射 mapMutations('模块名',['xxx']) 需要开启命名空间
      export default {
        namespaced: true,   // 开启命名空间
        state,
        mutations,
        actions,
        getters
      }
      

调用子模块中action

注意:默认模块中的 mutation 和 actions 会被挂载到全局,需要开启命名空间,才会挂载到子模块

  • 直接通过 store 调用 $store.dispatch('模块名/xxx',额外参数)
  • 通过 mapMutations 映射
    • 默认根级别的映射 mapActions(['xxx'])
    • 子模块的映射 mapActions('模块名',['xxx']) 需要开启命名空间
      export default {
        namespaced: true,   // 开启命名空间
        state,
        mutations,
        actions,
        getters
      }
      

Vue3快速入门

Vue3的优势
在这里插入图片描述


Vue3 组合式API vs Vue2 选项式 API

  • 代码量变少了

  • 分散式维护转为集中式维护,更容易封装复用
    在这里插入图片描述
    需求:单击按钮,让数字+1

  • 选项式API

    <script>
    export default {
    	data () {
    		return {
    			count: 0
    		}
    	},
    	methods: {
    		addCount () {
    			this.count++
    		}
    	}
    }
    </script>
    
  • 组合式API

    <script setup>
    import [ ref } from 'vue'
    const count = ref(0)
    const addCount = () => count.value++
    </script>
    

创建Vue3项目

create-vue是Vue官方新的脚手架工具,底层切换到了 vite(下一代构建工具),为了开发提供极速响应

前提环境条件:已安装 16.0 或更高版本的 Node.js

创建一个Vue应用:npm init vue@latest(这一指令将会安装并执行create-vue)
在这里插入图片描述


项目目录
在这里插入图片描述


关键文件:

  • vite.config.js —— 项目的配置文件 基于vite的配置
  • package.json —— 项目包文件 核心依赖项变成了 Vue3.x 和 vite
  • main.js —— 入口文件 createApp函数创建应用实例
  • app.vue —— 根组件 SFC单文件组件 script - template - style
    • 变化一:脚本script和模板template顺序调整
    • 变化二:模板template不再要求唯一根元素
    • 变化三:脚步script添加setup标识支持组合式API
  • Index.html —— 单页入口 提供id为app的挂载点

组合式API —— setup选项

setup选项的写法和执行时机
在这里插入图片描述


setup选项中写代码的特点
在这里插入图片描述


<script setup> 语法糖
在这里插入图片描述

组合式API —— reactive 函数

作用:接受对象类型数据的参数传入并返回一个响应式的对象
核心步骤:

<script setup>
// 导入
import { reactive } from 'vue'

// 执行函数,传入参数,变量接收
const state = reactive(对象类型数据)

</script>
  • 从 vue 包中导入 reactive 函数
  • 在<script setup> 中执行 reactive 函数并传入类型为对象的初始值,并使用变量接收返回值

组合式API —— ref 函数

作用:接受简单类型或者对象类型的数据传入并返回一个响应式的对象
核心语法

<script setup>
// 导入
import { ref } from 'vue'

// 执行函数,传入参数,变量接收
const state = ref(简单类型或者复杂类型数据)

</script>
  • 从 vue 包中导入 ref 函数
  • 在 <script setup> 中执行 ref 函数并传入初始值,使用变量接收 ref 函数的返回值
  • ref参数类型支持更好但是必须通过 .value 访问修改

组合式API —— computed

计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法

核心步骤:

<script setup>
// 导入
import { computed } from 'vue'

// 执行函数 变量接收 在回调参数中return计算值
const computedState = computed( () => {
	return 基于响应式数据做计算之后的值
})
</script>
  • 导入computed函数
  • 执行函数在回调参数中return基于响应式数据计算的值,用变量接收

计算属性Demo

<script setup>
import {computed, ref} from "vue";

// 声明数据
const list = ref([1,2,3,4,5,6,7,8])

// 基于list派生一个计算属性,从list中过滤出 > 2
const computedList = computed(()=>{
  return list.value.filter((item) => item>2 )
})

// 定义一个修改数组的方法
const addFn = () => {
  list.value.push(307)
}
</script>

<template>
  <div>
    <div>原始数据:{{list}}</div>
    <div>计算后的数据:{{computedList}}</div>
    <button @click="addFn">修改</button>
  </div>
</template>

最佳实践

  • 计算书中不应该有“副作用”
    • 比如异步请求/修改DOM
  • 避免直接修改计算属性的值
    • 计算属性应该是只读的,特殊情况可以配置 get set

组合式API —— watch

作用:侦听一个或者多个数据的变化,数据变化时执行回调函数
两个额外参数:1. immediate(立即执行) 2.deep(深度侦听)


监听单个数据

  • 导入watch函数
  • 执行watch函数传入要侦听的响应式数据==(ref对象)==和回调函数
<script setup>
// 1.导入watch
import { ref, watch } from 'vue'
const count = ref(0)

// 2.调用watch 侦听变化
watch(count,(newValue, oldValue) => {
	console.log(`count发生了变化,老值为${oldValue},新值为${newVaule}`)
})
</script>

监听多个数据

说明:同时监听多个响应式数据的变化,不管哪个数据变化都需要执行回调函数

<script setup>
// 1.导入watch
import { ref, watch } from 'vue'
const count = ref(0)
const name = ref('cp')

// 2.调用watch 侦听变化
watch(
	[count, name],
	// watch([ref对象1,ref对象2], (newArr, oldArr) => { …… })
	(count,([newCount, newName], [oldCount, oldName]) => {
		console.log('count或者name发生了变化',[newCount, newName], [oldCount, oldName])
	}
)
</script>

immediate

说明:在侦听器创建时立即出发回调,响应式数据变化之后继续执行回调

<script setup>
// 1.导入watch
import { ref, watch } from 'vue'
const count = ref(0)

// 2.调用watch 侦听变化
watch(count,(newValue, oldValue) => {
	console.log(`count发生了变化,老值为${oldValue},新值为${newVaule}`)
	}, {
		immediate: true
	}
)
</script>

deep

说明:deep深度监视,默认watch进行的是,浅层监视
coust ref1 = ref(简单类型) 可以直接监视
coust ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化

<script setup>
const userInfo = ref({
  name: '张三',
  age: 18
})

watch(userInfo, (newValue, oldValue) => {
  console.log(newValue, oldValue)
},{
  deep: true,
  immediate: true
})
</script>

精确侦听对象的某个属性

需求:在不开启deep的前提下,侦听age的变化,只有age变化时才执行回调

const info = ref({
	name: 'xw',
	age: 21
})
watch(
	() => info.value.age,
	(newValue, oldValue) => console.log('age发生变化了')
)

组合式API —— 生命周期函数

Vue3 的生命周期API(选项式 VS 组合式)

选项式API组合式API
beforeCreate/createdsetup
beforeMountOnBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted

组合式API —— 父子通信

组合式API下的父传子

  • 父组件中给子组件绑定属性
  • 子组件内部通过props选项接收
    在这里插入图片描述

组合式API下的子传父

  • 父组件中给子组件标签通过@绑定事件
  • 子组件通过emit方法触发事件
    在这里插入图片描述

组合式API —— 模板引用

通过ref标识获取真实的dom对象或者组件实例对象

  • 调用ef函数生成一个ref对象
  • 通过ref标识绑定ref对象到标签
<script setup>
import { ref } from 'vue'
// 1.调用ref函数得到ref对象
const h1Ref = ref(null)
</script>

<template>
<!-- 2.通过ref标识绑定ref对象 -->
<h1 ref="h1Ref">我是dom标签h1</h1>
</template>

defineExpose()

默认情况下在<script setup>语法糖下组件内部的属性和方法是不开放给父组件访问的,可以通过defineExpose编译宏指定哪些属性和方法是允许访问

在这里插入图片描述

组合式API —— provide 和 inject

作用和场景:顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信

跨层传递普通数据

  • 顶层组件通过provide函数提供数据
  • 底层组件通过inject函数获取数据

在这里插入图片描述


跨层传递响应式数据

  • 在调用provide函数时,第二个参数设置为ref对象

在这里插入图片描述


跨层传递方法

  • 顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据
    在这里插入图片描述

新特性 —— defineOptions

<script setup> 之前,如果要定义 props,emits 可以轻而易举地添加一个与 setup
平级的属性。但是有了 <script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性

为了解决这一问题,引入了 definePropsdefineEmits 这两个宏。但这只解决了 propsemits 这两个属性。如果我们要定义组件的 name 或其他自定义的属性,还是得回到最原始的用法——再添加一个普通<script>标签。这样就会存在两个<script>标签。让人无法接受

所以在Vue 3.3 中引入了 defineOptions 宏。顾名思义,主要是用来定义 Options API 的选项。可以用defineOptions 定义任意的选项, props,emits,expose,slots厨卫(因为这些可以使用 defineXXX 来做到)

<script setup>
defineOptions({
	name: 'Foo',
	inheritAttrs: false
	// ... 更多自定义属性
})
</script>

新特性 —— defineModel

  • 在Vue3中,自定义组件上使用v-model,相当于传递一个modelValue属性,同时触发 update:modelValue 事件

    <Chile v-model="isVisible">
    // 相当于
    <Child :modelValue="isVisible" @update:modelValue="isVisible=$event">
    
    <script setup>
    const modelValue = defineModel()
    modelValue.value++
    </script>
    

Pinia

Pinia 是 Vue 的最新 状态管理工具,是 Vuex 的代替品

  • 提供更加简单的API(去掉了 mutation)
  • 提供符合,组合式风格的API(和Vue3新语法统一)
  • 去掉了 modules 的概念,每一个 store 都是要给独立的模板
  • 配合 TypeScript 更加友好,提供可靠的类型推断

手动添加Pinia到Vue项目

  • 使用 Vite 创建一个空的 Vue3 项目

    npm create vue@latest
    
  • 按照官方文档 安装 pinia 到项目中

    import {createApp} from 'vue'
    import {createPinia} from 'pinia'
    import App from './App.vue'
    
    const pinia = createPinia()     // 创建Pinia实例
    const app = createApp(App)      // 创建根实例
    app.use(pinia)                  // Pinia插件的安装配置
    app.mount('#app')   // 视图挂载
    

Pinia基础使用 —— 计数器案例

  • 创建一个创库 store/counter.js

  • 定义store

    import { defineStore } from "pinia"
    import {computed, ref} from "vue";
    
    // 定义store
    // defineStore('仓库的唯一标识', () => { ... })
    
    export const useCounterStore = defineStore('counter', () => {
        // 声明数据 state —— count
        const count = ref(0)
        // 声明操作数据的方法 actions
        const addCount = (add) => count.value+=add
        // 声明基于数据派生的计算属性 getters
        const double = computed(() => count.value*2)
        // 声明数据 state —— msg
        const msg = ref('hello pinia')
    
        return {
            count,
            addCount,
            double,
            msg
        }
    })
    
  • 组件使用store

    <script setup>
    import { useCounterStore } from '@/store/counter'
    const counterStore = useCounterStore()    // 声明实例
    </script>
    
    <template>
      <div>
        我是Son1.vue  - {{counterStore.count}} - {{counterStore.double}}
        <button @click="counterStore.addCount(5)">+</button>
      </div>
    
    </template>
    

action异步实现

编写方式:异步action函数的写法和组件中获取异步数据的写法完全一致

接口地址:http://geek.itheima.net/v1_0/channels

需求:再Pinia中获取频道列表数据并把数据渲染App组件模板中

  • 创建一个创库 store/channel.js

  • 定义 store

    import { defineStore } from 'pinia'
    import {ref} from "vue";
    import axios from "axios";
    
    export const useChannelStore = defineStore('channel', () => {
        // 声明数据
        const channelList = ref([])
    
        // 声明操作数据的方法
        const getList = async () => {
            const {data: { data }} = await axios.get('http://geek.itheima.net/v1_0/channels')
            channelList.value = data.channels
            console.log(data.channels)
        }
    
        // 声明getters相关
    
        return {
            channelList,
            getList
        }
    })
    
  • 组件使用store
    在这里插入图片描述

storeToRefs方法

请注意,store 是一个用 reactive 包装的对象,这意味着不需要在 getters 后面写 .value,就像 setup 中的 props 一样,如果你写了,我们也不能解构它
为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()。它将为每一个响应式属性创建引用。当你只使用 store 的状态而不调用任何 action 时,它会非常有用。请注意,你可以直接从 store 中解构 action,因为它们也被绑定到 store 上:

在这里插入图片描述
注意:方法不需要使用storeToRefs方法解构,直接解构即可

Pinia持久化插件

官方文档

  • 安装插件 pinia-plugin-persistedstate

    npm i pinia-plugin-persistedstate
    
  • 在main.js中导入使用

    import {createApp} from 'vue'
    import App from './App.vue'
    import {createPinia} from 'pinia'
    // 导入持久化插件
    import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
    
    const pinia = createPinia()     // 创建Pinia实例
    const app = createApp(App)      // 创建根实例
    app.use(pinia.use(piniaPluginPersistedstate))                  // Pinia插件的安装配置
    app.mount('#app')   // 视图挂载
    
  • store仓库中,persist: true开启
    在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小吴在敲Bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值