Vue3文档梳理

Vue3文档梳理

通过 CDN 使用 Vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!-- 使用全局构建版本 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app">{{ message }}</div>

<script>
const { createApp } = Vue

createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>

<!-- 现代浏览器大多都已原生支持 ES 模块。因此我们可以像这样通过 CDN 以及原生 ES 模块使用 Vue -->
<!-- 使用 ES 模块构建版本 -->
<!-- 注意:script type="module" -->
<div id="app">{{ message }}</div>

<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
<!-- 使用 ES 模块构建版本 - 启用 Import maps -->
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
}
}
</script>

<div id="app">{{ message }}</div>

<script type="module">
import { createApp } from 'vue'

createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>

创建应用

1
<div id="app">{{ message }}</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { createApp } from 'vue'
const app = createApp({
data() {
return {
message: 1,
message2: 2
}
},
template: '<span>{{message2}}</span>' // 根组件,如果没有根组件,会渲染#app元素内内容:1;若有跟组件会以根组件内容替换#app元素内内容并渲染:2
})
app.mount('#app')

// 创建多个应用
import App from './app.vue' // 根组件
const app2 = createApp(App)
app2.mount('#app2')

模板语法

  • v-bind
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <div v-bind:id='myId'></div>
    <div :id='myId'></div>
    <!-- 绑定多个值 -->
    <div v-bind='objOfAttrs'></div>
    <!--
    const objOfAttrs = {
    id: 'container',
    class: 'wrapper'
    } -->

    <!-- 动态参数 -->
    <div v-bind:[attrName]='myId'></div>
    <div :[attrName]='myId'></div>
    <div v-on:[eventName]='myId'></div>
    <div @[eventName]='myId'></div>
    <!--
    const attrName = 'id'
    const eventName = 'click'
    -->

响应式基础

  • reactive和ref都是深层响应,reactive作用于对象、数组、Set等集合类型,ref全能。ref通过添加.value属性的ref对象实现响应
  • 当一个 ref 被嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包,但作为浅层响应式对象的属性或数组和集合类型被访问时不会解包。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const count = reactive({})
    const count2 = shallowReactive({})
    const r = ref({num: 1})
    count.r = r
    count2.r = r
    log(count.r) // 1 自动解包
    log(count2.r.value) // 1 不会解包

    const books = reactive([ref('Guide')])
    // 这里需要 .value
    console.log(books[0].value)

    const map = reactive(new Map([['count', ref(0)]]))
    // 这里需要 .value
    console.log(map.get('count').value)

计算属性

返回值为一个计算属性 ref。和其他一般的 ref 类似,通过.value访问,在模板会自动解包

1
2
3
4
5
6
7
8
9
10
import { computed } from 'vue'
const cpu = computed(() => {
return books.length > 0 ? true : false
})

computed({
get(){ return ''},
set(newVal){} // 有了set,计算属性便可写了,否则是只读的
})
// cpu.value; cpu.value = 2

监听器

