Nuxt3でビルド後にimgやfaviconの画像が404になってしまい調べた時のメモです。
目次
環境
- macOS Catalina (intel cpu)
- nuxt 3.6.2
- Node.js 16.19.1
URLの指定箇所
主に以下のような箇所にURL指定した場合を考えます。
- imgタグのsrc
- 独自のコンポーネントの引数
- nuxt.config.appのappプロパティに指定するfavicon
- コンポーネント内でのuseHeadで指定するfavicon
ビルドするソースコード
以下ソースをビルドして確認します。presetはnitroとしています。
export default defineNuxtConfig({
devtools: { enabled: true },
app: {
cdnURL: "http://www.example.com",
head: {
link: [{ rel: "shortcut icon", href: "/favicon.ico" }],
},
},
});
<script setup lang="ts">
const props = withDefaults(
defineProps<{
src: string;
alt: string;
}>(),
{
src: "",
alt: "",
},
);
</script>
<template>
<div class="image">
<img :src="props.src" :alt="props.alt" />
</div>
</template>
<script setup lang="ts">
useHead({
link: [{ rel: "shortcut icon", href: "/favicon.ico" }],
});
const assets2Src = "/assets/2.png";
</script>
<template>
<div>
<!-- assetsからのパスで展開される、baseURL指定の場合はプリフィックスにbaseURLがつく -->
<img src="~/assets/1.png" />
<!-- publicからのパス展開される、baseURL指定の場合はプリフィックスにbaseURLがつく -->
<img src="/img/1.png" />
<!-- そのまま展開される -->
<img :src="assets2Src" />
<!-- そのまま展開される -->
<Image src="/assets/2.png" />
</div>
</template>
ビルド後のソース
.output/server/chunks/app/server.mjsを見てみましょう。関連範囲を抽出すると以下のようになっています。
...
const appHead = { "meta": [{ "name": "viewport", "content": "width=device-width, initial-scale=1" }, { "charset": "utf-8" }], "link": [{ "rel": "shortcut icon", "href": "/favicon.ico" }], "style": [], "script": [], "noscript": [] };
...
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
__name: "Image",
__ssrInlineRender: true,
props: {
src: { default: "" },
alt: { default: "" }
},
setup(__props) {
const props = __props;
return (_ctx, _push, _parent, _attrs) => {
_push(<div${ssrRenderAttrs(mergeProps({ class: "image" }, _attrs))}><img${ssrRenderAttr("src", props.src)}${ssrRenderAttr("alt", props.alt)}></div>);
};
}
});
const _sfc_setup$3 = _sfc_main$3.setup;
_sfc_main$3.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/Image.vue");
return _sfc_setup$3 ? _sfc_setup$3(props, ctx) : void 0;
};
const _imports_0 = "" + __buildAssetsURL("1.205f966e.png");
const _imports_1 = "" + __publicAssetsURL("img/1.png");
const assets2Src = "/assets/2.png";
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
__name: "app",
__ssrInlineRender: true,
setup(__props) {
useHead({
link: [{ rel: "shortcut icon", href: "/favicon.ico" }]
});
return (_ctx, _push, _parent, _attrs) => {
const _component_Image = _sfc_main$3;
_push(<div${ssrRenderAttrs(_attrs)}><img${ssrRenderAttr("src", _imports_0)}><img${ssrRenderAttr("src", _imports_1)}><img${ssrRenderAttr("src", assets2Src)}>);
_push(ssrRenderComponent(_component_Image, { src: "/assets/2.png" }, null, _parent));
_push(</div>);
};
}
});
const _sfc_setup$2 = _sfc_main$2.setup;
_sfc_main$2.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("app.vue");
return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0;
};
imgのsrcの箇所
assets下のリソースはハッシュ値付きのファイルに、publicの下のものはそのままのファイル名となります。
const _imports_0 = "" + __buildAssetsURL("1.205f966e.png");
const _imports_1 = "" + __publicAssetsURL("img/1.png");
また、__xxxAssetsURLは以下のようになっています。app.cdnURLが指定されていれば優先され、指定されていない場合はbaseURLを起点にパスが解決されています。
function buildAssetsDir() {
return useRuntimeConfig().app.buildAssetsDir;
}
function buildAssetsURL(...path) {
return joinURL(publicAssetsURL(), buildAssetsDir(), ...path);
}
function publicAssetsURL(...path) {
const publicBase = useRuntimeConfig().app.cdnURL || useRuntimeConfig().app.baseURL;
return path.length ? joinURL(publicBase, ...path) : publicBase;
}
独自のコンポーネントの引数
予想通りかと思いますが、コンポーネントの引数なのでそのまま渡されています。
nuxt.config.appのappプロパティに指定するfavicon
予想通りかと思いますが、そのまま展開されています。
コンポーネント内でのuseHeadで指定するfavicon
予想通りかと思いますが、そのまま展開されています。
baseURLを指定した場合
サブパス指定でアプリケーションを展開したい場合、baseURLが使えます。しかし、app.headやuseHeadに指定したパスはそのまま展開されるので注意です。faviconは本来ドメインルートに配置するのでそういう構成であれば困ることはないかもしれません。
__publicAssetsURLで同等の処理にすることもできなくはなさそうです。
<script setup lang="ts">
useHead({
link: [{ rel: "shortcut icon", href: __publicAssetsURL("/favicon.ico") }],
});
const assets2Src = __publicAssetsURL("/assets/2.png");
</script>
<template>
<div>
<img src="~/assets/1.png" />
<img src="/img/1.png" />
<img :src="assets2Src" />
<Image src="/assets/2.png" />
</div>
</template>
まとめ
srcなどのURLの指定は、ビルド後にどのようにパスが展開されるのか意識して書くことが大事です。
参考リンク


コメント