一文掌握vue3基础,适合自学入门案例丰富
Vue3
本文从Vue3的基础语法出发,全面系统的介绍了Vue3的核心概念与应用,旨在帮助自学者更轻松地掌握Vue3。文章内容由浅入深,从通过CDN引入Vue3开始,逐步介绍了组合式API、模块化开发、以及常见的Vue3指令和功能并从单个html的使用过渡到vite项目工程,案例丰富,总结编者学习经验,适合自学vue3。
文章目录
- Vue3
- 通过cdn引用vue
- 组合式学习
- 模块化开发
- ref 与 reactive
- 绑定事件 v-on 简写 @
- 显示和隐藏 v-show
- 条件渲染 v-if
- 动态属性绑定v-bind
- v-for 遍历数组和对象
- 双向数据绑定 v-model
- v-model修饰符
- 渲染数据 v-text 和 v-html
- 计算属性computed
- 侦听器watch
- 图片轮播案例
- 记事本案例
- change事件&&购物车案例
- 购物车案例优化
- Axios获取后端数据
- 基于vite创建vue3项目
- vue组件学习
- 父传子defineprops
- 子传父defineEmits
- 跨组件通信-依赖注入
- 匿名插槽和具名插槽
- 作用域插槽
- 生命周期函数
- toRef 和 toRefs
- Pinia简介与安装
- 定义和使用Store
- Pinia持久化存储插件
- 鸣谢
渐进式的javascript框架,我们可以逐步引入vue的功能
通过cdn引用vue
你可以借助 script 标签直接通过 CDN 来使用 Vue:
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
通过 CDN 使用 Vue 时,不涉及“构建步骤”。这使得设置更加简单,并且可以用于增强静态的 HTML 或与后端框架集成。但是,你将无法使用单文件组件 (SFC) 语法。
组合式学习
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="vue.global.js"></script>
</head>
<body><div id="app"><h1>{{ message }}</h1><p>{{ web.title }} {{ web.url }}</p></div><script>Vue.createApp({// setup选项,用于设置响应式数据和方法等setup(){const message = Vue.ref('Hello Vue!')// 这是个对象,可以直接在模板中使用const web = Vue.reactive({title: 'Vue.js',url: 'https://cn.vuejs.org/' })return {message: message,// web: webweb}}}).mount('#app')</script></body>
</html>
模块化开发
使用es
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head>
<body><div id="app"><h1>{{ message }}</h1><p>{{ web.title }} {{ web.url }}</p></div><script type="module">import { createApp , reactive} from './vue.esm-browser.js'createApp({// setup选项,用于设置响应式数据和方法等setup(){// 这是个对象,可以直接在模板中使用const web = reactive({title: 'Vue.js',url: 'https://cn.vuejs.org/' })return {message: 'Hello Vue.js',// web: webweb}}}).mount('#app')</script></body>
</html>
ref 与 reactive
ref是一个引用,修改使用.value, 而reactive则直接修改对应属性即可
ref也可以存储数组
绑定事件 v-on 简写 @
显示和隐藏 v-show
<p v-show="show">...</p>
当show为false就隐藏
条件渲染 v-if
<p v-if="show"> ... </p>
不适用于频繁切换显示状态以下是更好的用法
<p v-if="show < 1000">用户较少</p>
<p v-else-if="show > 1000 && show < 10000 ">用户较多</p>
<p v-else>用户很多</p>
动态属性绑定v-bind
<input type="text" :value="web.url">
这样文本框的值就是web.url的值
简写直接用:
这个绑定可以用在class, img, src等用法,实现其值的动态绑定
放在li中为每行元素设置title和key
:title=“value.name”
:key=“value.id”
v-for 遍历数组和对象
<p>遍历数组或对象</p>
<ul><li v-for="value in arr">{{ value }}</li>
</ul>
const arr = Vue.reactive([1,2,3,4,5])
双向数据绑定 v-model
单向数据绑定:当数据发生改变时,视图会自动更新,但是用户手动修改input的值,数据不会自动更新
双向数据绑定会自动更新数据的值
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="vue.global.js"></script>
</head><body><div id="app"><h3>文本框 {{ data.text }}</h3><h3>单选框 {{ data.radio }}</h3><h3>多选框 {{ data.checkbox }}</h3><h3>下拉框 {{ data.select }}</h3><h3>记住密码 {{ data.remember }}</h3>双向数据绑定 <input type="text" v-model="data.text"><br><input type="radio" value="male" v-model="data.radio"> 男<input type="radio" value="female" v-model="data.radio"> 女<br><input type="checkbox" value="apple" v-model="data.checkbox"> 苹果<input type="checkbox" value="banana" v-model="data.checkbox"> 香蕉<input type="checkbox" value="orange" v-model="data.checkbox"> 橘子<br><select v-model="data.select"><option value="">请选择</option><option value="apple">苹果</option><option value="banana">香蕉</option><option value="orange">橘子</option></select><br><input type="checkbox" v-model="data.remember"> 记住密码</div><script>Vue.createApp({// setup选项,用于设置响应式数据和方法等setup() {const data = Vue.reactive({text: 'hello',radio: '',checkbox: [],select: '',remember: false})return {data}}}).mount('#app')</script></body></html>
v-model修饰符
之前的双向数据绑定是实时渲染的(默认形式),现在我们不需要实时渲染,只要按enter等才改
- 在失去焦点或enter后修改 v-model.lazy
- 输入框的值转为数字类型 v-model.number
- 去除首尾空格 v-model.trim
以上是常用的三种
渲染数据 v-text 和 v-html
v-html可以将数据解析为html格式,而v-text会解析为纯文本格式
计算属性computed
可以避免重复计算
侦听器watch
监听元素的变化
setup(){const date = reactive({year:2023,month:10})watch(date.year, (newValue, oldValue))=>{console.log("new:", newValue, "old:", oldValue);}
}
// 注意,json中对象和数组是通过引用传递的,所以old也是修改后的值,如果是普通变量,则会通过赋值传递,会保留old值
自动监听watchEffect
将不需要手动设定监听的对象
图片轮播案例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="vue.global.js"></script>
</head><body><div id="app"><h1>{{number}}</h1><h1>imageSrc: {{imageSrc}}</h1><img :src="imageSrc" style="width: 300px;"> <br><button @click="next">+</button><button @click="prev">-</button><ul><li v-for="value in 6"><button @click="jump(value)">{{value}}</a></li></ul></div><script>Vue.createApp({// setup选项,用于设置响应式数据和方法等setup() {const number = Vue.ref(1)// 这是个对象,可以直接在模板中使用const next = () => {number.value++if (number.value == 6) {number.value = 1}}const prev = () => {number.value--if (number.value == 1) {number.value = 6}}const web = Vue.reactive({title: 'Vue.js',url: 'https://cn.vuejs.org/'})const imageSrc = Vue.computed(() => {return `image/${number.value}.jpg`})const jump = (value) => {number.value = value}return {number,next,// web: webimageSrc,prev,jump,web}}}).mount('#app')</script></script>
</body></html>
记事本案例
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>记事本</title><script src="vue.global.js"></script>
</head>
</head>
<body><div id="app"><input type="text" v-model="data.content"><button @click="add">添加</button><ul><li v-for="(value, index) in data.list" :key="index">{{value}}<button @click="remove(index)">删除</button></li>{{data.list.length}}<button @click="clean">清空</button></ul></div><script>Vue.createApp({setup(){const data = Vue.reactive({content: '',list: ["· 吃饭", "· 睡觉", "· 打豆豆"]})const add = () =>{data.list.push(data.content)console.log(data.list)}const remove = (index) =>{data.list.splice(index, 1)console.log(data.list)}const clean = () =>{data.list = []console.log(data.list)}return {data,add,remove,clean}}}).mount('#app')</script>
</body>
</html>
change事件&&购物车案例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>购物车</title><script src="vue.global.js"></script>
</head><body><div id="app"><table><thead><tr><td><input type="checkbox" v-model="data.seleted" @change="selectAll"></td><td>商品名称</td><td>商品价格</td><td>库存</td><td colspan="2">操作</td></tr></thead><tbody><tr v-for="(item, index) in data.list"><td><input type="checkbox" :value="item" v-model="data.checkboxList" @change="checkSelected"></td><td>{{item.name}}</td><td>{{item.price}}</td><td>{{item.stock}}</td><td><button @click="add(index)">+</button></td><td>{{item.number}}</td><td><button @click="reduce(index)">-</button></td><td><button @click="del(item.id)">删除</button></td></tr></tbody><tfoot><tr><td>总价 {{totalPrice()}}</td></tr></tfoot></table></div><script>Vue.createApp({setup() {const data = Vue.reactive({seleted: false,checkboxList: [],list: [{id: 1,name: '商品1',price: 100,number: 0,stock: 3},{id: 2,name: '商品2',price: 200,number: 0,stock: 2},{id: 3,name: '商品3',price: 300,number: 0,stock: 1},{id: 4,name: '商品4',price: 400,number: 0,stock: 4},{id: 5,name: '商品5',price: 500,number: 0,stock: 5}]})const selectAll = () => {console.log(data.seleted)if (data.seleted) {data.checkboxList = data.list} else {data.checkboxList = []}console.log(data.checkboxList)}const checkSelected = () => {if (data.checkboxList.length == data.list.length && data.list.length != 0) {data.seleted = true} else {data.seleted = false}}const totalPrice = () => {let total = 0;for (let i = 0; i < data.checkboxList.length; i++) {total += data.list[i].price * data.list[i].number}return total}const add = (index) => {if (data.list[index].number < data.list[index].stock)data.list[index].number++;}const reduce = (index) => {if (data.list[index].number > 0) {data.list[index].number--;}}const del = (id) => {for (let i = 0; i < data.list.length; i++) {if (data.list[i].id == id) {data.list.splice(i, 1)break;}}for(let i = 0; i < data.checkboxList.length; i++){if(data.checkboxList[i].id == id){data.checkboxList.splice(i, 1)break;}}checkSelected()}return {data,selectAll,checkSelected,totalPrice,add,reduce,del}}}).mount('#app')</script>
</body></html>
购物车案例优化
使用侦听器watch来实现全选和取消全选
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>购物车</title><script src="vue.global.js"></script>
</head><body><div id="app"><table><thead><tr><td><input type="checkbox" v-model="data.seleted"></td><td>商品名称</td><td>商品价格</td><td>库存</td><td colspan="2">操作</td></tr></thead><tbody><tr v-for="(item, index) in data.list"><td><input type="checkbox" :value="item" v-model="data.checkboxList"></td><td>{{item.name}}</td><td>{{item.price}}</td><td>{{item.stock}}</td><td><button @click="add(index)">+</button></td><td>{{item.number}}</td><td><button @click="reduce(index)">-</button></td><td><button @click="del(item.id)">删除</button></td></tr></tbody><tfoot><tr><td>总价 {{totalPrice()}}</td></tr></tfoot></table></div><script>Vue.createApp({setup() {const data = Vue.reactive({seleted: false,checkboxList: [],list: [{id: 1,name: '商品1',price: 100,number: 0,stock: 3},{id: 2,name: '商品2',price: 200,number: 0,stock: 2},{id: 3,name: '商品3',price: 300,number: 0,stock: 1},{id: 4,name: '商品4',price: 400,number: 0,stock: 4},{id: 5,name: '商品5',price: 500,number: 0,stock: 5}]})// const selectAll = () => {// console.log(data.seleted)// if (data.seleted) {// data.checkboxList = data.list// } else {// data.checkboxList = []// }// console.log(data.checkboxList)// }// const checkSelected = () => {// if (data.checkboxList.length == data.list.length && data.list.length != 0) {// data.seleted = true// } else {// data.seleted = false// }// }let flag = true;Vue.watch(() => data.seleted, (newVal, oldVal) => {console.log(newVal, oldVal)if (newVal) {data.checkboxList = data.list} else {if (flag) {data.checkboxList = []}}})Vue.watch(() => data.checkboxList, (newVal, oldVal) => {console.log(newVal, oldVal)if (newVal.length == data.list.length && data.list.length != 0) {data.seleted = trueflag = true;} else {data.seleted = falseflag = false;}})const totalPrice = () => {let total = 0;for (let i = 0; i < data.checkboxList.length; i++) {total += data.list[i].price * data.list[i].number}return total}const add = (index) => {if (data.list[index].number < data.list[index].stock)data.list[index].number++;}const reduce = (index) => {if (data.list[index].number > 0) {data.list[index].number--;}}const del = (id) => {for (let i = 0; i < data.list.length; i++) {if (data.list[i].id == id) {data.list.splice(i, 1)break;}}for (let i = 0; i < data.checkboxList.length; i++) {if (data.checkboxList[i].id == id) {data.checkboxList.splice(i, 1)break;}}// checkSelected()}return {data,// selectAll,// checkSelected,totalPrice,add,reduce,del}}}).mount('#app')</script>
</body></html>
Axios获取后端数据
以下给出示例代码
axios.get{'url'}.then(response =>{console.log("get.data", response.data)
}).catch(error =>{console.log("get.error", error)
})let param = {title: data.content,...
}
axios.post{'url', param}.then(response =>{console.log("get.data", response.data)
}).catch(error =>{console.log("get.error", error)
})
基于vite创建vue3项目
在你的项目文件夹下打开cmd
需要下载node.js
npm create vite@latest
最后按照提示,以下是解释:
cd vite-vuedemo // 进入你刚创建好的项目目录
npm install // 根据你的选择开始下载项目文件
npm run dev // 开启端口运行网站
以下是一些好用的vue vscode扩展
如果你没有用vscode,请自行安装
提供语法高亮,语法提示
ai代码工具,方便好用
vue组件学习
在Vue 3中,组件是构建用户界面的基本单位。每个组件可以看作是一个封装了 HTML、CSS 和 JavaScript 逻辑的小模块,负责页面的一部分。组件的主要作用是提高代码的复用性和可维护性
删除无关内容,将main.js删减到以下形式(空项目:
import { createApp } from 'vue'
import App from './App.vue'createApp(App).mount('#app')
我们定义子组件footer.vue和header.vue,并编写以下内容:
<script setup></script><template><h3>Footer</h3>
</template><style scoped></style>
然后在父组件app.vue中渲染以上页面
注意这里Header 和 Footer必须首字母大写才能被识别为模板
<script setup>import Header from './components/header.vue';import Footer from './components/footer.vue';
</script><template><Header/><div>Test Content</div><Footer/>
</template><style scoped></style>
父传子defineprops
我们可以在<Header\>标签上定义一些属性来传递给子组件
<Header name="huohuopro" url="https://blog.csdn.net/2401_87092242"
然后在header.vue中接收:
<script setup>const props = defineProps(["name", "url"])confirm(props.name)confirm(props.url)
</script>
这便是最简单的传递。
当然,我们也可以使用对象的方式来传递,比如以下:
使用reactive定义对象,这里的reactive是响应式数据,咱们template中可以直接使用{{}}的形式来访问传递的数据,比如传递的userData有num属性,然后在子组件header中被{{props.userdata.num}}渲染,那么这里的num更新后(比如有一个按钮点一下就加一)会自动更新页面渲染,如果是普通数据,那么渲染好的页面不会再更新num
<script setup>import Header from './components/header.vue';import Footer from './components/footer.vue';import { reactive } from 'vue';const userData = reactive({name: 'huohuopro',url: 'https://blog.csdn.net/2401_87092242'});
</script><template><Header :userData="userData"/> // :即v-bind<div>Test Content</div><Footer/>
</template><style scoped></style>
使用defineProps接收对象
<script setup>
const props = defineProps({userData: {type: Object, // 设置传递的参数类型,这里传来的一个属性是对象required: true // 必须传递}
});
alert(props.userData.name)
</script><template><h3>Header</h3>
</template><style scoped></style>
子传父defineEmits
defineEmits传递自定义事件的名称,然后定义一个常量来接收,这样我们就可以将子组件定义的事件发送给父组件
在header.vue中编写
<script setup>const emits = defineEmits(['getWeb'])emits('getWeb', {name:"huohuopro", age:18})
</script>
然后在父组件中的Header接收,使用@传递的名称="别名"来接收
<Header @getWeb="emitsGetWeb"/>
最后再在父组件中定义这个方法,在这个方法内便有接收到的参数
const emitsGetWeb = (web) => {alert(web.name);alert(web.age);}
接下来我们在子组件定义一个方法, 点击就给父组件的userData.num加10,注意按钮和方法定义在子组件,点击向父组件传递10, 即emits(‘addNum’, 10)
<script setup>const emits = defineEmits(['getWeb', 'addNum'])emits('getWeb', {name:"huohuopro", age:18})const add = () =>{emits('addNum', 10)}
</script><button @click="add">addNum</button>
在父组件调用并定义方法:
const emitsAddNum = (data) => {userData.num += data;}<Header @addNum="emitsAddNum"/>
这里的基本流程是,子组件定义事件addNum,定义方法add,点击子组件的add后触发emits。父组件监听到子组件触发的 addNum
事件。这个事件的回调函数是 emitsAddNum
,它会接收子组件传递的数据(即 10
)。当 addNum
事件触发时,父组件会调用 emitsAddNum
方法,传递的数据 10
被作为参数传递给 emitsAddNum
。
跨组件通信-依赖注入
之前我们只是父组件传递给子组件,现在我们可以在父组件将内容传递给子组件的子组件
但是不能子传父
我们先分别在app.vue,header.vue中加入以下内容:
<script setup>import Header from './components/header.vue';import { reactive, ref } from 'vue';const web = reactive({name: "huohuo",url: "https://www.baidu.com"});const user = ref(0)</script><template><h3>App.vue-Top</h3>user: {{ user }}<!-- 子组件 --><Header /></template><style scoped></style>
<script setup>import Nav from './nav.vue';
</script><template><h3>header.vue-Middle</h3><!-- 子组件 --><Nav />
</template><style scoped></style>
然后新建nav.vue,作为孙子组件
<script setup></script>
<template><h3>这是nav,最底部的组件</h3></template>
<style scoped></style>
打开页面,应该是这样的:
在app中添加如下内容:
const web = reactive({name: "huohuo",url: "https://www.baidu.com"
});
const user = ref(0)
// provied可以将数据传递给所有的子组件,子组件通过inject来获取数据
provide('provideWeb', web);
provide('provideUser', user);
const userAdd = () => {user.value += 1;
}
provide('userAdd', userAdd);user: {{ user }}
然后header中就可以访问传递的方法和数据了
<script setup>import { inject } from 'vue';import Nav from './nav.vue';const web = inject('provideWeb');const funcUserAdd = inject('userAdd');console.log(web);console.log(funcUserAdd);
</script><template><h3>header.vue-Middle</h3><!-- 注入的provideWeb对象 --><h4>这是子组件收到的web对象</h4>{{ web.name }}<button @click="funcUserAdd()">添加用户</button><!-- 子组件 --><Nav />
</template><style scoped></style>
如果我们想要将数据进一步传递给nav,可以在子组件继续provide,就可以传递了。
匿名插槽和具名插槽
插槽的作用:将父组件定义的模板片段插入到子组件的特定位置。之前我们使用的如
App.vue:
<script setup>import Header from './components/header.vue'import Footer from './components/footer.vue'
</script><template><h3>父组件</h3><!-- <Header /> <Footer /> --><!-- 以下是匿名插槽的写法 --><Header><h3>这是给header的匿名插槽</h3></Header><!-- 以下是具名插槽的写法 v-slot定义插槽名为footerSlot --><Footer><!-- <template v-slot:footerSlot> 简写为# --><template #footerSlot><h3>这是给footer的具名插槽</h3></template></Footer>
</template>
<style scoped></style>
在header.vue中使用来使用匿名插槽
<template><h3>header子组件</h3><slot/>
</template>
footer.vue中 来使用具名插槽
<template><h3>footer子组件</h3><slot name="footerSlot"/>
</template>
作用域插槽
子组件向父组件传递数据,并在父组件定义的模板中渲染。
在footer.vue中的后可以跟定义的数据
比如:
<slot name="footerSlot" title="huohuo" user="1000"/>
然后在app.vue中就可以接收这些参数:
<Footer><!-- <template v-slot:footerSlot> 简写为# --><template #footerSlot="data"><h3>这是给footer的具名插槽</h3>{{ data.title }}{{ data.user }}</template></Footer>
这里的data就接收到了所有传递的数据,我们还可以直接使用title和user访问(解构的形式)
实例:开发一个表格组件,展示用户列表。管理员能看到“编辑”按钮,普通用户看不到
app.vue
<script setup>
import { ref } from 'vue'
import Table from './components/table.vue'const users = ref([{ id: 1, name: '张三', isAdmin: true },{ id: 2, name: '李四', isAdmin: false }
])// 定义编辑用户的方法
const editUser = (user) => {alert('编辑用户:' + user.name);
}
</script><template><Table :data="users"><!-- 作用域插槽 'header' 用于自定义表头内容 --><template #header><th>ID</th><th>姓名</th><th>操作</th></template><!-- 作用域插槽 'row' 用于自定义表格行的内容 --><!-- 'item' 是当前行数据的变量,通过作用域插槽传递 --><template #row="{ item }"><td>{{ item.id }}</td><td>{{ item.name }}</td><td><!-- 只有当用户是管理员时,才显示编辑按钮 --><button v-if="item.isAdmin" @click="editUser(item)">编辑</button></td></template></Table>
</template><style scoped></style>
table.vue
<script setup>import { defineProps } from 'vue';const props = defineProps({data:{type:Array,required:true,},});
</script><template><table><thead><tr><slot name="header"></slot></tr></thead><tbody><tr v-for="(item, index) in data" :key="index"><slot name="row" :item="item"></slot></tr></tbody></table>
</template><style scoped></style>
这里的张三是管理员,所以他能看到编辑按钮并进行编辑:
生命周期函数
生命周期函数是组件实例从创建到销毁过程中不同时间点自动调用的函数
介绍:DOM:Document Object Model(文档对象模型)
使用两个标准的获取方法:
(1)getElementById (通过标签的id获取标签,相对效率、准确率高;但不能精准选择元素)
(2)getElementsByTagName (通过标签明获取标签,获取到的元素是一个集合,也就是获取的是一个伪数组,即使一个标签获取的也一样是个集合)
除了document用于上面两种方法,其他的节点也一样拥有这两种方法;
制定了两个子规范:
(1)DOM Core(核心部分): 把xml文档设计为树形节点结构,并为这种结构的运行机制制订了一套规范化标准。同时定义了创建、编辑、操纵这些文档结构的属性和方法。(核心思想:树形节点结构、相关的属性与方法)
(2)DOM HTML: 针对HTML文档、标签集合,以及与个别HTML标签相关的元素定义了对象、属性和方法。
我们可以将生命周期划分为三个阶段:
- 挂载阶段:
- onBeforeMount
- 在组件实例即将被挂载到DOM树之前调用
- 此时模板还未编译或渲染到DOM,通常用于执行初始化操作
- 如获取异步数据,设置初始属性值等
- onMounted
- 组件成功挂载到DOM并完成首次渲染后调用
- 此时可以访问和操作DOM元素并执行与页面交互相关的逻辑
- onBeforeMount
- 更新阶段
- onBeforeUpdate
- 在组件更新之前即将重新渲染时调用
- 可以根据新的参数判断是否需要特殊处理
- 甚至可以选择阻止这次的更新
- onUpdated
- 在组件完成更新并重新渲染后调用
- 可以基于新的渲染结果并处理更新后的数据
- onBeforeUpdate
- 卸载阶段
- onBeforeUnmount
- 在组件从DOM中销毁之前调用
- 用于释放资源,如清理计时器,解绑事件监听器等
- onUnmounted
- 在组件已经从DOM中移除并销毁后调用
- 确保组件所占用的所有资源都被正确释放
- onBeforeUnmount
- 错误处理
- onErrorCaptured
- 在捕获到组件中的错误时调用
- 用于处理错误如记录错误日志等
- onErrorCaptured
挂载过程:
- 模板编译
- 将组件的模板转化为js代码
- 渲染
- 在模板编译后生成的JS代码渲染到页面上
- 生成虚拟DOM
- 挂载
- 在渲染完成后将虚拟的DOM挂载到DOM树上
- 使其在页面上显示出来
toRef 和 toRefs
toRefs是将一个响应式对象的所有属性转换为ref对象
toRef是将一个响应式对象的某个属性转换为ref变量
toRef 和 toRefs 主要用于需要保持解构后变量响应性的特定场景
Pinia简介与安装
Pinia是一个轻量级的状态管理库
状态管理库是用于管理应用全局状态的工具
以登陆为例
使用Pinia创建一个userStore来集中管理用户的登录状态和过期时间
当用户登录成功时:
设置userStore中用户的登录状态为已登录,并设置过期时间
当用户退出登录时:
修改userStore中用户的登录状态为未登录,并删除过期时间
Pinia和组件通信的区别
虽然Vue提供的父传子和子传父以及跨组件通信也可用于状态共享,但在大型项目中,随着组件数量的增加,会导致以下问题:
1.组件之间传递大量的props会使项目变得非常繁琐和难以维护
2.非父子组件间过度依赖provide/inject使状态散落在各个组件之间
Pinia可以解决以下问题
- 全局状态管理
- 所有组件都可以访问和修改状态,而不用在每个组件内进行状态管理
- 简化组件之间的通信
- 避免在组件间传递大量的props
- 状态持久化
- 可以将应用程序的状态保存到本地存储中
- 在应用程序重启后会保留登录状态,对于登录等场景非常有用
Pinia和localStorage的区别
LocalStorage
- 只能存储字符串类型
- 有大小限制,通常为5mb
Pinia
- 可以存储任何类型的数据,包括对象,数组等
- 没有大小限制,可以存储大量数据
总的来说,对于复杂的状态管理需求,Pinia是比较好的选择。
项目内使用npm install pinia 安装
npm install pinia
安装后在项目的package.json中可以看到pinia的版本等信息
然后在main.js中使用
import { createPinia } from 'pinia'
导入pinia实例,注册到vue应用中:
此处替换create的内容
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')
定义和使用Store
store是用来集中存储和管理组件之间共享状态的仓库
在src目录下新建一个文件夹stores,新建一个js文件。要创建仓库,首先要导入pinia的defineStore
// 从 Pinia 导入 defineStore 函数,用于定义一个 store
import { defineStore } from "pinia";
import { reactive, ref } from "vue";// 使用 defineStore 定义一个名为 'web' 的 store。
// 第一个参数是 store 的名称,第二个参数是一个工厂函数,返回 store 的状态、方法等。
export const useWebStore = defineStore('web', () => {const web = reactive({title: "huohuo", url: "https://blog.csdn.net/2401_87092242/article/details/147802631?spm=1001.2014.3001.5501" });const users = ref(1000);const userAdd = () => {users.value += 1; };// 返回一个对象,其中包含需要暴露给组件的响应式数据和方法return {web, users, userAdd }
});
以上我们就定义好了一个仓库并使其暴露在外,其中useWebStore是约定俗成的命名规范。
在app.vue中添加如下:
<script setup>import { useWebStore } from './store/web.js'const webStore = useWebStore()console.log(webStore.web)console.log(webStore.users)</script><template>{{ webStore.web.url }}<p></p>{{ webStore.users }}<p></p>{{ webStore.web.title }}<button @click="webStore.userAdd">添加用户</button>
</template><style scoped></style>
现在已经可以使用并修改库中的值了
Pinia持久化存储插件
pinia持久化插件也是存储到localStorage中,但是它可以自动状态同步并且更加易用。
这个插件可以自动将pinia的状态存储到localStorage中,无需手动的处理状态的读取和写入,并且和vue的数据完美结合,当状态改变时,依赖这些状态的组件会自动更新视图,更加灵活和强大。
官网地址:Home | pinia-plugin-persistedstate (prazdevs.github.io)
使用以下命令安装:
npm i pinia-plugin-persistedstate
同样在package.json中可以查看插件状态
在app.vue中导入并注册:
// 导入持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'// 注册持久化插件, 并传入pinia实例
pinia.use(piniaPluginPersistedstate)
随后我们将之前添加的库web.js的useWebStore后添加一个小参数就可以开启持久化存储:
注意注释标注的添加位置:
import { defineStore } from "pinia";
import { reactive, ref } from "vue";
export const useWebStore = defineStore('web', () => {const web = reactive({title:"huohuo",url:"https://blog.csdn.net/2401_87092242/article/details/147802631?spm=1001.2014.3001.5501"});const users = ref(1000);const userAdd = () => {users.value += 1;}return {web,users,userAdd}
},
// 在这将persist设置为true就可以将这个对象设置为持久化存储
{persist: true,
})
end
鸣谢
【2024最新版】3小时学会Vue3,小白零基础视频教程,web前端快速入门实战课程
邓瑞老师
相关文章:
一文掌握vue3基础,适合自学入门案例丰富
Vue3 本文从Vue3的基础语法出发,全面系统的介绍了Vue3的核心概念与应用,旨在帮助自学者更轻松地掌握Vue3。文章内容由浅入深,从通过CDN引入Vue3开始,逐步介绍了组合式API、模块化开发、以及常见的Vue3指令和功能并从单个html的使…...
OpenHarmony 5.0设置应用设置手势导航开关打开后重新关闭导航栏和设置界面重合
目录 1.背景 2.解决方案 1.背景 在OpenHarmony 5.0中从设置界面打开手势导航开关然后重新关闭,此时设置界面导航栏和设置列表主界面重合,导致设置界面无法点击最下面的关于设备 2.解决方案 首先参考之前的如何设置导航栏文档,我们可以自己…...
[ARM][汇编] 02.ARM 汇编常用简单指令
目录 1.数据传输指令 MRS - Move from Status Register 指令用途 指令语法 代码示例 读取 CPSR 到通用寄存器 在异常处理程序中读取 SPSR 使用场景 MSR - Move to Status Register 指令语法 使用场景 示例代码 改变处理器模式为管理模式 设置条件标志位 异常处理…...
系统架构设计(十七):微服务数据一致性和高可用策略
数据一致性问题 问题本质 由于每个微服务拥有独立数据库,跨服务操作不能用传统的数据库事务,面临“分布式事务”一致性挑战。 数据一致性策略 策略核心思想应用场景优缺点强一致性(Strong Consistency)所有操作实时同步成功&a…...
[Harmony]获取设备参数
获取屏幕宽度/屏幕高度/状态栏高度/导航栏高度/刘海高度/设备型号/系统版本号... DevicesUtil import window from ohos.window; import { common } from kit.AbilityKit; import display from ohos.display; import deviceInfo from ohos.deviceInfo; import i18n from ohos.…...
Python60日基础学习打卡D31
如何把一个文件,拆分成多个具有着独立功能的文件,然后通过import的方式,来调用这些文件?这样具有几个好处: 可以让项目文件变得更加规范和清晰可以让项目文件更加容易维护,修改某一个功能的时候࿰…...
命名常量集合接口INamedConstantCollection<T>实现
public interface INamedConstantCollection<TObject, TName> : IEnumerable<TObject>, IEnumerable where TName : IComparable{TObject this[TName name] { get; }TObject this[int index] { get; }int Count { get; }int Capacity { get; }} 这是一个泛型接口&…...
TYUT-企业级开发教程-第6章
这一章 考点不多 什么是缓存?为什么要设计出缓存? 企业级应用为了避免读取数据时受限于数据库的访问效率而导致整体系统性能偏低,通 常会在应用程序与数据库之间建立一种临时的数据存储机制,该临时存储数据的区域称 为缓存。缓存…...
反射在spring boot自动配置的应用
目录 一,背景 二,知识回顾 2.1 理解使用反射技术,读取配置文件创建目标对象(成员变量,方法,构造方法等) 三,springboot自动配置 3.1 反射在自动配置中的工作流程 3.2 浏览源码…...
项目进度延误,如何按时交付?
项目进度延误可以通过加强计划管理、优化资源分配、强化团队沟通、设置关键里程碑和风险管理机制等方式来实现按时交付。加强计划管理、优化资源分配、强化团队沟通、设置关键里程碑、风险管理机制。其中,加强计划管理尤为关键,因为明确而详细的计划能提…...
内网穿透:轻松实现外网访问本地服务
异步通知的是需要通过外网的域名地址请求到的,由于我们还没有真正上线,那支付平台如何请求到我们本地服务的呢? 这里可以使用【内网穿透】技术来实现,通过【内网穿透软件】将内网与外网通过隧道打通,外网可以读取内网…...
缺乏进度跟踪机制,如何掌握项目状态?
要有效掌握项目状态,必须建立明确的进度跟踪机制、使用专业的项目管理工具、定期召开沟通会议、设立清晰的关键里程碑和实施风险监控。其中,建立明确的进度跟踪机制是关键,通过系统地追踪项目各个阶段的完成情况,及时发现问题并采…...
ES 调优帖:关于索引合并参数 index.merge.policy.deletePctAllowed 的取值优化
最近发现了 lucene 9.5 版本把 merge 策略的默认参数改了。 * GITHUB#11761: TieredMergePolicy now allowed a maximum allowable deletes percentage of down to 5%, and the defaultmaximum allowable deletes percentage is changed from 33% to 20%. (Marc DMello)也就是…...
基于 STM32 单片机的实验室多参数安全监测系统设计与实现
一、系统总体设计 本系统以 STM32F103C8T6 单片机为核心,集成温湿度监测、烟雾检测、气体泄漏报警、人体移动监测等功能模块,通过 OLED 显示屏实时显示数据,并支持 Wi-Fi 远程传输。系统可对实验室异常环境参数(如高温、烟雾、燃气泄漏)及非法入侵实时报警,保障实验室安…...
Spring Boot-Swagger离线文档(插件方式)
Swagger2Markup简介 Swagger2Markup是Github上的一个开源项目。该项目主要用来将Swagger自动生成的文档转换成几种流行的格式以便于静态部署和使用,比如:AsciiDoc、Markdown、Confluence。 项目主页:https://github.com/Swagger2Markup/swagg…...
Linux下Docker使用阿里云镜像加速器
在中国大陆环境中配置 Docker 使用阿里云镜像加速器,并确保通过 Clash 代理访问 Docker Hub 我这里用的Debian12。 步骤 1:获取阿里云镜像加速器地址 登录阿里云容器镜像服务控制台:(qinyang.wang) 网址:阿里云登录 - 欢迎登录阿…...
每日c/c++题 备战蓝桥杯(洛谷P1440 求m区间内的最小值 详解(单调队列优化))
洛谷P1440 求m区间最小值:单调队列优化详解(从暴力到O(n)的蜕变) tags: [算法, 数据结构, 滑动窗口, 洛谷, C] 引言 在处理序列数据的区间查询问题时,暴力枚举往往难以应对大规模数据。本文以洛谷P1440为切入点,深入…...
从代码学习深度学习 - 预训练word2vec PyTorch版
文章目录 前言辅助工具1. 绘图工具 (`utils_for_huitu.py`)2. 数据处理工具 (`utils_for_data.py`)3. 训练辅助工具 (`utils_for_train.py`)预训练 Word2Vec - 主流程1. 环境设置与数据加载2. 跳元模型 (Skip-gram Model)2.1. 嵌入层 (Embedding Layer)2.2. 定义前向传播3. 训练…...
OpenCV图像边缘检测
1.概念 图像边缘检测是计算机视觉和图像处理中的基础任务,用于识别图像中像素值发生剧烈变化的区域,这些区域通常对应物体的边界、纹理变化或噪声。 1.1原理 图像中的边缘通常表现为灰度值的突变(如从亮到暗或从暗到亮的急剧变化)…...
AI能源危机:人工智能发展与环境可持续性的矛盾与解决之道
AI对能源的渴求正在演变成一个巨大的挑战。这不仅仅关乎电费支出,其环境影响也十分严重,包括消耗宝贵的水资源、产生大量电子垃圾,以及增加温室气体排放。 随着AI模型变得越来越复杂并融入我们生活的更多领域,一个巨大的问题悬而…...
基于flask+vue的电影可视化与智能推荐系统
基于flaskvue爬虫的电影数据的智能推荐与可视化系统,能展示电影评分、评论情感分析等直观的数据可视化图表,还能通过协同过滤算法为用户提供个性化电影推荐,帮助用户发现更多感兴趣的电影作品,具体界面如图所示。 本系统主要技术架…...
初步认识HarmonyOS NEXT端云一体化开发
视频课程学习报名入口:HarmonyOS NEXT端云一体化开发 1、课程设计理念 本课程采用"四维能力成长模型"设计理念,通过“能看懂→能听懂→能上手→能实战”的渐进式学习路径,帮助零基础开发者实现从理论认知到商业级应用开发的跨越。该模型将学习过程划分为四个维度…...
基于单片机的车辆防盗系统设计与实现
标题:基于单片机的车辆防盗系统设计与实现 内容:1.摘要 随着汽车保有量的不断增加,车辆被盗问题日益严峻,车辆防盗成为人们关注的焦点。本研究的目的是设计并实现一种基于单片机的车辆防盗系统。采用单片机作为核心控制单元,结合传感器技术、…...
LSM Tree算法原理
LSM Tree(Log-Structured Merge Tree)是一种针对写密集型场景优化的数据结构,广泛应用于LevelDB、RocksDB等数据库引擎中。其核心原理如下: 1. 写入优化:顺序写代替随机写 内存缓冲(MemTable):写入操作首先被写入内存中的数据结构(如跳表或平衡树),…...
通过 API 获取 1688 平台店铺所有商品信息的完整流程
在电商运营和数据分析中,获取 1688 平台店铺的商品信息是一项重要的任务。1688 作为国内领先的 B2B 电商平台,提供了丰富的开放平台 API 接口,方便开发者获取店铺商品的详细信息。本文将详细介绍如何通过 Python 调用 1688 的 API 接口&#…...
Python代码加密与发布方案详解
更多内容请见: python3案例和总结-专栏介绍和目录 文章目录 一、基础加密方案二、商业级加密方案三、高级混淆方案四、商业化发布方案五、反逆向技术六、最佳实践建议七、常见问题解决Python作为解释型语言,其源代码容易被查看和修改。本文将详细介绍多种Python代码保护方案,…...
Tractor S--二维转一维,然后最小生成树
P3073 [USACO13FEB] Tractor S - 洛谷 转成一维点图,然后最小生成树,最后的最大值就是最后一个点,记得记录维护连通块 同样的二维转一维---Cow Ski Area G---二维图转一维tarjan缩点-CSDN博客 #include<bits/stdc.h> using namespac…...
5月20日day31打卡
文件的规范拆分和写法 知识点回顾 规范的文件命名规范的文件夹管理机器学习项目的拆分编码格式和类型注解 作业:尝试针对之前的心脏病项目,准备拆分的项目文件,思考下哪些部分可以未来复用。 补充介绍: pyc文件的介绍 知识点回顾 …...
基于Spring Boot + Vue的教师工作量管理系统设计与实现
一、项目简介 随着高校信息化管理的发展,教师工作量管理成为教务系统中不可或缺的一部分。为此,我们设计并开发了一个基于 Spring Boot Vue 的教师工作量管理系统,系统结构清晰,功能完备,支持管理员和教师两个角色。…...
海康工业相机白平衡比选择器对应的值被重置后,如何恢复原成像
做项目的时候,有时候手抖,一不小心把一个成熟稳定的项目的相机配置,重置了,如何进行恢复呢,在不知道之前配置数据的情况下。 我在做项目的时候,为了让这个相机成像稳定一点,尤其是做颜色检测时…...
VMWare清理后,残留服务删除方案详解
VMWare清理后,残留服务删除方案详解 在虚拟化技术日益普及的今天,VMWare作为行业领先的虚拟化软件,广泛应用于企业和服务器的管理中。然而,由于其复杂的架构和深层次的系统集成,VMWare的卸载过程往往并不顺利。即使通…...
STM32定时器简单采集编码器脉冲
MCU:STM32H723ZGT6 编码器:(欧姆龙)E6B2-CWZ1X;1000P/R;8根线信号线分别为 A A- B B- Z Z- 以及5V和GND; A 脉冲输出 B 脉冲输出 Z 零点信号 当编码器旋转到零点时,Z信号会发出一个脉…...
第 4 章:网络与总线——CAN / Ethernet / USB-OTG
本章目标: 深入理解三种关键通信总线(CAN、Ethernet、USB-OTG)的协议架构、硬件接口与软件驱动 掌握 STM32(或同类 MCU)中各总线的寄存器配置、中断/DMA 驱动框架 通过实战案例,实现基于 CAN 总线的节点通信、基于 Ethernet 的 TCP/IP 通信,以及基于 USB-OTG 的虚拟串口…...
【python进阶知识】Day 31 文件的规范拆分和写法
知识点 规范的文件命名规范的文件夹管理机器学习项目的拆分编码格式和类型注解 机器学习流程 - 数据加载:从文件、数据库、API 等获取原始数据。 - 命名参考:load_data.py 、data_loader.py - 数据探索与可视化:了解数据特性,初期…...
leetcode 162. Find Peak Element
题目描述 如果nums[i-1]<nums[i]并且nums[i]>nums[i1],那么nums[i]就是峰值。除此情况之外,nums[i-1]和nums[i1]至少有一个大于nums[i],因为题目已经保证相邻的元素不相等。坚持向上坡方向走一定能达到一个峰值,如果往两边走…...
2025系统架构师---案例题(押题)
1. 微服务相关的概念: 微服务是一种架构风格,它将单体应用划分为一组小服务,服务之间相互协作,实现业务功能每个服务运行在独立的进程中,服务间采用轻量级的通信机制协作(通常是HTTP/JSON),每个服务围绕业务能力进行构建,并且能够通过自动化机制独立的部署。 微服务有…...
t检验详解:原理、类型与应用指南
t检验详解:原理、类型与应用指南 t检验(t-test)是一种用于比较两组数据均值是否存在显著差异的统计方法,适用于数据近似正态分布且满足方差齐性的场景。以下从核心原理、检验类型、实施步骤到实际应用进行系统解析。 一、t检验的…...
使用 OpenCV 实现万花筒效果
万花筒效果(Kaleidoscope Effect)是一种图像处理效果,通过对图像进行对称旋转或镜像处理,产生具有多重反射和对称的艺术效果。它常用于视频编辑、视觉艺术、游戏设计等领域,为图像添加富有创意和视觉冲击力的效果。 在…...
Rocketmq broker 是主从架构还是集群架构,可以故障自动转移吗
RocketMQ Broker的架构与故障转移机制 RocketMQ的Broker架构同时采用了主从架构和集群架构,并且支持故障自动转移。下面详细说明: 一、架构类型 1. 集群架构 RocketMQ天然支持分布式集群部署 一个RocketMQ集群包含多个Broker组(每组有主从) 不同Bro…...
MySQL中添加一个具有创建数据库权限的用户
要在MySQL中添加一个具有创建数据库权限的用户,可按以下步骤操作: 1. 登录MySQL 使用拥有足够权限(一般是root用户 )的账号登录到MySQL数据库。在命令行输入: mysql -u root -p然后输入对应的密码,即可进…...
Go语言使用通义灵码辅助开发 - AI编程助手提升效率
一、引言 Go 语言以其高效性能和简洁语法,成为构建微服务、分布式系统及高性能后端的首选。对于有其他语言编程经验的开发者和初学者,入门 Go 语言时,如何快速开发第一个程序是关键。传统方式如慢慢摸索、向老师请教或查找资料,效…...
演示:【WPF-WinCC3D】 3D工业组态监控平台源代码
一、目的:分享一个应用WPF 3D开发的3D工业组态监控平台源代码 二、功能介绍 WPF-WinCC3D是基于 WPF 3D研发的工业组态软件,提供将近200个预置工业模型(机械手臂、科幻零部件、熔炼生产线、机加生产线、管道等),支持组态…...
Oracle资源管理器
14.8资源管理器 14.8.1资源管理器的功能和控制种类 传统意义上,系统的资源分配是由 OS 来完成的,但是对于数据库资源,OS 分配资源会带来一些问题。以 Linux 为例,最为突出的一个问题是:Linux 的资源调度是基于进程的&…...
下载Ubuntu 64 位
学习目标: 下载 学习内容: 学习时间: 学习时间为学习时间 学习时间筋肉人为学习时间future 内容为笔记【有时比较抽象,有时比较过于详细,请宽恕。作者可能写的是仅个人笔记,筋肉人future】 学习产出&…...
ubuntu14.04/16.06 安装vscode(实测可以用)
地址:https://code.visualstudio.com/updates/v1_38 选择deb 这个版本还支持ubuntu14.04和16.06 sudo dpkg -i code_1.38.1-1568209190_amd64.deb sudo apt-get install -f安装成功,正常使用...
Linux命令大全
前言:工作中或多或少都会用到Linux服务器,我为大家分享一下常用命令 一丶文件与目录操作 命令作用示例ls列出目录内容ls -l(详细列表)cd切换目录cd /homepwd显示当前目录路径pwdmkdir创建目录mkdir -p dir1/dir2(递归…...
spark的缓存提升本质以及分区数量和task执行时间的先后
文章目录 示例代码缓存效果分析第1次 user.count第2次 user.count——这里解释了spark缓存提升的本质原因关于分区数量和task数量以及task的执行流程有多少个分区就有多少线程task并发执行不同分区数量对计算效率的提升 示例代码 import org.apache.spark.storage.StorageLeve…...
SQL次日留存率计算精讲:自连接与多字段去重的深度应用
一、问题拆解:理解次日留存率的计算逻辑 1.1 业务需求转换 题目:运营希望查看用户在某天刷题后第二天还会再来刷题的留存率。 关键分析点: 留存率 (第一天刷题且第二天再次刷题的用户数) / 第一天刷题的总用户数需…...
PostgreSQL初体验
目录 一:PostgreSQL 1.简介 3.优势 4.架构 5.应用场景 6.结论 二:安装PostgreSQL 1.编译安装 三:PostgreSQL架构 1.PG的逻辑结构 2.PG的物理结构 前言 在数据驱动的时代,掌握 PostgreSQL 这一全球顶尖的开源关系型数据…...
Vue 3.0 Transition 组件使用详解
Vue 3.0 的 Transition 组件提供了一种简单的方式来为元素或组件的进入/离开添加动画效果。下面是使用<script setup>语法糖的实现方式。 1. 基本用法 使用场景:当需要为元素的显示/隐藏添加简单的淡入淡出效果时,这是最基础的过渡实现方式。 &…...