watch
  • 可以监听一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const x = ref(0)
    const y = ref(0)

    // 单个 ref
    watch(x, (newX) => {
    console.log(`x is ${newX}`)
    })

    // getter 函数
    watch(
    () => x.value + y.value,
    (sum) => {
    console.log(`sum of x + y is: ${sum}`)
    }
    )

    // 多个来源组成的数组
    watch([x, () => y.value], ([newX, newY]) => {
    console.log(`x is ${newX} and y is ${newY}`)
    })
  • 注意,你不能直接侦听响应式对象的属性值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const obj = reactive({ count: 0 })

    // 错误,因为 watch() 得到的参数是一个 number
    watch(obj.count, (count) => {
    console.log(`count is: ${count}`)
    })

    // 正确,提供一个 getter 函数
    watch(
    () => obj.count,
    (count) => {
    console.log(`count is: ${count}`)
    }
    )
  • 深层监听:直接给 watch() 传入一个响应式对象,会隐式地创建一个深层侦听器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    const obj = reactive({ count: 0 })

    watch(obj, (newValue, oldValue) => {
    // 在嵌套的属性变更时触发
    // 注意:`newValue` 此处和 `oldValue` 是相等的
    // 因为它们是同一个对象!
    })

    obj.count++

    const state = reactive({ someObject: { count: 0 }})
    watch(
    () => state.someObject,
    () => {
    // 仅当 state.someObject 被整体替换时触发
    }
    )
    state.someObject = { count: 2 }

    watch(
    () => state.someObject,
    (newValue, oldValue) => {
    // 注意:`newValue` 此处和 `oldValue` 是相等的
    // *除非* state.someObject 被整个替换了
    },
    { deep: true } // 强制转换深层监听
    )
    state.someObject.count = 2
    watchEffect
    watch() 是懒执行的:仅当数据源变化时,才会执行回调。watchEffect() 会立即执行一遍回调函数,会在副作用发生期间追踪依赖。 它会在同步执行过程中,自动追踪所有能访问到的响应式属性。
    watchEffect 仅会在其同步执行期间,才追踪依赖。在使用异步回调时,只有在第一个 await 正常工作前访问到的属性才会被追踪。
    1
    2
    3
    4
    5
    // 在使用异步回调时
    watchEffect(async () => {
    const response = await fetch(url.value) // url为响应式属性且在第一个await之前被访问到,会被自动追踪
    data.value = await response.json() // data不是异步回调中第一个await之前被访问到的,所以不被追踪
    })
  • 默认情况下,侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。想要访问更新之后的DOM:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    watch(source, callback, {
    flush: 'post'
    })

    watchEffect(callback, {
    flush: 'post'
    })

    import { watchPostEffect } from 'vue'
    watchPostEffect(() => {
    /* 在 Vue 更新后执行 */
    })
  • 侦听器必须用同步语句创建:如果用异步回调创建一个侦听器,那么它不会绑定到当前组件上,你必须手动停止它,以防内存泄漏。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import { watchEffect } from 'vue'

    // 它会自动停止
    watchEffect(() => {})

    // ...这个则不会!
    setTimeout(() => {
    watchEffect(() => {})
    }, 100)

    // 要手动停止一个侦听器,请调用 watch 或 watchEffect 返回的函数
    const unwatch = watchEffect(() => {})
    // ...当该侦听器不再需要时
    unwatch()

    // 注意:注意,需要异步创建侦听器的情况很少,请尽可能选择同步创建。如果需要等待一些异步数据,你可以使用条件式的侦听逻辑
    // 需要异步请求得到的数据
    const data = ref(null)

    watchEffect(() => {
    if (data.value) {
    // 数据加载后执行某些操作...
    }
    })

模板引用($refs)

1
2
3
4
5
6
7
<template>
<input ref="input" />
{/* 内联函数模板引用 */}
<input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
{/* 组件方法 */}
<input :ref="refsInput">
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup>
import { ref, onMounted } from 'vue'

// 声明一个 ref 来存放该元素的引用
// 必须和模板里的 ref 同名
const input = ref(null)

onMounted(() => {
input.value.focus()
log(inputRef.value)
})
const inputRef = ref(null)
const refsInput = (el) => {
inputRef.value = el
}
</script>
  • v-for 中的模板引用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <script setup>
    import { ref, onMounted } from 'vue'

    const list = ref([
    /* ... */
    ])

    const itemRefs = ref([]) // 一个数组存放循环中的元素实例

    onMounted(() => console.log(itemRefs.value))
    </script>

    <template>
    <ul>
    <li v-for="item in list" ref="itemRefs">
    {{ item }}
    </li>
    </ul>
    </template>
  • 组件上的 ref
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <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>
    // 需要注意的是:使用了 <script setup> 的组件是默认私有的:一个父组件无法访问到一个使用了 <script setup> 的子组件中的任何东西,除非子组件在其中通过 defineExpose 宏显式暴露:
    // Child组件
    <script setup>
    import { ref } from 'vue'

    const a = 1
    const b = ref(2)

    defineExpose({
    a,
    b
    })
    </script>

组件

