资料
邓睿编程
【2024最新版】3小时学会Vue3,小白零基础视频教程,web前端快速入门实战课程
Vue3快速入门2 -案例篇
Vue.js - 渐进式 JavaScript 框架
Vue3快速入门
1.创建一个 vue 应用程序
"渐进式"是指可以按需引入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
<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
<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
<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
<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
<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
<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
<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
<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
<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
<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
<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
<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"})//监听 hobbywatch(hobby, (newValue, oldValue) => {console.log("oldValue", oldValue, "newValue", newValue)if (newValue == "2") {console.log("画画")}})//监听 datewatch(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 中的某个属性 yearwatch(() => 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
<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.图片轮播案例
<!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.记事本案例
<!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.购物车案例
<!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 = newArrcheckSelect() //检查勾选状态}//总价const totalPrice = () => {let total = 0for (let i = 0; i < data.checkboxList.length; i++) {total += data.checkboxList[i].price * data.checkboxList[i].number}return total}//全选/反选const selectAll = () => {if (data.selected) { //truedata.checkboxList = data.list} else { //falsedata.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.购物车优化案例
<!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 = 0for (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(累加器) 用于存储每次计算的结果, 初始值为 0item(当前元素) 在每次迭代过程中, 当前元素的值会被传递给回调函数*/return data.checkboxList.reduce((total, item) => total + item.price * item.number, 0)})/*//全选/反选const selectAll = () => {if (data.selected) { //truedata.checkboxList = data.list} else { //falsedata.checkboxList = []}}*///监听 data.selectedlet flag = truewatch(() => 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.checkboxListwatch(() => 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 = trueflag = true} else {data.selected = falseflag = false}}) return {data,sub,add,del,totalPrice,//selectAll,//checkSelect}}}).mount("#app")</script>
</body></html>
6.使用Axios实现文章搜索案例
<!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项目
官网https://cn.vitejs.dev基于Vite创建Vue3项目npm create vite@latestOk to proceed? (y) » yProject name: » demoSelect a framework: » VueSelect a variant: » JavaScriptDone. Now run:cd demonpm installnpm run devLocal: http://localhost:5173删除文件src\style.csssrc\components\HelloWorld.vue删除代码main.jsimport './style.css'修改代码src\App.vue<script setup></script><template>dengruicode.com</template><style scoped></style>
2.Vue3好用的VsCode插件
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-setupvbase-3-ts-setup重启vscode
3.导入组件
<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
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
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.跨组件通信-依赖注入
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.匿名插槽和具名插槽
插槽(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.作用域插槽
作用域插槽子组件向父组件传递数据,并在父组件定义的模板中渲染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.生命周期函数
生命周期函数是组件实例从创建到销毁过程中不同时间点自动调用的函数挂载阶段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
<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 简介
Pinia是一个轻量级的状态管理库Pinia官网https://pinia.vuejs.org/zh状态管理库是用于管理应用程序全局状态的工具以登录为例:使用Pinia创建一个userStore来集中管理用户的登录状态和过期时间当用户登录成功时:设置userStore中用户的登录状态为已登录,并设置过期时间当用户退出登录时:修改userStore中用户的登录状态为未登录,并删除过期时间Pinia 和 组件通信 的区别虽然Vue提供的父传子、子传父以及跨组件通信也可以用于状态共享,但在大型项目中,随着组件数量的增加,会导致以下问题:1.组件之间传递大量的props,会使项目变得非常繁琐和难以维护2.非父子组件间过度依赖provide/inject,使状态散落在各个组件之间Pinia 可以解决以下问题:1.全局状态管理所有组件都可以访问和修改状态,而不用在每个组件内部进行状态管理2.简化组件之间的通信避免在组件之间传递大量的props3.状态持久化可以将应用程序的状态保存到本地存储中,在应用程序重启后会保留状态,对于登录等场景非常有用总的来说,Pinia可以处理大型项目中复杂的状态管理需求,而父传子、子传父以及跨组件通信,可以解决一些简单的状态传递问题,更适合小型项目Pinia 和 localStorage 的区别localStorageLocalStorage只能存储字符串类型的数据LocalStorage有大小限制,通常为5MB左右PiniaPinia可以存储任何类型的数据,包括对象、数组等Pinia没有大小限制,可以存储大量的数据总的来说,对于复杂的状态管理需求,使用Pinia是更好的选择,而对于简单的状态管理需求,使用localStorage是更简单的解决方案
2.安装 Pinia 以及定义和使用 Store
安装Pinianpm install piniamain.jsimport { 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.jsimport { 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 持久化存储插件
官网
https://prazdevs.github.io/pinia-plugin-persistedstate/zh
安装
npm i pinia-plugin-persistedstate
注
pinia持久化插件也是存储到localStorage中,
为什么不直接使用localStorage?
自动状态同步持久化插件自动将Pinia的状态存储到localStorage中,无需手动处理状态的读取和写入
易用性无需手动处理localStorage的键值对存储、数据转换等繁琐过程
与Vue组件状态紧密集成持久化插件与Vue组件的响应式数据完美结合当状态改变时,依赖这些状态的组件会自动更新视图与仅仅从localStorage中读取静态数据相比更加灵活和强大main.jsimport { 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.jsimport { 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
})