五分钟掌握vue3!!!
创建Vue命令 利用vite脚手架创建
pnpm create vite [name] name你自己的项目名称
这个命令会让你自己选择
vue组件化:分模块写代码,数据渲染。
ref
使用ref声明响应式数据,响应式记忆技巧,打印出数据类型为xxxImpl,展开后不会显示数据,而是...
该响应式数据在js里要使用.value的方式读取值,在模版中会自动解包,不需要.value
ref的另一个作用是可以获取DOM对象(来自于vue2的),在标签添加一个ref属性,指向一个变量,该变量是ref方法的返回值
推荐使用ref声明基本数据类型的响应式,使用reactive声明引用类型的响应式
reactive:声明引用数据类型的响应式,在js中和模版中不需要使用.value的方式读取值
reactive声明的响应式数据不能重新赋值
解决reactive重新赋值响应式丢失的解决办法:
1、使用ref
2、使用Object.assign方法
争议:使用ref声明响应式数据为什么非要.value,能不能不用?
回答:可以实现,但不能实现,因为要区分普通数据和响应数据
8个常用生命周期
1、创建阶段,vue3中使用setup代替了beforeCreate和created两个生命周期
===使用场景:发送异步请求
2、挂载前后
onBeforeMount(() => { console.log('1、挂载前 onBeforeMount'); });
===使用场景:发送异步请求,操作DOM
onMounted(() => { console.log('2、挂载后 onMounted'); })
3、更新前后
onBeforeUpdate(() => { console.log('3、更新前 onBeforeUpdate'); }); onUpdated(() => { console.log('4、更新后 onUpdated'); })
4、卸载前后
===使用场景:清除定时器、清除全局变量,降低内存泄漏的风险
在vue组件中使用定时器,组件被销毁时定时器不会被销毁,因为定时器属于window对象的
onBeforeUnmount(() => { console.log('5、卸载前 onBeforeUnmount'); }); onUnmounted(() => { console.log('6、卸载后 onUnmounted'); })
vue指令
指令的作用都是用来进行DOM操作、渲染数据的
vue3常用指令:
v-on/@:绑定事件
v-mode:双向数据绑定
v-bind:给标签绑定动态属性
v-bind:给任意标签添加动态属性,使用频率非常高,所以提供了缩写形 式:
v-bind:属性名="{属性名1:布尔值,属性名2:布尔值,}"
v-bind:属性名= "[布尔值?属性名1:属性名2]"
缩写
:属性名="{属性名1:布尔值,属性名2:布尔值,}"
:属性名= "[布尔值?属性名1:属性名2]"
v-for:循环渲染数据
v-if/v-else-if/v-else:条件渲染
v-show:控制元素的显隐状态
v-text:渲染普通数据
v-html:渲染普通数据,会解析标签
v-slot:插槽
v-pre 不会解析标签中的特殊语法,比如{{ }}
v-once 只会渲染一次,不会改变数据
v-cloak 用于隐藏尚未完成编译的DOM模版
v-memo 缓存,提升性能的
计算属性:computed
computed 计算属性,是vue中一个较为重要的性能优化的API
对一些依赖现有数据通过计算出来的进行缓存(依赖缓存)
计算属性的使用前提:1、页面需要的数据js中没有 2、但是可以通过js中现有的数据计算出来
计算属性默认是只读无法修改,如果要能修改,使用set函数
在实际项目中计算属性更多的只使用get函数
插槽:v-slot
插槽:就是给父组件预留一个渲染将来才确定结构的位置
默认插槽/匿名插槽:全局只能有一个
具名插槽:可以有多个,就是给slot一个name属性指定一个名字
作用域插槽:子组件内部的数据需要传给父组件的插槽中使用
跨组件通信
provide/inject
provide('name', 'hello world') provide('user', user.value) import { inject } from "vue"; const name = inject("name"); const user = inject('user')
父传子:在父组件的子组件标签上声明一个自定义属性,指向要传的值,在子组件内部通过defineProps来接收
defineProps 编译器宏 不需要单独引入,直接使用即可
要修改父组件user里的数据,通过emits来发出一个修改的通知
使用defineExpose
在vue3中组件默认是关闭的,意思就是外面无法访问组件内部任何的数据和方法
可以通过defineExpose向外暴露指定的数据和方法
如果给原生的HTML标签绑定ref属性,那么获取到的就是DOM对象
如果给自定义组件绑定ref属性,那么获取到的就是子组件的实例
v-model
v-model的基本使用:v-model是对表单元素进行双向数据绑定的指令
v-model本质上就是v-bind:value和v-on:input的语法糖
v-model的高级使用 可以在自定义组件上使用
在自定义组件上使用的使用其实就是v-bind:modelValue和@update:modelValue的语法糖
Watch
使用Watch来监听输入框的变化,只要变化了就做一些额外的事情
watch(要监听的数据,执行的回调函数)
watch(msg, (newVal, oldVal) => {console.log("这里可以拿到新值和旧值,做一下额外的事情", newVal, oldVal); });
监听响应式数据watchEffect,默认立即执行,自动监听回调函数里面使用到的响应式数据,默认不能监听嵌套的引用数据类型,
watchEffect(() => {console.log(msg.value); });
小结:
在实际项目中优先使用watch对指定数据进行精准的监听,性能更好
watchEffect适合特定的场景下使用,它的性能比watch要更低一些
router:路由
路由配置数组
重定向:地址栏访问的是a页面,会自动跳转到指定页面,这个就是重定向
{path: "/",redirect: "/login", // 重定向:重新指向其它path,会改变网址meta: { show: false },},
嵌套路由:
{path: "/goods",component: GoodsView,meta: { title: "商品页", auth: true },// 重定向redirect: "/goods/list",// 嵌套路由children: [{path: "/goods/list", // 推荐这种写法便于查找component: () => import("../pages/goods/GoodList.vue"), //路由懒加载},{path: "/goods/type", // 推荐这种写法便于查找component: () => import("../pages/goods/GoodType.vue"), //路由懒加载},{path: "/goods/add", // 推荐这种写法便于查找component: () => import("../pages/goods/GoodAdd.vue"), //路由懒加载},],},
动态的路由数组,根据用户登录时获取的角色信息动态判断
const dynamicRoutes = [{path: "/total/order",component: () => import("../pages/sales/OrderTotal.vue"),meta: { role: ["super"] },},{path: "/total/sales",component: () => import("../pages/sales/SalesTotal.vue"),meta: { role: ["super"] },}, ];
路由模式
const router = createRouter({history: createWebHashHistory(), // 路由模式、hash模式、history模式、memory模式// history: createWebHistory(), // 历史模式// history: createMemoryHistory(),routes, // 路由中URL和组件一一对应的映射数组 });
addRoute方法把路由添加到路由实例中
dynamicRoutes.forEach((item) => {if (item.meta.role.includes("super")) {// 后面的super是用户登录时获取到的router.addRoute(item);} });
全局前置路由守卫:页面跳转完成前做一下事情,比如控制页面能够跳转,重定向
router.beforeEach((to, from, next) => {console.log(to?.meta?.title, from);document.title = to?.meta?.title;// to 到哪里去// from 从哪里来// next 放行,不传入参数代表直接放行,传入的参数为路由配置中的path,代表跳转到指定的路径next();// 路由拦截// 如果用户登录了,就会获取到一个token,此时可以利用token来判断用户是否登录,进而实现跳转// if (已经登录有token值) {// if (如果跳转的是登录页) {// next(强制跳转到首页);// } else {// next() // 放行// }// } else { // 没有登录// next("/login");// } });
全局路由后置守卫(当你真正进入到某个页面之后才执行)
router.afterEach((to, from) => {console.log(to, from); });
实际使用到两个全局守卫的场景:页面跳转时显示loading动画,页面跳转完成后隐藏loading动画
状态仓库
1、为什么要有状态仓库?
因为组件化的项目中会存在很多数据在多个组件里使用,这些数据使用传统的父子通信非常麻烦,耦合性太高,难以维护。这个时候可以由第三方声明一个空的状态仓库,所有可能会用到的共享数据全部存放在仓库中,组件直接从仓库中取数据或者直接通知仓库修改数据即可。
vue2中一个典型的方案就是vuex,vue3中典型的方案是pinia
2、什么时候用到状态仓库pinia?什么时候不建议使用?
如果项目中有多个组件用到相同的数据了,此时建议把该数据保存到pinia仓库中。如果某数据只在一个组件里使用到,不建议使用pinia
3、pinia的几个核心概念
每个defineStore都是一个独立的模块仓库
defineStore方法有两个参数:仓库id名,配置项对象
每个配置对象有三个核心概念:state、getters、actions
其中state就是仓库中存放数据的位置,getters就是仓库中的计算属性,actions里存放的是仓库中修改数据的方法
import { computed } from 'vue' import { useCounterStore } from './stores/counter.js' import TableView from './pages/TableView.vue' // 可以在组件中的任意位置访问 `store` 变量 ✨ const store = useCounterStore() console.log('🚀 ~ file: App.vue:11 ~ store:', store) // 组件本身没有该数据,是从外部获取到的数据,建议使用computed计算属性 const name = computed(() => store.name) // 修改仓库中的内容 const handleClick = () => {store.name = 'insight!!' // 修改仓库状态方式一,更简单一些// store.increment() // 修改仓库状态方式二,更规范一些store.$patch({count: store.count+10}) // 修改仓库状态方式三,适合批量修改数据 }