【VUE3】VUE组合式(响应式)API常见语法
pnpm常用命令
pnpm i //pnpm安装
VUE3常见语法汇总
ref() //const count = ref(0) //count.value(访问值,包括对象要加.value) //任何类型的值,包括深层嵌套的对象或则JS内置数据结构
await nextTick() //要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API
reactive() //只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)它不能持有如 string、number 或 boolean 这样的原始类型
模板语法1:<span>Message: {{ msg }}</span>
模板语法2(插入HTML):<span v-html="rawHtml"></span>
指令:<div v-bind:id="xd"></div> //常见指令 :id、:disable、:href
计算属性:(1)、import { ref, computed } from "vue"; (2)、const booksLength = computed(() => { return list.value.books.length > 0 ? "Yes" : "No"; }); (3)、 <span>{{ booksLength }}</span> // 不是以一个函数执行,不加()
条件渲染:v-if、v-else、v-else-if、v-show
列表渲染:(1)、v-for(<li v-for="item in courseList">{{ item }}</li>) ;(2)、of(<li v-for="item of courseList">{{ item }}</li>)
#v-if和v-for是不推荐一起使用在同一个标签
事件监听:v-on:click="handler" 或则 @click="handler"
侦听器:watch(我们需要在状态变化时执行一些“副作用:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态)
侦听器:watchEffect()
onMounted:是一个生命周期钩子(生命周期函数),用于在组件实例被挂载到 DOM 上后执行代码。
onUnMounted://组件卸载之后
- 计算属性
为什么后面不加(),不是以一个函数执行
使用缓存,减少性能消耗
<script setup>
import { ref, computed } from "vue";
const list = ref({
books: [
"语文",
"数学",
"英语",
],
});
// 一个计算属性 ref
const booksLength = computed(() => {
return list.value.books.length > 0 ? "Yes" : "No";
});
</script>
<template>
<p>拥有书籍的个数:</p>
<span>{{ booksLength }}</span>
</template>
- 可写计算属性
<script setup>
import { ref, computed } from "vue";
const firstName = ref("老");
const lastName = ref("王");
const fullName = computed({
// getter
get() {
return firstName.value + " " + lastName.value;
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(" ");
},
});
// fullName.value = "范 冰冰";
</script>
<template>
<p>Has published books:</p>
<span>{{ fullName }}</span>
</template>
- 列表渲染
v-if和v-for是不推荐一起使用在同一个标签
当它们同时存在于一个节点上时,v-if 比 v-for 的优先级更高
这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名
对象的循环便利:便利对象的值、键、索引如下:
<script setup>
import { ref } from "vue";
const courseObject = ref({ front: "js", back: "java" });
</script>
<template>
<ul>
<li v-for="(value, key, index) in courseObject" v-if="!value">
{{ value }} - {{ key }} -{{ index }}
</li>
</ul>
</template>
- 事件修饰符
.stop //阻止事件的向上传播,阻止上层事件的发生
.prevent //阻止行为
.self //提交事件将不再重新加载页面
.capture //添加事件监听器时,使用 `capture` 捕获模式
.once //点击事件最多被触发一次
.passive //滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成
- 按键修饰符
.enter //enter键
.tab
.delete (捕获“Delete”和“Backspace”两个按键)
.esc
.space
.up
.down
.left
.right
- 系统按键修饰符
.ctrl
.alt
.shift
.meta
- 表单输入绑定
(1)、没使用vue时需要手动处理双向数据绑定
<input
:value="text"
@input="event => text = event.target.value"
>
(2)、v-model 指令帮我们简化了这一步骤
<input v-model="text">
- 侦听器
我们需要在状态变化时执行一些“副作用:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态
<script setup>
import { ref, watch } from "vue";
const question = ref("");
const answer = ref("答案");
const loading = ref(false);
// 可以直接侦听一个 ref
watch(question, (newQuestion) => {
if (newQuestion.includes("?")) {
loading.value = true;
answer.value = "Thinking...";
setTimeout(() => {
answer.value = "是的";
}, 1000);
}
});
</script>
<template>
<p>
提问问题
<input v-model="question" :disabled="loading" />
</p>
<p>{{ answer }}</p>
</template>
(1)即时回调侦听器
watch 默认是懒执行的:仅当数据源变化时,才会执行回调
watch(
source,
(newValue, oldValue) => {
// 立即执行,且当 `source` 改变时再次执行
},
{ immediate: true }
)
(2)、一次性侦听器
源变化时触发一次 once: true
watch(
source,
(newValue, oldValue) => {
// 当 `source` 变化时,仅触发一次
},
{ once: true }
)
watch vs. watchEffect
wathchEffect(() => {
console.log('count is :${obj.value.count}');
})
watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:
-
watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
-
watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。
-
组件上的ref
使用场景
1. 获取某个元素的位置、宽高
2. 子组件的内容
<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const child = ref(null)
onMounted(() => {
// child.value 是 <Child /> 组件的实例
})
</script>
<template>
<Child ref="child" />
</template>
- Vue3的所有生命周期函数流程
- Vue3生命周期函数整理总结
初始化阶段
- beforeCreate
- created
此阶段无法获取template里面DOM节点
<script setup>
/**
组件加载前
/
</script>
挂载阶段
- onMounted(挂载之后)
- onBeforeMount
更新阶段
- onBeforeUpdate
- onUpdated
卸载阶段
- onBeforeUnmount
- onUnmounted(卸载之后)
第7集 Vue3单页面组件开发基础—组件定义+使用
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构
- 定义组件
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
- 使用组件
要使用一个子组件,我们需要在父组件中导入它
<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<h1>Here is a child component!</h1>
<ButtonCounter />
</template>
-
父子组件参数传递
defineProps 是一个仅
<script setup>
中可用的编译宏命令,并不需要显式地导入。
(1)、子组件定义
<script setup>
defineProps(['title'])
</script>
<template>
<h4>{{ title }}</h4>
</template>
(2)、Demo代码2
const props = defineProps({
width: {
type: String,
default: "25%",
required: false,
},
title: {
type: String,
default: "",
required: false,
},
dialogVisible: {
type: Boolean,
default: false,
required: true,
},
});
- 父组件传递函数给子组件
// 子组件
defineEmits(["childClick"]);
@click="$emit('childClick')"
// 父组件
@childClick="()=>{}"
第9集 Vue3单页面组件开发基础—插槽
- defineExpose
// 子组件
const xd=ref(0)
defineExpose({xd})
// 父组件
const childRef=ref('')
<child-component ref='childRef' />
<span>{{childRef.xd}}<span/>
- 插槽 slot
// 子组件
<template>
<slot></slot>
</template>
// 父组件
<child-component>
小滴课堂
</<child-component>
第五章 深入掌握Vue3组件进阶内容
第1集 Vue3单页面组件开发进阶—组件注册+命名
- 全局注册
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import ChildComponent from "./ChildComponent.vue";
const app = createApp(App);
app.component("ChildComponent", ChildComponent);
app.mount("#app");
全局注册虽然很方便,但有以下几个问题:
-
不必要的文件体积
全局注册,但并没有被使用的组件无法在生产打包时被自动移除 (也叫“tree-shaking”),如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。 -
难维护
全局注册在大型项目中使项目的依赖关系变得不那么明确,在父组件中使用子组件时,不太容易定位子组件的实现,和使用过多的全局变量一样,这可能会影响应用长期的可维护性
第2集 Vue3单页面组件开发进阶—props
- props声明
<script setup>
const props = defineProps(['xd'])
console.log(props.xd)
</script>
<script setup>
defineProps({
xd: String,
name: String
})
</script>
defineProps中定义属性是单向数据流、如果需要更改可以再维护一个变量
const props = defineProps(['xd'])
// 计数器只是将 props.xd 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.xd)
进一步处理/转换
const props = defineProps(['xd'])
// 该 prop 变更时计算属性也会自动更新
const newXd = computed(() => props.xd+'老王')
第3集 Vue3单页面组件开发进阶—defineModel
- 双向数据绑定
// 子组件
<script setup>
const model = defineModel();
</script>
<template>
<h2>子组件</h2>
<input v-model="model" />
</template>
// 父组件
<script setup>
import ChildComponent from "./ChildComponent.vue";
import { ref } from "vue";
const xd = ref("小滴课堂");
</script>
<template>
<h1>父组件</h1>
<span>{{ xd }}</span>
<ChildComponent v-model="xd"></ChildComponent>
</template>
- 多数据绑定
// 父组件
<ChildComponent v-model:xd="xd" v-model:aa="aa"></ChildComponent>
// 子组件
const xd = defineModel("xd");
const aa = defineModel("aa");
<input type="text" v-model="xd" />
<input type="text" v-model="aa" />
第4集 Vue3单页面组件开发进阶—依赖注入
- provide + inject
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
<script setup>
import { inject } from 'vue'
const message = inject('message')
</script>
- 和响应式数据配合
<!-- 在供给方组件内 -->
<script setup>
import { provide, ref } from 'vue'
const xd = ref('小滴课堂')
function updateXd() {
xd.value = 'xdclass.net'
}
provide('xd', {
xd,
updateXd
})
</script>
<!-- 在注入方组件 -->
<script setup>
import { inject } from 'vue'
const { xd, updateXd } = inject('xd')
</script>
<template>
<button @click="updateXd">{{ xd }}</button>
</template>
第5集 Vue3单页面组件开发进阶—KeepAlive
概念:KeepAlive 是一个内置组件,它的功能是在多个组件间动态切换时,缓存被移除的组件实例。
<script setup>
import ChildComponent1 from "./ChildComponent1.vue";
import ChildComponent2 from "./ChildComponent2.vue";
import { ref } from "vue";
const xd = ref(true);
</script>
<template>
<h1>父组件</h1>
<button @click="() => (xd = !xd)">切换组件</button>
<KeepAlive><ChildComponent1 v-if="xd" /></KeepAlive>
<ChildComponent2 v-if="!xd" />
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
<template>
<h2>子组件1</h2>
<span>count:{{ count }}</span
><button @click="count++">+</button>
</template>
第六章 实现单页面应用Vue3规模化的路由使用—Vue Router
第1集 Vue3单页面实现规模化Vue Router基础—入门使用
- 安装
pnpm install vue-router@4 //@4:指定版本
- 使用
import { createRouter, createWebHashHistory } from "vue-router";
import ChildComponent1 from "./ChildComponent1.vue";
import ChildComponent2 from "./ChildComponent2.vue";
const routes = [
{ path: "/one", component: ChildComponent1 },
{ path: "/two", component: ChildComponent2 },
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
<script setup>
import { ref } from "vue";
const xd = ref(true);
</script>
<template>
<h1>父组件</h1>
<p>
<!-- 通过传递 `to` 来指定链接 -->
<router-link to="/one">去组件1</router-link> <br />
<router-link to="/two">去组件2</router-link>
</p>
<div>
<!-- 路由出口 -->
<router-view></router-view>
</div>
</template>
第2集 Vue3单页面实现规模化Vue Router基础—嵌套路由+编程式导航
- 嵌套路由
import { createRouter, createWebHashHistory } from "vue-router";
import ChildComponent1 from "./ChildComponent1.vue";
import ChildComponent2 from "./ChildComponent2.vue";
import ChildComponent3 from "./ChildComponent3.vue";
const routes = [
{
path: "/one",
component: ChildComponent1,
},
{
path: "/two",
component: ChildComponent2,
children: [
{
path: "aa",
component: ChildComponent3,
},
],
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
- 编程式导航
<script setup>
import router from "./router.js";
const routerChange = (n) => {
router.push(n);
};
</script>
<template>
<h1>父组件</h1>
<p>
<button @click="routerChange('/one')">打开组件1</button> <br />
<button @click="routerChange('/two')">打开组件2</button><br />
<button @click="routerChange('/two/aa')">打开组件3</button>
</p>
<div>
<router-view></router-view>
</div>
</template>
第3集 Vue3单页面实现规模化Vue Router基础—命名+重定向
- 命名+重定向
重定向:redirect
//router.js
import { createRouter, createWebHashHistory } from "vue-router";
import ChildComponent1 from "./ChildComponent1.vue";
import ChildComponent2 from "./ChildComponent2.vue";
import ChildComponent3 from "./ChildComponent3.vue";
const routes = [
{
path: "/",
redirect: "/one",
},
{
path: "/one",
name: "one",
component: ChildComponent1,
},
{
path: "/two",
name: "two",
component: ChildComponent2,
children: [
{
path: "three",
name: "three",
component: ChildComponent3,
},
],
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
<script setup>
import router from "./router.js"
const routerChange = (n) => {\
router.push({ name: n });
}
</script>
<template>
<button @click="routerChange(three)">打开组件3</button>
</template>
第4集 Vue3单页面实现规模化Vue Router基础—路由传参
- 传参
query(推荐方式)
//父组件
//Url传参
<router-link :to="/one?xd=小滴课堂&state=xdclass">打开组件2</router-link>
//对象传参
<router-link
:to="{ path: '/one', query: { xd: '小滴课堂', state: 'xdclass' } }"
>打开组件1</router-link
>
//子组件
import { useRoute } from 'vue-router'
const route = useRoute();
<span>{{route.query.xd }}</span>
<span>{{route.query.state}}</span>
- params
//父组件中通过名字的方式传参
<router-link :to="{ name: 'one', params: { xd: '小滴课堂' } }"
>打开组件1</router-link
>
//router.js 配置params方式传参
path: "/one/:xd"
//子组件获取参数
route.params.xd
-变程式导航传参
const routerChange = (n, obj) => {
router.push({ path: n, query: obj });
};
第5集 Vue3单页面实现规模化Vue Router进阶—历史记录模式+导航守卫
- 不同的历史记录模式
- 哈希模式:createWebHashHistory() 推荐
- html5模式:createWebHistory()
- 导航守卫(可以做权限验证)
//router.js
router.beforeEach((to, from, next) => {
if (to.meta.isAuth) {
if (localStorage.getItem("token") === "1") {
next();
} else {
alert("请先登录");
}
} else {
next();
}
});
第6集 Vue3单页面实现规模化Vue Router进阶—路由懒加载
- 路由懒加载
component: () => import("./ChildComponent1.vue"),
第七章 玩转Vue3拥有组合式API的状态管理库—Pinia
第1集 为什么使用Pinia?
- 为什么使用Pinia?
- Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态
- 如果你熟悉组合式 API 的话,你可能会认为可以通过一行简单的 export const state = reactive({}) 来共享一个全局状态
- Devtools 支持
- 热更新
- 支持服务端渲染
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
- 对比Vuex
- 提供了一个更简单的 API,符合组合式 API 风格的 API
- 搭配 TypeScript 一起使用时有非常可靠的类型推断支持
第2集 Vue3跨组件共享状态Pinia—安装+使用示例
- 安装
pnpm i pinia@2
- 使用
(1)、引入pinia
// main.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')
(2)、定义 defineStore
写法1(推荐方式):
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
写法2:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 }
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++
},
},
})
(2)、使用
import {useCounterStore } from "./store/counter"
const counter=useCounterStore ();
pinia数据:<span>{{ counter.count }}</span>
<button @click="counter.increment">加</botton>