创建项目
node版本建议16
目录
setup语法糖
reactive
ref
computed
watch
deep
注意:建议不开启deep ,避免性能消耗
总结
生命周期函数
props 父传子
父组件
<script setup>
import HelloWorld from './components/HelloWorld.vue'
import {ref,reactive} from 'vue'
let name=ref("小飞")
let obj =reactive({
sex:'女',
age:20
})
</script>
<template>
<HelloWorld :msg="name" :o="obj"/>
</template>
子组件
<script setup>
// 对象写法
// let props= defineProps({
// msg: {
// type: String,
// required: true,
// default:'默认值',
// validator(){
// return ...
// },
// 自定义类型校验函数
// o:{
// type:Object,
// }
// })
//对象可以简写成
defineProps({
msg:String,
o:Object
})
// 数组写法
let props=defineProps(['msg','o'])
console.log(props);
</script>
<template>
<h1 class="green">{{ msg }}</h1>
<h1 class="green">{{ o.sex }}</h1>
<h1 class="green">{{ o.age}}</h1>
</template>
子组件用defineProps接收,接收到的是基本类型,模版上可以直接写,如果是对象,加点,
注意,接收到的数据一般是改不了,也不建议改,
- Boolean 类型的未传递 prop 将被转换为 false。这可以通过为它设置 default 来更改——例如:设置为 default: undefined 将与非布尔类型的 prop 的行为保持一致。
传参可以使用陀峰式也可以-的形式,例如:max-length , maxLength
script要拿这个数据,props.属性
子传父
ref
defineExpose()
v-show v-if
<script setup>
import { ref } from "vue"
const isShow = ref(true)
</script>
<template>
<h1>App组件</h1>
<button @click="isShow = !isShow">切换</button>
v-show 可以根据值来决定元素是否显示(通过display来切换元素的显示状态)
v-if 可以根据表达式的值来决定是否显示元素(会直接将元素删除)
- v-show通过css来切换组件的显示与否,切换时不会涉及到组件的重新渲染
切换的性能比较高。
但是初始化时,需要对所有组件进行初始化(即使组件暂时不显示)
所以它的初始化的性能要差一些!
- v-if通过删除添加元素的方式来切换元素的显示,切换时反复的渲染组件,
切换的性能比较差。
v-if只会初始化需要用到的组件,所以它的初始化性能比较好
v-if可以和 v-else-if 和 v-else结合使用
v-if可以配合template使用 --template在页面中不显示,
-->
<!-- <div v-if="isShow">
<h2>我是if中的内容</h2>
</div>
<div v-else>
<h2>我是else中的内容!</h2>
</div> -->
<template v-if="isShow">
<h2>我是一个h2</h2>
<h3>我是h3</h3>
</template>
</template>
动态组件
<template>
<component is="div">我是DIV标签</component>
以上代码会解析为div标签
<!--
component 是一个动态组件
- component最终以什么标签呈现由is属性决定
-->
</template>
<script setup>
import { ref } from "vue"
import A from "./components/A.vue"
import B from "./components/B.vue"
const isShow = ref(true)
</script>
<template>
<button @click="isShow = !isShow">切换</button>
<component :is="isShow ? A : B"></component>
</template>
is后面的属性可以是组件名,
v-bind()
<script setup>
const color="rgb(247,169,1)"
</script>
<template>
<h1>我是H1标签</h1>
</template>
<style scoped>
h1{
background-color:v-bind(color);
}
</style>
可以在srcipt定义一个变量, style用v-bind()来绑定, 这样可以动态的改变样式
<script setup>
import { ref } from "vue"
const imgPath = ref("/images/messi.png")
const changeImg = () => {
imgPath.value = "/images/neymar.png"
}
const attrs = {
id: "box1",
class: "hello"
}
const attrName = "title"
const attrValue = "这是一个title属性"
const isDisabled = ""
</script>
<template>
<
当我们需要为标签动态的设置属性时,需要使用v-bind指令,
v-bind可以简写为 :
当我们为一个布尔值设置属性时,
如果值为true,则元素上有该属性(转换后为true,也算true)
如果值为false,则元素没有该属性(转换后为false,也算false)
特殊情况:"" 空串,在这里会被当成真值
-->
<!-- <button @click="changeImg">切换图片</button> -->
<img :[attrName]="attrValue" :src="imgPath" alt="梅西" />
<div :="attrs"></div>
<input type="text" :disabled="isDisabled" />
</template>
attrs的标签,id和hello属性都有
模版的基本语法
<script setup>
const msg = "我爱vue"
const html = `<h2>我是一段html代码</h2>`
</script>
<template>
<!--
- 在模板中,可以直接访问到组件中声明的变量
- 除了组件中的变量外,vue也为我们提供了一些全局对象可以访问:
比如:Date、Math、RegExp ...
除此之外,也可以通过app对象来向vue中添加一些全局变量
app.config.globalProperties
- 使用插值(双大括号),只能使用表达式
表达式,就是有返回值的语句
- 插值实际上就是在修改元素的textContent,
如果内容中含有标签,标签会被转义显示,不会作为标签生效
指令:
- 指令模板中为标签设置的一些特殊属性,它可以用来设置标签如何显示内容
- 指令使用v-开头
v-text 将表达式的值作为元素的textContent插入,作用同{{}}
使用指令时,不需要通过{{}}来指定表达式
v-html 将表达式的值作为元素的innerHTML插入,有xss共计的风险
-->
<h1>{{ "hello" + "world" }}</h1>
<div>{{ html }}</div>
<div v-text="html"></div>
<div v-html="html"></div>
在main.js文件可以设置全局的属性
添加方法时,需要用bind改变this指向,因为这里是点alert 不是指向全局
全局定义了,可以直接使用
<h1>{{ hello }}</h1>
<div>{{ alert(123)}}</div>
动态样式
<script setup>
const arr = ["box1", "box2", "box3"]
const arr2 = [{ box1: true }, { box2: false }, { box3: true }]
const style = {
color: "red",
backgroundColor: "#bfa"
}
</script>
<template>
<div :class="arr">我是第一个</div>
<div :class="arr2">我是第二个</div>
<div :style='style'>我是第三个</div>
</template>
<style scoped>
.box1{
color:aqua
}
.box2{
background-color:aqua;
}
.box3{
border: 1px solid red;
}
</style>
class 数组是全部使用,对象形式,里面的布尔值决定使用不使用这个样式,
style样式,在script 里直接定义样式,
v-for 与v-if
同时使用 v-if 和 v-for 是不推荐的**,因为这样二者的优先级不明显。
当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名:
<!--
这会抛出一个错误,因为属性 todo 此时
没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
在外新包装一层 <template> 再在其上使用 v-for 可以解决这个问题 (这也更加明显易读):
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
scoped
注意,随机生成的属性,子组件的根元素,会受到父组件的样式影响,===只限于子组件,是单根组件,如果是多根组件,则不会受影响
<script setup>
import A from './components/A.vue'
</script>
<template>
<div class="app">
<h1>我是父组件h1</h1>
<A></A>
</div>
</template>
<style scoped>
.app h1{
color: red;
}
</style>
<script setup>
</script>
<template>
<h1>我是子组件h1</h1>
</template>
<style scoped>
</style>
<script setup>
import A from './components/A.vue'
</script>
<template>
<div class="app">
<h1>我是父组件h1</h1>
<A></A>
</div>
</template>
<style scoped>
.app :deep(h1){
color: red;
}
</style>
<script setup>
</script>
<template>
<div>
<h1>我是子组件h1</h1>
</div>
</template>
<style scoped>
</style>
当子组件h1不是根组件,这时。就要使用:deep() 方法来 实现
插槽
插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的标签。
默认插槽
子组件
<template>
<div>
<slot></slot> 在哪里渲染
</div>
</template>
在父组件给这个插槽填充内容
<Dialog>
<template v-slot>
1233
</template>
</Dialog>
还可以写成这样
<Dialog> 12333 </Dialog>
具名插槽
具名插槽其实就是给插槽取个名字。一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据这个名字把内容填充到对应插槽中
子组件
<div>
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
</div>
有name属性的是具名插槽
父组件使用需对应名称
<Dialog>
<template v-slot:header>
<div>1</div>
</template>
<template v-slot>
<div>2</div>
</template>
<template v-slot:footer>
<div>3</div>
</template>
</Dialog>
插槽简写
<Dialog>
<template #header>
<div>1</div>
</template>
<template #default>
<div>2</div>
</template>
<template #footer>
<div>3</div>
</template>
</Dialog>
默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容
子组件
<div>
<slot> 我是默认内容</slot>
</div>
父组件
<Dialog>
</Dialog>
渲染作用域 插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的。举例来说:
<script setup>
import {ref} from 'vue'
import HelloWorld from './components/HelloWorld.vue'
const num=ref(0)
</script>
<template>
<!-- 父组件 -->
<hello-world>
<template v-slot:a>
{{ num }}
</template>
</hello-world>
</template>
<template>
<!-- 子组件 -->
<slot name="a"></slot>
</template>
插槽内容无法访问子组件的数据。Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。换言之:
父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。
动态插槽
插槽可以是一个变量名
<Dialog>
<template #[name]>
<div>
23
</div>
</template>
</Dialog>
const name = ref('header')
作用域插槽
<script setup>
import SlotDemo from "./components/SlotDemo.vue"
</script>
<template>
<SlotDemo>
<template v-slot:s2="{ stuName, age, gender }"> 解构
<div >{{stuName, age, gender}}</div>
</template>
</SlotDemo>
</template>
<script setup>
const stuName = "孙悟空"
const age = 18
const gender = "男"
</script>
<template>
<div>
slotDemo组件
<div>
<slot
:gender="gender"
:age="age"
:stuName="stuName"
></slot>
</div>
</div>
</template>
事件处理
- 内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与
onclick类似)。
内联事件处理器通常用于简单场景,例如
data() {
return {
count: 0
}
}
<button @click="count++">Add 1</button>
<p>Count is: {{ count }}</p>
方法事件处理器
<script setup>
import {ref} from 'vue'
const count= ref(0)
function gosearch(e){
count.value++
console.log(e);
}
</script>
<template>
<div>{{ count }}</div>
<button @click="gosearch">点我加1</button>
</template>
不传参时,默认vue帮我们传入了一个事件对象event
<script setup>
import {ref} from 'vue'
const count= ref(0)
function gosearch(a,e){
count.value++
console.log(a,e);
}
</script>
<template>
<div>{{ count }}</div>
<button @click="gosearch('123',$event)">点我加1</button>
</template>
当我们手动传参数,要使用事件对象,可以直接传$event ,不传的话,默认是没有
事件修饰符
.stop 停止事件冒泡
.capture 在捕获阶段触发事件
.prevent 取消默认行为
.self 只有事件由自身触发时才会有效
.once 绑定一个一次性的事件
.passive 主要用于提升滚动事件的性能
透传属性
<script setup>
import C from "./components/C.vue"
function showMsg() {
alert("hello")
}
</script>
<template>
<C class="box2" style="color: red" @click="showMsg"></C>
</template>
<script setup>
</script>
<template>
<h2 class="box3" style="font-size: 60px">我是组件C</h2>
</template>
/* 透传属性
- 在组件上设置属性,会自动传递给组件的根元素
- 这样一来可以方便我们在父组件中为子组件来设置属性
- 透传会发生在没有被声明为props和emit的属性上
- 自动的透传只适用单根组件
*/
当子组件,是单根组件时,属性默认会传递过来
<!--
在模板中,可以通过$attrs来访问透传过来的属性,
可以手动指定透传过来的属性要添加到哪些元素
-->
<!-- {{ $attrs }} -->
$attrs 是一个对象,里面有父组件的属性
<template>
<h2 class="box3" :class="$attrs.class" style="font-size: 60px">
我是组件C
</h2>
<h3 :class="$attrs.class" :style="$attrs.style">我也是组件C</h3>
</template>
当子组件不是单根组件时,默认的就会失效,可以通过$attrs 来手动设置
<script setup>
import { useAttrs } from "vue"
const attrs = useAttrs()
console.log(attrs)
</script>
在script中,可以通过useAttrs()来获取透传过来的属性
<script>
export default {
inheritAttrs: false
}
</script>
当我想取消默认传过来的属性,可以多设置一层script 标签,设置 inheritAttrs: false,
双向绑定
<script setup>
import { ref } from "vue"
const text = ref("")
const bool = ref("是")
const hobbies = ref([])
const gender = ref("女")
const friend = ref("猪八戒")
function submitHandler() {
console.log(text.value)
// 将text提交给服务器,在根据服务器返回的数据做后续的操作
}
</script>
<template>
<h1>Hello Vue</h1>
<form @submit.prevent="submitHandler">
在vue中,为我们提供了v-model了可以快速完成表单的双向数据绑定
<div>信息:<input type="text" v-model="text" /></div>
<div>
是否:<input
type="checkbox"
v-model="bool"
true-value="是"
false-value="否"
/>
</div>
<div>
爱好:
<input
v-model="hobbies"
type="checkbox"
name="hobby"
value="足球"
/>足球
<input
v-model="hobbies"
type="checkbox"
name="hobby"
value="篮球"
/>篮球
<input
v-model="hobbies"
type="checkbox"
name="hobby"
value="羽毛球"
/>羽毛球
<input
v-model="hobbies"
type="checkbox"
name="hobby"
value="乒乓球"
/>乒乓球
</div>
<div>
性别:
<input v-model="gender" type="radio" name="gender" value="男" />男
<input v-model="gender" type="radio" name="gender" value="女" />女
</div>
<div>
朋友:
<select v-model="friend">
<option disabled value="">请选择你的好朋友...</option>
<option>孙悟空</option>
<option>猪八戒</option>
<option>沙和尚</option>
</select>
</div>
<div>
<button>提交</button>
</div>
</form>
</template>
select框 如果 v-model 表达式的初始值不匹配任何一个选择项,<select> 元素会渲染成一个“未选择”的状态。在 iOS 上,这将导致用户无法选择第一项,因为 iOS 在这种情况下不会触发一个 change 事件。因此,我们建议提供一个空值的禁用选项,如上面的例子所示。
多选 (值绑定到一个数组):
修饰符
.lazy 使用change来处理数据
.trim 去除前后的空格
.number 将数据转换为数值
自定义事件
方法一:
父组件
<script setup>
import StudentList from "./components/StudentList.vue"
import { reactive } from "vue";
const STU_ARR = reactive([
{
id: 1,
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
},
{
id: 2,
name: "猪八戒",
age: 28,
gender: "男",
address: "高老庄"
},
{
id: 3,
name: "沙和尚",
age: 38,
gender: "男",
address: "流沙河"
},
{
id: 4,
name: "唐僧",
age: 16,
gender: "男",
address: "女儿国"
}
])
function delStudentByIndex(index){
STU_ARR.splice(index,1)
}
</script>
<template>
<!-- 可以将组件中的方法以自定义事件的形式发送给其他的组件 -->
<StudentList :stu="STU_ARR" @delStu="delStudentByIndex"></StudentList>
</template>
子组件
<script setup>
const porps=defineProps(['stu'])
</script>
<template>
<table>
<caption>
学生列表
</caption>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>住址</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(s,index) in stu" :key="s.id">
<td>{{ s.id }}</td>
<td>{{ s.name }}</td>
<td>{{ s.age }}</td>
<td>{{ s.gender }}</td>
<td>{{ s.address }}</td>
<td>
<!-- 在模板中可以通过$emit()来触发自定义事件 -->
<a href="#" @click.prevent="$emit('delStu', index)">删除</a>
</td>
</tr>
</tbody>
</table>
</template>
props接收到的传过来的数据,一般不修改, 删除数据,需要通知父组件去删改
方法二:直接props传了一个函数,
<script setup>
import StudentList from "./components/StudentList.vue"
import { reactive } from "vue";
const STU_ARR = reactive([
{
id: 1,
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
},
{
id: 2,
name: "猪八戒",
age: 28,
gender: "男",
address: "高老庄"
},
{
id: 3,
name: "沙和尚",
age: 38,
gender: "男",
address: "流沙河"
},
{
id: 4,
name: "唐僧",
age: 16,
gender: "男",
address: "女儿国"
}
])
function delStudentByIndex(index){
STU_ARR.splice(index,1)
}
</script>
<template>
<!-- 可以将组件中的方法以自定义事件的形式发送给其他的组件 -->
<StudentList :stu="STU_ARR" :fn="delStudentByIndex"></StudentList>
</template>
子组件
<script setup>
const porps=defineProps(['stu',"fn"])
// 定义一个删除学生的方法
const delStuHandler = (index) => {
if (confirm("该操作不可恢复,请确认!")) {
porps.fn(index)
// 这里执行父组件的删除函数
}
}
</script>
<template>
<table>
<caption>
学生列表
</caption>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>住址</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(s,index) in stu" :key="s.id">
<td>{{ s.id }}</td>
<td>{{ s.name }}</td>
<td>{{ s.age }}</td>
<td>{{ s.gender }}</td>
<td>{{ s.address }}</td>
<td>
<a href="#" @click.prevent="delStuHandler(index)">删除</a>
</td>
</tr>
</tbody>
</table>
</template>
方法三:
<script setup>
import StudentList from "./components/StudentList.vue"
import { reactive } from "vue";
const STU_ARR = reactive([
{
id: 1,
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
},
{
id: 2,
name: "猪八戒",
age: 28,
gender: "男",
address: "高老庄"
},
{
id: 3,
name: "沙和尚",
age: 38,
gender: "男",
address: "流沙河"
},
{
id: 4,
name: "唐僧",
age: 16,
gender: "男",
address: "女儿国"
}
])
function delStudentByIndex(index){
STU_ARR.splice(index,1)
}
</script>
<template>
<!-- 可以将组件中的方法以自定义事件的形式发送给其他的组件 -->
<StudentList :stu="STU_ARR" @destu="delStudentByIndex"></StudentList>
</template>
子组件
<script setup>
const porps=defineProps(['stu',"fn"])
//用defineEmits接收
const emits=defineEmits(['destu'])
// 定义一个删除学生的方法
const delStuHandler = (index) => {
if (confirm("该操作不可恢复,请确认!")) {
emits("destu", index)
//触发
}
}
</script>
<template>
<table>
<caption>
学生列表
</caption>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>住址</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(s,index) in stu" :key="s.id">
<td>{{ s.id }}</td>
<td>{{ s.name }}</td>
<td>{{ s.age }}</td>
<td>{{ s.gender }}</td>
<td>{{ s.address }}</td>
<td>
<a href="#" @click.prevent="delStuHandler(index)">删除</a>
</td>
</tr>
</tbody>
</table>
</template>
增加元素
app组件
<script setup>
import StudentList from "./components/StudentList.vue"
import StudentForm from './components/StudentForm.vue'
import { reactive } from "vue";
const STU_ARR = reactive([
{
id: 1,
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
},
{
id: 2,
name: "猪八戒",
age: 28,
gender: "男",
address: "高老庄"
},
{
id: 3,
name: "沙和尚",
age: 38,
gender: "男",
address: "流沙河"
},
{
id: 4,
name: "唐僧",
age: 16,
gender: "男",
address: "女儿国"
}
])
// 删除
function delStudentByIndex(index){
STU_ARR.splice(index,1)
}
// 添加学生
function addstudent(newStu){
let addid=STU_ARR.length+1
newStu.id=addid
STU_ARR.push(newStu)
}
</script>
<template>
<!-- 可以将组件中的方法以自定义事件的形式发送给其他的组件 -->
<StudentList :stu="STU_ARR" @destu="delStudentByIndex"></StudentList>
<hr />
<StudentForm @addstu="addstudent"></StudentForm>
</template>
StudentForm组件,
<script setup>
import { reactive } from 'vue';
const emits=defineEmits(['addstu'])
// 创建一个ref来存储新的学生信息
const newStu = reactive({
name: "",
age: 1,
gender: "男",
address: ""
})
const submitHandler = () => {
// newStu是 响应式数据,需要复制到新的空对象
emits('addstu',{...newStu})
newStu.name = ""
newStu.age = 1
newStu.gender = "男"
newStu.address = ""
}
</script>
<template>
<form @submit.prevent="submitHandler">
<div>姓名 <input type="text" v-model="newStu.name" /></div>
<div>年龄 <input type="number" min="1" v-model="newStu.age" /></div>
<div>
性别
<input
v-model="newStu.gender"
type="radio"
name="gender"
value="男"
/>
男
<input
v-model="newStu.gender"
type="radio"
name="gender"
value="女"
/>
女
</div>
<div>
住址
<input v-model="newStu.address" type="text" />
</div>
<div>
<button>添加</button>
</div>
</form>
</template>
依赖注入
依赖注入
- 通过依赖注入,可以跨域多层组件向其他的组件传递数据
- 步骤:
1. 设置依赖(provide) provide(name, value)
2. 注入数据 (inject) const value = inject(name, default)
能够使用, 2个组件,必须是后代关系( 比如父子或爷孙等)
APP组件
<script setup>
import StudentList from "./components/StudentList.vue"
import StudentForm from './components/StudentForm.vue'
import { reactive ,provide} from "vue";
const STU_ARR = reactive([
{
id: 1,
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
},
{
id: 2,
name: "猪八戒",
age: 28,
gender: "男",
address: "高老庄"
},
{
id: 3,
name: "沙和尚",
age: 38,
gender: "男",
address: "流沙河"
},
{
id: 4,
name: "唐僧",
age: 16,
gender: "男",
address: "女儿国"
}
])
// 设置依辣
provide('add',addstudent)
// 删除
function delStudentByIndex(index){
STU_ARR.splice(index,1)
}
// 添加学生
function addstudent(newStu){
let addid=STU_ARR.length+1
newStu.id=addid
STU_ARR.push(newStu)
}
</script>
<template>
<!-- 可以将组件中的方法以自定义事件的形式发送给其他的组件 -->
<StudentList :stu="STU_ARR" @destu="delStudentByIndex"></StudentList>
<hr />
<StudentForm ></StudentForm>
</template>
<script setup>
import { reactive,inject } from 'vue';
//注入数据
const addstudent=inject('add')
// 创建一个ref来存储新的学生信息
const newStu = reactive({
name: "",
age: 1,
gender: "男",
address: ""
})
const submitHandler = () => {
addstudent({...newStu})
//可以直接使用注册的函数
newStu.name = ""
newStu.age = 1
newStu.gender = "男"
newStu.address = ""
}
</script>
<template>
<form @submit.prevent="submitHandler">
<div>姓名 <input type="text" v-model="newStu.name" /></div>
<div>年龄 <input type="number" min="1" v-model="newStu.age" /></div>
<div>
性别
<input
v-model="newStu.gender"
type="radio"
name="gender"
value="男"
/>
男
<input
v-model="newStu.gender"
type="radio"
name="gender"
value="女"
/>
女
</div>
<div>
住址
<input v-model="newStu.address" type="text" />
</div>
<div>
<button>添加</button>
</div>
</form>
</template>
注意,假设 爷组件 有 设置依辣,父组件也有设置相同的依辣,那么孙(子),会优先使用父组件的数据
pinia
用你喜欢的包管理器安装 pinia:
npm install pinia
main.js文件
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
Store 是什么?
Store (如 Pinia) 是一个保存状态和业务逻辑的实体,它并不与你的组件树绑定。换句话说,它承载着全局状态。它有点像一个永远存在的组件,每个组件都可以读取和写入它。它有三个概念,state、getter 和 action,我们可以假设这些概念相当于组件中的 data、 computed 和 methods。
在src下 配置stroe文件
与 Vue 的选项式 API 类似,我们也可以传入一个带有 state、actions 与 getters 属性的 Option 对象
你可以认为 state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods)。
上面是Option 对象写法, 还可以写成setup函数(组合api)
在 Setup Store 中:
ref()就是state属性computed()就是gettersfunction()就是actions
使用 Store
<script setup>
import { useCounterStore } from '@/stores/counter'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
</script>
<template>
<div>{{ store.count }}</div>
<div>{{ store.double }}</div>
<button @click="store.addnumber">点我加1</button>
</template>
优化
import { storeToRefs } from "pinia"; 引入storeToRefs
const { count ,double } =storeToRefs(store) 只能解构属性
const { addnumber} =store 方法可以直接解构
<div>{{ count }}</div>
<div>{{ double }}</div>
<button @click="addnumber">点我加1</button>
不用storeToRefs解构,直接解构,解构出来的属性不具有响应性
重置 state
你可以通过调用 store 的 $reset() 方法将 state 重置为初始值。
const store = useStore()
store.$reset()
变更 state
方法一:直接修改,不推荐
state:()=>({ count:0 ,name:"张三"}),
<div>{{ name }}</div>
<button @click="()=>name='李四'">修改name属性</button>
方法二: 通过$patch ( 此方法,是替换state里面的属性,没有修改的不替换,对数组不友好)
state:()=>({
count:0 ,
name:"张三",
list:['a','b','c']
}),
<div>{{ list }}</div>
<div>{{ name }}</div>
<button @click="hander">修改name和list属性</button>
function hander(){
store.$patch({
name:"李四",
list:['d']
})
}
修改前
修改后
有些变更真的很难实现或者很耗时:任何集合的修改(例如,向数组中添加、移除一个元素或是做 splice 操作)都需要你创建一个新的集合。因此,$patch 方法也接受一个函数来组合这种难以用补丁对象实现的变更。
方法三:$patch` 方法也接受一个函数来组合这种难以用补丁对象实现的变更。
store.$patch((state) => {
state.list.push('d')
state.name = '李四'
})
两种变更 store 方法的主要区别是,$patch() 允许你将多个变更归入 devtools 的同一个条目中。同时请注意,直接修改 state,$patch() 也会出现在 devtools 中
订阅state
store的订阅
- 当store中的state发生变化时,做一些响应的操作
- store.$subscribe(函数, 配置对象)
stuStore.$subscribe(
(mutation, state) => {
// mutation 表示修改的信息
// console.log(mutation.events)
// console.log("state发生变化了", state)
// 使用订阅时不要在回调函数中直接修改state,不然会被递归
// state.age++
},
{ detached: true }
)
{ detached: true } 第二个参数,此订阅器即便在组件卸载之后仍会被保留
通过 store 的 $subscribe() 方法侦听 state 及其变化。比起普通的 watch(),使用 $subscribe() 的好处是 subscriptions 在 patch 后只触发一次
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
action方法:
actions:{
addnumber( ...args){
// console.log(this);
// 指向了这个store实例对象
console.log(args);
this.count++
}
}
action里面的方法,默认传入了一个事件对象,是拿不到state,可以用this
$onAction 用来订阅action的调用 ()====》action里面定义的方法被触发时调用
stuStore.$onAction(({ name, store, args, after, onError }) => {
after(() => {
console.log(name + "成功执行!")
})
onError((err) => {
console.log(name + "执行失败!", err)
})
})
name 调用的action的名字
store store的实例
args action接收到的参数
after() 可以设置一个回调函数,函数会在action成功调用后触发
onError() 可以设置一个回调函数,函数会在action调用失败后触发
计算属性 getters
// 计算属性
getters: {
// double:state=>state.count*2,
double(){
return this.count*2
}
},
补充:字体图标Remix Icon - Open source icon library
怎么使用呢
这样就可以使用了,想用哪个,直接复制相应代码