本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-09-24
<template>
<button @click="inc">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue'
export const count = ref(0)
export const inc = () => count.value++
</script>
会被编译成
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const inc = () => count.value++
return {
count,
inc,
}
},
}
</script>
<script setup="props, { emit }">
import { watchEffect } from 'vue'
watchEffect(() => console.log(props.msg))
emit('foo')
</script>
会被编译成
<script>
import { watchEffect } from 'vue'
// setup is exported as a named export so it can be imported and tested
export function setup(props, { emit }) {
watchEffect(() => console.log(props.msg))
emit('foo')
return {}
}
export default {
setup,
}
</script>
<script setup>
export { default as Foo } from './Foo.vue'
export { default as Bar } from './Bar.vue'
export const ok = Math.random()
</script>
<template>
<Foo/>
<Bar/>
<component :is="ok ? Foo : Bar"/>
</template>
<script setup="props">
import { computed } from 'vue'
export default {
props: {
msg: String,
},
inheritAttrs: false,
}
export const computedMsg = computed(() => props.msg + '!!!')
</script>
会被编译成:
<script>
import { computed } from 'vue'
const __default__ = {
props: {
msg: String,
},
inheritAttrs: false,
}
export function setup(props) {
const computedMsg = computed(() => props.msg + '!!!')
return {
computedMsg,
}
}
__default__.setup = setup
export default __default__
</script>
<script setup="props" lang="ts">
import { computed } from 'vue'
// declare props using TypeScript syntax
// this will be auto compiled into runtime equivalent!
declare const props: {
msg: string
}
export const computedMsg = computed(() => props.msg + '!!!')
</script>
会被编译成:
<script lang="ts">
import { computed, defineComponent } from 'vue'
export default defineComponent({
props: ({
msg: String
} as unknown) as undefined,
setup(props: {
msg: string
}) {
const computedMsg = computed(() => props.msg + '!!!')
return {
computedMsg,
}
}
})
</script>
会额外引入 ts 所需要的 defineComponent
,并且会将针对 props
的声明,转换为选项中对应的 props
。
<script>
performGlobalSideEffect()
// this can be imported as `import { named } from './*.vue'`
// 外部可以引入该.vue文件暴露的 named 属性
export const named = 1
</script>
<script setup>
import { ref } from 'vue'
export const count = ref(0)
</script>
会被编译成:
<script>
import { ref } from 'vue'
performGlobalSideEffect()
export const named = 1
export function setup() {
const count = ref(0)
return {
count
}
}
export default { setup }
</script>
单文件组件(SFC, Single File Components) 的编译相关API是在 @vue/compiler-sfc
这个包里面的,它暴露一个 compileScript
方法来处理 <script setup>
的编译。
import { parse, compileScript } from '@vue/compiler-sfc'
const descriptor = parse(`...`)
if (descriptor.script || descriptor.scriptSetup) {
const result = compileScript(descriptor) // returns SFCScriptBlock
console.log(result.code)
console.log(result.bindings)
}
关于 bindings
,是指单文件编译时会输出的元数据。
比如:
<script setup="props">
export const foo = 1
export default {
props: ['bar']
}
</script>
bindings
会是:
{
foo: 'setup',
bar: 'props'
}
这个对象可以被传递到模板编译器:
import { compile } from '@vue/compiler-dom'
compile(template, {
bindingMetadata: bindings
})
由于模块执行语义的差异,<script setup>
是需要依赖于单文件组件的上下文的,当引入外部的 .js
或 .ts
文件,会给开发人员和IDE工具造成混淆,因此,<script setup>
是不能够使用src
属性来引入外部文件的。
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style vars="{ color }">
.text {
color: var(--color);
}
</style>
这个 <div>
会被编译成:
<div style="--color:red" class="text">hello</div>
<style>
.text {
color: var(--color);
}
</style>
<!-- 这种方式是h5支持的,使用style属性声明原生css变量,在选中该元素的选择器的样式规则中可以直接使用 -->
<!-- 别的元素 无法使用该css变量 -->
当使用 <style scoped>
的时候,这个 div
会被编译成:
<div style="--6b53742-color:red" class="text">hello</div>
<style>
.text {
color: var(--6b53742-color);
}
</style>
在与原生css变量一起使用的时候没,需要加上 global:
前缀。
<style scoped vars="{ color }">
.text {
color: var(--color);
font-size: var(--global:fontSize);
}
</style>
会被编译成:
<div style="--6b53742-color:red" class="text">hello</div>
<style>
.text {
color: var(--6b53742-color);
font-size: var(--fontSize);
}
</style>
单文件组件 <style scoped>
现在可以包含全局规则或只针对插槽内容的规则。
<style scoped>
/* deep selectors */
::v-deep(.foo) {}
/* 简写 */
:deep(.foo) {}
/* targeting slot content */
::v-slotted(.foo) {}
/* 简写 */
:slotted(.foo) {}
/* one-off global rule */
::v-global(.foo) {}
/* 简写 */
:global(.foo) {}
</style>
在Vue2中,存在 >>>
和 /deep/
两种写法,由于一些原因,在Vue3中将不再使用这两种写法。
其中 >>>
的写法,在使用一些css预编译语言时会有影响,比如 sass,因为 >>>
毕竟不是一个 css 原生的连接符。
而后来改用了 /deep/
,它的问题则是,这种写法原本就是一个 css 提案,甚至已经在 Chrome
上进行了实现,但后来这个提案被废弃了。这就可能会对一些开发人员造成混淆,他们可能会担心在Vue SFC中继续使用 /deep/
这个被废弃的写法会使得他们的代码在浏览器中不再支持。虽然在Vue中这个写法只是编译时,在最终的 css 代码中不会出现。
但是为了避免这种混淆,Vue3 提供了 :deep() / :slotted() / :global()
来在 <style scoped>
中进行规则的书写。
需要注意的是,不再是作为连接符使用 ::v-deep .bar {}
而是使用参数的形式来使用 ::v-deep(.bar) {}
总结:
>>>
and /deep/
support are deprecated.
::v-deep
usage as a combinator is deprecated.
编译结果如下:
<style>
::v-deep(.bar) {}
/* 也可以省略v-前缀 */
:deep(.bar) {}
/* 会被编译成 */
[v-data-xxxxxxx] .bar {}
/* ----------------- */
::v-slotted(.foo) {}
/* 会被编译成 */
.foo[v-data-xxxxxxx-s] {}
/* ----------------- */
::v-global(.foo) {}
/* 会被编译成 */
.foo {}
</style>