nuxt/content v2 に置き換えてみる
対象は https://github.com/yamanoku/archives
*.md を nuxt/content を使用して静的出力するようにしてみている
サイト
マイグレーション方法
ひとまず愚直にマイグレーションに従ってみる
"dependencies": {
"@nuxt/content": "^1.15.1",
"core-js": "^3.21.1",
"nuxt": "2.13.3"
},
を
"dependencies": {
"@nuxt/content": "^2.0",
"nuxt": "^3.0.0-rc.3"
},
に変更(core-jsなんで入れたか忘れた…)
あとは TypeScript 周りもいらないかもしれないので
"@nuxt/types": "^2.15.3",
"@nuxt/typescript-build": "^2.1.0",
上記も削除してみる
npm scripts まわりも更新
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"preview": "nuxt preview",
Global Components を差し替え
- components/global
+ components/content
Fetching Content まわりはだいぶ変わってるのでまず読んでみる
There is no global $content variable, instead you can use queryContent composable to fetch contents.
グローバル変数 $content
はないが、代わりに queryContent
composable を使用してコンテンツを取得することができる。
const posts = await this.$content('/blog', { deep: true }).only(['title']).fetch()
と書いてたのが
const { data: posts } = await useAsyncData('posts-list', () => queryContent('/blog').only(['title']).find())
となる
queryContent provide same utilities as legacy $content with some improvements:
queryContent
は従来の $content
と同じユーティリティを提供しているが、いくつかの改良が加えられている。
fetch
dropped in favor of new find utils
find
: retrieve a list of contentsfindOne
: retrive first matched contentsurround
dropped in favor offindSurround
where
utility can be chained- There is no
search
utility for full text search.- Find utilities does not contains body of documents and they only include meta information of parsed contents. You can fetch contents body using
getContentDocument
(non-reactive) oruseContentDocument
(reactive) composables- There is new
findNavigation
utility to retrieve navigation object
-
fetch
が廃止、新しいfind
ユーティリティが採用-
find
: コンテンツのリストを取得 -
findOne
: 最初にマッチしたコンテンツを取得
-
-
surround
が削除、findSurround
が採用 -
where
ユーティリティが繋げて使えるようになった - 全文検索のための
search
ユーティリティはない - find ユーティリティはドキュメントの本文を含まず、パースされたコンテンツのメタ情報のみを含む。コンテンツ本体は、
getContentDocument
(non-reactive) あるいはuseContentDocument
(reactive) 変数を使って取得できる - ナビゲーションオブジェクトを取得するための新しく
findNavigation
ユーティリティが追加
Rendering Content まわりも見てみる
<NuxtContent>
component removed in favor of a<ContentRenderer>
component.
<ContentDoc>
component receive document path then fetch and render the document.
<NuxtContent>
コンポーネントは削除され、<ContentRenderer>
コンポーネントが採用された。
<ContentDoc>
コンポーネントは、ドキュメントのパスを受け取り、ドキュメントを fetch してレンダリングする。
You can go even faster if you know that route.path will be the same as your content files, use the
<ContentDoc>
component:
ルートパスがコンテンツファイルと同一なのが分かっている場合は、<ContentDoc>
コンポーネントを使用すると、さらに高速に処理することができる。
The
<ContentDoc>
component will fetch the document for the current route path and use<ContentRenderer>
to render it.
<ContentDoc>
コンポーネントは、現在のルートパスのドキュメントを取得し、<ContentRenderer>
を使用してレンダリングする。
Nuxt1のドキュメントは以下より(日本語版)
script setup 対応にしたいけどガッと変えることができるだろうかーだろうかー
layout を変更した
<template>
- <div>
- <global-header />
- <nuxt />
- <global-footer />
- </div>
+ <global-header />
+ <slot />
+ <global-footer />
</template>
-<script lang="ts">
-import Vue from 'vue'
+<script lang="ts" setup>
import GlobalHeader from '@/components/global/Header.vue'
import GlobalFooter from '@/components/global/Footer.vue'
-
-export default Vue.extend({
- components: {
- GlobalHeader,
- GlobalFooter,
- },
-})
</script>
index.vue
- <nuxt-link :to="article.path">
- <budoux-ja>
- {{ article.title }}
- </budoux-ja>
+ <nuxt-link :to="article._path">
+ {{ article.title }}
</nuxt-link>
</h2>
<p>
@@ -29,79 +27,66 @@
</main>
</template>
-<script>
-import Vue from 'vue'
-import dayjs from 'dayjs'
+<script setup lang="ts">
+import dayjs from 'dayjs';
-Vue.config.ignoredElements = ['budoux-ja']
+const { data: articles } = await useAsyncData('home', () => queryContent('/').sort({'date': 0}).find());
-export default {
- async asyncData({ $content }) {
- const query = $content('/', { deep: true }).sortBy('date', 'desc')
- const articles = await query.fetch()
- return {
- articles,
- }
- },
- head() {
- return {
- title: 'アーカイブ',
- meta: [
- {
- hid: 'description',
- name: 'description',
- content:
- 'このページはyamanokuこと大山奥人が書いてきた過去の記事やログを収集したページです。',
- },
- {
- hid: 'og:title',
- property: 'og:title',
- content: 'アーカイブ',
- },
- {
- hid: 'og:description',
- property: 'og:description',
- content:
- 'このページはyamanokuこと大山奥人が書いてきた過去の記事やログを収集したページです。',
- },
- {
- hid: 'og:image',
- property: 'og:image',
- content: 'https://yamanoku.net/ogp/ogp-archive@2x.png',
- },
- {
- hid: 'og:image:alt',
- property: 'og:image:alt',
- content: 'Archive Document',
- },
- {
- hid: 'twitter:title',
- name: 'twitter:title',
- content: 'アーカイブ',
- },
- {
- hid: 'twitter:description',
- name: 'twitter:description',
- content:
- 'このページはyamanokuこと大山奥人が書いてきた過去の記事やログを収集したページです。',
- },
- {
- hid: 'twitter:image',
- property: 'twitter:image',
- content: 'https://yamanoku.net/ogp/ogp-archive@2x.png',
- },
- {
- hid: 'twitter:image:alt',
- property: 'twitter:image:alt',
- content: 'Archive Document',
- },
- ],
- }
- },
- methods: {
- dateTime(time) {
- return dayjs(time).format('YYYY-MM-DD')
+const dateTime = (time: string):string => {
+ return dayjs(time).format('YYYY-MM-DD')
+};
+
+useHead({
+ title: 'アーカイブ',
+ meta: [
+ {
+ hid: 'description',
+ name: 'description',
+ content:
+ 'このページはyamanokuこと大山奥人が書いてきた過去の記事やログを収集したページです。',
+ },
+ {
+ hid: 'og:title',
+ property: 'og:title',
+ content: 'アーカイブ',
+ },
+ {
+ hid: 'og:description',
+ property: 'og:description',
+ content:
+ 'このページはyamanokuこと大山奥人が書いてきた過去の記事やログを収集したページです。',
+ },
+ {
+ hid: 'og:image',
+ property: 'og:image',
+ content: 'https://yamanoku.net/ogp/ogp-archive@2x.png',
+ },
+ {
+ hid: 'og:image:alt',
+ property: 'og:image:alt',
+ content: 'Archive Document',
+ },
+ {
+ hid: 'twitter:title',
+ name: 'twitter:title',
+ content: 'アーカイブ',
+ },
+ {
+ hid: 'twitter:description',
+ name: 'twitter:description',
+ content:
+ 'このページはyamanokuこと大山奥人が書いてきた過去の記事やログを収集したページです。',
+ },
+ {
+ hid: 'twitter:image',
+ property: 'twitter:image',
+ content: 'https://yamanoku.net/ogp/ogp-archive@2x.png',
+ },
+ {
+ hid: 'twitter:image:alt',
+ property: 'twitter:image:alt',
+ content: 'Archive Document',
},
- },
-}
+ ]
+});
</script>
slug.vue は [...slug].vue に改名してアップデート
nuxt-content
から content-renderer
に置き換え対応
- <nuxt-content :document="page" />
+ <content-renderer :value="page" />
<script>
部分
<script setup lang="ts">
import dayjs from 'dayjs'
import DeprecationAlertOneYear from '@/components/global/DeprecationAlertOneYear.vue'
const dateTime = (time: string):string => {
return dayjs(time).format('YYYY-MM-DD')
};
const { path } = useRoute();
const { data: page } = await useAsyncData(`content-${path}`, () => {
return queryContent().where({ _path: path }).findOne()
});
const gitHubLink = computed(() => {
return `https://github.com/yamanoku/archives/issues/new?title=アーカイブのドキュメントにまつわる修正依頼&labels=feedback&body=URL:https://archives.yamanoku.net${path}%0A修正依頼内容:%0A`
});
const twitterLink = computed(() => {
return `https://twitter.com/share?url=https://archives.yamanoku.net${path}&text=@yamanoku`;
});
const editLink = computed(() => {
return `https://github.com/yamanoku/archives/edit/main/content${path}.md`;
});
useHead({
title: page.title,
meta: [
{
hid: 'description',
name: 'description',
content: page.description,
},
{
hid: 'og:title',
property: 'og:title',
content: page.title,
},
{
hid: 'og:description',
property: 'og:description',
content: page.description,
},
{
hid: 'og:image',
property: 'og:image',
content: `https://archives.yamanoku.net/og-images${path}.png`,
},
{
hid: 'og:image:alt',
property: 'og:image:alt',
content: page.title,
},
{
hid: 'twitter:title',
name: 'twitter:title',
content: page.title,
},
{
hid: 'twitter:description',
name: 'twitter:description',
content: page.description,
},
{
hid: 'twitter:image',
property: 'twitter:image',
content: `https://archives.yamanoku.net/og-images${path}.png`,
},
{
hid: 'twitter:image:alt',
property: 'twitter:image:alt',
content: page.title,
},
]
});
</script>
中身が undefined
になっていたのだが value で呼び出す必要があった
useHead({
- title: page.title,
+ title: page.value.title,
meta: [
{
hid: 'description',
name: 'description',
- content: page.description,
+ content: page.value.description,
},
{
hid: 'og:title',
property: 'og:title',
- content: page.title,
+ content: page.value.title,
},
{
hid: 'og:description',
property: 'og:description',
- content: page.description,
+ content: page.value.description,
},
{
hid: 'og:image',
property: 'og:image',
content: `https://archives.yamanoku.net/og-images${path}.png`,
},
{
hid: 'og:image:alt',
property: 'og:image:alt',
- content: page.title,
+ content: page.value.title,
},
{
hid: 'twitter:title',
name: 'twitter:title',
- content: page.title,
+ content: page.value.title,
},
{
hid: 'twitter:description',
name: 'twitter:description',
- content: page.description,
+ content: page.value.description,
},
{
hid: 'twitter:image',
property: 'twitter:image',
content: `https://archives.yamanoku.net/og-images${path}.png`,
},
{
hid: 'twitter:image:alt',
property: 'twitter:image:alt',
- content: page.title,
+ content: page.value.title,
},
]
});
置き換えしてみて立ち上げてみる
ぬーん
file:///Users/yamanoku/archives/.nuxt/dev/index.mjs:34
import { visit } from 'file:///Users/yamanoku/archives/node_modules/unist-util-visit/index.js';
^^^^^
SyntaxError: Named export 'visit' not found. The requested module 'file:///Users/yamanoku/works/archives/node_modules/unist-util-visit/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'file:///Users/yamanoku/archives/node_modules/unist-util-visit/index.js';
const { visit } = pkg;
at ModuleJob._instantiate (internal/modules/esm/module_job.js:124:21)
at async ModuleJob.run (internal/modules/esm/module_job.js:179:5)
at async Loader.import (internal/modules/esm/loader.js:178:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
at async handleMainPromise (internal/modules/run_main.js:59:12)
モジュールは別だけど関連しそう?
npm why してみた
$ npm why unist-util-visit
unist-util-visit@2.0.3 dev
node_modules/unist-util-visit
unist-util-visit@"^2.0.3" from rehype-plugin-auto-resolve-layout-shift@1.0.0
node_modules/rehype-plugin-auto-resolve-layout-shift
dev rehype-plugin-auto-resolve-layout-shift@"1.0.0" from the root project
unist-util-visit@"^2.0.3" from rehype-plugin-image-native-lazy-loading@1.2.0
node_modules/rehype-plugin-image-native-lazy-loading
dev rehype-plugin-image-native-lazy-loading@"1.2.0" from the root project
unist-util-visit@4.1.0
node_modules/hast-util-raw/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from hast-util-raw@7.2.1
node_modules/hast-util-raw
hast-util-raw@"^7.2.0" from rehype-raw@6.1.1
node_modules/rehype-raw
rehype-raw@"^6.1.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@3.1.0
node_modules/mdast-util-definitions/node_modules/unist-util-visit
unist-util-visit@"^3.0.0" from mdast-util-definitions@5.1.0
node_modules/mdast-util-definitions
mdast-util-definitions@"^5.0.0" from mdast-util-to-hast@12.1.1
node_modules/mdast-util-to-hast
mdast-util-to-hast@"^12.1.0" from remark-rehype@10.1.0
node_modules/remark-rehype
remark-rehype@"^10.1.0" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-hast@"^12.1.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/mdast-util-to-hast/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from mdast-util-to-hast@12.1.1
node_modules/mdast-util-to-hast
mdast-util-to-hast@"^12.1.0" from remark-rehype@10.1.0
node_modules/remark-rehype
remark-rehype@"^10.1.0" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-hast@"^12.1.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/mdast-util-to-markdown/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from mdast-util-to-markdown@1.3.0
node_modules/mdast-util-to-markdown
mdast-util-to-markdown@"^1.0.0" from mdast-util-gfm@2.0.1
node_modules/mdast-util-gfm
mdast-util-gfm@"^2.0.0" from remark-gfm@3.0.1
node_modules/remark-gfm
remark-gfm@"^3.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-markdown@"^1.3.0" from mdast-util-gfm-footnote@1.0.1
node_modules/mdast-util-gfm-footnote
mdast-util-gfm-footnote@"^1.0.0" from mdast-util-gfm@2.0.1
node_modules/mdast-util-gfm
mdast-util-gfm@"^2.0.0" from remark-gfm@3.0.1
node_modules/remark-gfm
remark-gfm@"^3.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-markdown@"^1.3.0" from mdast-util-gfm-strikethrough@1.0.1
node_modules/mdast-util-gfm-strikethrough
mdast-util-gfm-strikethrough@"^1.0.0" from mdast-util-gfm@2.0.1
node_modules/mdast-util-gfm
mdast-util-gfm@"^2.0.0" from remark-gfm@3.0.1
node_modules/remark-gfm
remark-gfm@"^3.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-markdown@"^1.3.0" from mdast-util-gfm-table@1.0.4
node_modules/mdast-util-gfm-table
mdast-util-gfm-table@"^1.0.0" from mdast-util-gfm@2.0.1
node_modules/mdast-util-gfm
mdast-util-gfm@"^2.0.0" from remark-gfm@3.0.1
node_modules/remark-gfm
remark-gfm@"^3.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-markdown@"^1.3.0" from mdast-util-gfm-task-list-item@1.0.1
node_modules/mdast-util-gfm-task-list-item
mdast-util-gfm-task-list-item@"^1.0.0" from mdast-util-gfm@2.0.1
node_modules/mdast-util-gfm
mdast-util-gfm@"^2.0.0" from remark-gfm@3.0.1
node_modules/remark-gfm
remark-gfm@"^3.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
mdast-util-to-markdown@"^1.3.0" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/rehype-external-links/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from rehype-external-links@1.0.1
node_modules/rehype-external-links
rehype-external-links@"^1.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/rehype-sort-attribute-values/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from rehype-sort-attribute-values@4.0.0
node_modules/rehype-sort-attribute-values
rehype-sort-attribute-values@"^4.0.0" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/rehype-sort-attributes/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from rehype-sort-attributes@4.0.0
node_modules/rehype-sort-attributes
rehype-sort-attributes@"^4.0.0" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/rehype-slug/node_modules/unist-util-visit
unist-util-visit@"^4.0.0" from rehype-slug@5.0.1
node_modules/rehype-slug
rehype-slug@"^5.0.1" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/remark-emoji/node_modules/unist-util-visit
unist-util-visit@"^4.1.0" from remark-emoji@3.0.2
node_modules/remark-emoji
remark-emoji@"^3.0.2" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
unist-util-visit@4.1.0
node_modules/@nuxt/content/node_modules/unist-util-visit
unist-util-visit@"^4.1.0" from @nuxt/content@2.0.0
node_modules/@nuxt/content
@nuxt/content@"^2.0" from the root project
使わせてもらってる rehype-plugin-auto-resolve-layout-shift の影響?
使わせてもらってる rehype-plugin たちが対応してないみたいなので抜いてみる
またモジュールが足りてない指摘も有るので追加対応もする
- unified
- unist-util-visit
これで npm run dev
にて立ち上げ自体はできるようになった
tsconfig.json をスッキリさせた
{
+ "extends": "./.nuxt/tsconfig.json"
- "compilerOptions": {
- "target": "ES2018",
- "module": "ESNext",
- "moduleResolution": "Node",
- "lib": [
- "ESNext",
- "ESNext.AsyncIterable",
- "DOM"
- ],
- "esModuleInterop": true,
- "allowJs": true,
- "sourceMap": true,
- "strict": true,
- "noEmit": true,
- "experimentalDecorators": true,
- "baseUrl": ".",
- "paths": {
- "~/*": [
- "./*"
- ],
- "@/*": [
- "./*"
- ]
- },
- "types": [
- "@nuxt/types",
- "@nuxt/content",
- "@types/node"
- ]
- },
- "exclude": [
- "node_modules",
- ".nuxt",
- "dist"
- ]
}
nuxt.config.ts にして一部精査
-const rehypePlugins = [
- 'rehype-plugin-auto-resolve-layout-shift',
- 'rehype-plugin-image-native-lazy-loading',
-]
+import { defineNuxtConfig } from 'nuxt'
-if (process.env.NODE_ENV === 'production') {
- rehypePlugins.push([
- 'rehype-plugin-auto-resolve-layout-shift',
- { type: 'maxWidth', maxWidth: 720 },
- ])
-}
-
-export default {
+export default defineNuxtConfig({
target: 'static',
telemetry: false,
head: {
@@ -33,18 +23,17 @@ export default {
],
},
css: ['modern-normalize', 'yama-normalize'],
- buildModules: ['@nuxt/typescript-build'],
modules: ['@nuxt/content'],
content: {
- markdown: {
- remarkExternalLinks: {
- target: '_blank',
- rel: 'noopener noreferrer',
- },
- prism: {
- theme: 'prism-themes/themes/prism-a11y-dark.css',
- },
- rehypePlugins,
+ highlight: {
+ theme: 'dark-plus'
},
},
-}
+ vue: {
+ config: {
+ compilerOptions: {
+ isCustomElement: (tag: string) => tag.startsWith('budoux-ja')
+ }
+ }
+ }
+});
- highlight が指定しても効かない 🤔
- vue config compilerOptions 周りも効かない 🤔
eslint 設定もアップデート
追加パッケージ
$ npm install -D typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser @nuxtjs/eslint-config-typescript
.eslintrc.js
root: true,
env: {
browser: true,
- node: true,
+ es2021: true,
+ node: true
},
extends: [
- '@nuxtjs/eslint-config-typescript',
- 'plugin:prettier/recommended',
- 'plugin:nuxt/recommended',
+ 'prettier',
+ 'plugin:@typescript-eslint/recommended',
+ '@nuxtjs/eslint-config-typescript'
],
- plugins: [],
- // add your custom rules here
- rules: {},
+ parserOptions: {
+ ecmaVersion: 'latest',
+ parser: '@typescript-eslint/parser',
+ sourceType: 'module'
+ },
+ plugins: ['vue', '@typescript-eslint']
}
2.0.1 がリリースされてた
- queryContent('/my-directory').sort({ createdAt: 0 })
+ queryContent('/my-directory').sort({ createdAt: -1 })
降順が -1 になったので変更
const { data: articles } = await useAsyncData('home', () =>
- queryContent('/').sort({ date: 0 }).find()
+ queryContent('/').sort({ date: -1 }).find()
)
ページ遷移したときに位置がそのままになってしまう問題の対応
app/router.options.ts
import type { RouterConfig } from '@nuxt/schema'
export default <RouterConfig>{
scrollBehavior(to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ top: 0, left: 0 })
}, 100)
})
},
}
すんません辞めました