props
  • props
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // <script setup> 模式下
    // defineProps 是一个仅 <script setup> 中可用的编译宏命令,并不需要显式地导入。声明的 props 会自动暴露给模板。defineProps 会返回一个对象,其中包含了可以传递给组件的所有 props:
    defineProps(['title'])
    const props = defineProps(['title'])

    // setup() 函数模式下
    export default{
    props: ['title'],
    setup(props) {
    // props.title
    }
    }
  • props校验
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    defineProps({
    // 基础类型检查
    // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
    propA: Number,
    // 多种可能的类型
    propB: [String, Number],
    propD: {
    type: Number,
    required: true,
    default: 100
    },
    // 对象类型的默认值
    propE: {
    type: Object,
    // 对象或数组的默认值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
    return { message: 'hello' }
    }
    },
    // 自定义类型校验函数
    propF: {
    validator(value) {
    // The value must match one of these strings
    return ['success', 'warning', 'danger'].includes(value)
    }
    },
    // 函数类型的默认值
    propG: {
    type: Function,
    // 不像对象或数组的默认,这不是一个工厂函数。这会是一个用来作为默认值的函数
    default() {
    return 'Default function'
    }
    }
    })

    emit
  • emit
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!-- child.vue -->
    <template>
    <div class="blog-post">
    <button @click="$emit('add-one')">加一</button>
    <button @click="handleSome">复杂操作</button>
    </div>
    </template>

    <!-- parent.vue -->
    <child @add-one='num++' @handleSome-emit='handleDoSome'></child>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // child.vue
    // <script setup> 模式下
    defineEmits('add-one')
    const emit = defineEmits('handleSome-emit')
    function handleSome () {
    emit('handleSome-emit', 123)
    }

    // setup() 函数模式下
    export default{
    emits: ['add-one', 'handleSome-emit'],
    setup(props, ctx) { // ctx 上下文
    ctx.emit('handleSome-emit')
    }
    setup(props, {emit}) { // emit结构
    emit('handleSome-emit')
    }
    }
  • emit 校验
    1
    2
    <!-- 子组件 -->
    <div><button @click="handleCheckEmit">事件校验</button></div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 子组件
    const emit = defineEmits({
    commonEmit: null, // 不需要校验是事件
    emitCheck: (v1, v2, v3) => {
    if (v1 && v2) {
    console.log(v1, v2, v3, 'child-true')
    return true // 校验通过返回true
    } else {
    console.warn('error-emit-check')
    return false // 校验不通过返回false
    }
    }
    })

    const handleCheckEmit = () => {
    emit('commonEmit', '普通emit')
    emit('emitCheck', 1, 1, 3) // 能通过的
    // emit('emitCheck', 0, 1, 3) 不能通过的
    }
    1
    2
    <!-- 父组件 -->
    <child @emitCheck="handleEmitCheck" @commonEmit="handleCommonEmit" />
    1
    2
    3
    4
    5
    6
    7
    // 父组件
    function handleCommonEmit(v) {
    console.log(v) // 普通emit
    }
    const handleEmitCheck = (v1, v2, v3) => {
    console.log(v1, v2, v3, 'parent-true') // 不管是否通过校验,都会正常执行,打印结果
    }

    注意:不管校验是否通过,都不阻碍代码执行,也就是说校验没用,只能在不通过是打印个日志而已。

组件v-model
1
2
3
4
5
6
7
8
9
10
11
12
<input v-model="searchText" />
<!-- 上面的代码其实等价于下面这段 (编译器会对 v-model 进行展开):👇 -->
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
<!-- 当使用在一个组件上时,v-model 会被展开为如下的形式:👇 -->
<CustomInput v-model="searchText" />
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>

v-model 默认props是modelValue,默认emit是update:modelValue

所以,CustomInput组件内部应该是这样

1
2
3
4
5
6
7
8
9
10
11
12
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<!-- modelValue和update:modelValue是默认固定的 -->
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>

也可以是getter、setter计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed(() => {
getter() {
return props.modelValue
}
setter(v) {
emit('update:modelValue', v)
}
})
</script>
<template>
<input v-model="value" />
</template>

如果想要自定义v-model的props名称,可以用v-model:title这种形式,title就是自定义的名称
这样,便可实现多v-model同时使用的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<CustomInput v-model:title="titleText" v-model:msg="msgText" />
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['title', 'msg'])
const emit = defineEmits(['update:title', 'update:msg'])
</script>
<template>
<input
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
<input
:value="msg"
@input="$emit('update:msg', $event.target.value)"
/>
</template>

