1. vue3 各种 API 使用注意点
1.1 vue3 获取 dom
import {ref, onMounted} from "vue"
onMounted(() => {
const dom = ref()
dom.value.innerHTML // 获取 dom 属性
})
获取 dom 属性的代码需要写在 setup 生命周期之后,否则获取不到 dom
1.2 ref 和 shallowRef 的区别
ref 是【深层次】的响应,而 shallowRef 是【浅层次】的响应,也就是说修改了用 shallowRef 定义的对象的【其中的属性值】时,视图并不会发生变化,如果修改一整个对象还是会触发视图更新的。需要注意的是,ref 定义的变量 不能和 shallowRef 定义的变量一起使用,会触发更新。
const obj = shallowRef({ name: 123 })
obj.value.name = 321 // 不会触发视图更新
obj.value = { name: 321 } // 会触发视图更新
1.3 triggerRef
强制更新依赖,有点类似于 vue2 强制更新视图,使用方式是:triggerRef(variable)。 上面为什么说 ref 定义的变量 不能和 shallowRef 定义的变量一起使用会触发更新呢,因为 ref 底层会调用这个 API
1.4 customRef
自定义响应式函数
function MyRef<T>(value: T) {
return customRef((track, trigger) => {
return {
get () {
track() //
return value
},
set(newValue: T) {
value = newValue
trigger()
}
}
})
}
const c = MyRef(2)
++c.value
1.5 reactive
与 ref 不同的是:
- ref 支持所有类型;取值、赋值都需要添加 .value
- reactive 只支持引用类型 Array Object Map Set;取值、赋值都不需要添加 .value;proxy 不能直接赋值,否则破 坏对象的响应式 ,解决方法1:数组直接用 push + 解构(list.push(...newList)),解决方法2:把数组作为一个属性去赋值
1.6 toRef
toRef 用于对响应式对象的其中一个属性赋值给单独的一个变量,且这个变量也是响应式的,非响应式对象使用 toRef 则还是非响应式变量
const obj = reactive({ name: 'a', age: 12 })
const name = toRef(obj, 'name')
1.7 toRefs
toRefs 用于对响应式对象的所有属性 解构 出来
const obj = reactive({ name: 'a', age: 12 })
const { name, age } = toRefs(obj)
1.8 toRaw
toRaw 用于解除响应式对象,变成普通对象
const obj = reactive({ name: 'a', age: 12 })
const newObj = toRaw(obj)
// 或者
const newObj2 = obj['__v_raw']
1.9 watch 监听器
vue3的 【watch】 监听器有一定的变化:
- 其中监听 【对象】 时,其旧值跟新值是一样的,也就是无法捕获到旧值;
- 监听使用【ref】定义的对象需要添加配置项【deep: true】,而监听使用【reactive】的对象则不需要;
- 只监听对象中的其中一个属性时,需要用一个【函数返回其对象属性】,例如 watch(() => obj.foo.name, (newVal, oldVal) => {})
- watch 配置项里有一项 -- flush,有三个值【pre】-- 组件更新之前调用,【sync】-- 同步执行,【post】-- 组件更新之后执行
1.10 watchEffect 高级监听器
这是一个新的监听器,可以实现和普通 watch 监听器一样的功能,但是写法更简单,其特点如下:
- 只要回调中有使用到的 响应式变量 均可以监听到其值的改变
- 还可以在回调中传入一个 参数(也是一个回调函数),其可以在监听之前先执行(首次监听不会执行)
- 整个 watchEffect 有一个返回值,是一个函数,可以执行这个函数达到停止监听的作用
- 配置项里有一项 -- flush,有三个值【pre】-- 组件更新之前调用,【sync】-- 同步执行,【post】-- 组件更新之后执行
- 配置项中有一项 -- onTrigger,此函数用来调试,在监听触发时启动,可以监听到整个 watchEffect 的内容
const obj = reactive({ name: 'a', age: 12 })
const stop = watchEffect((onInvalidate) => {
console.log('obj.name===>', obj.name)
if (obj.name === 'a111') stop() // 执行stop即可停止监听,而 onInvalidate 却会再执行最后一次
onInvalidate(() => { // 无论此回调的编写顺序在其他代码之后也会优先执行,但首次监听不会被执行
console.log('I am do the first.')
})
}, {
flush: 'post',
onTrigger (e) {
console.log(e)
debugger
}
})
1.11 生命周期
- beforeCreate create 在 setup 语法糖模式是没有这两个生命周期的,setup 代替了
- onBeforeMount 读不到 dom,onMounted 可以读取到 dom
- onBeforeUpdate 获取的是更新之前的 dom ,onUpdated 获取更新之后的 dom
- onRenderTracked 和 onRenderTriggered 是两个调试API,回调中可以传递一个参数,用于展示组件渲染和更新时组件的状态
- getCurrentInstance 可以获取当前组件的实例
1.12 父子组件通信
- 父传子:props
// 没有ts的写法
const props = defineProps(['name'])
const props = defineProps({
name: {
type: String,
default: '123'
}
})
// 有ts的写法,withDefaults 方法可以给 props 设置默认值
interface Props {
name: string
}
const props = withDefaults(defineProps<Props>(),
name: '123'
)
console.log(props.name)
- 子传父:emit
// 没有ts的写法
const emit = defineEmits(['on-click'])
// 有ts的写法
interface Emits {
// 事件名,传参,无返回值
(e: 'on-click', data: string): void
}
const emit = defineEmits<Emits>()
// 调用
emit('on-click', '123')
2. 常用vue3插件
2.1 mitt 全局事件总线
2.1.1 安装
npm install mitt -S
2.1.2 配置
// main.ts
import mitt from 'mitt'
const Mit = mitt()
const app = createApp(App)
app.config.globalProperties.$Bus = Mit
declare module 'vue' { // 配置提示
export interface ComponentCustomPropertied {
$Bus: typeof Mit
}
}
2.1.3 使用
// ChildA.vue
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
const instance = getCurrentInstance() // 获取当前对象实例
const num = ref<number>(0)
instance.proxy.$Bus.emit('addNum', num) // 发布事件
</script>
// ChildB.vue
<script setup lang="ts">
import { ref, getCurrentInstance } from 'vue'
const instance = getCurrentInstance() // 获取当前对象实例
const num1 = ref<number>(0)
instance.proxy.$Bus.on('addNum', (num: number) => num1.value = num) // 订阅事件
instance.proxy.$Bus.off('addNum') // 删除某个事件
instance.proxy.$Bus.all.clear() // 删除所有事件
</script>
2.2 unplugin-auto-import 自动引入 vue 组合式API(ref, reactive ... )
2.2.1 安装
npm install -D unplugin-auto-import
2.2.2 配置
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite' // [/vite] 是在【vite】构建工具中使用,【webpack】使用 [/webpack]
export default defineConfig({
plugins: [
vue(),
AutoImport({
import: ['vue'],
dts: 'src/auto-import.d.ts'
})
]
})
2.2.3 配置完以上代码就可以在整个项目中随意使用 vue 的组合式API,而不需要手动引入
3. vue3 新特性
3.1 Css新特性
// 父组件
<script lang="ts" setup>
import CssNew from '@/components/HelloWorld2/cssNew/cssNew.vue'
</script>
<template>
<div class="container">
<CssNew>
<div class="cssNewDiv">123123</div>
<div>321321</div>
</CssNew>
</div>
</template>
<style scoped lang="scss">
// 全局样式
:global(div) {
color: red;
}
</style>
// 子组件
<script lang="ts" setup>
// 动态Css
import { ref, useCssModule } from 'vue'
type Style = {
background: string,
border: string
}
const style: Style = ref({
background: 'transparent',
border: 'none'
})
setTimeout(() => {
style.value.background = 'gray'
style.value.border = '1px solid #666'
}, 2000)
// cssModule 形式
const cssModule = useCssModule('cssM')
console.log(cssModule)
</script>
<template>
<div class="cssNew">
<slot></slot>
<div :class="[cssModule.color, cssModule.border]">
<p>cssModule</p>
</div>
</div>
</template>
<style scoped lang="scss">
.cssNew {
background: v-bind('style.background');
border: v-bind('style.border');
transition: all .5s;
}
//:slotted 插槽样式
:slotted(.cssNewDiv) {
color: pink;
}
</style>
<style module="cssM">
.color {
color: blue;
}
.border {
border: 1px solid #666;
}
</style>
4. H5适配
利用 postcss 来实现对H5的适配
4.1 修改 index.html 文件,添加如下样式
<style>
html, body, #app {
height: 100%;
overflow: hidden!important;
}
* {
padding: 0;
margin: 0;
}
/**
px 固定的单位,不会随着屏幕的变化而变化
rem r = root 1rem = html { font-size: 16px } 1rem = 16px
375屏幕 适合多少html-font size 引入淘宝的flexible.js
vw vh 相对于视口宽高进行控制的 375屏幕 1vw = 3.75px
百分比是相对于父元素的 viewport 是相对于视口
UI 设计稿给的 px px to viewport 插件
*/
</style>
4.2 修改 tsconfig.json 文件,添加如下两行配置
{
"compilerOptions": {
"noImplicitAny": false
},
"include": [ "plugins/**/*"]
}
4.3 在根目录下创建【plugins】文件夹,并在此文件夹中新建【postcss-px-to-viewport.ts】文件
// postcss 的插件在 vite 内置了 postCss
import { Plugin } from 'postcss'
const Options = {
viewportWidth: 375 // 设计稿默认 375px
}
type Options = {
viewportWidth?: number
}
export const PostCssPxToViewport = (options: Options = Options): Plugin => {
const opt = Object.assign({}, Options, options)
return {
postcssPlugin: 'postcss-px-to-viewport',
// 钩子函数
Declaration(node) {
if (node.value.includes('px')) {
console.log(node.prop, node.value)
const num = parseFloat(node.value)
node.value = `${((num / opt.viewportWidth) * 100).toFixed(2)}vw`
}
}
}
}
4.4 修改 vite.config.ts 文件,添加如下代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { PostCssPxToViewport } from './plugins/postcss-px-to-viewport'
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "./src/bem.scss";`
}
}
}
})
5. 其他
5.1 对于vue3赋予 ref,reactive 类型的变量
在浏览器中打印时需要一层层点开,可以通过控制台右上角的【设置】按钮,勾选【启用自定义格式化程序】,之后打印的 ref 或 reactive 变量就不用一层层点开查看value值