文章目录
Vue3官方文档
1.指令
v-bind
用于动态地绑定一个或多个属性,或一个组件 prop 到表达式。
<!-- 绑定一个属性 -->
<img v-bind:src="imageSrc">
<!-- 使用对象语法同时绑定多个属性 -->
<img v-bind="{ src: imageSrc, alt: imageAlt }">
<!-- 缩写 -->
<img :src="imageSrc">
v-model
在表单控件元素上创建双向数据绑定。
<input v-model="message" placeholder="编辑我...">
<p>消息是:{{ message }}</p>
v-for
用于基于源数据多次渲染一个元素或模板块。
<ul>
<li v-for="item in items" :key="item.id">
{{ item.text }}
</li>
</ul>
v-if / v-else-if / v-else
条件性地渲染一块内容。
<p v-if="user.isLoggedIn">欢迎回来,{{ user.name }}!</p>
<p v-else>请登录。</p>
v-show
通过切换 CSS 属性 display
来条件性地渲染元素。
<p v-show="isVisible">现在你看到我了</p>
v-on
监听 DOM 事件,并在事件触发时执行表达式。
<button v-on:click="sayHello">点击我</button>
<!-- 缩写 -->
<button @click="sayHello">点击我</button>
v-once
只渲染元素和组件一次,随后的重新渲染将不会更新。
<span v-once>This will never change: {{msg}}</span>
v-cloak
与 CSS 结合使用,用于在 Vue 实例被创建之前保持原始的 HTML,避免闪烁。
<style>
[v-cloak] {
display: none;
}
</style>
<div v-cloak>
{{ message }}
</div>
v-text 和 v-html
v-text
:更新元素的文本内容。v-html
:更新元素的 HTML 内容,需要小心使用以避免 XSS 攻击。
<!-- 文本内容 -->
<p v-text="message"></p>
<!-- HTML 内容 -->
<div v-html="rawHtml"></div>
v-memo
对子树的响应性依赖进行跟踪,并仅在依赖发生变化时重新渲染。
<template v-memo="memoKey">
<!-- 复杂的模板 -->
</template>
自定义指令
除了内置指令,Vue 也允许你定义自己的指令。
javascript复制
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
// 当绑定元素插入到 DOM 中时...
mounted(el) {
// 聚焦元素
el.focus();
}
})
// 使用
<input v-focus>
2.插槽 slot /
2.1默认插槽
默认插槽是未指定名称的插槽,子组件中使用 <slot>
标签来定义,默认情况下,所有未被具名插槽匹配的内容都会被分发到默认插槽。
子组件(ChildComponent.vue):
<template>
<div>
<slot></slot> <!-- 默认插槽 -->
</div>
</template>
父组件(ParentComponent.vue):
<template>
<ChildComponent>
<p>这里是一些插入的内容</p>
</ChildComponent>
</template>
2.2具名插槽
具名插槽允许你定义多个插槽,每个插槽都有一个名字。在子组件中,使用 <slot>
标签的 name
属性来定义具名插槽。
子组件(ChildComponent.vue):
<template>
<div>
<slot name="header"></slot> <!-- 具名插槽 -->
<slot name="footer"></slot> <!-- 具名插槽 -->
</div>
</template>
父组件(ParentComponent.vue):
<template>
<ChildComponent>
<template v-slot:header>
<h1>这是头部内容</h1>
</template>
<template v-slot:footer>
<p>这是底部内容</p>
</template>
</ChildComponent>
</template>
Vue 3 引入了 #
符号作为 v-slot
的缩写,使得具名插槽的写法更加简洁。
<ChildComponent>
<template #header>
<h1>这是头部内容</h1>
</template>
<template #footer>
<p>这是底部内容</p>
</template>
</ChildComponent>
2.3作用域插槽
作用域插槽允许子组件将数据作为插槽的一部分传递回父组件。在子组件中,使用 v-slot
指令来定义作用域插槽。
子组件(ChildComponent.vue):
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item">{{ item.defaultText }}</slot> <!-- 作用域插槽 -->
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: [{ id: 1, defaultText: '默认文本1' }, { id: 2, defaultText: '默认文本2' }]
};
}
}
</script>
父组件(ParentComponent.vue):
<template>
<ChildComponent>
<template v-slot:default="slotProps">
<span>{{ slotProps.item.id }} - {{ slotProps.item.text }}</span>
</template>
</ChildComponent>
</template>
使用 #
符号简化作用域插槽的写法:
<ChildComponent>
<template #default="slotProps">
<span>{{ slotProps.item.id }} - {{ slotProps.item.text }}</span>
</template>
</ChildComponent>
3.v:bind 的同名简写
- :id=id 可以写成 :id
4.前端Vue异常
- 信息、警告(可能版本问题等,根错误不同)
5.VUE声明响应式数据
- 使用ref( )就行,
- reactive( ) 只能声明引用数据类型
6.计算属性:computed(就是有依赖缓存)
使用场景:(常用的就是get函数,set函数不太常用)
- 需要的数据JS中没有
- 可以通过现有的数据通过操作得到数据
7.v-if 和 v-for 优先级
- vue2中 v-if > v-for
- vue3中 v-if < v-for
8.element-ui 框架使用思路
直接‘快速开始’ 看需要安装什么对应的安装,比如使用到了图标就去安装图标,
然后使用组件,找到对应的组件以及对应的代码开始测试。
9. 组件通信 Vue3官方文档
父子组件通信
-
父到子(Props)
父组件通过props向子组件传递数据。子组件通过声明props来接收。
// 子组件 <script setup> import { defineProps } from 'vue'; const props = defineProps({ dataName: String // 或其他类型 }); </script>
-
子到父(Events)
:子组件通过$emit向父组件发送事件和数据,父组件监听这些事件。
// 子组件 <script setup> import { defineEmits } from 'vue'; const emit = defineEmits(['update:name']); function updateData() { emit('update:name', newValue); } </script>
父组件监听 update:name 事件:
<!-- 父组件 --> <ChildComponent @update:name="handleNameUpdate" />
父组件中处理事件:
function handleNameUpdate(newValue) { // 更新数据 }
爷孙组件通信
-
爷爷到孙子(Provide / Inject)爷爷组件使用 provide 提供数据,孙子组件使用 inject 注入数据。
// 爷爷组件 <script setup> import { provide } from 'vue'; provide('dataName', dataValue); </script>
孙子组件注入数据:
<script setup> import { inject } from 'vue'; const dataName = inject('dataName'); </script>
使用 v-model
进行双向绑定
-
自定义组件的
v-model
:类似于父子组件通信,
v-model
可以用于自定义组件,以实现双向绑定。
// 子组件 <script setup> import { defineProps, defineEmits } from 'vue'; const props = defineProps({ modelValue: String // 或其他类型 }); const emit = defineEmits(['update:modelValue']); function updateModel(value) { emit('update:modelValue', value); } </script>
父组件使用
v-model
html
<!-- 父组件 --> <ChildComponent v-model="parentData" />
在这里,
parentData
是父组件中的数据,
v-model
会自动使用
update:modelValue
事件和
modelValue
prop 来实现双向绑定。
10.vue Router 路由:局部跳转 官网文档
1.首先下载 pnpm add vue-router@4
2.在main.js文件中引入实例–VueRouter–创建vue实例–注册vue实例
3.在app.vue中引入 import { RouterLink, RouterView } from ‘vue-router’;
4.然后使用 RouterLink 就相当于 a标签
5.注意:使用要给 路由的出口 RouterView
路由跳转:
- 使用上面的 RouterLink 标签 注意:给 路由的出口 RouterView
- 使用路由实例的push方法和 replace方法都能实现路由跳转,replace方法是替换历史记录得到
- 使用路由实例的back方法可以实现返回上一页,使用实例的go方法传入整数可以跳转n个页面 n 为-1 就是返回上一页
路由传参:
在实际项目中路由通常来实现页面跳转,很多时候页面跳转要传递一些数据到目标页面,为了满足这种路由跳转的时候
声明式路由传参:使用RouterLink标签来实现页面跳转,也可以使用query和 params实现传参,情况跟下面的编程式传参一样
编程式路由传参 (使用js实现路由跳转)
props传参【了解】
-
//query传参 (path+query搭配使用) //query 传参,查询字符串传参,path+query搭配使用(类似于get请求 参数拼接在 浏览器的url 后面,刷新页面数据不会丢失 和 控制台的 query 有参数) router.push({ // query路由传参 path:'/goods',//跳转到目标路由 query:{ id:'120' //传的数据 } })
-
//params传参 //params 动态路由传参,name+query搭配使用 (要在路由的配置path后面加要接收的参数,比如path:'goods/:id/:age')更安全一点, //(vue2中,不配置path可以接收到参数,但是刷新页面数据丢失) router.push({ // params传参 name: 'goods', params: { id: '120' } })
重定向:就是访问一个路径,自动跳转到另外一个路径,比如访问404页面
路由模式
1.hash模式,哈希模式。纯前端实现,底层监听了onhashchange事件,局部刷新,很丑
2.history,历史模式,需要后端支持,底层就是html5 history api:pushState 和 replaceState 局部刷新 漂亮一些
3.memory模式,主要用于node端,页面的渲染不会反映在url上 【了解】
//路由模式
history:createWebHashHistory(),//hash模式,
history: createWebHistory(),//history 历史模式
history:createMemoryHistory(),//memory
路由元信息
//在传参的位置加上 meta配置
//query传参的配置
{
path: '/goods',
component: GoodsView,
//元信息(Meta Information)
meta:{title:'商品页',auth:false, KeepAlive:true}
},
二级路由
在原本的路由里添加一个children属性,
例:
//query传参的配置
{
path: '/goods',
component: GoodsView,
meta: { title: '商品页', auth: false, KeepAlive: true },
//重定项
redirect:'/goods/list',
//嵌套路由
children: [
{
// path:'list',
path: '/goods/list', //推荐 便于查找
component: () => import('../pages/goods/GoodsList.vue'), //路由懒加载
},
{
// path:'type',
path:'/goods/type', //推荐 便于查找
component:() => import('../pages/goods/GoodsType.vue'), //路由懒加载
},
{
// path:'add',
path:'/goods/add', //推荐 便于查找
component:() => import('../pages/goods/GoodsAdd.vue'), //路由懒加载
}
]
},
注意:不要忘记给出口,给到根路由的页面,这里例子是GoodsView
//出口
<template>
<div>
商品页
<button @click="handleClick">返回上一页</button>
<div class="box">
<div class="left">
<RouterLink to="/goods/list">商品列表</RouterLink><br>
<RouterLink to="/goods/type">商品类型</RouterLink><br>
<RouterLink to="/goods/add">商品添加</RouterLink><br>
</div>
<!-- 二级路由出口 -->
<RouterView class="right"></RouterView>
</div>
</div>
</template>
<!--如果都配置完了,右侧没有信息的话,可以在配置路由的地方加一个重定向
//重定项
redirect:'/goods/list'
-->
动态路由
// index.js 中
//动态路由,根据用户登录的角色判断数据信息
const dynamicRoutes = [
{
path: '/total/order',
component: () => import('../pages/sales/OrderTotal.vue'),
meta: { roles: ['super'] }
},
{
path: '/total/sales',
component: () => import('../pages/sales/SalesTotal.vue'),
meta: { roles: ['super'] }
},
]
//addRoute方法吧路由添加到路由实例中
dynamicRoutes.forEach((item) => {
if (item.meta.roles.includes('super')) { //后面的super是用户登录时获取到的用户的角色
router.addRoute(item)
}
})
11.导航守卫
实际使用到两全局守卫的场景:页面跳转是时显示loading动画,页面跳转完成后隐藏loading动画
-
全局前置路由守卫:页面跳转完成前,比如控制页面能否跳转、重定向。
router.beforeEach((to, from, next) => { console.log(to, from, 'to, from, next'); //to 到哪里去 //from 从那里来 //next 传入参数为路由配置中的path,代表跳转到指定的路由 next() //不传参就代表直接放行 //场景:如果用户登陆了就可以获取到token,此时可以利用token来判断用户是否登录,进而 // if (已经登录, 有token) { // if (跳转的是登录页) { // next(强制跳转到首页) // } else { // next() // } // } else { //没有登录 // next('login') // } })
-
全局后置路由守卫:页面跳转后完成一些事情
// 问什么这里没有第三个参数,因为这里页面已经渲染完毕,不需要进行操作 router.afterEach((to, from) =>{ console.log(to,from); })
12.pinia Vue的专属状态管理库
12.1首先安装
yarn add pinia
# 或者使用 npm
npm install pinia
12.2创建实例并调用
//mian.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
12.3新建stores文件夹
//counter.js
import { defineStore } from "pinia"
//定义一个仓库 3个核心配置项(state、getters、actions)
export const useCounterStore = defineStore('counter', {
//相当于vue3 中的 ref/reactive
state: () => {
return {
count: 0,
name: 'Eduardo'
}
},
//计算属性
getters: {
doubleCount: (state) => state.count * 2,
},
//方法 相当于vue3的methods
actions: {
increment() {
this.count++
},
},
})
12.4调用
//App.vue
import { computed } from 'vue'
//注意这里没有用@符号,因为没有配置别名
import { useCounterStore } from './stores/counter.js'
import { useCounterStore1 } from './stores/do-exercise.js'
// 可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
console.log(store.name);
//组件本身没有改数据,是从外部获取到得的数据,建议使用computed
const name = computed(() => store.name)
//修改仓库中的内容
const handleClick = () => {
store.name = 'insight!!!'
}
13.自定义hooks
-
自定义hooks使用场景:
①项目中如果有多处都是用到了统一中逻辑的js代码,此时就可以把这些代码封装成自定义hooks,达到代码的复用。
②扩展:vue3与vue2的一样
例子:响应式width
<!--demo.vue 页面-->
<template>
<div>
<table id="table" border :style="{ borderCollapse: 'collapse', width: width + 'px' }">
<thead>
<tr>
<th>ID</th>
<th>名称</th>
<th>I单价</th>
<th>数量</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr v-for="item in data">
<td>{{item.id}}</td>
<td>{{ item.name }}</td>
<td>{{ item.price }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.subtotal }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<!--demo.vue 页面-->
<script setup>
//使用下方 hooks
//使用 响应式宽度
import { useResize} from './hooks/useResize.js'
const {width} = useResize();
</script>
封装hooks
//useResize
import { onMounted, onUnmounted, ref } from 'vue';
//这里export导出 可以在其他组件使用
export const useResize = (initialWidth = 200) => {
const width = ref(initialWidth)
const resizeFn = () => {
width.value = document.body.offsetWidth
}
onMounted(() => {
//初始渲染
resizeFn()
//挂载后监听
window.addEventListener('resize', resizeFn)
})
onUnmounted(() => {
//卸载后移除监听事件
window.removeEventListener('resize', resizeFn)
})
return {
width
}
}
14.KeepAlive 缓存
在 Vue 3 中,<KeepAlive>
是一个内置组件,用于缓存不活动的组件实例,而不是销毁它们。这对于提高大型应用的性能非常有用,因为它避免了重复渲染组件的开销。以下是 <KeepAlive>
组件的一些关键特性和用法。
App.vue页面
<!-- App.vue -->
<script setup>
import { shallowRef } from 'vue'
import CompA from './CompA.vue'
import CompB from './CompB.vue'
const current = shallowRef(CompA)
</script>
<template>
<div class="demo">
<label><input type="radio" v-model="current" :value="CompA" /> A</label>
<label><input type="radio" v-model="current" :value="CompB" /> B</label>
<KeepAlive :include="['CompA','CompB']">
<component :is="current"></component>
</KeepAlive>
<component :is="current"></component>
</div>
</template>
CompA.vue页面
<!-- CompA.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<p>Current component: A</p>
<span>count: {{ count }}</span>
<button @click="count++">+</button>
</template>
CompB.vue页面
<!-- CompB.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<p>Current component: A</p>
<span>count: {{ count }}</span>
<button @click="count++">+</button>
</template>