自定义v-model的修饰符:v-model.capitalize capitalize属于自定义的修饰符(自带的有.lazy;.trim;.number等)
组件的 v-model 上所添加的修饰符,可以通过 modelModifiers prop 在组件内访问到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) } // modelModifiers是个对象
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) { // 首字母大写
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>

自定义model和修饰符共用时的写法:👇

1
<MyComponent v-model:title.capitalize="myText">
1
2
3
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
透传 Attributes

“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id。

  • 当一个组件以 单个元素 为根作渲染时,透传的 attribute 会自动被添加到根元素上.
    1
    2
    3
    4
    5
    6
    <!-- <MyButton> 的模板 -->
    <button>click me</button>
    <!-- 透传class -->
    <MyButton class="large" />
    <!-- 渲染结果 -->
    <button class="large">click me</button>
  • 深层组件会继承透传attr
  • 禁用 Attributes 继承
    1
    2
    3
    4
    5
    6
    <script>
    // 使用普通的 <script> 来声明选项
    export default {
    inheritAttrs: false // 禁用后
    }
    </script>
  • 自定义透传attr: v-bind=”$attrs”显示绑定到元素上
    1
    2
    3
    <div class="btn-wrapper">
    <button class="btn" v-bind="$attrs">click me</button>
    </div>
  • 多根节点不会自动透传,需手动绑定
  • 获取透传属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // <script setup>
    import { useAttrs } from 'vue'
    const attrs = useAttrs()

    // setup()
    export default {
    setup(props, ctx) {
    // 透传 attribute 被暴露为 ctx.attrs
    console.log(ctx.attrs)
    }
    }
    插槽
  • 具名插槽
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!-- BaseLayout.vue -->
    <div class="container">
    <header>
    <slot name="header"></slot>
    </header>
    <main>
    <slot></slot>
    </main>
    <footer>
    <slot name="footer"></slot>
    </footer>
    </div>
    <!-- parent.vue -->
    <BaseLayout>
    <template v-slot:header>
    <!-- header 插槽的内容放这里 -->
    </template>
    <!-- v-slot简写# -->
    <template #footer></template>
    </BaseLayout>
  • 动态插槽
    1
    2
    3
    4
    5
    <base-layout>
    <template v-slot:[dynamicSlotName]></template>
    <!-- 缩写为 -->
    <template #[dynamicSlotName]></template>
    </base-layout>
  • 作用域插槽: 将子组件数据传递给父组件访问
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- <MyComponent> 的模板 -->
    <div>
    <slot :text="greetingMessage" :count="1"></slot>
    </div>

    <MyComponent v-slot="slotProps">
    {{ slotProps.text }} {{ slotProps.count }}
    </MyComponent>
    <!-- 解构 -->
    <MyComponent v-slot="{text, count}">
    {{ text }} {{ count }}
    <!-- 具名作用域插槽 -->
    <template #header="headerProps">
    {{ headerProps }}
    <!-- 此处访问不到text👇 -->
    {{ text }}
    </template>
    </MyComponent>

    插槽作用域相互封闭,一个插槽访问不到另一个插槽作用域

依赖注入(provide 和 inject)

一个父组件相对于其所有的后代组件,会作为 依赖提供者。任何后代的组件树,无论层级有多深,都可以 注入 由父组件提供给整条链路的依赖。

  • provide
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // <script setup>
    import { provide } from 'vue'
    provide('message', 'hello!') // (名:值)
    provide('more', '多次调用!')

    // setup()
    import { provide } from 'vue'
    export default {
    setup() {
    provide('message', 'hello!') // 需同步调用
    }
    }
    // 应用层
    import { createApp } from 'vue'
    const app = createApp({})
    app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
  • inject
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // <script setup>
    import { inject } from 'vue'
    const msg = inject('message')
    // 如果没有祖先组件提供 "message"
    // `value` 会是 "这是默认值"
    const value = inject('message', '这是默认值') // 可添加默认值,和props类似

    // setup()
    import { inject } from 'vue'
    export default {
    setup() {
    const message = inject('message')
    return { message }
    }
    }
  • 和响应式数据配合使用:需要更改数据时,都在提供方更改,避免各个组件内更改冲突
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // <!-- 在供给方组件内 -->
    import { provide, ref } from 'vue'
    const location = ref('North Pole')
    function updateLocation() { // 更新数据方法,供子组件调用
    location.value = 'South Pole'
    }
    provide('location', {
    location,
    updateLocation
    })

    // <!-- 在注入方组件 -->
    import { inject } from 'vue'
    const { location, updateLocation } = inject('location')

    <template>
    <button @click="updateLocation">{{ location }}</button>
    </template>
  • 使用 Symbol 作注入名:确保唯一性,避免冲突
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // keys.js
    export const myInjectionKey = Symbol() // 统一在keys.js里定义注入名

    // 在供给方组件中
    import { provide } from 'vue'
    import { myInjectionKey } from './keys.js'
    provide(myInjectionKey, { /*要提供的数据*/ });

    // 注入方组件
    import { inject } from 'vue'
    import { myInjectionKey } from './keys.js'
    const injected = inject(myInjectionKey)
    异步组件

命名空间组件

可以使用带 . 的组件标签,例如 <Foo.Bar> 来引用嵌套在对象属性中的组件。这在需要从单个文件中导入多个组件的时候非常有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// /form-components/index.js
import header from './header.vue'
import body from './body.vue'
export {
header,
body
}

// 组件
<script setup>
import * as Form from './form-components'
</script>

<template>
<Form.Header>
<Form.Body>label</Form.Body>
</Form.Header>
</template>
  • 注意:必须是vue3+ts的项目才支持命名空间组件

自定义指令

  • 必须遵循 vNameOfDirective 这样的命名规范

在 <script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令: vFocus 即可以在模板中以 v-focus

1
2
3
<template>
<h1 v-my-directive>This is a Heading</h1>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// <script setup>
const vMyDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}

