|
| 1 | +--- |
| 2 | +title: Nuxt components 文件夹背后的设计 |
| 3 | +slug: intro-nuxt-components-design |
| 4 | +update_time: 2023/10/15 23:15:36 |
| 5 | +create_time: 2023/10/15 16:18:10 |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +关联: |
| 10 | + |
| 11 | +[[Nuxt 3 内置组件 NuxtIsland]] |
| 12 | + |
| 13 | +nuxt 内置了很多约定,对 `components` 文件夹也有很多默认的行为,这里按照官方文档给出的说明,做个梳理。信息来源 https://nuxt.com/docs/guide/directory-structure/components |
| 14 | + |
| 15 | +### 默认全局导入 |
| 16 | + |
| 17 | +`components` 文件夹是放 vue 组件的地方,在这个文件夹里的所有组件,包括嵌套文件夹的组件会自动注册到全局组件,在编写业务时候无需手动导入。 |
| 18 | + |
| 19 | +举例: |
| 20 | + |
| 21 | +```text |
| 22 | +/components |
| 23 | + - a.vue |
| 24 | + - b/c/d.vue |
| 25 | +``` |
| 26 | + |
| 27 | +提示:本质是开发时候全局导入,构建按需加载。这里可以提供一个截图。 |
| 28 | + |
| 29 | +延伸:这里对应 vite 里的 `unplugin-vue-compnents` 插件,参考 [Github Repo](https://github.com/unplugin/unplugin-vue-components) 也是按需加载。 |
| 30 | + |
| 31 | +### 自定义来源、过滤文件 |
| 32 | + |
| 33 | +高级自定义:默认的 components 可以通过配置文件修改,补充更多组件来源,并且可以给组件设置前缀 prefix 这样从命名上可以读取来源,避免来源混乱。配置文件本身是数组,会从上到下进行匹配。 |
| 34 | + |
| 35 | +如何设置来源,暂时不提,我理解如果编写组件的插件,需要用到,可以用来自动导入。插件选项 - deepMerge 并 push 到选项里。不意外会遇到重名优先级的问题,这个可以通过 devtools 确认组件来源。 |
| 36 | + |
| 37 | +组件不一定是 `.vue` ,还有可能是 `ts/tsx` 或者其他后缀,或者习惯 `xx.comp.vue` 命名设置,可以通过设置进行过滤,这样实现大组件自动导入,小组件不导入。具体是 `extensions` 参数。 |
| 38 | + |
| 39 | +### 嵌套组件的命名处理 |
| 40 | + |
| 41 | +对于这个组件 `components/aa/bb/cc.vue` 注册的名称是 `AaBbCc` 组件。如果有疑问,打开 devtools 看就可以了。 |
| 42 | +注意:这种情况推荐的做法是 把 `cc.vue` 组件的路径补齐,就叫 `AaBbCc.vue`,和路径重复的部分会自动忽略。 |
| 43 | + |
| 44 | +如果不想要路径,可以关闭 `prefix:false` ,这样组件就不会携带路径了,开发者自己维护命名即可。 |
| 45 | + |
| 46 | +### 动态组件 |
| 47 | + |
| 48 | +vue 中可以通过 `<component is='xxx' />` 试下动态组件加载。在传统 vue 项目中,需要逐一 import 导入等待调用。 |
| 49 | + |
| 50 | +在 nuxt 中思路相同,先声明再调用,声明导入的方式简化实现,文档提到了两个方案,一个是导入、一个是调用 `resolveComponent` 方法。 |
| 51 | + |
| 52 | +```vue |
| 53 | +<script setup lang="ts"> |
| 54 | +import { SomeComponent } from '#components' |
| 55 | +
|
| 56 | +const MyButton = resolveComponent('MyButton') |
| 57 | +</script> |
| 58 | +
|
| 59 | +<template> |
| 60 | + <component :is="clickable ? MyButton : 'div'" /> |
| 61 | + <component :is="SomeComponent" /> |
| 62 | +</template> |
| 63 | +``` |
| 64 | + |
| 65 | +注意: `resolveComponent` 方法只接受常量,不要动态拼,这个是误区。 |
| 66 | + |
| 67 | +### 全局组件 |
| 68 | + |
| 69 | +有别于按需引用,有全局组件可以保证运行时可用。两个方案: |
| 70 | +- 配置文件里给具体文件夹 global 属性 |
| 71 | +- 放入 `components/global/` 文件夹 |
| 72 | + |
| 73 | +### 动态、懒加载导入组件 |
| 74 | + |
| 75 | +具体组件前缀添加 Lazy ,比如 `<MyComp>` 改为 `<LazyMyComp>` 轻松实现 lazy load |
| 76 | + |
| 77 | +这样做的好处是,减少运行时的开销。尤其是配合 `<LazyMyComp v-if=false />` 进行使用 |
| 78 | + |
| 79 | +### 显式、明确、直接导入 |
| 80 | + |
| 81 | +某些情况下,需要组件引入来源,避免歧义,可以通过 `#/components` 导入组件。 |
| 82 | + |
| 83 | + |
| 84 | +## 定义客户端渲染和服务端渲染 |
| 85 | +### 内置 ClientOnly 组件 |
| 86 | + |
| 87 | + |
| 88 | +归类到 Nuxt 内置组件 |
| 89 | + |
| 90 | +场景:无视 SSR,单纯 spa 实现组件,比如用户登录状态等。 |
| 91 | + |
| 92 | +在遇到渲染困难时候有奇效。 |
| 93 | + |
| 94 | +```html |
| 95 | +<ClientOnly> |
| 96 | + <div>a</div> |
| 97 | + <template #fallback>兜底内容</template> |
| 98 | +</ClientOnly> |
| 99 | +``` |
| 100 | + |
| 101 | +### client.vue 组件 |
| 102 | + |
| 103 | +某些业务组件天生需要在客户端运行,除了 `ClientOnly` 组件,还有一种修改文件名的方案 `xxx.client.vue` |
| 104 | + |
| 105 | +在 components 中,所有 `.client.vue` 的组件,默认就是 ClientOnly |
| 106 | +注意:自动导入和 `#components` 导入会生效,通过相对路径导入不会生效。 |
| 107 | +注意:`.client.vue` 组件实际会在组件挂载后渲染,所以如果需要访问模板数据,需要这样写: |
| 108 | + |
| 109 | +```ts |
| 110 | +onMounted(async()=>{ |
| 111 | + await nextTick(()=>{ |
| 112 | + // do this |
| 113 | + } |
| 114 | +}) |
| 115 | +``` |
| 116 | +
|
| 117 | +### server.vue 组件 - server-only |
| 118 | +
|
| 119 | +背景介绍: |
| 120 | +
|
| 121 | +类比 astro 的 island 架构、react 的 react server component,让组件的服务端渲染,nuxt 提供了仅在服务端渲染的方案。 |
| 122 | +
|
| 123 | +某些场景,比如 footer、sidebar 场景大部分情况是静态内容或者变化很小,这种情况下不必要参与 ssr + client 水合,让组件在服务端渲染就很有效了。 |
| 124 | +
|
| 125 | +因此:这种情况下,默认返回 html 结果,如果需要变化,会生成新的网络请求,重新渲染对应组件。 |
| 126 | +
|
| 127 | +该项功能目前是 beta,需要专门开启,背后依赖的是 `<nuxt-island />`,继续阅读 [[Nuxt 3 内置组件 NuxtIsland]] 相关内容。 |
| 128 | +
|
| 129 | +疑问中:看起来 server.vue 和 nuxt-island 是两件事,前者是后者的包装? |
| 130 | +
|
| 131 | +吐槽:我怀疑这里还有问题,踩坑崩溃,如果你发现 server 组件无论如何也展示不出来,可能需要删掉 .nuxt 然后重新跑一次 dev... |
| 132 | +
|
| 133 | +### server component |
| 134 | +
|
| 135 | +需要明确的是 server-only 和 island component,渲染过程中,服务端渲染会发出内部请求、浏览器渲染会发出 network 请求,称之为 NuxtIslandResponse |
| 136 | +
|
| 137 | +这意味着: |
| 138 | +- 服务端创建一个新的 vue app 实例,创建这个 NuxtIslandResponse |
| 139 | +- `island ctx` 会被创建 |
| 140 | +- 这个 `island ctx` 开发者控制不了,是隔离的 |
| 141 | +- 渲染 island 的过程,插件也会重新运行,或者设定 island =false 忽略,比如统计之类的? |
| 142 | +
|
| 143 | +还有个 `nuxtApp.ssrContext.islandContext` 先不管 |
| 144 | +
|
| 145 | +### .client 和 .server 可以配对使用 |
| 146 | +
|
| 147 | +可以陪读使用,但为了 .client 在水合时候能正常匹配,渲染的 html 在初次渲染必须一样,也就是结构必须一样 |
| 148 | +
|
| 149 | +## DevOnly 组件 |
| 150 | +
|
| 151 | +这个组件只会在 dev 生肖,生产构建不包含 |
| 152 | +
|
| 153 | +## NuxtClientFallback 组件 |
| 154 | +
|
| 155 | +如果 SSR 发声了错误,会展示这个组件 |
| 156 | +
|
| 157 | +## 库开发者 |
| 158 | +
|
| 159 | +自己开发的插件如何注册到 component 中? |
| 160 | +
|
| 161 | +这里先不看了。大意是在 hooks 里添加回调。 |
| 162 | +
|
| 163 | +似乎可以给 element 之类的组件库添加支持。 |
| 164 | +
|
| 165 | +
|
0 commit comments