资料
邓睿编程
【2024最新版】3小时学会Vue3,小白零基础视频教程,web前端快速入门实战课程
Vue3快速入门2 -案例篇
Vue.js - 渐进式 JavaScript 框架
Vue3快速入门
1.创建一个 vue 应用程序
html
"渐进式"是指可以按需引入Vue.js的部分功能, 而不必全量引入整个框架
html
<div id="app">
{{ msg }}
<h2>{{ web.title }}</h2>
<h3>{{ web.url }}</h3>
</div>
js
/*
<div id="app"></div> 指定一个 id 为 app 的 div 元素
{{ }} 插值表达式, 可以将 Vue 实例中定义的数据在视图中进行渲染
如: Vue 实例中定义一个 msg 变量, 值为 "Hello world", 在模板中若使用插值表达式 {{ msg }} 则会被渲染成 "Hello world"
响应式数据是指当数据发生变化时, 模板中依赖于该数据的部分会自动更新
*/
/*
//创建一个 Vue 应用程序
Vue.createApp({
//Composition API(组合式 API) 的 setup选项 用于设置响应式数据和方法等
setup() {
//Composition API 的 reactive()函数 用于创建响应式数据
const web = Vue.reactive({ //Vue.reactive 创建一个响应式数据对象 web, 其中包含 title 和 url 属性
title: "邓瑞编程",
url: "dengruicode.com"
})
//返回数据
return {
msg: "success",
web
}
}
}).mount("#app") //将 Vue 应用程序挂载(mount) 到 app 元素上
*/
//将 Vue 对象中的 createApp、reactive 属性赋值给 createApp、reactive 变量
const { createApp, reactive } = Vue //解构赋值语法
createApp({
setup() {
const web = reactive({
title: "邓瑞编程",
url: "dengruicode.com"
})
return {
msg: "success",
web
}
}
}).mount("#app")
2.模块化开发
html
html
<div id="app">
{{ msg }}
<h2>{{ web.title }}</h2>
<h3>{{ web.url }}</h3>
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const web = reactive({
title: "邓瑞编程",
url: "dengruicode.com"
})
return {
msg:"success",
web
}
}
}).mount("#app")
</script>
VSCode 扩展
Live Server
3.ref和reactive
html
html
<div id="app">
msg: {{ msg }}
<h3>web.title: {{ web.title }}</h3>
<h3>web.url: {{ web.url }}</h3>
<h3>web.number: {{ number }}</h3>
</div>
js
<script type="module">
import { createApp, ref, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const number = ref(10) //ref用于存储单个基本类型的数据, 如:数字、字符串等
number.value = 20 //使用ref创建的响应式对象, 需要通过.value属性来访问和修改其值
const web = reactive({ //用于存储复杂数据类型, 如:对象或数组等
title: "邓瑞编程",
url: "dengruicode.com"
})
web.url = "www.dengruicode.com" //使用reactive创建的响应式对象, 可以直接通过属性名来访问和修改值
return {
msg: "success",
number,
web
}
}
}).mount("#app")
</script>
4.绑定事件 v-on 简写@
html
html
<div id="app">
<h3>{{ msg }}</h3>
<h3>{{ web.url }}</h3>
<h3>{{ web.user }}</h3>
<h3>{{ sub(100, 20) }}</h3>
<!-- v-on:click 表示在 button 元素上监听 click 事件 -->
<button v-on:click="edit">修改</button> <br>
<!-- @click 简写形式 -->
<button @click="add(20, 30)">加法</button> <br>
<!--
enter space tab 按键修饰符
keyup是在用户松开按键时才触发
keydown是在用户按下按键时立即触发
-->
回车 <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(10, 20)"> <br>
w <input type="text" @keyup.w="add(5, 10)"> <br>
<!-- 组合快捷键 -->
Ctrl + Enter <input type="text" @keyup.ctrl.enter="add(40, 60)"> <br>
Ctrl + A <input type="text" @keyup.ctrl.a="add(20, 30)">
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const web = reactive({
title: "邓瑞编程",
url: "dengruicode.com",
user: 0
})
const edit = () => {
web.url = "www.dengruicode.com"
//msg = "邓瑞编程" //错误示例 不能直接改变msg的值,因为msg是一个普通变量, 不是响应式数据
}
const add = (a, b) => {
web.user += a + b
}
const sub = (a, b) => {
return a - b
}
return {
msg: "success", //普通变量, 非响应式数据, 在模板中普通变量不会自动更新
web, //响应式数据
edit, //方法
add,
sub,
}
}
}).mount("#app")
</script>
5.显示和隐藏 v-show
html
html
<div id="app">
<h3>{{ web.show }}</h3>
<p v-show="web.show">邓瑞编程 dengruicode.com</p>
<button @click="toggle">点击切换显示状态</button>
</div>
js
<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>
6.条件渲染 v-if
html
html
<div id="app">
<h3>{{ web.show }}</h3>
<p v-show="web.show">邓瑞编程</p>
<p v-if="web.show">dengruicode.com</p>
<button @click="toggle">点击切换显示状态</button>
<p v-if="web.user < 1000">新网站</p>
<p v-else-if="web.user >= 1000 && web.user < 10000">优秀网站</p>
<p v-else-if="web.user >= 10000 && web.user < 100000">资深网站</p>
<p v-else>超级网站</p>
</div>
js
<script type="module">
/*
v-show 通过 css display属性 来控制元素的显示或隐藏
v-if 用于对元素进行条件渲染. 当条件为 true 时, 渲染该元素, 为 false 时, 则不渲染
v-show 适用于频繁切换元素的显示状态, 因为只改变 display 属性, 不需要重新渲染整个组件
v-if 适用于较少改变的场景, 因为频繁从 dom 中删除或添加元素, 会导致性能下降
*/
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const web = reactive({
show: true,
user: 500
})
const toggle = () => {
web.show = !web.show
}
return {
web,
toggle
}
}
}).mount("#app")
</script>
7.动态属性绑定 v-bind 简写:
html
html
<div id="app">
<!-- :value -->
<h3>value="dengruicode.com"</h3>
<input type="text" value="dengruicode.com">
<h3>v-bind:value="web.url"</h3>
<input type="text" v-bind:value="web.url">
<h3>简写 :value="web.url"</h3>
<input type="text" :value="web.url">
<!-- :src -->
<h3>src="windows.jpg"</h3>
<img src="windows.jpg">
<h3>:src="web.img"</h3>
<img :src="web.img">
<!-- :class -->
<h3>class="textColor"</h3>
<b class="textColor">邓瑞编程</b>
<h3>:class="{textColor:web.fontStatus}"</h3>
<b :class="{textColor:web.fontStatus}">dengruicode.com</b>
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const web = reactive({
url: "www.dengruicode.com",
img: "windows.jpg",
fontStatus: true
})
return {
web
}
}
}).mount("#app")
</script>
8.遍历数组或对象 v-for
html
html
<div id="app">
<ul>
<li v-for="(value, index) in data.number">
index=> {{ index }} : value=> {{ value }}
</li>
</ul>
<ul>
<li v-for="value in data.user">
value=> {{ value }}
</li>
</ul>
<ul>
<li v-for="(value, key) in data.user">
key=> {{ key }} : value=> {{ value }}
</li>
</ul>
<ul>
<li v-for="(value, key, index) in data.user">
index=> {{ index }} : key=> {{ key }} : value=> {{ value }}
</li>
</ul>
<ul>
<!-- <template> 标签可以用来包装多个元素或者多行代码, 不会在页面中渲染 -->
<template v-for="(value, key, index) in data.user">
<li v-if="index == 1">
index=> {{ index }} : key=> {{ key }} : value=> {{ value }}
</li>
</template>
</ul>
<ul>
<!-- :key="value.id" 为 每个 li 元素设置一个唯一的 key 值 -->
<li v-for="(value, index) in data.teacher" :title="value.name" :key="value.id">
index=> {{ index }} : value.id=>{{ value.id }} value.name=>{{ value.name }} value.web=>{{ value.web }}
</li>
</ul>
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const data = reactive({
number: ["十", "十一", "十二"], //数组
user: { //对象
name: "Luna",
gender: "女"
},
teacher: [ //包含两个对象的数组
{ id: 100, name: "邓瑞", web: "dengruicode.com" },
{ id: 101, name: "David", web: "www.dengruicode.com" }
]
})
return {
data
}
}
}).mount("#app")
</script>
9.双向数据绑定 v-model
html
html
<div id="app">
<h3>文本框 {{ data.text }}</h3>
<h3>单选框 {{ data.radio }}</h3>
<h3>复选框 {{ data.checkbox }}</h3>
<h3>记住密码 {{ data.remember }}</h3>
<h3>下拉框 {{ data.select }}</h3>
<!-- 单向数据绑定 当数据发生改变时, 视图会自动更新. 但用户手动更改 input 的值, 数据不会自动更新 -->
单向数据绑定 <input type="text" :value="data.text">
<hr>
<!--
双向数据绑定 当数据发生改变时, 视图会自动更新. 当用户手动更改 input 的值, 数据也会自动更新
对于 <input type="text">, v-model 绑定的是 input 元素的 value 属性
-->
双向数据绑定 <input type="text" v-model="data.text">
<hr>
<!--
单选框
对于 <input type="radio">, v-model 绑定的是 input 元素的选中状态
-->
<input type="radio" v-model="data.radio" value="1">写作
<input type="radio" v-model="data.radio" value="2">画画
<hr>
<!--
复选框
对于 <input type="checkbox">, v-model 绑定的是 input 元素的选中状态
-->
<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">运动
<hr>
<!-- 记住密码 -->
<input type="checkbox" v-model="data.remember">记住密码
<hr>
<!--
下拉框
对于 <select>, v-model 绑定的是 select 元素的选中状态
-->
<select v-model="data.select">
<option value="">请选择</option>
<option value="A">写作</option>
<option value="B">画画</option>
<option value="C">运动</option>
</select>
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const data = reactive({
text: "dengruicode.com", //文本框
radio: "", //单选框
checkbox: [], //复选框
remember: false, //单个复选框-记住密码
select: "" //下拉框
})
return {
data
}
}
}).mount("#app")
</script>
10.v-model修饰符
html
html
<div id="app">
<h3>文本框 {{ data.text }}</h3>
<h3>单选框 {{ data.radio }}</h3>
<h3>复选框 {{ data.checkbox }}</h3>
<h3>记住密码 {{ data.remember }}</h3>
<h3>下拉框 {{ data.select }}</h3>
<!-- 单向数据绑定 当数据发生改变时, 视图会自动更新. 但用户手动更改 input 的值, 数据不会自动更新 -->
单向数据绑定 <input type="text" :value="data.text">
<hr>
<!--
双向数据绑定 当数据发生改变时, 视图会自动更新. 当用户手动更改 input 的值, 数据也会自动更新
对于 <input type="text">, v-model 绑定的是 input 元素的 value 属性
-->
双向数据绑定 <input type="text" v-model="data.text">
<hr>
<!--
单选框
对于 <input type="radio">, v-model 绑定的是 input 元素的选中状态
-->
<input type="radio" v-model="data.radio" value="1">写作
<input type="radio" v-model="data.radio" value="2">画画
<hr>
<!--
复选框
对于 <input type="checkbox">, v-model 绑定的是 input 元素的选中状态
-->
<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">运动
<hr>
<!-- 记住密码 -->
<input type="checkbox" v-model="data.remember">记住密码
<hr>
<!--
下拉框
对于 <select>, v-model 绑定的是 select 元素的选中状态
-->
<select v-model="data.select">
<option value="">请选择</option>
<option value="A">写作</option>
<option value="B">画画</option>
<option value="C">运动</option>
</select>
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const data = reactive({
text: "dengruicode.com", //文本框
radio: "", //单选框
checkbox: [], //复选框
remember: false, //单个复选框-记住密码
select: "" //下拉框
})
return {
data
}
}
}).mount("#app")
</script>
11.渲染数据 v-text 和 v-html
html
html
<div id="app">
<h3>{{ web.title }}</h3>
<!-- v-text 将数据解析为纯文本格式 -->
<h3 v-text="web.title"></h3>
<!-- v-html 将数据解析为 html 格式 -->
<h3 v-html="web.url"></h3>
</div>
js
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const web = reactive({
title: "邓瑞编程",
url:"<i style='color:blue;'>www.dengruicode.com</i>"
})
return {
web
}
}
}).mount("#app")
</script>
12.计算属性 computed
html
html
<div id="app">
<h3>add: {{ add() }}</h3>
<h3>add: {{ add() }}</h3>
<h3>sum: {{ sum }}</h3>
<h3>sum: {{ sum }}</h3>
x <input type="text" v-model.number="data.x"> <br>
y <input type="text" v-model.number="data.y">
</div>
js
<script type="module">
import { createApp, reactive, computed } from './vue.esm-browser.js'
createApp({
setup() {
const data = reactive({
x: 10,
y: 20
})
//方法-无缓存
let add = () => {
console.log("add") //打印两次
return data.x + data.y
}
//计算属性-有缓存 [计算属性根据其依赖的响应式数据变化而重新计算]
const sum = computed(() => {
console.log("sum") //打印一次
return data.x + data.y
})
return {
data,
sum,
add
}
}
}).mount("#app")
</script>
13.侦听器 watch
html
html
<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="date.year">
<option value="">请选择</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
<option value="2025">2025</option>
</select>
月
<select v-model="date.month">
<option value="">请选择</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</div>
js
<script type="module">
import { createApp, ref, reactive, watch } from './vue.esm-browser.js'
createApp({
setup() {
const hobby = ref("") //爱好
const date = reactive({ //日期
year: "2023",
month: "10"
})
//监听 hobby
watch(hobby, (newValue, oldValue) => {
console.log("oldValue", oldValue, "newValue", newValue)
if (newValue == "2") {
console.log("画画")
}
})
//监听 date
watch(date, (newValue, oldValue) => {
/*
JS中对象和数组是通过引用传递的, 而不是通过值传递
当修改对象或数组的值时, 实际上修改的是对象或数组的引用, 而不是创建一个新的对象或数组
所以,如果修改了对象或数组的值,那么打印出来的结果则是修改后的值
*/
console.log("oldValue", oldValue, "newValue", newValue)
if (newValue.year == "2025") {
console.log("2025")
}
if (newValue.month == "11") {
console.log("11")
}
})
//监听 date 中的某个属性 year
watch(() => date.year, (newValue, oldValue) => {
console.log("oldValue", oldValue, "newValue", newValue)
if (date.year == "2024") {
console.log("2024")
}
})
return {
hobby,
date
}
}
}).mount("#app")
</script>
14.自动侦听器 watchEffect
html
html
<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="date.year">
<option value="">请选择</option>
<option value="2023">2023</option>
<option value="2024">2024</option>
<option value="2025">2025</option>
</select>
月
<select v-model="date.month">
<option value="">请选择</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
</select>
</div>
js
<script type="module">
/*
watch需要显式指定要监听的属性, 并且只有当监听的属性发生变化时才会执行
若需要更精细地控制或需要获取到原值, 需要使用watch
*/
import { createApp, ref, reactive, watchEffect } from './vue.esm-browser.js'
createApp({
setup() {
const hobby = ref("") //爱好
const date = reactive({ //日期
year: "2023",
month: "10"
})
//自动监听
watchEffect(() => {
console.log("------ 监听开始")
if (hobby.value == "2") {
console.log("画画")
}
if (date.year == "2025") {
console.log("2025")
}
if (date.month == "11") {
console.log("11")
}
console.log("------ 监听结束")
})
return {
hobby,
date
}
}
}).mount("#app")
</script>
Vue3快速入门2-案例篇
2.图片轮播案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h3>{{ number }}</h3>
<!-- <img src="/images/1.jpg" style="width: 300px;"> -->
<img :src=`/images/${number}.jpg` style="width: 300px;"> <hr>
<button @click="prev">上一张</button>
<button @click="next">下一张</button>
<ul>
<li v-for="(value, index) in 4">
<a href="#" @click="jump(value)">{{ value }}</a>
</li>
</ul>
</div>
<script type="module">
import { createApp, ref } from './vue.esm-browser.js'
createApp({
setup() {
const number = ref(1)
//上一张
const prev = () => {
number.value--
if (number.value == 0) {
number.value = 4
}
}
//下一张
const next = () => {
number.value++
if (number.value == 5) {
number.value = 1
}
}
//跳转
const jump = (value) => {
number.value = value
}
return {
number,
prev,
next,
jump
}
}
}).mount("#app")
</script>
</body>
</html>
3.记事本案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-model="data.content">
<button @click="add">添加</button> <hr>
<ul>
<li v-for="(value, index) in data.list">
{{ value }} <button @click="del(index)">删除</button>
</li>
</ul>
记录数 {{ data.list.length }} <br>
<button @click="clear">清空</button>
</div>
<script type="module">
import { createApp, reactive } from './vue.esm-browser.js'
createApp({
setup() {
const data = reactive({
content: "",
list: ["邓瑞编程", "dengruicode.com"],
})
//添加
const add = () => {
if (data.content == "") {
alert("请填写内容")
return
}
data.list.push(data.content) //push 向数组末尾添加一个或多个元素
data.content = "" //清空文本框
}
//删除
const del = (index) => {
data.list.splice(index, 1) //splice(要删除元素的索引位置, 要删除的元素数量)
}
//清空
const clear = () => {
data.list = []
}
return {
data,
add,
del,
clear
}
}
}).mount("#app")
</script>
</body>
</html>
4.购物车案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
table {
width: 600px;
color: #8f8e8e;
text-align: center;
border-collapse: collapse;
}
table thead {
background: #F5F5F5;
}
table tr {
height: 30px;
line-height: 30px;
border: 1px solid #ececec;
}
</style>
</head>
<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" v-model="data.checkboxList" :value="value" @change="checkSelect" />
</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 } 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 sub = (value) => {
value.number--
if (value.number <= 1) {
value.number = 1
}
}
//加
const add = (value) => {
value.number++
if (value.number >= value.stock) {
value.number = value.stock
}
}
//删除
const del = (index, id) => {
data.list.splice(index, 1) //splice(要删除元素的索引位置, 要删除的元素数量)
//filter 筛选符合条件的元素, 返回一个新的数组
let newArr = data.checkboxList.filter((value, index) => {
return value.id != id
})
data.checkboxList = newArr
checkSelect() //检查勾选状态
}
//总价
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 selectAll = () => {
if (data.selected) { //true
data.checkboxList = data.list
} else { //false
data.checkboxList = []
}
}
//检查勾选状态
const checkSelect = () => {
if (data.checkboxList.length != data.list.length || data.list.length == 0) {
data.selected = false
} else {
data.selected = true
}
}
return {
data,
sub,
add,
del,
totalPrice,
selectAll,
checkSelect
}
}
}).mount("#app")
</script>
</body>
</html>
5.购物车优化案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
table {
width: 600px;
color: #8f8e8e;
text-align: center;
border-collapse: collapse;
}
table thead {
background: #F5F5F5;
}
table tr {
height: 30px;
line-height: 30px;
border: 1px solid #ececec;
}
</style>
</head>
<body>
<div id="app">
<table>
<thead>
<tr>
<!-- <td><input type="checkbox" v-model="data.selected" @change="selectAll" /></td> -->
<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" v-model="data.checkboxList" :value="value" @change="checkSelect" /></td>
-->
<td><input type="checkbox" v-model="data.checkboxList" :value="value" /></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> -->
<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
}],
})
//减
const sub = (value) => {
value.number--
if (value.number <= 1) {
value.number = 1
}
}
//加
const add = (value) => {
value.number++
if (value.number >= value.stock) {
value.number = value.stock
}
}
//删除
const del = (index, id) => {
data.list.splice(index, 1) //splice(要删除元素的索引位置, 要删除的元素数量)
//filter 筛选符合条件的元素, 返回一个新的数组
let newArr = data.checkboxList.filter((value, index) => {
return value.id != id
})
data.checkboxList = newArr
//checkSelect() //检查勾选状态
}
/*
//总价
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 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 selectAll = () => {
if (data.selected) { //true
data.checkboxList = data.list
} else { //false
data.checkboxList = []
}
}
*/
//监听 data.selected
let flag = true
watch(() => data.selected, (newValue, oldValue) => {
//console.log("newValue:",newValue,"oldValue:",oldValue)
if (newValue) {
data.checkboxList = data.list
} else {
if (flag) {
data.checkboxList = []
}
}
//console.log(data.checkboxList)
})
/*
//检查勾选状态
const checkSelect = () => {
if (data.checkboxList.length == data.list.length && data.list.length != 0) {
data.selected = true
} else {
data.selected = false
}
}
*/
//监听 data.checkboxList
watch(() => data.checkboxList, (newValue, oldValue) => {
console.log("newValue:", newValue, "oldValue:", oldValue)
console.log(newValue.length)
if (newValue.length == data.list.length && data.list.length != 0) {
data.selected = true
flag = true
} else {
data.selected = false
flag = false
}
})
return {
data,
sub,
add,
del,
totalPrice,
//selectAll,
//checkSelect
}
}
}).mount("#app")
</script>
</body>
</html>
6.使用Axios实现文章搜索案例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/axios.min.js"></script>
</head>
<body>
<div id="app">
<select v-model="data.type">
<option value="0">请选择</option>
<option value="1">ID</option>
<option value="2">标题</option>
</select>
<input type="text" v-model="data.content">
<button @click="search">搜索</button>
<ul>
<li v-for="(value, index) in data.list">
{{ value }}
</li>
</ul>
</div>
<script type="module">
import { createApp, reactive } from './js/vue.esm-browser.js'
createApp({
setup() {
const data = reactive({
type: "0", //搜索类型
content: "", //搜索内容
list: [],
})
//搜索
const search = () => {
//console.log(data.content)
data.list = [] //清空
if (data.type == "1") {
let id = data.content //参数
//get请求
axios.get(`http://127.0.0.1/article/get/id/${id}`).then(response => {
console.log("get.data:", response.data)
if(response.data.status == "success"){
if(response.data.data){
data.list.push(response.data.data) //push 向数组末尾添加一个或多个元素
}
}
}).catch(error => {
console.log("get.error:", error)
})
} else if (data.type == "2") {
//参数
let param = {
title: data.content
}
//post请求 [axios post的默认请求头是 application/json]
axios.post('http://127.0.0.1/article/postJson/search', param).then(response => {
console.log("postJson.data:", response.data)
if(response.data.status == "success"){
for(let i=0; i<response.data.data.length; i++){
data.list.push(response.data.data[i]) //push 向数组末尾添加一个或多个元素
}
}
}).catch(error => {
console.log("postJson.error:", error)
})
}
}
return {
data,
search
}
}
}).mount("#app")
</script>
</body>
</html>
Vue3快速入门3-组件篇
1.基于Vite创建Vue3项目
html
官网
https://cn.vitejs.dev
基于Vite创建Vue3项目
npm create vite@latest
Ok to proceed? (y) >> y
Project name: >> demo
Select a framework: >> Vue
Select a variant: >> JavaScript
Done. Now run:
cd demo
npm install
npm run dev
Local: http://localhost:5173
删除文件
src\style.css
src\components\HelloWorld.vue
删除代码
main.js
import './style.css'
修改代码
src\App.vue
<script setup>
</script>
<template>
dengruicode.com
</template>
<style scoped>
</style>
2.Vue3好用的VsCode插件
html
Vue Language Features (Volar)
TypeScript Vue Plugin (Volar)
Vue VSCode Snippets
别名路径跳转
注
Vue VSCode Snippets自定义模板
C:\Users\David\.vscode\extensions\sdras.vue-vscode-snippets-3.1.1\snippets
模板
vbase-3-setup
vbase-3-ts-setup
重启vscode
3.导入组件
html
<script setup>
//导入子组件
//App.vue是父组件,因为它包含了header.vue和footer.vue两个子组件
import Header from "./components/header.vue"
import Footer from "./components/footer.vue"
</script>
<template>
<Header/>
dengruicode.com
<Footer/>
</template>
<style scoped>
</style>
4.父传子 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,
ip: '127.0.0.1'
}
*/
//响应式数据
const propsWeb = reactive({
user: 10,
ip: '127.0.0.1'
})
//添加用户
const userAdd = () => {
propsWeb.user++
console.log(propsWeb.user)
}
</script>
<template>
<!-- 父传子 - 方式1 -->
<Header propsName="邓瑞编程" propsUrl="dengruicode.com" />
dengruicode.com
<button @click="userAdd">添加用户</button>
<!-- 父传子 - 方式2 -->
<!-- <Footer v-bind="propsWeb" /> -->
<Footer :="propsWeb" />
</template>
<style scoped></style>
header.vue
<script setup>
//子组件
//接收方式1 - 数组
/*
defineProps是Vue3的编译时宏函数,
用于接收父组件向子组件传递的属性(props)
注
当使用Vue编译器编译包含defineProps的组件时,
编译器会将这些宏替换为相应的运行时代码
*/
const props = defineProps(["propsName","propsUrl"])
console.log(props)
</script>
<template>
<h3>Header</h3>
</template>
<style scoped>
</style>
footer.vue
<script setup>
//子组件
//接收方式2 - 对象
/*
const props = defineProps({
user: Number,
ip: String
})
*/
const props = defineProps({
user: Number,
ip: {
type: String,
required: true, //true表示必传属性,若未传则会提示警告信息
default: 'localhost' //未传默认值
}
})
console.log(props)
</script>
<template>
<h3>Footer</h3>
user: {{ props.user }}
</template>
<style scoped>
</style>
5.子传父 defineEmits
html
App.vue
<script setup>
import { reactive,ref } from 'vue'
//导入子组件
import Header from "./components/header.vue"
//响应式数据
const web = reactive({
name: "邓瑞编程",
url: 'dengruicode.com'
})
const user = ref(0)
//子传父
const emitsWeb = (data) => {
console.log("emitsWeb:",data)
web.url = data.url
}
const emitsUser = (data) => {
console.log("emitsUser:",data)
user.value += data
}
</script>
<template>
<!-- 子传父 -->
<Header @web="emitsWeb" @user="emitsUser" />
{{ web.url }} - {{ user }}
</template>
<style scoped></style>
header.vue
<script setup>
//子组件
/*
defineEmits是Vue3的编译时宏函数,
用于子组件向父组件发送自定义事件
*/
//子传父
//定义一个名为 emits 的对象, 用于存储自定义事件
const emits = defineEmits(["web","user"])
//发送名为 web 和 user 的自定义事件
emits("web", {name:"邓瑞",url:"www.dengruicode.com"})
//添加用户
const userAdd = () => {
//发送名为 user 的自定义事件
emits("user", 10)
}
</script>
<template>
<h3>Header</h3>
<button @click="userAdd">添加用户</button>
</template>
<style scoped>
</style>
6.跨组件通信-依赖注入
html
App.vue
<script setup>
import { provide, ref } from 'vue'
//导入子组件
import Header from "./components/header.vue"
//provide用于父组件将 数据 提供给所有子组件
/*
若使用了provide和inject来进行数据传递,
则一般不需要再使用defineProps
*/
provide("provideWeb",{name:"邓瑞",url:"www.dengruicode.com"})
//传递响应式数据
const user = ref(0)
provide("provideUser",user)
//添加用户
const userAdd = () => {
user.value++
}
//用于父组件将 函数 提供给所有子组件
provide("provideFuncUserAdd",userAdd)
</script>
<template>
<h3>App.vue-Top组件</h3>
{{ user }}
<!-- 子组件 -->
<Header/>
</template>
<style scoped></style>
header.vue
<script setup>
import { provide, inject } from 'vue'
//导入子组件
import Nav from "./nav.vue"
//子组件通过inject注入父组件提供的 响应式数据
const user = inject("provideUser")
console.log("provideUser:",user.value)
//provide用于父组件将 数据 提供给所有子组件
provide("provideUrl","dengruicode.com")
</script>
<template>
<h3>header.vue-Middle组件</h3>
<!-- 子组件 -->
<Nav/>
</template>
<style scoped>
</style>
nav.vue
<script setup>
//子组件
import { inject } from 'vue'
//子组件通过inject注入父组件提供的 数据
const web = inject("provideWeb")
console.log("provideWeb:",web)
const url = inject("provideUrl")
console.log("provideUrl:",url)
//子组件通过inject注入父组件提供的 函数
const funcUserAdd = inject("provideFuncUserAdd")
console.log("provideFuncUserAdd:",funcUserAdd)
</script>
<template>
<h3>nav.vue-Bottom组件</h3>
<button @click="funcUserAdd">添加用户</button>
</template>
<style scoped>
</style>
7.匿名插槽和具名插槽
html
插槽(slot)
是指可以在父组件内自定义模板片段,
在子组件中可以将定义的模板片段插入到子组件的特定位置
App.vue
<script setup>
//导入子组件
import Header from "./components/header.vue"
import Footer from "./components/footer.vue"
</script>
<template>
<h3>App.vue</h3>
<!-- <Header/> -->
<!-- 匿名插槽 -->
<Header>
<a href="http://dengruicode.com">邓瑞编程</a>
</Header>
<!-- 具名插槽 -->
<Footer>
<template v-slot:url>
<a href="http://www.dengruicode.com">网址</a>
</template>
<!-- v-slot:user 简写 #user -->
<template #user>
1000
</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" />
<slot name="user" />
</template>
<style scoped>
</style>
8.作用域插槽
html
作用域插槽
子组件向父组件传递数据,并在父组件定义的模板中渲染
App.vue
<script setup>
//导入子组件
import Header from "./components/header.vue"
import Footer from "./components/footer.vue"
</script>
<template>
<h3>App.vue</h3>
<!-- <Header/> -->
<!-- 匿名插槽 -->
<Header>
<a href="http://dengruicode.com">邓瑞编程</a>
</Header>
<!-- 具名插槽 -->
<Footer>
<template v-slot:url>
<a href="http://www.dengruicode.com">网址</a>
</template>
<!--
v-slot:user 简写 #user
作用域插槽
子组件将url和title数据传递给 name="user" 的插槽,
父组件通过 #user="data" 来接收这些数据
<template #user="data">
1000 {{ data.url }} {{ data.title }}
</template>
-->
<!-- 解构 -->
<template #user="{url,title}">
1000 {{ url }} {{ title }}
</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" />
<slot name="user" url="dengruicode.com" title="邓瑞编程" />
</template>
<style scoped>
</style>
9.生命周期函数
html
生命周期函数
是组件实例从创建到销毁过程中不同时间点自动调用的函数
挂载阶段
onBeforeMount
在组件实例即将被挂载到DOM树之前调用
此时模板还未编译或渲染到DOM,通常用于执行初始化操作,
如:获取异步数据、设置初始属性值等
onMounted
在组件成功挂载到DOM并完成首次渲染后调用
此时可以访问和操作DOM元素,
并执行与页面交互相关的逻辑
更新阶段
onBeforeUpdate (由于响应式数据变化)
在组件更新之前即将重新渲染时调用
可以根据新的参数判断是否需要进行特殊处理,
甚至可以选择阻止此次更新过程
onUpdated
在组件完成更新并重新渲染后调用
可以基于新的渲染结果处理更新后的数据
卸载阶段
onBeforeUnmount
在组件从DOM中销毁之前调用
用于释放资源,如:清理计时器、解绑事件监听器等
onUnmounted
在组件已经从DOM中移除并销毁后调用
确保组件所占用的所有资源都被正确释放
错误处理
onErrorCaptured
在捕获到组件中的错误时调用
用于处理错误,如:记录错误日志等
注
组件挂载的过程
模板编译
将组件的模板转换为JS代码
渲染
在模板编译后生成的JS代码渲染到页面上,
生成虚拟DOM
挂载
在渲染完成后将虚拟DOM挂载到真实的DOM树上,
使其在页面上显示出来
<script setup>
import { onMounted, onUpdated, ref } from 'vue'
//在组件成功挂载到DOM并完成首次渲染后调用
onMounted(() => {
console.log("onMounted")
})
//在组件更新之后调用
onUpdated(() => {
console.log("onUpdated:",user.value)
})
const user = ref(0)
console.log("user:",user.value)
</script>
<template>
<h3>App.vue</h3>
{{ user }}
<button @click="user++">添加用户</button>
</template>
<style scoped>
</style>
10.toRef和toRefs
html
<script setup>
import { reactive, toRef, toRefs } from 'vue'
/*
let {name,url} = reactive({
name:"邓瑞编程",
url:"dengruicode.com"
})
*/
let web = reactive({
name:"邓瑞编程",
url:"dengruicode.com"
})
//toRefs将一个响应式对象的所有属性转换为ref对象
//let {name,url} = toRefs(web)
//toRef将一个响应式对象的某个属性转换为ref变量
let url = toRef(web, "url")
const setUrl = () => {
console.log(url)
url.value = "www.dengruicode.com"
}
</script>
<template>
{{ url }}
<button @click="setUrl">设置网址</button>
</template>
<style scoped>
</style>
Vue3 快速入门4-Pinia
1.Pinia 简介
html
Pinia是一个轻量级的状态管理库
Pinia官网
https://pinia.vuejs.org/zh
状态管理库是用于管理应用程序全局状态的工具
以登录为例:
使用Pinia创建一个userStore来集中管理用户的登录状态和过期时间
当用户登录成功时:
设置userStore中用户的登录状态为已登录,并设置过期时间
当用户退出登录时:
修改userStore中用户的登录状态为未登录,并删除过期时间
Pinia 和 组件通信 的区别
虽然Vue提供的父传子、子传父以及跨组件通信也可以用于状态共享,
但在大型项目中,随着组件数量的增加,会导致以下问题:
1.组件之间传递大量的props,会使项目变得非常繁琐和难以维护
2.非父子组件间过度依赖provide/inject,使状态散落在各个组件之间
Pinia 可以解决以下问题:
1.全局状态管理
所有组件都可以访问和修改状态,而不用在每个组件内部进行状态管理
2.简化组件之间的通信
避免在组件之间传递大量的props
3.状态持久化
可以将应用程序的状态保存到本地存储中,
在应用程序重启后会保留状态,对于登录等场景非常有用
总的来说,Pinia可以处理大型项目中复杂的状态管理需求,
而父传子、子传父以及跨组件通信,可以解决一些简单的状态传递问题,
更适合小型项目
Pinia 和 localStorage 的区别
localStorage
LocalStorage只能存储字符串类型的数据
LocalStorage有大小限制,通常为5MB左右
Pinia
Pinia可以存储任何类型的数据,包括对象、数组等
Pinia没有大小限制,可以存储大量的数据
总的来说,对于复杂的状态管理需求,使用Pinia是更好的选择,
而对于简单的状态管理需求,使用localStorage是更简单的解决方案
2.安装 Pinia 以及定义和使用 Store
html
安装Pinia
npm install pinia
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 { reactive, ref } from 'vue'
import { defineStore } from 'pinia'
/*
定义一个基于 Pinia 的 Store
第1个参数 web 是 useWebStore 在应用中的唯一标识符(ID)
第2个参数是 Setup函数 或 Option对象
*/
export const useWebStore = defineStore('web', () => {
//定义一个响应式对象,存储网站信息
const web = reactive({
title: "邓瑞编程",
url: "dengruicode.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:",webStore.web)
console.log("webStore.users:",webStore.users)
</script>
<template>
{{ webStore.web.url }}
{{ webStore.users }}
<button @click="webStore.userAdd" >添加用户</button>
</template>
<style scoped>
</style>
3.Pinia 持久化存储插件
html
官网
https://prazdevs.github.io/pinia-plugin-persistedstate/zh
安装
npm i pinia-plugin-persistedstate
注
pinia持久化插件也是存储到localStorage中,
为什么不直接使用localStorage?
自动状态同步
持久化插件自动将Pinia的状态存储到localStorage中,
无需手动处理状态的读取和写入
易用性
无需手动处理localStorage的键值对存储、数据转换等繁琐过程
与Vue组件状态紧密集成
持久化插件与Vue组件的响应式数据完美结合
当状态改变时,依赖这些状态的组件会自动更新视图
与仅仅从localStorage中读取静态数据相比更加灵活和强大
main.js
import { createApp } from 'vue'
//导入Pinia的createPinia方法,用于创建Pinia实例(状态管理库)
import { createPinia } from 'pinia'
//从 pinia-plugin-persistedstate 模块中导入 piniaPluginPersistedstate
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import App from './App.vue'
//创建一个Pinia实例,用于在应用中集中管理状态(store)
const pinia = createPinia()
//将插件添加到 pinia 实例上
pinia.use(piniaPluginPersistedstate)
//createApp(App).mount('#app')
const app = createApp(App)
app.use(pinia) //将Pinia实例注册到Vue应用中
app.mount('#app')
web.js
import { reactive, ref } from 'vue'
import { defineStore } from 'pinia'
/*
定义一个基于 Pinia 的 Store
第1个参数 web 是 useWebStore 在应用中的唯一标识符(ID)
第2个参数是 Setup函数 或 Option对象
*/
export const useWebStore = defineStore('web', () => {
//定义一个响应式对象,存储网站信息
const web = reactive({
title: "邓瑞编程",
url: "dengruicode.com"
})
//定义一个响应式引用,存储用户数
const users = ref(1000)
//定义方法
const userAdd = () => {
users.value++
}
return {
web,
users,
userAdd
}
},
{
//持久化存储到 localStorage 中
persist: true
})