setup()函数写法
这种写法需要通过return{...} 将template需要的数据进行暴露
<script lang="ts">
import {defineComponent,ref} from 'vue'
// 可以帮助我们校验vue组件内部的ts语法代码
export default defineComponent({
setup(){
// refImpl对象 count.value才可以获取ref里面的值 Object.defineProperty
let count = ref(100)
const changeCount = ()=>{
count.value++
}
return {
count,
changeCount
}
}
})
</script>
setup标签写法
这种写法不需要return
<script setup lang='ts'>
import { ref,reactive } from 'vue'
</script>
ref全家桶
ref
接受一个内部值并返回一个响应式且可变的ref对象.ref对象仅有一个.value property,指向该内部值.
// 基本数据类型/复杂数据类型可以通过ref进行包裹变成refImpl对象
const num:number = ref(0)
// ref通过xxx.value来获取num的值
console.log(num.value) // 0
注意被ref包装之后需要.value 来进行赋值
isRef
判断一个对象是否是ref对象
import { ref, Ref,isRef } from 'vue'
let message: Ref<string | number> = ref("我是message")
let notRef:number = 123
const changeMsg = () => {
message.value = "change msg"
console.log(isRef(message)); //true
console.log(isRef(notRef)); //false
}
shallowRef
创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的
修改其属性是非响应式的这样是不会改变的
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import { Ref, shallowRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value.name = '大满'
}
</script>
<style>
</style>
这样是可以被监听到的修改value
import { Ref, shallowRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value = { name: "大满" }
}
triggerRef
强制更新页面DOM
<template>
<div>
<button @click="changeMsg">change</button>
<div>{{ message }}</div>
</div>
</template>
<script setup lang="ts">
import { Ref, shallowRef,triggerRef } from 'vue'
type Obj = {
name: string
}
let message: Ref<Obj> = shallowRef({
name: "小满"
})
const changeMsg = () => {
message.value.name = '大满'
triggerRef(message)
}
</script>
<style>
</style>
customeRef
自定义ref
customRef 是个工厂函数要求我们返回一个对象 并且实现 get 和 set 适合去做防抖之类的
<template>
<div ref="div">小满Ref</div>
<hr>
<div>
{{ name }}
</div>
<hr>
<button @click="change">修改 customRef</button>
</template>
<script setup lang='ts'>
import { ref, reactive, onMounted, shallowRef, customRef } from 'vue'
function myRef<T = any>(value: T) {
let timer:any;
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newVal) {
clearTimeout(timer)
timer = setTimeout(() => {
console.log('触发了set')
value = newVal
trigger()
},500)
}
}
})
}
const name = myRef<string>('小满')
const change = () => {
name.value = '大满'
}
</script>
<style scoped>
</style>
reactive全家桶
reactive
用来绑定复杂的数据类型 例如 对象 数组,不能绑定基本类型数据
你如果用ref去绑定对象 或者 数组 等复杂的数据类型 我们看源码里面其实也是 去调用reactive
使用reactive 去修改值无须.value
import { reactive } from 'vue'
let person = reactive({
name:"张三"
})
person.name = "李四"
readonly
拷贝一份proxy对象将其设置为只读
import { reactive ,readonly} from 'vue'
const person = reactive({count:1})
const copy = readonly(person)
//person.count++
copy.count++
shallowReactive
只能对浅层的数据 如果是深层的数据只会改变值 不会改变视图
<template>
<div>
<div>{{ state }}</div>
<button @click="change1">test1</button>
<button @click="change2">test2</button>
</div>
</template>
<script setup lang="ts">
import { shallowReactive } from 'vue'
const obj = {
a: 1,
first: {
b: 2,
second: {
c: 3
}
}
}
const state = shallowReactive(obj)
function change1() {
state.a = 7
}
function change2() {
state.first.b = 8
state.first.second.c = 9
console.log(state);
}
</script>
<style>
</style>
to全家桶
toRef
如果原始对象是非响应式的就不会更新视图 数据是会变的,如果原始对象是响应式的是会更新视图并且改变数据的
<template>
<div>
<button @click="change">按钮</button>
{{state}}
</div>
</template>
<script setup lang="ts">
import { reactive, toRef } from 'vue'
const obj = {
foo: 1,
bar: 1
}
const state = toRef(obj, 'bar')
// bar 转化为响应式对象
const change = () => {
state.value++
console.log(obj, state);
}
</script>
toRefs
可以帮我们批量创建ref对象主要是方便我们解构使用
import { reactive, toRefs } from 'vue'
const obj = reactive({
foo: 1,
bar: 1
})
let { foo, bar } = toRefs(obj)
foo.value++
console.log(foo, bar);
toRaw
将响应式对象转换为普通对象
import { reactive, toRaw } from 'vue'
const obj = reactive({
foo: 1,
bar: 1
})
const state = toRaw(obj)
// 响应式对象转化为普通对象
const change = () => {
console.log(obj, state);
}
computed计算属性
计算属性就是当依赖的属性的值发生变化的时候,才会触发他的更改,如果依赖的值,不发生变化的时候,使用的是缓存中的属性值。
函数写法
import { computed, reactive, ref } from 'vue'
let price = ref(0)//$0
let m = computed<string>(()=>{
return `$` + price.value
})
price.value = 500
对象写法
<template>
<div>{{ mul }}</div>
<div @click="mul = 100">click</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
let price = ref<number | string>(1)//$0
let mul = computed({
get: () => {
return price.value
},
set: (value) => {
price.value = 'set' + value
}
})
</script>
<style>
</style>
watch侦听器
watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用
watch第一个参数监听源
watch第二个参数回调函数cb(newVal,oldVal)
watch第三个参数一个options配置项是一个对象{
immediate:true //是否立即调用一次
deep:true //是否开启深度监听
监听ref对象
import { ref, watch } from 'vue'
let message = ref({
nav:{
bar:{
name:""
}
}
})
watch(message, (newVal, oldVal) => {
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
},{
immediate:true,
deep:true
})
监听多个ref 注意变成数组啦
import { ref, watch ,reactive} from 'vue'
let message = ref('')
let message2 = ref('')
watch([message,message2], (newVal, oldVal) => {
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
})
监听reactive对象
使用reactive监听深层对象开启和不开启deep 效果一样
import { ref, watch ,reactive} from 'vue'
let message = reactive({
nav:{
bar:{
name:""
}
}
})
watch(message, (newVal, oldVal) => {
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
})
监听reactive单一值
import { ref, watch ,reactive} from 'vue'
let message = reactive({
name:"",
name2:""
})
watch(()=>message.name, (newVal, oldVal) => {
console.log('新的值----', newVal);
console.log('旧的值----', oldVal);
})
watchEffect高级侦听器
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
如果用到message 就只会监听message 就是用到几个监听几个 而且是非惰性 会默认调用一次
let message = ref<string>('')
let message2 = ref<string>('')
watchEffect(() => {
//console.log('message', message.value);
console.log('message2', message2.value);
})
组件详情
生命周期
在我们使用 Vue3 组合式API 是没有 beforeCreate 和 created 这两个生命周期的
onBeforeMount()在组件DOM实际渲染安装之前调用。在这一步中,根元素还不存在。
onMounted()在组件的第一次渲染后调用,该元素现在可用,允许直接DOM访问
onBeforeUpdate()数据更新时调用,发生在虚拟 DOM 打补丁之前。
onUpdated()DOM更新后,updated的方法即会调用。
onBeforeUnmount()在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted()卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载。
less/sass/scoped
less/sass 概述
Less (Leaner Style Sheets 的缩写) 是一门向后兼容的 CSS 扩展语言。这里呈现的是 Less 的官方文档(中文版),包含了 Less 语言以及利用 JavaScript 开发的用于将 Less 样式转换成 CSS 样式的 Less.js 工具。
因为 Less 和 CSS 非常像,因此很容易学习。而且 Less 仅对 CSS 语言增加了少许方便的扩展,这就是 Less 如此易学的原因之一。
官方文档 Less 快速入门 | Less.js 中文文档 - Less 中文网
sass 和 less 一样 都是css预处理器
官方文档 Sass教程 Sass中文文档 | Sass中文网
在vite中使用less | sass
npm install less -D
npm install sass -D
在style标签注明即可
<style lang="less">
</style>
<style lang="scss">
</style>
scoped
实现组件的私有化, 当前style属性只属于当前模块.
在DOM结构中可以发现,vue通过在DOM结构以及css样式上加了唯一标记,达到样式私有化,不污染全局的作用
BEM架构
他是一种css架构 oocss 实现的一种 (面向对象css) ,BEM实际上是block、element、modifier的缩写,分别为块层、元素层、修饰符层,element UI 也使用的是这种架构
父子传递数据
defineProps
父组件通过v-bind绑定一个数据,然后子组件通过defineProps接受传过来的值,
父组件代码:
<template>
<div class="layout">
<Menu v-bind:data="data" title="我是标题"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
const data = reactive<number[]>([1, 2, 3])
</script>
子组件接受值
通过defineProps 来接受 defineProps是无须引入的直接使用即可
如果我们使用的TypeScript
可以使用传递字面量类型的纯类型语法做为参数
ts写法如下
<template>
<div class="menu">
菜单区域 {{ title }}
<div>{{ data }}</div>
</div>
</template>
<script setup lang="ts">
defineProps<{
title:string,
data:number[]
}>()
</script>
js写法如下
defineProps({
title:{
default:"",
type:string
},
data:Array
})
withDefaults
TS 特有的默认值方式
withDefaults是个函数也是无须引入开箱即用接受一个props函数第二个参数是一个对象设置默认值
type Props = {
title?: string,
data?: number[]
}
withDefaults(defineProps<Props>(), {
title: "张三",
data: () => [1, 2, 3]
})
子组件给父组件传参
defineEmits
子组件代码:
<template>
<div class="menu">
<button @click="clickTap">派发给父组件</button>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue'
const list = reactive<number[]>([4, 5, 6])
const emit = defineEmits(['on-click'])
//如果用了ts可以这样两种方式
// const emit = defineEmits<{
// (e: "on-click", name: string): void
// }>()
const clickTap = () => {
emit('on-click', list)
}
</script>
我们在子组件绑定了一个click 事件 然后通过defineEmits 注册了一个自定义事件
点击click 触发 emit 去调用我们注册的事件on-click 然后传递参数list
父组件接受子组件的事件
<template>
<div class="layout">
<Menu @on-click="getList"></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang="ts">
import Menu from './Menu/index.vue'
import Header from './Header/index.vue'
import Content from './Content/index.vue'
import { reactive } from 'vue';
const data = reactive<number[]>([1, 2, 3])
const getList = (list: number[]) => {
console.log(list,'父组件接受子组件');
}
</script>
我们从Menu 组件接受子组件派发的事件on-click 后面是我们自己定义的函数名称getList,会把参数返回过来
defineExpose
子组件暴露给父组件内部属性,通过defineExpose
我们从父组件获取子组件实例通过ref
<Menu ref="refMenu"></Menu>
//这样获取是有代码提示的
<script setup lang="ts">
import MenuCom from '../xxxxxxx.vue'
//注意这儿的typeof里面放的是组件名字(MenuCom)不是ref的名字 ref的名字对应开头的变量名(refMenu)
const refMenu = ref<InstanceType<typeof MenuCom>>()
</script>
然后打印menus.value 发现没有任何属性
这时候父组件想要读到子组件的属性可以通过 defineExpose暴露
const list = reactive<number[]>([4, 5, 6])
defineExpose({
list
})
这个时候父组件就可以拿到了