当前位置: 首页 > news >正文

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开启
    在这里插入图片描述

相关文章:

Vue2+Vue3

文章目录 Vue快速上手Vue是什么第一个Vue程序插值表达式Vue核心特性&#xff1a;响应式 Vue指令v-htmlv-show 与 v-ifv-else 与 v-else-ifv-onv-bindv-forv-model指令修饰符 计算属性watch侦听器&#xff08;监视器&#xff09;watch——简写watch——完整写法 Vue生命周期 和 …...

华为云云耀云服务器L实例评测|redis漏洞回顾 MySQL数据安全解决 搭建主从集群MySQL 相关设置

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;期间遇到过MySQL数据库被攻击的情况&#xff0c;数据丢失&#xff0c;还好我有几份备份&#xff0c;没有造成太大的损失&#xff1b;后来有发现Redis数据库被攻击的情况&#xff0c;加入了redis密…...

【C++】详解std::thread

2023年9月10日&#xff0c;周日下午开始 2023年9月10日&#xff0c;周日晚上23:35完成 虽然这篇博客我今天花了很多时间去写&#xff0c;但是我对std::thread有了一个完整的认识 不过有些内容还没完善&#xff0c;以后有空再更新.... 目录 头文件类的成员类型方法(construc…...

Apache HTTPD 漏洞复现

文章目录 Apache HTTPD 漏洞复现1. Apache HTTPD 多后缀解析漏洞1.1 漏洞描述1.2 漏洞复现1.3 漏洞利用1.4 获取GetShell1.5 漏洞防御 2. Apache HTTPD 换行解析漏洞-CVE-2017-157152.1 漏洞描述2.2 漏洞复现2.3 漏洞利用2.4 修复建议 3. Apache HTTP Server_2.4.49 路径遍历和…...

【C++从入门到精通】第2篇:C++基础知识(中)

文章目录 2.1 iostream介绍&#xff1a;cout、cin和endl2.1.1 输入/输出库2.1.2 std::cout2.1.3 std::endl2.1.4 std::cout是缓冲的2.1.5 std::endl与\n2.1.6 std::cin2.1.7 总结2.1.8 练习时间 2.2 未初始化的变量和未定义的行为2.2.1 未初始化的变量2.2.2 未定义行为2.2.3 明…...

【RuoYi移动端】uni-app中实现生成二维码功能(代码示例)

完整示例&#xff1a; <template><view><view class"titleBar">执法检查“通行码”信息</view><view class"twoCode"><canvas canvas-id"qrcode"></canvas></view></view> </templat…...

深度解剖数据在栈中的应用

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大一&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 望小伙伴们点赞&#x1f44d;收藏✨加关注哟&#x1f495;&#x1…...

Android10 SystemUI系列 需求定制(一)状态栏控制中心默认tile定制属性适配

一、前言 SystemUI 所包含的界面和模块比较多,这一节主要分享一下控制中心默认tile 列表的实现,通过配置可以实现 下拉状态栏,控制中心默认的tile显示 二、准备工作 按照惯例先找一下控制中心的代码,主要在下面这个路径下 frameworks/base/packages/SystemUI/src/com/andr…...

【微信小程序】文章设置

设置基本字体样式&#xff1a;行高、首行缩进 font-size: 32rpx;line-height: 1.6em;text-indent: 2em;padding: 20rpx 0;border-bottom: 1px dashed var(--themColor); 两端对齐 text-align: justify; css文字两行或者几行显示省略号 css文字两行或者几行显示省略号_css…...

程序员在线周刊(冒泡算法篇)

大家好&#xff0c;欢迎来到程序员在线周刊&#xff01;本期我们将深入探讨一种经典的排序算法——冒泡算法&#xff0c;并附上具体的代码实现。 目录 简介代码原理广告广告1广告2广告3 简介 冒泡算法是一种简单但效率较低的排序算法&#xff0c;它的原理非常直观&#xff1a…...

string

目录 六、STL简介 (一)什么是STL (二)STL的版本 (三)STL六大组件 七、string (一)标准库中的string 1、string类 2、string常用的接口 1)string类对象的常见构造 2)string类对象的容量操作 3)string类对象的访问及遍历操作 4)string类对象的修改操作 5)string类非成…...

html的日期选择插件

1.效果 2.文档 https://layui.gitee.io/v2/docs/ 3.引入 官网地址&#xff1a; https://layui.gitee.io/v2/ 引入&#xff08;在官网下载&#xff0c;&#xff09;jquery-1.7.2.min.js,layui/layui.js **<link href"js/layui/css/layui.css" rel"stylesh…...

