Vue2+Vue3

文章目录

  • Vue快速上手
    • Vue是什么
    • 第一个Vue程序
    • 插值表达式
    • Vue核心特性:响应式
  • Vue指令
    • v-html
    • v-show 与 v-if
    • v-else 与 v-else-if
    • v-on
    • v-bind
    • v-for
    • v-model
    • 指令修饰符
  • 计算属性
  • watch侦听器(监视器)
    • watch——简写
    • watch——完整写法
  • Vue生命周期 和 生命周期的四个阶段
    • Vue生命周期函数(钩子函数)
  • 工程化开发
    • 脚手架 Vue cli
    • 脚手架目录文件介绍
  • 组件化开发
    • 普通组件的注册使用
    • scoped样式冲突
  • 组件通信
    • 父子通信流程图
      • prop
    • 非父子通信
    • 表单类组件封装
    • .sync修饰符
    • ref和$refs
    • Vue异步更新、$nextTick
  • 自定义指令
  • 插槽
    • 默认插槽
    • 插槽后备内容(默认值)
    • 具名插槽
    • 作用于插槽
  • 路由
    • VueRouter的介绍
    • VueRouter的使用(5+2)
    • 路由的封装抽离
    • 使用router-link(声明式导航)
    • 声明式导航——跳转传参
    • 重定向
    • 编程式导航
    • 组件缓存(keep-alive)
  • ESlint代码规范
  • vuex
    • 概述
    • 创建一个空仓库
    • 提供与访问vuex的数据
    • 修改vuex的数据
    • actions
    • getters
    • 模块module(进阶语法)
  • Vue3快速入门
    • 创建Vue3项目
    • 组合式API —— setup选项
    • 组合式API —— reactive 函数
    • 组合式API —— ref 函数
    • 组合式API —— computed
    • 组合式API —— watch
    • 组合式API —— 生命周期函数
    • 组合式API —— 父子通信
    • 组合式API —— 模板引用
    • 组合式API —— provide 和 inject
    • 新特性 —— defineOptions
    • 新特性 —— defineModel
  • Pinia
    • 手动添加Pinia到Vue项目
    • Pinia基础使用 —— 计数器案例
    • action异步实现
    • storeToRefs方法
    • Pinia持久化插件

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: Findchildren:[{ //子路由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}// 定义mutationsmutations: {// 第一个参数是当前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) {// 一秒钟后,给一个数,去修改 numsetTimeout(() => {context.commit('changeCount', num)}, 1000)}
    }
    
  • 页面中 dispatch 调用

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

辅助函数mapActions

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

actions: {setAsyncCount (context, num) {// 一秒钟后,给一个数,去修改 numsetTimeout(() => {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 —— countconst count = ref(0)// 声明操作数据的方法 actionsconst addCount = (add) => count.value+=add// 声明基于数据派生的计算属性 gettersconst double = computed(() => count.value*2)// 声明数据 state —— msgconst 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.channelsconsole.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开启
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/135139.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网页的快捷方式打开自动全屏--Chrome、Firefox 浏览器相关设置

Firefox 的全屏方式与 Chrome 不同&#xff0c;Chrome 自带全屏模式以及APP模式&#xff0c;通过简单的参数即可设置&#xff0c;而Firefox暂时么有这个功能&#xff0c;Firefox 的全屏功能可以通过全屏插件实现。 全屏模式下&#xff0c;按 F11 不会退出全屏&#xff0c;鼠标…

GDB之(任意门)跳到任意位置(十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Pytorch-CNN-Mnist

文章目录 model.pymain.py网络设置注意事项及改进运行截图 model.py import torch.nn as nn class CNN_cls(nn.Module):def __init__(self,in_dim28*28):super(CNN_cls,self).__init__()self.conv1 nn.Conv2d(1,32,1,1)self.pool1 nn.MaxPool2d(2,2)self.conv2 nn.Conv2d(3…

Web 第一步:HTTP 协议(基础)

这里是JavaWeb的开头部分&#xff01;那么先解释一下吧&#xff1a; Web&#xff1a;全球广域网&#xff0c;也称为万维网&#xff08;www&#xff09;&#xff0c;能够通过浏览器访问的网站。 JavaWeb&#xff1a;是用Java技术来解决相关 Web 互联网领域的技术栈。 &#xf…

vite和webpack的区别

vite和webpack的区别 1、前言2、Webpack2.1 Webpack简述2.2 Webpack常用插件 3、Vite3.1 Vite简述3.2 Vite插件推荐 4、区别4.1 开发模式不同4.2 打包效率不同4.3 插件生态不同4.4 配置复杂度不同4.5 热更新机制不同 5、总结 1、前言 Webpack和Vite是现代前端开发中非常重要的…

深度解析shell脚本的命令的原理之ls

ls是一个常用的Unix和Linux命令&#xff0c;它的功能是列出目录内容。当运行ls命令时&#xff0c;操作系统会执行一系列步骤&#xff0c;以获取和显示指定目录中的文件和子目录。以下是对这个命令的深度解析&#xff1a; 解析参数和选项&#xff1a;首先&#xff0c;ls命令会解…

Linux:centos9的本地yum仓库配置

其实9和7的配置方法是差不多一样的&#xff0c;只不过你使用7的本地yum仓库里面直接挂载就可以直接把仓库位置指向挂载点 具体可以看我往期文章&#xff0c;但是先看完我下面的描述再去看我链接的文章才能看懂如何配置centos9的yum仓库 Linux&#xff1a;YUM仓库服务_鲍海超-…

Android Media3 ExoPlayer 开启缓存功能

ExoPlayer 开启播放缓存功能&#xff0c;在下次加载已经播放过的网络资源的时候&#xff0c;可以直接从本地缓存加载&#xff0c;实现为用户节省流量和提升加载效率的作用。 方法一&#xff1a;采用 ExoPlayer 缓存策略 第 1 步&#xff1a;实现 Exoplayer 参考 Exoplayer 官…

利用Python将dataframe格式的所有列的数据类型转换为分类数据类型

一、样例理解 import pandas as pd import numpy as np# 创建测试数据 feature_names [col1 , col2, col3, col4, col5, col6] values np.random.randint(20, size(10,6))dataset pd.DataFrame(data values, columns feature_names)print("转换前的数据为\n",d…

NetSuite知识会汇编-管理员篇顾问篇2023

本月初&#xff0c;开学之际&#xff0c;我们发布了《NetSuite知识会汇编-用户篇 2023》&#xff0c;这次发布《NetSuite知识会汇编-管理员篇&顾问篇2023》。本篇挑选了近两年NetSuite知识会中的一些文章&#xff0c;涉及开发、权限、系统管理等较深的内容&#xff0c;共19…

Python 基于PyCharm断点调试

视频版教程 Python3零基础7天入门实战视频教程 PyCharm Debug&#xff08;断点调试&#xff09;可以帮助开发者在代码运行时进行实时的调试和错误排查&#xff0c;提高代码开发效率和代码质量。 准备一段代码 def add(num1, num2):return num1 num2if __name__ __main__:f…

使用stelnet进行安全的远程管理

1. telnet有哪些不足&#xff1f; 2.ssh如何保证数据传输安全&#xff1f; 需求&#xff1a;远程telnet管理设备 用户定义需要在AAA模式下&#xff1a; 开启远程登录的服务&#xff1a;定义vty接口 然后从R2登录&#xff1a;是可以登录的 同理R3登录&#xff1a; 在R1也可以查…

全国职业技能大赛云计算--高职组赛题卷①(容器云)

全国职业技能大赛云计算--高职组赛题卷①&#xff08;容器云&#xff09; 第二场次题目&#xff1a;容器云平台部署与运维任务1 Docker CE及私有仓库安装任务&#xff08;5分&#xff09;任务2 基于容器的web应用系统部署任务&#xff08;15分&#xff09;任务3 基于容器的持续…

驱动开发,IO模型,信号驱动IO实现过程

1.信号驱动IO框架图 分析&#xff1a; 信号驱动IO是一种异步IO方式。linux预留了一个信号SIGIO用于进行信号驱动IO。进程主程序注册一个SIGIO信号的信号处理函数&#xff0c;当硬件数据准备就绪后会发起一个硬件中断&#xff0c;在中断的处理函数中向当前进程发送一个SIGIO信号…

opencv形状目标检测

1.圆形检测 OpenCV图像处理中“找圆技术”的使用-图像处理-双翌视觉OpenCV图像处理中“找圆技术”的使用,图像处理,双翌视觉https://www.shuangyi-tech.com/news_224.htmlopencv 找圆心得&#xff0c;模板匹配比霍夫圆心好用 - 知乎1 相比较霍夫找直线算法&#xff0c; 霍夫找…

TCP详解之流量控制

TCP详解之流量控制 发送方不能无脑的发数据给接收方&#xff0c;要考虑接收方处理能力。 如果一直无脑的发数据给对方&#xff0c;但对方处理不过来&#xff0c;那么就会导致触发重发机制&#xff0c;从而导致网络流量的无端的浪费。 为了解决这种现象发生&#xff0c;TCP 提…

大语言模型之十-Byte Pair Encoding

Tokenizer 诸如GPT-3/4以及LlaMA/LlaMA2大语言模型都采用了token的作为模型的输入输出&#xff0c;其输入是文本&#xff0c;然后将文本转为token&#xff08;正整数&#xff09;&#xff0c;然后从一串token&#xff08;对应于文本&#xff09;预测下一个token。 进入OpenAI官…

MFC 如何启用/禁用菜单(返灰/不可点击状态)

1、为页面&#xff08;窗口&#xff09;添加一个菜单栏和子菜单 2、在XXDlg.h文件中定义一个菜单栏变量和bool变量 CMenu m_Menu; //菜单变量 bool m_EnableMenu;//菜单栏中某个子菜单禁用/启用&#xff08;变灰&#xff09;的控制变量3、在OnInitDialog函数中进行初始化&…

常见的数码管中的引脚分布情况

简单介绍 数码管&#xff0c;实际就是用了7段亮的线段表示常见的数字或字符。常见的像下面几种&#xff08;图片是网络中的截图&#xff09;。事件中使用到的知识还是单片机中最基础的矩阵扫描。记得其中重要的有“余晖效应”&#xff0c;好像是要把不用的亮段关闭&#xff0c…

SpringBoot整合Flowable

1. 配置 &#xff08;1&#xff09; 引入maven依赖 <dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.7.2</version></dependency><!-- MySQL连接 -->&l…