Vue3
1、创建一个Vue3应用
html
<script src="vue.global.js"></script>
<body>
<div id="app">
{{msg}}
<h2>{{person.title}}</h2>
<h2>{{person.name}}</h2>
</div>
<script>
// 采用解构的方式可以省略Vue.createApp和Vue.reactive前的Vue
const{createApp,reactive}=Vue;
Vue.createApp({
// setup选项用于设置响应式数据和方法
setup(){
const person=Vue.reactive({
title:"Vue学习",
name:"sss",
})
return{
msg:"success",
person
}
}
}).mount("#app") //mount用于挂载到id为app的标签上
</script>
</body>
2、Vue3模块化开发
html
<body>
<div id="app">
{{msg}}
<h2>{{person.title}}</h2>
<h2>{{person.name}}</h2>
</div>
<!-- 模块化开发 -->
<script type="module">
// 采用解构的方式可以省略Vue.createApp和Vue.reactive前的Vue
import {createApp,reactive} from './vue.esm-browser.js'
createApp({
// setup选项用于设置响应式数据和方法
setup(){
const person=reactive({
title:"Vue学习",
name:"sss",
})
return{
msg:"success",
person
}
}
}).mount("#app") //mount用于挂载到id为app的标签上
</script>
</body>
3、ref和reactive的区别
javascript
<body>
<div id="app">
{{msg}}
<h2>{{person.title}}</h2>
<h2>{{person.name}}</h2>
</div>
<script type="module">
import {createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const number=ref(10);//ref用于存储单个基本类型的数据,如:数字、字符串、数组等
const arr=ref([1,2,3,4]);
number.value=200;//使用ref创建的响应式对象,需要通过.value属性来访问和修改其值
// reactive用于存储复杂数据类型,如对象或数组等
const person=reactive({
title:"Vue学习",
name:"sss",
})
person.name="scu";//使用reactive创建的响应式对象,可以直接通过属性名来访问和修改值
return{
msg:"success",
person
}
}
}).mount("#app")
</script>
</body>
4、绑定事件v-on
javascript
<body>
<div id="app">
{{msg}}
<h2>{{person.title}}</h2>
<h2>{{person.name}}</h2>
<!-- 绑定事件 -->
<button v-on:click="edit">修改</button>
<br>
<!-- v-on简写形式 -->
<button @click="edit">修改(简写形式)</button>
</div>
<script type="module">
import {createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const person=reactive({
title:"Vue学习",
name:"sss",
})
//构建函数
const edit=()=>{
person.name="hjklakhs";
}
return{
msg:"success",
person,
edit
}
}
}).mount("#app")
</script>
</body>
5、按键修饰符
按键修饰符:
- enter回车
- space空格
- tab键
- keyup是在用户松开按键时才触发
- keydown是在用户按下按键时立即触发
html
<body>
<div id="app">
{{msg}}
<h2>{{person.title}}</h2>
<h2>{{person.name}}</h2>
<h2>{{person.user}}</h2>
<!-- 按键修饰符 -->
回车<input type="text" @keyup.enter="add(40,60)"><br>
空格<input type="text" @keyup.space="add(20,30)"><br>
Tab <input type="text" @keydown.tab="add(30,40)"><br>
w <input type="text" @keyup.w="add(32,43)"><br>
<!-- 组合快捷键 -->
ctrl + Enter <input type="text" @keyup.ctrl.enter="add(34,58)"><br>
ctrl + A <input type="text" @keyup.ctrl.a="add(22,30)"><br>
</div>
<script type="module">
import {createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const person=reactive({
title:"Vue学习",
name:"sss",
user:0
})
const add=(x,y)=>{
person.user+=x+y;
}
return{
msg:"success",
person,
add
}
}
}).mount("#app")
</script>
</body>
6、显示和隐藏v-show
- v-show通过css display属性来控制元素的显示或隐藏
- v-show适用于频繁切换元素的显示状态,因为只改变display属性,不需要重新渲染整个组件
javascript
<body>
<div id="app">
{{web.show}}
<hr>
<!-- 显示和隐藏v-show -->
<p v-show="web.show">西游记 黑神话悟空</p>
<button @click="toggle()">切换状态</button>
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const web=reactive({
show:true
})
const toggle=()=>{
web.show=!web.show;
}
return{
web,
toggle
}
}
}).mount("#app")
</script>
</body>
7、条件渲染v-if
- v-if用于对元素进行条件渲染,当条件为true时,渲染该元素,为false时,则不渲染
- v-if适用于较少改变的场景,因为频繁从dom中删除或添加元素,会导致性能下降
javascript
<body>
<div id="app">
{{web.show}}
<hr>
<!-- v-if条件渲染 -->
<p v-if="web.show">西游记 黑神话悟空</p>
<button @click="toggle()">切换状态</button>
<!-- v-if-else -->
<p v-if="web.user<1000">新网站</p>
<p v-else-if="web.user>=1000&&web.user<=10000">优秀网站</p>
<p v-else="web.user">资深网站</p>
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const web=reactive({
show:true,
user:10005
})
const toggle=()=>{
web.show=!web.show;
}
return{
web,
toggle
}
}
}).mount("#app")
</script>
</body>
8、动态属性绑定v-bind
- 单向数据绑定:当数据发生改变时,视图会自动更新,但用户手动更改input的值,数据不会自动更新。
- 单向数据绑定<input type="text" :value="data.txt">
html
<style>
.textColor{
color: rebeccapurple;
}
</style>
<body>
<div id="app">
<!-- 动态属性绑定v-bind -->
<!-- :value -->
<h3>value="www.baidu.com"</h3>
<!-- <input type="text" value="www.baidu.com"> -->
<input type="text" v-bind:value="web.url">
<!-- 简写 -->
<input type="text" :value="web.url">
<!-- :src -->
<h3>src="dhimg1.png"</h3>
<!-- <img src="../resource/image/dhimg1.png" alt=""> -->
<img :src="web.img" alt="">
<!-- :class -->
<h3>class="textColor"</h3>
<!-- <b class="textColor">西游记</b> -->
<b :class="{textColor:web.Status}">西游记</b>
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const web=reactive({
url:"www.baidu.com",
img:"../resource/image/dhimg1.png",
Status:true
})
return{
web
}
}
}).mount("#app")
</script>
</body>
9、遍历数组或对象v-for
javascript
<body>
<div id="app">
<!-- 遍历数组或对象v-for -->
<ul>
<li v-for="(value,index) in web.number">
{{index}}:{{value}}
</li>
</ul>
<ul>
<li v-for="(value,key,index) in web.user">
{{index}}:{{key}}:{{value}}
</li>
</ul>
<ul>
<template v-for="(value,key,index) in web.user">
<li v-if="index==1">
{{index}}:{{key}}:{{value}}
</li>
</template>
</ul>
<ul>
<li v-for="(value,index) in web.teacher" :title="value.name" :key="value.id">
{{index}}:{{value.id}}:{{value.name}}:{{value.web}}
</li>
</ul>
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const web=reactive({
number:["十","十一","十二"],//数组
user:{
name:"sss",
age:20
},//对象
teacher:[
{id:100,name:"lucy",web:"baidu.com"},
{id:101,name:"jucy",web:"bai.com"}
]//包含两个对象的数组
})
return{
web
}
}
}).mount("#app")
</script>
</body>
10、双向数据绑定v-model
- 双向数据绑定:当数据发生改变时,视图会自动更新,用户手动更改input的值,数据也会自动更新。对于<input type="text">,v-model绑定的是input元素的value属性。
- 双向数据绑定<input type="text" v-model="data.txt">
html
<body>
<div id="app">
<!-- 双向数据绑定v-model -->
<h3>文本框{{data.text}}</h3>
<h3>单选框{{data.radio}}</h3>
<h3>复选框{{data.checkbox}}</h3>
<h3>记住密码{{data.remember}}</h3>
<h3>下拉框{{data.select}}</h3>
<!-- 单向数据绑定 -->
<input type="text" :value="data.text">
<br>
<!-- 双向数据绑定 -->
<input type="text" v-model="data.text">
<br>
<!-- 单选框 -->
<input type="radio" v-model="data.radio" value="1">写作
<input type="radio" v-model="data.radio" value="2">画画
<br>
<!-- 复选框 -->
<input type="checkbox" v-model="data.checkbox" value="a">写作
<input type="checkbox" v-model="data.checkbox" value="b">画画
<input type="checkbox" v-model="data.checkbox" value="c">运动
<br>
<!-- 单个复选框 -->
<input type="checkbox" v-model="data.remember">记住密码
<br>
<!-- 下拉框 -->
<select v-model="data.select">
<option value="">请选择</option>
<option value="A">写作</option>
<option value="B">画画</option>
<option value="C">运动</option>
</select>
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const data=reactive({
text:"baidu.com",//文本框
radio:"",//单选框
checkbox:[],//复选框
remember:false,//单个复选框 记住密码
select:""//下拉框
})
return{
data
}
}
}).mount("#app")
</script>
</body>
11、v-model修饰符
- 在失去焦点或按下回车键之后渲染用 v-model.lazy
- 输入框的值转换为数字类型用 v-model.number
- 去除首尾空格 v-model.trim
html
<body>
<div id="app">
<h3>{{web.url}}</h3>
<h3>{{web.user}}</h3>
实时渲染<input type="text" v-model="web.url"><br>
在失去焦点或按下回车键之后渲染<input type="text" v-model.lazy="web.url"><br>
输入框的值转换为数字类型<input type="text" v-model.number="web.user"><br>
去除首尾空格<input type="text" v-model.trim="web.url">
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const web=reactive({
url:"baidu.com",
user:10
})
return{
web
}
}
}).mount("#app")
</script>
</body>
12、渲染数据v-text和v-html
- v-text将数据解析为纯文本格式
- v-html将数据解析为html格式
html
<body>
<div id="app">
<h3>{{web.title}}</h3>
<h3 v-text="web.title"></h3>
<h3 v-html="web.url"></h3>
<h3 v-text="web.url"></h3>
</div>
<script type="module">
import{createApp,reactive} from './vue.esm-browser.js'
createApp({
setup(){
const web=reactive({
url:"<i style='color:blue;'>baidu.com</i>",
title:"javascript"
})
return{
web
}
}
}).mount("#app")
</script>
</body>
13、计算属性computed
html
<body>
<div id="app">
<h3>{{add()}}</h3>
<h3>{{add()}}</h3>
<!-- sub是属性不是方法 -->
<h3>{{sub}}</h3>
<h3>{{sub}}</h3>
x <input type="text" v-model.number="data.x"><br>
y <input type="text" v-model.number="data.y">
</div>
<script type="module">
// 导入computed
import{createApp,reactive,computed} from './vue.esm-browser.js'
createApp({
setup(){
const data=reactive({
x:10,
y:20
})
// 方法:无缓存
const add=()=>{
console.log("add");//打印流程
return data.x+data.y;
}
// 计算属性-有缓存(计算属性根据其依赖的响应式数据变化而重新计算)
const sub=computed(()=>{
console.log("sub");
return data.x-data.y;
})
return{
data,
add,
sub
}
}
}).mount("#app")
</script>
</body>
14、侦听器watch
html
<body>
<div id="app">
爱好
<select v-model="hobby">
<option value="">请选择</option>
<option value="1">写作</option>
<option value="2">画画</option>
<option value="3">运动</option>
</select>
<hr>
年
<select v-model="data.year">
<option value="">请选择</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
<option value="2025">2025</option>
</select>
月
<select v-model="data.month">
<option value="">请选择</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</div>
<script type="module">
// 导入watch
import{createApp,reactive,ref,watch} from './vue.esm-browser.js'
createApp({
setup(){
const hobby=ref("");//爱好
const data=reactive({
year:"2023",
month:"10"
})
// 侦听对象hobby
watch(hobby,(newValue,oldValue)=>{
console.log(newValue,oldValue);
if(newValue=="2"){
console.log("画画");
}
})
// 监听date
watch(data,(oldValue,newValue)=>{
/*
JS中对象和数组是通过引用传递的,而不是通过值传递
当修改对象或数组的值时,实际上修改的是对对象或数组的引用,而不是创建一个新的对象或数组
所以,如果修改了对象或数组的值,那么打印出来的结果则是修改后的值
*/
console.log(oldValue,newValue);
if(newValue.year=="2025"){
console.log("2025");
}
})
// 监听date中的某个属性year
watch(()=>data.year,(newValue,oldValue)=>{
console.log(oldValue,newValue);
if(data.year=="2024"){
console.log("2024");
}
})
return{
hobby,
data
}
}
}).mount("#app")
</script>
</body>
15、自动侦听器watchEffect
html
<body>
<div id="app">
爱好
<select v-model="hobby">
<option value="">请选择</option>
<option value="1">写作</option>
<option value="2">画画</option>
<option value="3">运动</option>
</select>
<hr>
年
<select v-model="data.year">
<option value="">请选择</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
<option value="2025">2025</option>
</select>
月
<select v-model="data.month">
<option value="">请选择</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</div>
<script type="module">
// 导入watchEffect
import{createApp,reactive,ref,watchEffect} from './vue.esm-browser.js'
createApp({
setup(){
const hobby=ref("");//爱好
const data=reactive({
year:"2023",
month:"10"
})
// 自动监听
watchEffect(()=>{
console.log("监听开始");
if(hobby.value=="2"){
console.log("画画");
}
if(data.year=="2025"){
console.log("2025");
}
if(data.month=="10"){
console.log("10");
}
console.log("监听结束");
})
return{
hobby,
data
}
}
}).mount("#app")
</script>
</body>
16、图片轮播案例
html
<body>
<div id="app">
<h3>{{number}}</h3>
<img :src=`../resource/image/dhimg${number}.png` style="width: 300px;"><br>
<button @click="next">下一张</button>
<button @click="prev">上一张</button>
<hr>
<ul>
<li v-for="value in 4">
<a href="#" @click="jump(value)">{{value}}</a>
</li>
</ul>
</div>
<script type="module">
import{createApp,reactive,ref} from './vue.esm-browser.js'
createApp({
setup(){
const number=ref(2)
const next=()=>{
number.value++;
if(number.value=="5"){
number.value=1;
}
}
const prev=()=>{
number.value--;
if(number.value=="0"){
number.value=5;
}
}
const jump=(value)=>{
number.value=value;
}
return{
number,
next,
prev,
jump
}
}
}).mount("#app")
</script>
</body>
17、记事本案例
html
<body>
<div id="app">
<input type="text" v-model="data.content">
<button @click="add">添加</button>
<ul>
<li v-for="(value,index) in data.list">
{{value}}
<button @click="del(index)">删除</button>
</li>
</ul>
{{data.list.length}}
<button @click="clear">清空</button>
</div>
<script type="module">
import{createApp,reactive,ref} from './vue.esm-browser.js'
createApp({
setup(){
const data=reactive({
content:"baidu.com",
list:["sss","yyy"]
})
const add=()=>{
data.list.push(data.content);
console.log(data.list)
}
const del=(index)=>{
data.list.pop(index);
}
const clear=()=>{
data.list=[];
}
return{
data,
add,
del,
clear
}
}
}).mount("#app")
</script>
</body>
18、购物车案例
html
<body>
<div id="app">
<table>
<thead>
<tr>
<td><input type="checkbox" v-model="data.selected" @change="selectAll"></td>
<td>商品</td>
<td>单价</td>
<td>库存</td>
<td colspan="2">操作</td>
</tr>
</thead>
<tbody>
<tr v-for="(value,index) in data.list">
<td><input type="checkbox" :value="value" v-model="data.checkboxList" @change="changeSelect"></td>
<td>{{value.name}}</td>
<td>{{value.price}}</td>
<td>{{value.stock}}</td>
<td>
<button @click="sub(value)">-</button>
{{value.number}}
<button @click="add(value)">+</button>
</td>
<td><button @click="del(index,value.id)">删除</button></td>
</tr>
</tbody>
<tfoot>
<tr>
<td>总价{{totalPrice()}}</td>
</tr>
</tfoot>
</table>
</div>
<script type="module">
import{createApp,reactive,ref} from './vue.esm-browser.js'
createApp({
setup(){
const data=reactive({
selected:false,
checkboxList:[],
list:[{
id:1,
name:"铅笔",
price:10,
number:1,
stock:3
},
{
id:2,
name:"鼠标",
price:20,
number:2,
stock:5
},
{
id:3,
name:"键盘",
price:30,
number:1,
stock:6
}],
})
const selectAll=()=>{
console.log(data.selected);
if(data.selected){
data.checkboxList=data.list;
}else{
data.checkboxList=[];
}
}
const changeSelect=()=>{
if(data.checkboxList.length==data.list.length&&data.list.length!=0){
data.selected=true;
}else{
data.selected=false;
}
}
const totalPrice=()=>{
let total=0;
for(let i=0;i<data.checkboxList.length;i++){
total+=data.checkboxList[i].price*data.checkboxList[i].number;
}
return total;
}
const add=(value)=>{
value.number++;
if(value.number>=value.stock){
value.number=value.stock;
}
}
const sub=(value)=>{
value.number--;
if(value.number<=1){
value.number=1;
}
}
const del=(index,id)=>{
data.list.splice(index,1);
let newArr=data.checkboxList.filter((value,index)=>{
return value.id!=id;
})
data.checkboxList=newArr;
changeSelect();
}
return{
data,
selectAll,
changeSelect,
totalPrice,
add,
sub,
del
}
}
}).mount("#app")
</script>
</body>
19、购物车案例优化
html
<body>
<div id="app">
<table>
<thead>
<tr>
<td><input type="checkbox" v-model="data.selected" ></td>
<td>商品</td>
<td>单价</td>
<td>库存</td>
<td colspan="2">操作</td>
</tr>
</thead>
<tbody>
<tr v-for="(value,index) in data.list">
<td><input type="checkbox" :value="value" v-model="data.checkboxList" @change="changeSelect"></td>
<td>{{value.name}}</td>
<td>{{value.price}}</td>
<td>{{value.stock}}</td>
<td>
<button @click="sub(value)">-</button>
{{value.number}}
<button @click="add(value)">+</button>
</td>
<td><button @click="del(index,value.id)">删除</button></td>
</tr>
</tbody>
<tfoot>
<tr>
<td>总价{{totalPrice}}</td>
</tr>
</tfoot>
</table>
</div>
<script type="module">
import{createApp,reactive,watch,computed} from './vue.esm-browser.js'
createApp({
setup(){
const data=reactive({
selected:false,
checkboxList:[],
list:[{
id:1,
name:"铅笔",
price:10,
number:1,
stock:3
},
{
id:2,
name:"鼠标",
price:20,
number:2,
stock:5
},
{
id:3,
name:"键盘",
price:30,
number:1,
stock:6
}],
})
let flag=true;
watch(()=>data.selected,(newValue,oldValue)=>{
if(newValue){
data.checkboxList=data.list;
}else{
if(flag){
data.checkboxList=[];
}
}
})
watch(()=>data.checkboxList,(newValue,oldValue)=>{
if(newValue.length==data.list.length&&data.list.length!=0){
data.selected=true;
}else{
data.selected=false;
flag=false;
}
})
const totalPrice=computed(()=>{
/*
reduce定义:用于对数组中所有元素进行迭代操作,并将每次操作的结果累加到一个初始值上
reduce接收两个参数:一个是累加器函数,另一个是初始值
reduce:将data.checkboxList数组中的每个checkbox对象的price和number属性进行相乘,
并将结果累加到初始值0上,最后返回累加的结果
total(累加器)用于存储每次计算的结果,初始值为0
item(当前元素)在每次迭代过程中,当前元素的值会被传递给回调函数
*/
return data.checkboxList.reduce((total,item)=>total+item.price*item.number,0)
})
const add=(value)=>{
value.number++;
if(value.number>=value.stock){
value.number=value.stock;
}
}
const sub=(value)=>{
value.number--;
if(value.number<=1){
value.number=1;
}
}
const del=(index,id)=>{
data.list.splice(index,1);
let newArr=data.checkboxList.filter((value,index)=>{
return value.id!=id;
})
data.checkboxList=newArr;
}
return{
data,
totalPrice,
add,
sub,
del
}
}
}).mount("#app")
</script>
</body>
Vite
1、基于Vite创建Vue3项目
- 在指定目录下打开cmd执行命令 npm create vite@latest
- 打开src文件夹删除style.css文件和HelloWorld.vue文件
- 打开main.js文件删除 import './style.css'
- 打开App.vue文件删除 import HelloWorld from './components/HelloWorld.vue' 和 默认样式
2、导入组件
javascript
<script setup>
//导入子组件
//App.vue是父组件,因为它包含了header.vue和footer.vue两个子组件
import Header from "./components/header.vue"
import Footer from "./components/footer.vue"
</script>
<template>
<Header/>
<Footer/>
</template>
<style scoped>
</style>
3、父传子defineProps
html
/*App.vue文件*/
<script setup>
import { reactive } from 'vue';
//导入子组件
//App.vue是父组件,因为它包含了header.vue和footer.vue两个子组件
import Header from './components/header.vue';
import Footer from './components/footer.vue';
// const propsweb = {
// user:10,
// url:"ww.baidu.com"
// }
const propsweb = reactive({
user:10,
url:"www.baidu.com"
})
const userAdd=()=>{
propsweb.user++;
console.log(propsweb.user);
}
</script>
<template>
<!-- 父传子 - 方式1 -->
<Header propsname="孙悟空" propsurl="baidu.com" />
<button @click="userAdd">添加用户</button>
<!-- 父传子 - 方式2 -->
<!-- <Footer v-bind="propsweb"/> -->
<!-- v-bind简写 -->
<Footer :="propsweb"/>
</template>
<style scoped>
</style>
/*header.vue文件*/
<script setup>
//接收方式:数组
const props = defineProps(["propsname","propsurl"])
console.log(props);
</script>
<template>
<h3>header</h3>
</template>
<style scoped>
</style>
/*footer.vue文件*/
<script setup>
//接收方式:对象
const props=defineProps({
user:Number,
url:{
type:String,
required:false,//true表示必传属性,若未传则会提示警告信息
default:"baidu.com"//未传默认值
}
})
console.log(props)
</script>
<template>
<h3>footer</h3> {{ props.user }}
</template>
<style scoped>
</style>
4、子传父defineEmits
html
/*App.vue文件*/
<script setup>
import{reactive,ref} from 'vue'
import Header from './components/header.vue'
// 响应式数据
const web = reactive({
name:"孙悟空",
url:"baidu.com"
})
const user = ref(0)
const emitsGetWeb=(data)=>{
console.log(data)
web.url=data.url;
}
const emitsUserAdd=(data)=>{
console.log(data);
user.value+=data;
}
</script>
<template>
<Header @getweb="emitsGetWeb" @userAdd="emitsUserAdd"/>
{{ web.url }} - {{ user }}
</template>
<style scoped>
</style>
/*header.vue文件*/
<script setup>
const emits = defineEmits(["getweb","userAdd"])
emits("getweb",{name:"黑神话",url:"www.baidu.com"})
const add=()=>{
emits("userAdd",10)
}
</script>
<template>
<h3>header</h3>
<button @click="add">添加用户</button>
</template>
<style scoped>
</style>
5、跨组件通信-依赖注入
html
/*App.vue文件*/
<script setup>
import{ref,provide,reactive} from 'vue'
// 导入子组件
import Header from './components/header.vue'
// 响应式数据
const web = reactive({
name:"孙悟空",
url:"baidu.com"
})
//provide用于父组件将数据提供给所有子组件
/*
若使用了provide和inject来进行数据传递,
则一般不需要再使用defineProps
*/
provide("provideWeb",web)
const user = ref(0)
provide("provideUser",user)
const userAdd=()=>{
user.value++;
}
//用于父组件将函数提供给所有子组件
provide("provideFuncUserAdd",userAdd)
</script>
<template>
<h3>App.vue-Top组件</h3>
user:{{ user }}
<!-- 子组件 -->
<Header/>
</template>
<style scoped>
</style>
/*header.vue文件*/
<script setup>
// 导入子组件
import Footer from "./footer.vue"
import {inject} from 'vue'
//子组件通过inject注入父组件提供的响应式数据
const user = inject("provideUser")
console.log("provideUser",user)
</script>
<template>
<h3>header.vue-Middle组件</h3>
<!-- 子组件 -->
<Footer/>
</template>
<style scoped>
</style>
/*footer.vue文件*/
<script setup>
import { inject } from 'vue';
//子组件通过inject注入父组件提供的响应式数据
const web=inject("provideWeb");
console.log("provideWeb",web);
//子组件通过inject注入父组件提供的响应式数据
const FuncUserAdd = inject("provideFuncUserAdd")
console.log(FuncUserAdd)
</script>
<template>
<h3>footer.vue-Bottom组件</h3>
<button @click="FuncUserAdd">添加用户</button>
</template>
<style scoped>
</style>
6、匿名插槽和具体插槽v-slot
- 插槽是指可以在父组件内自定义模板片段,在子组件中可以将定义的模板片段插入到子组件的特定位置。
html
/*App.vue文件*/
<script setup>
// 导入子组件
import Header from './components/header.vue';
import Footer from './components/footer.vue';
</script>
<template>
<h3>App.vue</h3>
<Header>
<a href="baidu.com">百度</a>
</Header>
<Footer>
<!-- <template v-slot:url> -->
<!-- v-slot简写形式 -->
<template #url>
<a href="www.baidu.com">网址</a>
</template>
</Footer>
</template>
<style scoped>
</style>
/*header.vue文件*/
<script setup>
</script>
<template>
<h3>header.vue子组件</h3>
<!-- 匿名插槽 -->
<slot/>
</template>
<style scoped>
</style>
/*footer.vue文件*/
<script setup>
</script>
<template>
<h3>footer.vue子组件</h3>
<!-- 具名插槽 -->
<slot name="url"/>
</template>
<style scoped>
</style>
7、作用域插槽
- 作用域插槽:子组件向父组件传递数据,并在父组件定义的模板中渲染
html
/*App.vue文件*/
<script setup>
// 导入子组件
import Header from './components/header.vue';
import Footer from './components/footer.vue';
</script>
<template>
<h3>App.vue</h3>
<Header>
<a href="baidu.com">百度</a>
</Header>
<Footer>
<!-- <template v-slot:url> -->
<!-- 作用域插槽 -->
<template #url="data">
<!-- 解构形式 -->
<!-- <template #url="{title,user}"> -->
<!-- data用于接收数据 -->
{{ data.title }}
{{ data.user }}
<a href="www.baidu.com">网址</a>
</template>
</Footer>
</template>
<style scoped>
</style>
/*footer.vue文件*/
<script setup>
</script>
<template>
<h3>footer.vue子组件</h3>
<!-- 具名插槽 -->
<slot name="url" title="摆渡" user="1000"/>
</template>
<style scoped>
</style>
8、生命周期函数
生命周期函数是组件实例从创建到销毁过程中不同时间点自动调用的函数。
1、挂载阶段:
- onBeforeMount:在组件实例即将被挂载到DOM之前调用,此时模板还未编译或渲染到DOM,通常用于执行初始化操作,如:获取异步数据、设置初始属性值等。
- onMounted:在组件成功挂载到DOM并完成首次渲染后调用,此时可以访问和操作DOM元素,并执行与页面交互相关的逻辑。
2、更新阶段:
- onBeforeUpdate (由于响应式数据变化):在组件更新之前在重新渲染时之前调用,可以根据新的参数判断是否需要进行特殊处理,甚至可以选择阻止此次更新过程。
- onUpdated:在组件完成更新并重新渲染后调用,可以基于新的渲染结果处理更新后的数据。
3、卸载阶段:
- onBeforeUnmount:在组件从DOM中销毁之前调用,用于释放资源,如:清理计时器、解绑事件监听器等。
- onUnmounted:在组件已经从DOM中移除并销毁后调用,确保组件所占用的所有资源都被正确释放。
4、错误处理:
- onErrorCaptured:在捕获到组件中的错误时调用,用于处理错误,如:记录错误日志等。
html
<script setup>
import { onMounted,onUpdated,ref} from 'vue';
// 在组件成功挂载到DOM并完成首次渲染后调用
onMounted(()=>{
console.log("onMounted")
})
// 组件更新后调用
onUpdated(()=>{
console.log("onUpdated")
})
const user = ref(0)
console.log(user.value)
</script>
<template>
{{ user }}
<button @click="user++">添加用户</button>
</template>
<style scoped>
</style>
9、toRef和toRefs
- toRefs将一个响应式对象的所有属性转换为ref对象
- toRef将一个响应式对象的某个属性转换为ref变量
html
<script setup>
import {reactive,toRefs,toRef} from 'vue';
let web=reactive({
name:"黑神话",
url:"baidu.com"
})
// let{name,url}=toRefs(web);
let url=toRef(web,"url")
console.log(url)
</script>
<template>
{{ url }}
</template>
<style scoped>
</style>
Pinia
1、Pinia简介
- Pinia是一个轻量级的状态管理库,状态管理库是用于管理应用程序全局状态的工具。
- Pinia可以处理大型项目中复杂的状态管理需求,而父传子、子传父以及跨组件通信,可以解决一些简单的状态传递问题,更适合小型项目。
- 对于复杂的状态管理需求,使用Pinia是更好的选择,而对于简单的状态管理需求,使用localStorage是更简单的解决方案。
- 以登录为例:使用Pinia创建一个userStore来集中管理用户的登录状态和过期时间。
- 当用户登录成功时:设置userStore中用户的登录状态为已登录,并设置过期时间。
- 当用户退出登录时:修改userStore中用户的登录状态为未登录,并删除过期时间。
2、安装Pinia以及定义和使用Store
- 在终端中输入命令安装npm install pinia
javascript
//main.js文件
import { createApp } from 'vue'
//导入Pinia的createPinia方法,用于创建Pinia实例(状态管理库)
import {createPinia} from 'pinia'
import App from './App.vue'
//创建一个Pinia实例,用于在应用中集中管理状态(store)
const pinia = createPinia()
// createApp(App).mount('#app')
const app = createApp(App)
app.use(pinia)//将Pinia实例注册到Vue应用中
app.mount("#app")
//web.js文件
// 定义仓库
import { defineStore } from "pinia";
import { reactive,ref } from "vue";
//定义一个基于Pinia的Store
export const useWebStore = defineStore('web',()=>{
const web=reactive({
title:"仓库管理",
url:"baidu.com"
})
const users=ref(1000)
const userAdd=()=>{
users.value++;
}
return{
web,
users,
userAdd
}
})
//App.vue文件
<script setup>
import {useWebStore} from './stores/web.js'
const webStore =useWebStore()
console.log(webStore.web)
console.log(webStore.users)
</script>
<template>
{{ webStore.web.url }}
{{ webStore.users }}
<button @click="webStore.userAdd">添加用户</button>
</template>
<style scoped>
</style>
3、Pinia持久化存储插件
1、安装npm i pinia-plugin-persistedstate
2、将插件添加到 pinia 实例上
javascript
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
3、创建 Store 时,将 persist
选项设置为 true
。
javascript
//组合式语法
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useStore = defineStore(
'main',
() => {
const someState = ref('你好 pinia')
return { someState }
},
{
persist: true,
},
)