模板语法
// 1、插值表达式
<div>{{ message }}</div>
<p>{{ count + 1 }}</p>
// 2、指令系统
<!-- 条件渲染 -->
<!-- 如果条件为false则不渲染该dom-->
<div v-if="isActive">显示A内容</div>
<div v-else>显示B内容</div>
<!-- 如果条件为false则该dom的display为false 高频则推荐该方式-->
<div v-show="isActive">显示内容</div>
<!-- html结构渲染 -->
<div v-html="htmlStr"></div>
<!-- 数组列表渲染 -->
<li v-for="(item, index) in items" :key="item.id">{{ item.name }}</li>
<!-- 对象渲染 -->
<li v-for="(value, key, index) in obj" :key="value.id">{{ value.name }}</li>
<!-- 动态属性绑定 -->
<img :src="imageUrl" :alt="imageDescription">
// 4、模板里面访问所需全局对象可配置在main.js
const app = createApp(App)
app.config.globalProperties.TestConfig = {}
app.mount('#app')
vue3 模板语法具有沙盒特性,保证安全性,只能访问部分全局对象。
禁止的操作类型
1、全局对象访问 - 无法访问window、document等
2、危险操作 - 如delete操作符、with语句
3、函数定义 - 无法在模板中定义新函数
4、赋值操作 - 无法在模板中修改变量值
5、不要在标签上同时使用v-if和v-for,因为优先级不明显
类与样式绑定
// 1、动态类属性绑定 isActive是布尔值
<!-- 对象语法 -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
<!-- 数组语法 -->
<div :class="['active','text-danger']"></div>
// 动态样式绑定
<!-- 对象语法 -->
<p :style="{ color: 'red', fontSize: '10px' }"></p>
<!-- 数组语法 -->
<p :style="[{ color: 'red' }, { fontSize: '10px' }]"></p>
事件
<!-- 事件 -->
<!-- $event、e都是事件对象 -->
<button v-on:click="add">添加</button>
<button @click="add">添加</button>
<button @click="add($event)">添加</button>
<button @click="(e) => add(e)">添加</button>
<!-- 修饰符 -->
<!-- 阻止冒泡 -->
<button @click.stop="reduce"></button>
<!-- 阻止默认事件 -->
<button @click.prevent="reduce"></button>
<!-- 只有事件在该元素本身才能触发该函数,非子元素 -->
<button @click.self="reduce"></button>
<!-- 改变事件触发的阶段,捕获阶段触发,而非冒泡阶段 -->
<button @click.capture="reduce"></button>
<!-- 只触发一次 -->
<button @click.once="reduce"></button>
<!-- 提高页面滚动性能 -->
<button @click.passive="reduce"></button>
<!-- 按键修饰符 -->
<button @keydown.left="reduce"></button>
<button @keyup.enter="reduce"></button>
<!-- ... -->
<!-- 组合修饰符 -->
<button @click.ctrl="reduce"></button>
<!-- ... -->
v-model、defineModel(vue3.4)
v-model 既可以实现数据到表单元素之间的双向绑定,也可以用于父子组件之间的数据双向绑定
<!-- 使用场景一:基本表单组件(将组件实例上的searchText绑定给input的value属性) -->
<input v-model="searchText" />
<!-- 使用场景二:父子组件双向绑定 -->
<!-- 当父组件中pageNum发生变化时,子组件会自动更新currentPage的值,同样当子组件的currentPage发生变化时,父组件也会自动更新pageNum的值 -->
<!-- 父组件 -->
<pagination v-model:page="pageNum" />
<!-- 子组件 -->
const props = defineProps({
page: {
type: Number,
default: 1
}
});
const emit = defineEmits(['update:page']);
const currentPage = computed<number | undefined>({
get: () => props.page,
set: value => {
emit('update:page', value);
}
});
defineModel仅用于父子组件之间的数据双向绑定,它只是一个语法糖,内部还是用的props取值以及自定义事件来完成父组件数据的更新,并没有打破单向数据流
<!-- 父组件 -->
<Home v-model:count1="state.count" v-model:count2="_count1" name="张三"></Home>
<!-- 子组件 -->
const count1 = defineModel('count1')
const count2 = defineModel('count2')
function update(){
count1.value++;
count2.value++;
}
ref
ref 是用来创建响应式引用的主要方法。它通常用于基本数据类型(如字符串、数字、布尔值、对象、数组等)的响应式包装。在模板中可以直接使用,但在 JavaScript 中需要通过 .value 属性来访问或修改它的值
1、ref具有深层次响应式特点,主要针对对象;
2、通过shallowRef创建的响应式,不会深层递归,不具备深层响应式特点;
import { ref } from 'vue';
const count = ref(0);
const userName = '洛可可白';
// 在 JavaScript 中访问
console.log(count.value); // 0
count.value++; // 增加计数
// 在模板中访问
// <div>{{ count }}</div>
// ref也可以用于获取dom元素
<div><div ref="myDiv"></div>
const myDiv = ref(null);
myDiv.value.innerHTML = userName;
reactive
reactive 是用来创建响应式对象的方法,适用于处理对象和数组。reactive对象的属性可以在模板和 JavaScript 中直接访问和修改,不需要 .value
1、reactive具有深层次响应式特点;
2、通过shallowReactive创建的响应式,不会深层递归,不具备深层响应式特点;
3、使用reactive创建对象时不要去直接替换reactive关联的对象,会丢失响应式;
4、对reactive的数据进行对象解构时,解构的数据也会丢失响应式;
5、如果将ref作为reactive创建对象的属性,获取该属性值时,不需要解包(无需.value);但是如果将ref作为shallowReactive创建对象的属性时,不会自动解包。
import { reactive } from 'vue';
const state = reactive({
count: 0,
userName: '洛可可白'
});
// 在 JavaScript 中访问和修改
console.log(state.count); // 0
state.count++; // 增加计数
// 在模板中直接访问
// <div>{{ state.count }}</div>
// <div>{{ state.userName }}</div>
ref和reactive
vue中的响应式是通过Proxy来实现的,Proxy只能拦截对象,无法拦截原始值;那如果需要把原始值转成响应式如何处理?
1、用户自己把原始值包装成对象,再使用reactiveApi;
2、框架层面帮你处理成对象(使用refApi),ref有两种处理方式:
A、如果是原始值,使用Object.defineProperty来实现;
B、如果是对象,使用reactiveApi来实现;
C、reactive响应式内部是由Proxy来实现;
响应式其它API
toRef、toRefs、unRef、readonly、isRef、isReactive、isProxy、isReadonly
import {
isReactive,
reactive,
toRef,
isRef,
unref,
toRefs,
shallowReactive,
shallowReadonly,
readonly,
isProxy,
} from 'vue'
const state = reactive({
count: 1,
obj: {
value: 1,
},
})
// 将响应式对象属性转化为ref
const countRef = toRef(state, 'count') // => const countRef = ref(state.count)
// 将整个reactive对象的所有属性都转化为ref对象
const refs = toRefs(state) // 取值 => const count = refs.count.value
// 判断一个对象是否是ref
isRef(countRef) // true
// 解包ref对象,如果不是ref直接返回值
unref(countRef) // 1
// 判断是否是reactive
isReactive(state) // true
isReactive(state.obj) // true reactive是深层次响应式,所以下层对象也是reactive对象
// 创建只读代理 只能获取不能改值
const readObj = readonly(state || countRef)
// isProxy 检查一个对象是否由reactive、readonly、shallowReactive、shallowReadonly创建的代理
// 因为ref内部是用了两种方式,所以它并不是单一的Proxy代理实现
isProxy(state)
computed
computed 用于创建计算属性,这些属性的值是基于其他响应式数据源派生出来的。计算属性是惰性求值的,只有当它们的依赖响应式数据发生改变时才会重新计算,对比模板中直接使用函数来实现,它的性能更好
computed值也可以修改,但是computed的设计思想只用来获取值,所以不建议进行修改操作
import { ref, computed } from 'vue'
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
// computed的修改使用
import { computed, isReactive, reactive, ref } from 'vue'
const state = reactive({
count: 1,
obj: {
value: 1,
},
})
const count = ref(0)
const newCount = computed({
get(value) {
return state.count + 10
},
set(value) {
count.value = value
},
})
setTimeout(() => {
newCount.value = newCount.value + 1
}, 2000)
watch
watch 用于观察响应式数据的变化,并在数据变化时执行特定的函数。
1、watch的执行顺序优于computed
2、watch可以监听ref、reactive、computed,以及Getter函数(监听函数返回值),以及数组形式的ref或者reactive
3、watch监听的对象是reactive数据时,监听层级是深层次的
4、如果想要监听reactive对象属性值时,应该使用回调函数形式来监听【watch(()=> getValue, () => {})】
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
function getValue() {
return state.count / 2
}
watch(getValue, (newV, oldV) => {})
watch([_count1, _count2, state, state2], (newV, oldV) => {},
{
immediate: true, // 默认false 组件渲染时会执行一次
deep: true, // 默认true 深度监听,但是如果监听对象是computed值或者Getter函数时默认就是false
once:true // 默认false 监听器回调函数只执行一次
flush:'post' // dom更新之后执行
},
)
watchEffect
watchEffect 是一个基于响应式数据源的观察者,当响应式数据源变化时重新执行。
1、会立即执行一次
2、多依赖项监听可以使用watchEffect,而不是watch
3、如果只需要监听reactive对象的某个属性,那么watchEffect函数中只需要用到该属性即可,性能对比watch更优
import { ref, watchEffect } from 'vue';
const count = ref(0);
const unwatch = watchEffect(() => {
console.log(`Count is now: ${count.value}`);
});
unwatch() // 停止侦听器
生命周期
onBeforeMount(() => {})
onMounted(() => {})
onBeforeUpdate(() => {})
onUpdated(() => {})
onBeforeUnmount(() => {})
onUnmounted(() => {})
provide 和 inject
provide 和 inject 是用于组件间通信的 API,允许父组件提供数据,子组件注入并使用这些数据,用于跨层级父子组件通信
import { provide, inject } from 'vue';
// 父组件
provide('injectedValue', 'This is provided by parent');
// 子组件
const injectedValue = inject('injectedValue');
nextTick
nextTick 是一个全局方法,用于在下次 DOM 更新循环结束之后执行延迟回调。在修改了响应式数据之后,通常用于等待 DOM 更新
import { nextTick } from 'vue';
nextTick(() => {
console.log('DOM has been updated!');
});
宏(defineProps、defineEmits)
defineProps用于父组件给子组件传递数据
<!-- 父组件 -->
<Home name="张三"></Home>
<Home user-name="李四"></Home>
<!-- 非字符串类型,必须使用动态方式传递参数 -->
<Home user-name="王五" :age="18"></Home>
<!-- 子组件 -->
<!-- 模块中可以不需要props前缀 -->
const props = defineProps({
name: String,
userName: String,
age: Number,
})
<template>
<div>{{ name }}</div>
<div>{{ userName }}</div>
<div>{{ age }}</div>
</template>
defineEmits自定义事件由父组件给子组件传递事件,用于子组件主动触发该事件或者给父组件传递数据
<!-- 父组件 -->
<Home @reduce="reduce" name="张三"></Home>
<!-- 子组件 -->
const emit = defineEmits({
reduce: Function,
})
<template>
<!-- 写法1 -->
<button @click="emit('reduce', 2)">-</button>
<!-- 写法2 -->
<button @click="$emit('reduce', 2)">-</button>
</template>
插槽
插槽是一种让父组件能够向子组件传递内容的机制。插槽可以分为默认插槽、具名插槽和作用域插槽
默认插槽
子组件在模板中预留一个插槽位,父组件可以传入内容填充这个位置
// 子组件
<template>
<div>
<slot />
</div>
</template>
// 父组件
<template>
<ChildComponent>
<p>这是父组件传递的内容。</p>
</ChildComponent>
</template>
具名插槽
子组件可以定义多个插槽位,父组件可以指定内容填充到哪个插槽位。
// 子组件
<template>
<div>
<slot name="header"></slot>
<slot name="main"></slot>
<slot name="footer"></slot>
</div>
</template>
// 父组件
<template>
<ChildComponent>
<template v-slot:header>
<h1>这是头部内容</h1>
</template>
<template v-slot:main>
<p>这是主体内容</p>
</template>
<template v-slot:footer>
<footer>这是底部内容</footer>
</template>
</ChildComponent>
</template>
作用域插槽
除了传递内容,插槽还可以传递数据
// 子组件
<template>
<div>
<slot :user="user">{{ user.name }}</slot>
</div>
</template>
<script>
export default {
data() {
return {
user: { name: '张三', age: 30 }
};
}
};
</script>
// 父组件
<template>
<ChildComponent>
<template v-slot:default="slotProps">
<p>{{ slotProps.user.name }} 今年 {{ slotProps.user.age }} 岁。</p>
</template>
</ChildComponent>
</template>
自定义 hooks
根目录 src 下创建 hooks 文件夹,然后创建一个 xxx.js 文件。写法同 react 自定义 hooks 差不多,返回一个对象,对象中的属性和方法可以提供给父组件使用
export default () => {
const state = reactive<IState>({
lightWall: undefined,
})
/**
* 创建光墙
* @param position 中心点
*/
const createLightWall = () => {
const id = v4()
const lightWall = new LightWall({
id,
ziCesium,
})
state.lightWall = lightWall
};
onUnmounted(() => {})
return {
createLightWall,
};
};
Pinia 介绍
Pinia 是一个专门为 Vue.js 设计的状态管理库,它提供了一种简单和直观的方式来管理应用程序的状态。在使用 Pinia 时,可以轻松地创建定义状态的存储,然后将其与 Vue 组件绑定,使它们能够使用该状态。和 Vuex 相比,Pinia 更加简单易用,体积更小,同时具有更好的 TypeScript 支持和插件系统
// 步骤一:main.ts中需要挂载pinia
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount('#app');
// 步骤二:在src目录下创建store文件夹,store文件下创建一个modules文件夹,然后modules文件夹下面创建一个user.ts文件,内容如下
import { defineStore } from 'pinia';
const useUserStore = defineStore({
id: 'user',
state: (): UserState => ({
userInfo:null
}),
actions: {
async RESET_STATE() {
this.$reset();
},
setUserInfo(userInfo) {
this.userInfo = userInfo
},
},
});
export default useUserStore;
// 步骤三:在store文件夹下创建一个index.ts文件,内容如下
import useUserStore from './modules/user';
const useStore = () => ({
user: useUserStore(),
});
export default useStore;
// 步骤四:使用
import useStore from '@/store';
import { onMounted } from 'vue';
const { user } = useStore();
onMounted(()=>{
console.log(user.userInfo)
})

被折叠的 条评论
为什么被折叠?