OPPO哲库事件 “ 始末 ” ! 集体打哑谜?

1►OPPO哲库解散 2019 年&#xff0c;美国商务部以“科技网络安全”为由&#xff0c;将华为公司及其70家附属公司列入出口管制“实体名单”。与此同时&#xff0c;OPPO 创始人兼 CEO陈明永对外宣布&#xff0c;公司将为未来三年内投入 500 亿元用于前沿技术和深水区技术的探索…...

数据聚类分析

K均值 1.1 数据来源(随机生成) import matplotlib.pyplot as plt from sklearn.datasets import make_blobsX, y make_blobs(n_samples150,n_features2,centers3,cluster_std0.5,shuffleTrue,random_state0) # plt.scatter(X[:, 0], X[:, 1], cwhite, markero, edgecolorsbl…...

前 40 个 Microsoft Excel 面试问题答案

1&#xff09;什么是 Microsoft Excel&#xff1f; Microsoft Excel 是一个电子电子表格应用程序&#xff0c;使用户可以使用按行和列细分的电子表格系统&#xff0c;使用公式存储&#xff0c;组织&#xff0c;计算和处理数据。 它还提供了使用外部数据库进行分析&#xff0c;…...

ros2学习笔记:shell环境变量脚本setup.bash[-z][-n][-f]参数作用

-n作用 [ -n 字符串 ] or [ 字符串 ] 字符串的长度为非零&#xff08;有内容&#xff09;则为真。加-n与不加-n结果相同。 -z作用 [ -z 字符串 ] 字符串的长度为零则为真。 字符串为空即NULL时为真&#xff0c;与上面的-n相反。 -f作用 [ -f FILE ] 如果 FILE 存在且是一…...

xss渗透(跨站脚本攻击)

一、什么是XSS? XSS全称是Cross Site Scripting即跨站脚本&#xff0c;当目标网站目标用户浏览器渲染HTML文档的过程中&#xff0c;出现了不被预期的脚本指令并执行时&#xff0c;XSS就发生了。 这里我们主要注意四点&#xff1a; 1、目标网站目标用户&#xff1b; 2、浏览…...

9参数化重采样时频变换,基于MATLAB平台,程序已调通,可直接替换数据进行分析。

参数化重采样时频变换&#xff0c;基于MATLAB平台&#xff0c;程序已调通&#xff0c;可直接替换数据进行分析。 9matlab参数化重采样时频变换 (xiaohongshu.com)...

RK3568平台开发系列讲解(调试篇)系统运行相关频率设置

🚀返回专栏总目录 文章目录 一、CPU 频率设置二、DDR 频率设置三、NPU 频率设置沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 CPU 默认是 interactive 状态,它会根据 CPU 使用率和目标负载来动态地调整 CPU 频率。为获得更高运行速度或者性能评估,我们需要手动固…...

嵌入式:驱动开发 Day2

作业&#xff1a;字符设备驱动&#xff0c;完成三盏LED灯的控制 驱动代码&#xff1a; mychrdev.c #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/io.h> #include &q…...

RK3399平台开发系列讲解(入门篇)VIM的基础命令

🚀返回专栏总目录 文章目录 一、Vim 命令速查二、其他命令三、Vim模式沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 本篇将介绍Vim相关命令。 一、Vim 命令速查 简单说明一下,这张图上展示了一个键盘。图中的“•”表示,单个字母不是完整的命令,必须再有进一步…...

Rocky Linux 安装图解(替代centos)服务器+桌面

centos自从20年底转变为不稳定版本后&#xff0c;有很多替代方案 经过近3年的发展&#xff0c;rocky linux算是一个比较好的选择&#xff0c;一是依照red hat企业版来做&#xff0c;二是rocky的发起者也是centos的创始人 如果想安装debian&#xff0c;可以参考&#xff1a;deb…...

webpack 基础配置

常见配置 文件打包的出口和入口webpack如何开启一台服务webpack 如何打包图片&#xff0c;静态资源等。webpack 配置 loader配置 plugin配置sourceMap配置 babel 语法降级等 接下来 &#xff0c; 我们先从webpack的基本配置 开始吧&#xff01; 在准备 配置之前 , 搭建一个 …...

C语言和mfc按格式读取文件数据

fscanf()函数的功能是从文件中按格式读取一个或多个数据&#xff1b; 例如文件中有一行数据&#xff0c; 22 3.34 hello 则使用 fscanf(fp, "%d%f%s", &a, &f, str) 可一次读取整型、浮点、字符串三个数据&#xff1b; 此函数位于C标准库头文件<stdio…...