// 如果指令是从别处导入的,可以通过重命名来使其符合命名规范
import { myDirective as vMyDirective } from './MyDirective.js'

// setup()
export default {
setup() {
/*...*/
},
directives: {
// 在模板中启用 v-focus
focus: {
/* ... */
}
}
}

CSS功能

  • 深度选择器 :deep()同::v-deep
    1
    2
    3
    .a :deep(.b) {
    /* ... */
    }
  • 全局选择器,让某个样式作用到全局,:global()同新建<style>
    1
    2
    3
    :global(.red) {
    color: red;
    }
  • CSS Modules
    一个 <style module> 标签会被编译为 CSS Modules 并且将生成的 CSS class 作为 $style 对象暴露给组件:

    最好单独新建<style module>标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <template>
    <!-- 非具名module 默认对象为$style -->
    <p :class="$style.red">This should be red</p>
    </template>

    <style module>
    .red {
    color: red;
    }
    </style>

    <!-- 自定义module名称 -->
    <p :class="stl.red">This should be red</p>
    <style module="stl">
    .red {
    color: red;
    }
    </style>
    在js中通过useCssModule能获取相应css对象
    1
    2
    3
    4
    5
    6
    7
    import { useCssModule } from 'vue'
    // 在 setup() 作用域中...
    // 默认情况下, 返回 <style module> 的 class
    useCssModule()

    // 具名情况下, 返回 <style module="classes"> 的 class
    useCssModule('classes')
  • CSS中的v-bind()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <script setup>
    const theme = {
    color: 'red'
    }
    </script>

    <template>
    <p>hello</p>
    </template>

    <style scoped>
    p {
    color: v-bind('theme.color');
    }
    </style>

vite中环变量与模式

通过import.meta.env对象获取环境变了

  • import.meta.env.MODE: {string} 应用运行的模式。
  • import.meta.env.BASE_URL: {string} 部署应用时的基本 URL。他由base 配置项决定。
  • import.meta.env.PROD: {boolean} 应用是否运行在生产环境。
  • import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)。
  • import.meta.env.SSR: {boolean} 应用是否运行在 server 上。

.env文件

  • .env # 所有情况下都会加载
  • .env.local # 所有情况下都会加载,但会被 git 忽略
  • .env.[mode] # 只在指定模式下加载
  • .env.[mode].local # 只在指定模式下加载,但会被 git 忽略
  • 默认模式:development、production

只有以 VITE_ 为前缀的变量才会暴露,例如:VITE_SOME_KEY=123