SQLyog 各版本下载与安装(目前最新版本为13.2.0)

文章目录 一、SQLyog Ultimate 各版本下载1. For Windows x642. For Windows x86 二、SQLyog Community 各版本下载1. For Windows x642. For Windows x863. For Linux x86_644. For Linux i386 三 、SQLyog 安装四、如何解决SQLyog试用期到期问题五、最后 数据库可视化工具&am…...

CopyOnWrite 容器

CopyOnWrite容器是Java并发包中提供的一种特殊类型的集合,它的特点是在进行修改操作时不会修改原始容器,而是创建一个新的容器副本进行修改,这样可以避免并发修改异常(ConcurrentModificationException)。 主要的CopyOnWrite容器包括: CopyOnWriteArrayList:这是一个基…...

云服务部署:AWS、Azure和GCP比较

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

Linux安装Ansible管理工具

条件情况说明 准备4台机器&#xff0c;是单master集群安装 192.168.186.128 ansible 192.168.186.129 node1 192.168.186.130 node2 192.168.186.131 node3 #永久修改主机名 hostnamectl set-hostname ansible && bash #在ansible上操作 hostnamectl set-hostname n…...

七天学会C语言-第二天(数据结构)

1. If 语句&#xff1a; If 语句是一种条件语句&#xff0c;用于根据条件的真假执行不同的代码块。它的基本形式如下&#xff1a; if (条件) {// 条件为真时执行的代码 } else {// 条件为假时执行的代码 }写一个基础的If语句 #include<stdio.h> int main(){int x 10;…...

高级功能的PID控制器在电离规等真空计线性化处理中的应用

摘要&#xff1a;针对高真空度用皮拉尼计和电离规信号的非线性和线性两种输出规格&#xff0c;为改进高真空度的测量和控制精度&#xff0c;本文提出了线性化处理的解决方案。解决方案的关键是采用多功能超高精度的真空压力控制器&#xff0c;具体内容一是采用控制器自带的最小…...

哈尔滨大型网站建设电话/站长工具关键词

Buffer Pools 1 Locks vs. Latches 在讨论 DBMS 如何保护其内部元素时&#xff0c;我们需要区分 lock 和 latches。 锁 lock 保护数据库逻辑内容&#xff08;例如&#xff0c;元组、表、数据库&#xff09;免受其他事务的影响。在交易期间持有。需要能够回滚更改。 锁存器…...

用asp做网站遇到的问题/线上销售渠道有哪些

查看配置文件&#xff1a;https://raw.githubusercontent.com/antirez/redis/5.0.3/redis.conf 主要参数说明 redis.conf 配置项说明如下&#xff1a; 序号配置项说明1 daemonize no Redis 默认不是以守护进程的方式运行&#xff0c;可以通过该配置项修改&#xff0c;使用 y…...

做网站销售的话术/seo首页网站

#!/bin/bash cat /root/.ssh/id_rsa.pub >> /root.ssh/authorized_keys 将公钥导到auth文件中去 host1cat /root/a ##a文件里的内容是你想要scp公钥的目标主机地址 for i in $host1; do command1"scp /root/.ssh/authorized_keys $i:/root/.ssh/authorized…...

关于茶网站模板/在线crm

第一次备份&#xff0c;没什么经验&#xff0c;搜了一下&#xff0c;发现很简单&#xff0c;但是大多都是win7上面的demo&#xff0c;我这里用的Windows Server 2008&#xff0c;备份之后发现.dump文件找不到&#xff0c;搜了一下才发现&#xff0c;生成备份的文件&#xff0c;…...

bae wordpress 3.8/新浪舆情通

版本&#xff1a;myeclipse6.5 设置项目默认编码1.myEclipse默认的新项目的编码是GBK变为UTF-8:Window->Preferences->General->Workspace->Text file encoding 将其改为UFT-8即可.js和jsp统一UTF-8:优化myEclipse启动速度 1.禁用myeclipse updating indexes ,用…...

网站建设公司比较/seo系统培训班

第五章 泛型 23、 请不要在新代码中使用原生态类型 声明中具有一个或者多个类型参数的类或者接口&#xff0c;就是泛型类或者泛型接口。泛型类和接口统称为泛型。 每种泛型可以定义一种参数化的类型&#xff0c;格式为&#xff1a;先是类或者接口的名称&#xff0c;接…...