[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$ffghaKVNrxyPQcZLTWwyCyY2cefWVGeEBIvyavZHuJWQ":3,"$fU1nbp02MR4vmTHpQ6xYH0OsmvYYn7C6HrDfPFFcvScM":1443,"$fJhquL7YfQaPWSLX-5YL34uTWkjEhm4Vtff1K59F9PME":1447,"$fA-CuUuRrVAShC7GVSRIFUcoyEhkceB1Gidn0UilGmYA":1451,"$fG_Hv3CPNYga3rc8vnetGbyxz-eP9XJxYg2lMd-5Jndk":1455,"$fJ1mnrsSE5uA7nTP0VplpyQrWyGkIUFKbais9yrK_CT0":1459,"$fblyW92bWIMSA2B6WVrCCeGg1qQOXZESYKSCzIX6euUE":1463,"$fZWSxRNs5mLTuREEW8ysOSDTOYCgU0OtFIxD-SeGssfk":1467,"$fYMFTXicEdFiJWPz_ZHchm9SyeZO6M9ENGfiwkPlHys8":1471},{"id":4,"title":5,"body":6,"date":1429,"description":1430,"extension":1431,"icon":1432,"meta":1433,"navigation":322,"ogImage":1434,"path":1435,"published":322,"publishedAt":1436,"seo":1437,"stem":1438,"tags":1439,"updatedAt":1436,"__hash__":1442},"tech/tech/introduce-unhead.md","\u003Chead>管理ライブラリ Unhead の紹介",{"type":7,"value":8,"toc":1411},"minimark",[9,13,21,35,39,43,57,60,63,66,73,76,87,238,244,247,257,261,267,462,468,471,481,492,495,660,666,669,681,684,687,690,1108,1114,1346,1349,1355,1359,1379,1382,1390,1393,1396,1404,1407],[10,11,12],"h2",{"id":12},"はじめに",[14,15,16,17,20],"p",{},"本記事は JavaScript ツール群の UnJS の一つである Unhead について紹介する記事です。",[18,19],"br",{},"\n業務で触れる機会があったので備忘録がてら記事にまとめていこうと思います。",[14,22,23,24,26,27,29,30,34],{},"現代の SSR/CSR で作られる Web アプリケーションではページタイトル・メタ情報・OGP などを画面に応じて動的に変更する必要があります。",[18,25],{},"\nこれらを毎回手動で管理するのは大変で煩雑になります。",[18,28],{},"\nこうした課題を解決するために登場したのが、UnJS 製のヘッド管理ライブラリ",[31,32,33],"strong",{},"Unhead","です。",[10,36,38],{"id":37},"unhead-とは","Unhead とは？",[40,41],"external-link-card-wrapper",{"url":42},"https://unhead.unjs.io/",[14,44,45,46,34,49,51,52,56],{},"Web アプリケーション向けの",[31,47,48],{},"フレームワークに依存しないヘッド管理ライブラリ",[18,50],{},"\n通常、メタタグや OGP タグはテンプレートに直書きしたり、手動で状態を管理する必要がありますが、Unhead を使うと",[53,54,55],"code",{},"\u003Chead>","の管理を SSR/CSR 双方に対応できます。",[10,58,59],{"id":59},"使ってみる",[14,61,62],{},"今回は Vue に Unhead をインストールして使ってみます。",[40,64],{"url":65},"https://unhead.unjs.io/docs/vue/head/guides/get-started/installation",[67,68,70],"h3",{"id":69},"usehead",[53,71,72],{},"useHead()",[40,74],{"url":75},"https://unhead.unjs.io/docs/vue/head/api/composables/use-head",[14,77,78,80,81,83,84,86],{},[53,79,72],{},"は",[53,82,55],{},"タグの設定を管理する型安全なリアクティブ API を提供します。",[18,85],{},"\nまた、後に紹介する Composables でも使用される Core Composables です。",[88,89,94],"pre",{"className":90,"code":91,"language":92,"meta":93,"style":93},"language-vue shiki shiki-themes material-theme-lighter github-dark-high-contrast github-dark","\u003Cscript setup lang=\"ts\">\nuseHead({\n  title: \"Unhead Demo\",\n  meta: [{ name: \"description\", content: \"This is a demo of Unhead!\" }],\n});\n\u003C/script>\n","vue","",[53,95,96,132,146,167,216,228],{"__ignoreMap":93},[97,98,101,105,109,113,116,119,123,127,129],"span",{"class":99,"line":100},"line",1,[97,102,104],{"class":103},"seLpV","\u003C",[97,106,108],{"class":107},"siCa7","script",[97,110,112],{"class":111},"sOohs"," setup",[97,114,115],{"class":111}," lang",[97,117,118],{"class":103},"=",[97,120,122],{"class":121},"sPUPB","\"",[97,124,126],{"class":125},"sSIes","ts",[97,128,122],{"class":121},[97,130,131],{"class":103},">\n",[97,133,135,139,143],{"class":99,"line":134},2,[97,136,138],{"class":137},"s7KVs","useHead",[97,140,142],{"class":141},"sdyPO","(",[97,144,145],{"class":103},"{\n",[97,147,149,153,156,159,162,164],{"class":99,"line":148},3,[97,150,152],{"class":151},"sLCpo","  title",[97,154,155],{"class":103},":",[97,157,158],{"class":121}," \"",[97,160,161],{"class":125},"Unhead Demo",[97,163,122],{"class":121},[97,165,166],{"class":103},",\n",[97,168,170,173,175,178,181,184,186,188,191,193,196,199,201,203,206,208,211,214],{"class":99,"line":169},4,[97,171,172],{"class":151},"  meta",[97,174,155],{"class":103},[97,176,177],{"class":141}," [",[97,179,180],{"class":103},"{",[97,182,183],{"class":151}," name",[97,185,155],{"class":103},[97,187,158],{"class":121},[97,189,190],{"class":125},"description",[97,192,122],{"class":121},[97,194,195],{"class":103},",",[97,197,198],{"class":151}," content",[97,200,155],{"class":103},[97,202,158],{"class":121},[97,204,205],{"class":125},"This is a demo of Unhead!",[97,207,122],{"class":121},[97,209,210],{"class":103}," }",[97,212,213],{"class":141},"]",[97,215,166],{"class":103},[97,217,219,222,225],{"class":99,"line":218},5,[97,220,221],{"class":103},"}",[97,223,224],{"class":141},")",[97,226,227],{"class":103},";\n",[97,229,231,234,236],{"class":99,"line":230},6,[97,232,233],{"class":103},"\u003C/",[97,235,108],{"class":107},[97,237,131],{"class":103},[67,239,241],{"id":240},"useheadsafe",[53,242,243],{},"useHeadSafe()",[40,245],{"url":246},"https://unhead.unjs.io/docs/vue/head/api/composables/use-head-safe",[14,248,249,80,251,253,254,256],{},[53,250,243],{},[53,252,72],{},"のセキュリティに焦点を当てたラッパーです。",[18,255],{},"\n安全な値のみを許可するように入力を制限し、信頼できないコンテンツを扱うときに XSS 攻撃からの保護を提供します。",[258,259,260],"h4",{"id":260},"ユースケース",[14,262,263,264,266],{},"例えば、ユーザーのプロフィールページでユーザの設定した文字列を",[53,265,55],{},"内で使用する場合があると思います。XSS攻撃のリスクがある項目を安全にフィルタリングしてくれます。",[88,268,270],{"className":90,"code":269,"language":92,"meta":93,"style":93},"\u003Cscript setup lang=\"ts\">\nimport { useHeadSafe } from '@unhead/vue'\n\nconst profile = await useProfile(userId)\n\nuseHeadSafe({\n  title: profile.pageTitle,\n  meta: [\n    { name: 'description', content: userProfile.pageDescription },\n    ...profile.customMetaTags // これらは安全にフィルタリングされます\n  ]\n})\n\u003C/script>\n",[53,271,272,292,318,324,347,351,360,377,387,421,439,445,453],{"__ignoreMap":93},[97,273,274,276,278,280,282,284,286,288,290],{"class":99,"line":100},[97,275,104],{"class":103},[97,277,108],{"class":107},[97,279,112],{"class":111},[97,281,115],{"class":111},[97,283,118],{"class":103},[97,285,122],{"class":121},[97,287,126],{"class":125},[97,289,122],{"class":121},[97,291,131],{"class":103},[97,293,294,298,301,304,306,309,312,315],{"class":99,"line":134},[97,295,297],{"class":296},"stP2V","import",[97,299,300],{"class":103}," {",[97,302,303],{"class":141}," useHeadSafe",[97,305,210],{"class":103},[97,307,308],{"class":296}," from",[97,310,311],{"class":121}," '",[97,313,314],{"class":125},"@unhead/vue",[97,316,317],{"class":121},"'\n",[97,319,320],{"class":99,"line":148},[97,321,323],{"emptyLinePlaceholder":322},true,"\n",[97,325,326,330,334,338,341,344],{"class":99,"line":169},[97,327,329],{"class":328},"sGRfs","const",[97,331,333],{"class":332},"sSuNx"," profile",[97,335,337],{"class":336},"sUBcA"," =",[97,339,340],{"class":296}," await",[97,342,343],{"class":137}," useProfile",[97,345,346],{"class":141},"(userId)\n",[97,348,349],{"class":99,"line":218},[97,350,323],{"emptyLinePlaceholder":322},[97,352,353,356,358],{"class":99,"line":230},[97,354,355],{"class":137},"useHeadSafe",[97,357,142],{"class":141},[97,359,145],{"class":103},[97,361,363,365,367,369,372,375],{"class":99,"line":362},7,[97,364,152],{"class":151},[97,366,155],{"class":103},[97,368,333],{"class":141},[97,370,371],{"class":103},".",[97,373,374],{"class":141},"pageTitle",[97,376,166],{"class":103},[97,378,380,382,384],{"class":99,"line":379},8,[97,381,172],{"class":151},[97,383,155],{"class":103},[97,385,386],{"class":141}," [\n",[97,388,390,393,395,397,399,401,404,406,408,410,413,415,418],{"class":99,"line":389},9,[97,391,392],{"class":103},"    {",[97,394,183],{"class":151},[97,396,155],{"class":103},[97,398,311],{"class":121},[97,400,190],{"class":125},[97,402,403],{"class":121},"'",[97,405,195],{"class":103},[97,407,198],{"class":151},[97,409,155],{"class":103},[97,411,412],{"class":141}," userProfile",[97,414,371],{"class":103},[97,416,417],{"class":141},"pageDescription ",[97,419,420],{"class":103},"},\n",[97,422,424,427,430,432,435],{"class":99,"line":423},10,[97,425,426],{"class":336},"    ...",[97,428,429],{"class":141},"profile",[97,431,371],{"class":103},[97,433,434],{"class":141},"customMetaTags ",[97,436,438],{"class":437},"sZPSj","// これらは安全にフィルタリングされます\n",[97,440,442],{"class":99,"line":441},11,[97,443,444],{"class":141},"  ]\n",[97,446,448,450],{"class":99,"line":447},12,[97,449,221],{"class":103},[97,451,452],{"class":141},")\n",[97,454,456,458,460],{"class":99,"line":455},13,[97,457,233],{"class":103},[97,459,108],{"class":107},[97,461,131],{"class":103},[67,463,465],{"id":464},"useseometa",[53,466,467],{},"useSeoMeta()",[40,469],{"url":470},"https://unhead.unjs.io/docs/vue/head/api/composables/use-seo-meta",[14,472,473,80,475,477,478,480],{},[53,474,467],{},[53,476,72],{},"のSEOに焦点を当てたラッパーです。",[18,479],{},"\nTypeScriptをサポートしたフラットオブジェクトとしてメタタグを定義できます。",[14,482,483,484,487,488,491],{},"これにより、",[53,485,486],{},"property","属性の代わりに",[53,489,490],{},"name","を使用するようなよくある間違いを避けることができ、100以上のmetaタグが完全に型付けされているため、タイプミスを防ぐことができます。",[258,493,260],{"id":494},"ユースケース-1",[88,496,498],{"className":90,"code":497,"language":92,"meta":93,"style":93},"\u003Cscript setup lang=\"ts\">\nimport { useSeoMeta } from '@unhead/vue'\n\nuseSeoMeta({\n  title: 'About',\n  description: 'My about page',\n  ogDescription: 'Still about my about page',\n  ogTitle: 'About',\n  ogImage: 'https://example.com/image.png',\n  twitterCard: 'summary_large_image',\n})\n\u003C/script>\n",[53,499,500,520,539,543,552,567,583,599,614,630,646,652],{"__ignoreMap":93},[97,501,502,504,506,508,510,512,514,516,518],{"class":99,"line":100},[97,503,104],{"class":103},[97,505,108],{"class":107},[97,507,112],{"class":111},[97,509,115],{"class":111},[97,511,118],{"class":103},[97,513,122],{"class":121},[97,515,126],{"class":125},[97,517,122],{"class":121},[97,519,131],{"class":103},[97,521,522,524,526,529,531,533,535,537],{"class":99,"line":134},[97,523,297],{"class":296},[97,525,300],{"class":103},[97,527,528],{"class":141}," useSeoMeta",[97,530,210],{"class":103},[97,532,308],{"class":296},[97,534,311],{"class":121},[97,536,314],{"class":125},[97,538,317],{"class":121},[97,540,541],{"class":99,"line":148},[97,542,323],{"emptyLinePlaceholder":322},[97,544,545,548,550],{"class":99,"line":169},[97,546,547],{"class":137},"useSeoMeta",[97,549,142],{"class":141},[97,551,145],{"class":103},[97,553,554,556,558,560,563,565],{"class":99,"line":218},[97,555,152],{"class":151},[97,557,155],{"class":103},[97,559,311],{"class":121},[97,561,562],{"class":125},"About",[97,564,403],{"class":121},[97,566,166],{"class":103},[97,568,569,572,574,576,579,581],{"class":99,"line":230},[97,570,571],{"class":151},"  description",[97,573,155],{"class":103},[97,575,311],{"class":121},[97,577,578],{"class":125},"My about page",[97,580,403],{"class":121},[97,582,166],{"class":103},[97,584,585,588,590,592,595,597],{"class":99,"line":362},[97,586,587],{"class":151},"  ogDescription",[97,589,155],{"class":103},[97,591,311],{"class":121},[97,593,594],{"class":125},"Still about my about page",[97,596,403],{"class":121},[97,598,166],{"class":103},[97,600,601,604,606,608,610,612],{"class":99,"line":379},[97,602,603],{"class":151},"  ogTitle",[97,605,155],{"class":103},[97,607,311],{"class":121},[97,609,562],{"class":125},[97,611,403],{"class":121},[97,613,166],{"class":103},[97,615,616,619,621,623,626,628],{"class":99,"line":389},[97,617,618],{"class":151},"  ogImage",[97,620,155],{"class":103},[97,622,311],{"class":121},[97,624,625],{"class":125},"https://example.com/image.png",[97,627,403],{"class":121},[97,629,166],{"class":103},[97,631,632,635,637,639,642,644],{"class":99,"line":423},[97,633,634],{"class":151},"  twitterCard",[97,636,155],{"class":103},[97,638,311],{"class":121},[97,640,641],{"class":125},"summary_large_image",[97,643,403],{"class":121},[97,645,166],{"class":103},[97,647,648,650],{"class":99,"line":441},[97,649,221],{"class":103},[97,651,452],{"class":141},[97,653,654,656,658],{"class":99,"line":447},[97,655,233],{"class":103},[97,657,108],{"class":107},[97,659,131],{"class":103},[67,661,663],{"id":662},"usescript",[53,664,665],{},"useScript()",[40,667],{"url":668},"https://unhead.unjs.io/docs/vue/head/api/composables/use-script",[14,670,671,673,674,677,678,680],{},[53,672,665],{},"はサードパーティースクリプト（Google Analytics や 広告タグなど）を",[31,675,676],{},"安全かつ効率的に読み込むため","のコンポーザブルです。",[18,679],{},"\nサードパーティースクリプトのパフォーマンス、セキュリティ、ライフサイクルを管理できます。",[14,682,683],{},"これにより、スクリプトがロードされる前にトラッキング処理を実行してしまうミスや、同一スクリプトを重複して読み込んでしまうパフォーマンス上の問題を防ぐことができます。",[258,685,260],{"id":686},"ユースケース-2",[14,688,689],{},"このようなサードパーティのスクリプトがあるとします",[88,691,696],{"className":692,"code":693,"filename":694,"language":695,"meta":93,"style":93},"language-js shiki shiki-themes material-theme-lighter github-dark-high-contrast github-dark","(function (globalName) {\n  if (!window[globalName]) {\n    const queue = []\n    const api = function (...args) {\n      queue.push(args)\n    }\n\n    api._queue = queue\n    api._ready = false\n    api.init = function () {\n      api._ready = true\n      console.log(`[${globalName}] initialized`)\n      console.log(`[${globalName}] queued calls:`, queue)\n    }\n    api.track = function (eventName, payload) {\n      if (!api._ready) {\n        return queue.push(['track', eventName, payload])\n      }\n      console.log(`[${globalName}] Tracking:`, eventName, payload)\n    }\n\n    window[globalName] = api\n  }\n})('myScript')\n","myScript.js","js",[53,697,698,717,740,753,778,794,799,803,818,833,851,865,896,927,932,960,982,1015,1021,1057,1062,1067,1085,1091],{"__ignoreMap":93},[97,699,700,702,705,708,712,714],{"class":99,"line":100},[97,701,142],{"class":141},[97,703,704],{"class":328},"function",[97,706,707],{"class":103}," (",[97,709,711],{"class":710},"senS2","globalName",[97,713,224],{"class":103},[97,715,716],{"class":103}," {\n",[97,718,719,722,724,727,730,733,735,738],{"class":99,"line":134},[97,720,721],{"class":296},"  if",[97,723,707],{"class":151},[97,725,726],{"class":336},"!",[97,728,729],{"class":141},"window",[97,731,732],{"class":151},"[",[97,734,711],{"class":141},[97,736,737],{"class":151},"]) ",[97,739,145],{"class":103},[97,741,742,745,748,750],{"class":99,"line":148},[97,743,744],{"class":328},"    const",[97,746,747],{"class":332}," queue",[97,749,337],{"class":336},[97,751,752],{"class":151}," []\n",[97,754,755,757,761,763,766,768,771,774,776],{"class":99,"line":169},[97,756,744],{"class":328},[97,758,760],{"class":759},"s8Xov"," api",[97,762,337],{"class":336},[97,764,765],{"class":328}," function",[97,767,707],{"class":103},[97,769,770],{"class":336},"...",[97,772,773],{"class":710},"args",[97,775,224],{"class":103},[97,777,716],{"class":103},[97,779,780,783,785,788,790,792],{"class":99,"line":218},[97,781,782],{"class":141},"      queue",[97,784,371],{"class":103},[97,786,787],{"class":137},"push",[97,789,142],{"class":151},[97,791,773],{"class":141},[97,793,452],{"class":151},[97,795,796],{"class":99,"line":230},[97,797,798],{"class":103},"    }\n",[97,800,801],{"class":99,"line":362},[97,802,323],{"emptyLinePlaceholder":322},[97,804,805,808,810,813,815],{"class":99,"line":379},[97,806,807],{"class":141},"    api",[97,809,371],{"class":103},[97,811,812],{"class":141},"_queue",[97,814,337],{"class":336},[97,816,817],{"class":141}," queue\n",[97,819,820,822,824,827,829],{"class":99,"line":389},[97,821,807],{"class":141},[97,823,371],{"class":103},[97,825,826],{"class":141},"_ready",[97,828,337],{"class":336},[97,830,832],{"class":831},"sBxIE"," false\n",[97,834,835,837,839,842,844,846,849],{"class":99,"line":423},[97,836,807],{"class":141},[97,838,371],{"class":103},[97,840,841],{"class":137},"init",[97,843,337],{"class":336},[97,845,765],{"class":328},[97,847,848],{"class":103}," ()",[97,850,716],{"class":103},[97,852,853,856,858,860,862],{"class":99,"line":441},[97,854,855],{"class":141},"      api",[97,857,371],{"class":103},[97,859,826],{"class":141},[97,861,337],{"class":336},[97,863,864],{"class":831}," true\n",[97,866,867,870,872,875,877,880,882,885,887,889,892,894],{"class":99,"line":447},[97,868,869],{"class":141},"      console",[97,871,371],{"class":103},[97,873,874],{"class":137},"log",[97,876,142],{"class":151},[97,878,879],{"class":121},"`",[97,881,732],{"class":125},[97,883,884],{"class":121},"${",[97,886,711],{"class":141},[97,888,221],{"class":121},[97,890,891],{"class":125},"] initialized",[97,893,879],{"class":121},[97,895,452],{"class":151},[97,897,898,900,902,904,906,908,910,912,914,916,919,921,923,925],{"class":99,"line":455},[97,899,869],{"class":141},[97,901,371],{"class":103},[97,903,874],{"class":137},[97,905,142],{"class":151},[97,907,879],{"class":121},[97,909,732],{"class":125},[97,911,884],{"class":121},[97,913,711],{"class":141},[97,915,221],{"class":121},[97,917,918],{"class":125},"] queued calls:",[97,920,879],{"class":121},[97,922,195],{"class":103},[97,924,747],{"class":141},[97,926,452],{"class":151},[97,928,930],{"class":99,"line":929},14,[97,931,798],{"class":103},[97,933,935,937,939,942,944,946,948,951,953,956,958],{"class":99,"line":934},15,[97,936,807],{"class":141},[97,938,371],{"class":103},[97,940,941],{"class":137},"track",[97,943,337],{"class":336},[97,945,765],{"class":328},[97,947,707],{"class":103},[97,949,950],{"class":710},"eventName",[97,952,195],{"class":103},[97,954,955],{"class":710}," payload",[97,957,224],{"class":103},[97,959,716],{"class":103},[97,961,963,966,968,970,973,975,977,980],{"class":99,"line":962},16,[97,964,965],{"class":296},"      if",[97,967,707],{"class":151},[97,969,726],{"class":336},[97,971,972],{"class":141},"api",[97,974,371],{"class":103},[97,976,826],{"class":141},[97,978,979],{"class":151},") ",[97,981,145],{"class":103},[97,983,985,988,990,992,994,997,999,1001,1003,1005,1008,1010,1012],{"class":99,"line":984},17,[97,986,987],{"class":296},"        return",[97,989,747],{"class":141},[97,991,371],{"class":103},[97,993,787],{"class":137},[97,995,996],{"class":151},"([",[97,998,403],{"class":121},[97,1000,941],{"class":125},[97,1002,403],{"class":121},[97,1004,195],{"class":103},[97,1006,1007],{"class":141}," eventName",[97,1009,195],{"class":103},[97,1011,955],{"class":141},[97,1013,1014],{"class":151},"])\n",[97,1016,1018],{"class":99,"line":1017},18,[97,1019,1020],{"class":103},"      }\n",[97,1022,1024,1026,1028,1030,1032,1034,1036,1038,1040,1042,1045,1047,1049,1051,1053,1055],{"class":99,"line":1023},19,[97,1025,869],{"class":141},[97,1027,371],{"class":103},[97,1029,874],{"class":137},[97,1031,142],{"class":151},[97,1033,879],{"class":121},[97,1035,732],{"class":125},[97,1037,884],{"class":121},[97,1039,711],{"class":141},[97,1041,221],{"class":121},[97,1043,1044],{"class":125},"] Tracking:",[97,1046,879],{"class":121},[97,1048,195],{"class":103},[97,1050,1007],{"class":141},[97,1052,195],{"class":103},[97,1054,955],{"class":141},[97,1056,452],{"class":151},[97,1058,1060],{"class":99,"line":1059},20,[97,1061,798],{"class":103},[97,1063,1065],{"class":99,"line":1064},21,[97,1066,323],{"emptyLinePlaceholder":322},[97,1068,1070,1073,1075,1077,1080,1082],{"class":99,"line":1069},22,[97,1071,1072],{"class":141},"    window",[97,1074,732],{"class":151},[97,1076,711],{"class":141},[97,1078,1079],{"class":151},"] ",[97,1081,118],{"class":336},[97,1083,1084],{"class":141}," api\n",[97,1086,1088],{"class":99,"line":1087},23,[97,1089,1090],{"class":103},"  }\n",[97,1092,1094,1096,1099,1101,1104,1106],{"class":99,"line":1093},24,[97,1095,221],{"class":103},[97,1097,1098],{"class":141},")(",[97,1100,403],{"class":121},[97,1102,1103],{"class":125},"myScript",[97,1105,403],{"class":121},[97,1107,452],{"class":141},[14,1109,1110,1111,1113],{},"よくあるスクリプトです。初期化処理とトラックイベントが用意されています。",[18,1112],{},"\n今回は動作確認のために、初期化処理やトラッキング時に console.log() を出力するようにしています。",[88,1115,1117],{"className":90,"code":1116,"language":92,"meta":93,"style":93},"\u003Cscript setup lang=\"ts\">\nimport { useScript } from '@unhead/vue'\n\nconst { onLoaded } = useScript('/scripts/myscript.js', {\n  trigger: 'client',\n  use: () => window.myScript,\n})\n\n// スクリプトロード後に初期化＆イベントを送る\nonLoaded((api) => {\n  console.log('myscript loaded:', api)\n  api.init()\n  api.track('pageview', { url: '/' })\n})\n\u003C/script>\n",[53,1118,1119,1139,1158,1162,1190,1206,1227,1233,1237,1242,1259,1283,1295,1332,1338],{"__ignoreMap":93},[97,1120,1121,1123,1125,1127,1129,1131,1133,1135,1137],{"class":99,"line":100},[97,1122,104],{"class":103},[97,1124,108],{"class":107},[97,1126,112],{"class":111},[97,1128,115],{"class":111},[97,1130,118],{"class":103},[97,1132,122],{"class":121},[97,1134,126],{"class":125},[97,1136,122],{"class":121},[97,1138,131],{"class":103},[97,1140,1141,1143,1145,1148,1150,1152,1154,1156],{"class":99,"line":134},[97,1142,297],{"class":296},[97,1144,300],{"class":103},[97,1146,1147],{"class":141}," useScript",[97,1149,210],{"class":103},[97,1151,308],{"class":296},[97,1153,311],{"class":121},[97,1155,314],{"class":125},[97,1157,317],{"class":121},[97,1159,1160],{"class":99,"line":148},[97,1161,323],{"emptyLinePlaceholder":322},[97,1163,1164,1166,1168,1171,1173,1175,1177,1179,1181,1184,1186,1188],{"class":99,"line":169},[97,1165,329],{"class":328},[97,1167,300],{"class":103},[97,1169,1170],{"class":332}," onLoaded",[97,1172,210],{"class":103},[97,1174,337],{"class":336},[97,1176,1147],{"class":137},[97,1178,142],{"class":141},[97,1180,403],{"class":121},[97,1182,1183],{"class":125},"/scripts/myscript.js",[97,1185,403],{"class":121},[97,1187,195],{"class":103},[97,1189,716],{"class":103},[97,1191,1192,1195,1197,1199,1202,1204],{"class":99,"line":218},[97,1193,1194],{"class":151},"  trigger",[97,1196,155],{"class":103},[97,1198,311],{"class":121},[97,1200,1201],{"class":125},"client",[97,1203,403],{"class":121},[97,1205,166],{"class":103},[97,1207,1208,1211,1213,1215,1218,1221,1223,1225],{"class":99,"line":230},[97,1209,1210],{"class":137},"  use",[97,1212,155],{"class":103},[97,1214,848],{"class":103},[97,1216,1217],{"class":328}," =>",[97,1219,1220],{"class":141}," window",[97,1222,371],{"class":103},[97,1224,1103],{"class":141},[97,1226,166],{"class":103},[97,1228,1229,1231],{"class":99,"line":362},[97,1230,221],{"class":103},[97,1232,452],{"class":141},[97,1234,1235],{"class":99,"line":379},[97,1236,323],{"emptyLinePlaceholder":322},[97,1238,1239],{"class":99,"line":389},[97,1240,1241],{"class":437},"// スクリプトロード後に初期化＆イベントを送る\n",[97,1243,1244,1247,1249,1251,1253,1255,1257],{"class":99,"line":423},[97,1245,1246],{"class":137},"onLoaded",[97,1248,142],{"class":141},[97,1250,142],{"class":103},[97,1252,972],{"class":710},[97,1254,224],{"class":103},[97,1256,1217],{"class":328},[97,1258,716],{"class":103},[97,1260,1261,1264,1266,1268,1270,1272,1275,1277,1279,1281],{"class":99,"line":441},[97,1262,1263],{"class":141},"  console",[97,1265,371],{"class":103},[97,1267,874],{"class":137},[97,1269,142],{"class":151},[97,1271,403],{"class":121},[97,1273,1274],{"class":125},"myscript loaded:",[97,1276,403],{"class":121},[97,1278,195],{"class":103},[97,1280,760],{"class":141},[97,1282,452],{"class":151},[97,1284,1285,1288,1290,1292],{"class":99,"line":447},[97,1286,1287],{"class":141},"  api",[97,1289,371],{"class":103},[97,1291,841],{"class":137},[97,1293,1294],{"class":151},"()\n",[97,1296,1297,1299,1301,1303,1305,1307,1310,1312,1314,1316,1319,1321,1323,1326,1328,1330],{"class":99,"line":455},[97,1298,1287],{"class":141},[97,1300,371],{"class":103},[97,1302,941],{"class":137},[97,1304,142],{"class":151},[97,1306,403],{"class":121},[97,1308,1309],{"class":125},"pageview",[97,1311,403],{"class":121},[97,1313,195],{"class":103},[97,1315,300],{"class":103},[97,1317,1318],{"class":151}," url",[97,1320,155],{"class":103},[97,1322,311],{"class":121},[97,1324,1325],{"class":125},"/",[97,1327,403],{"class":121},[97,1329,210],{"class":103},[97,1331,452],{"class":151},[97,1333,1334,1336],{"class":99,"line":929},[97,1335,221],{"class":103},[97,1337,452],{"class":141},[97,1339,1340,1342,1344],{"class":99,"line":934},[97,1341,233],{"class":103},[97,1343,108],{"class":107},[97,1345,131],{"class":103},[14,1347,1348],{},"Consoleを確認するとイベントが発火していることがわかります。",[14,1350,1351],{},[1352,1353],"img",{"alt":93,"src":1354},"https://res.cloudinary.com/dyoyv8djx/image/upload/v1746431984/tsukiyama-blog/introduce-unhead/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2025-05-05_16.58.47_et58l8.png",[10,1356,1358],{"id":1357},"nuxt-ではどうなのか","Nuxt ではどうなのか？",[14,1360,1361,1362,1364,1365,1367,1368,1370,1371,1374,1375,1378],{},"Unhead は Nuxt 3 でもデフォルトで採用されており、",[53,1363,72],{}," や ",[53,1366,467],{}," は追加インストールなしでそのまま使えます。",[18,1369],{},"\n(",[53,1372,1373],{},"nuxt v3.16","からは",[53,1376,1377],{},"unhead v2","がデフォルトでサポートされています)",[40,1380],{"url":1381},"https://nuxt.com/blog/v3-16",[14,1383,1384,1386,1387,1389],{},[53,1385,665],{},"は Nuxt 3 にデフォルトでは含まれていません。",[18,1388],{},"\nNuxt Scripts を追加でインストールすることで使用できます。",[40,1391],{"url":1392},"https://scripts.nuxt.com/",[10,1394,1395],{"id":1395},"おわりに",[14,1397,1398,1399,1401,1403],{},"今回は、Unhead の基本的な使い方と主要な Composables について紹介しました。",[18,1400],{},[53,1402,55],{},"の管理は少しの設定ミスでページスピードが遅くなったりセキュリティ上の脅威になるので適切に設定していきたいですね。",[14,1405,1406],{},"次回は、Nuxt Modules の Nuxt Scripts についても紹介したいと思います。",[1408,1409,1410],"style",{},"html pre.shiki code .seLpV, html code.shiki .seLpV{--shiki-light:#39ADB5;--shiki-default:#F0F3F6;--shiki-dark:#E1E4E8}html pre.shiki code .siCa7, html code.shiki .siCa7{--shiki-light:#E53935;--shiki-default:#72F088;--shiki-dark:#85E89D}html pre.shiki code .sOohs, html code.shiki .sOohs{--shiki-light:#9C3EDA;--shiki-default:#91CBFF;--shiki-dark:#B392F0}html pre.shiki code .sPUPB, html code.shiki .sPUPB{--shiki-light:#39ADB5;--shiki-default:#ADDCFF;--shiki-dark:#9ECBFF}html pre.shiki code .sSIes, html code.shiki .sSIes{--shiki-light:#91B859;--shiki-default:#ADDCFF;--shiki-dark:#9ECBFF}html pre.shiki code .s7KVs, html code.shiki .s7KVs{--shiki-light:#6182B8;--shiki-default:#DBB7FF;--shiki-dark:#B392F0}html pre.shiki code .sdyPO, html code.shiki .sdyPO{--shiki-light:#90A4AE;--shiki-default:#F0F3F6;--shiki-dark:#E1E4E8}html pre.shiki code .sLCpo, html code.shiki .sLCpo{--shiki-light:#E53935;--shiki-default:#F0F3F6;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .stP2V, html code.shiki .stP2V{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#FF9492;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sGRfs, html code.shiki .sGRfs{--shiki-light:#9C3EDA;--shiki-default:#FF9492;--shiki-dark:#F97583}html pre.shiki code .sSuNx, html code.shiki .sSuNx{--shiki-light:#90A4AE;--shiki-default:#91CBFF;--shiki-dark:#79B8FF}html pre.shiki code .sUBcA, html code.shiki .sUBcA{--shiki-light:#39ADB5;--shiki-default:#FF9492;--shiki-dark:#F97583}html pre.shiki code .sZPSj, html code.shiki .sZPSj{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#BDC4CC;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .senS2, html code.shiki .senS2{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#FFB757;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .s8Xov, html code.shiki .s8Xov{--shiki-light:#90A4AE;--shiki-default:#DBB7FF;--shiki-dark:#B392F0}html pre.shiki code .sBxIE, html code.shiki .sBxIE{--shiki-light:#FF5370;--shiki-default:#91CBFF;--shiki-dark:#79B8FF}",{"title":93,"searchDepth":148,"depth":148,"links":1412},[1413,1414,1415,1427,1428],{"id":12,"depth":134,"text":12},{"id":37,"depth":134,"text":38},{"id":59,"depth":134,"text":59,"children":1416},[1417,1418,1421,1424],{"id":69,"depth":148,"text":72},{"id":240,"depth":148,"text":243,"children":1419},[1420],{"id":260,"depth":169,"text":260},{"id":464,"depth":148,"text":467,"children":1422},[1423],{"id":494,"depth":169,"text":260},{"id":662,"depth":148,"text":665,"children":1425},[1426],{"id":686,"depth":169,"text":260},{"id":1357,"depth":134,"text":1358},{"id":1395,"depth":134,"text":1395},"2025-05-05T00:00:00.000Z","フレームワーク非依存\u003Chead>管理ライブラリのUnheadの紹介です。","md","/avatar_bwg8e2.webp",{},"https://res.cloudinary.com/dyoyv8djx/image/upload/v1746433649/tsukiyama-blog/introduce-unhead/Frame_6_pwvdwd.png","/tech/introduce-unhead",null,{"title":5,"description":1430},"tech/introduce-unhead",[1440,1441],"JavaScript","UnJs","aDEaGZN8D9dTVyTTp9fKxBwqkygsoGGZIFfniYrJoFw",{"title":1444,"image":1445,"description":1446},"Third-Party Scripts Meets Nuxt DX · Nuxt Scripts","https://scripts.nuxt.com/_og/s/c_Home,title_Nuxt+Scripts,description_Third-Party+Scripts+Meets+Nuxt+DX,headline_scripts.nuxt.com.png","Better performance, privacy, security and DX for third-party scripts.",{"title":1448,"image":1449,"description":1450},"Nuxt 3.16 · Nuxt Blog","https://nuxt.com/assets/blog/v3.16.png","Nuxt 3.16 is out - packed with features and performance improvements",{"title":1452,"image":1453,"description":1454},"useHeadSafe() · Unhead","https://unhead.unjs.io/_og/d/c_Unhead,title_useHeadSafe(),description_~U2FmZWx5IG1hbmFnZSBoZWFkIHRhZ3Mgd2l0aCBYU1MgcHJvdGVjdGlvbiB1c2luZyB1c2VIZWFkU2FmZSgpLiBTYW5pdGl6ZSB1bnRydXN0ZWQgdXNlciBpbnB1dCBmb3IgdGl0bGVzLCBtZXRhIHRhZ3MsIGFuZCBvdGhlciBoZWFkIGVsZW1lbnRzLg,frameworkIcon_i-logos-vue,p_Ii9kb2NzL3Z1ZS9oZWFkL2FwaS9jb21wb3NhYmxlcy91c2UtaGVhZC1zYWZlIg.png","Safely manage head tags with XSS protection using useHeadSafe(). Sanitize untrusted user input for titles, meta tags, and other head elements.",{"title":1456,"image":1457,"description":1458},"useScript() · Unhead","https://unhead.unjs.io/_og/d/c_Unhead,title_useScript(),description_~TG9hZCB0aGlyZC1wYXJ0eSBzY3JpcHRzIHdpdGggdXNlU2NyaXB0KCkuIFNtYXJ0IGRlZmF1bHRzIGZvciBwZXJmb3JtYW5jZSwgbGF6eSBsb2FkaW5nIHRyaWdnZXJzLCBhbmQgQVBJIHByb3h5aW5nIGZvciBhbmFseXRpY3MgYW5kIHdpZGdldHMu,frameworkIcon_i-logos-vue,p_Ii9kb2NzL3Z1ZS9oZWFkL2FwaS9jb21wb3NhYmxlcy91c2Utc2NyaXB0Ig.png","Load third-party scripts with useScript(). Smart defaults for performance, lazy loading triggers, and API proxying for analytics and widgets.",{"title":1460,"image":1461,"description":1462},"Unhead · Full stack \u003Chead> package","https://unhead.unjs.io/_og/d/c_Home,title_~JXNpdGVOYW1lICVzZXBhcmF0b3IgRnVsbCBzdGFjayA8aGVhZD4gcGFja2FnZQ,description_Unhead+is+the+any-framework+document+head+manager+built+for+performance+and+delightful+developer+experience..png","Unhead is the any-framework document head manager built for performance and delightful developer experience.",{"title":1464,"image":1465,"description":1466},"useSeoMeta() · Unhead","https://unhead.unjs.io/_og/d/c_Unhead,title_useSeoMeta(),description_~QWRkIFNFTyBtZXRhIHRhZ3Mgd2l0aCB1c2VTZW9NZXRhKCkuIFR5cGUtc2FmZSBBUEkgZm9yIE9wZW4gR3JhcGgsIFR3aXR0ZXIgY2FyZHMsIGFuZCAxMDArIG1ldGEgdGFncyB3aXRoIGF1dG9tYXRpYyBwcm9wZXJ0eS9uYW1lIGhhbmRsaW5nLg,frameworkIcon_i-logos-vue,p_Ii9kb2NzL3Z1ZS9oZWFkL2FwaS9jb21wb3NhYmxlcy91c2Utc2VvLW1ldGEi.png","Add SEO meta tags with useSeoMeta(). Type-safe API for Open Graph, Twitter cards, and 100+ meta tags with automatic property/name handling.",{"title":1468,"image":1469,"description":1470},"Installing Unhead with Vue · Unhead","https://unhead.unjs.io/_og/d/c_Unhead,title_Installing+Unhead+with+Vue,description_Set+up+Unhead+in+Vue+with+createHead()+and+app.use().+Full+SSR+support+with+transformHtmlTemplate().+Works+with+Vue+3+and+Vite.,frameworkIcon_i-logos-vue,p_Ii9kb2NzL3Z1ZS9oZWFkL2d1aWRlcy9nZXQtc3RhcnRlZC9pbnN0YWxsYXRpb24i.png","Set up Unhead in Vue with createHead() and app.use(). Full SSR support with transformHtmlTemplate(). Works with Vue 3 and Vite.",{"title":1472,"image":1473,"description":1474},"useHead() · Unhead","https://unhead.unjs.io/_og/d/c_Unhead,title_useHead(),description_~TWFuYWdlIGRvY3VtZW50IGhlYWQgdGFncyB3aXRoIHVzZUhlYWQoKS4gU2V0IHRpdGxlcywgbWV0YSB0YWdzLCBzY3JpcHRzLCBhbmQgc3R5bGVzIHdpdGggZnVsbCBUeXBlU2NyaXB0IHN1cHBvcnQgYW5kIHJlYWN0aXZlIHVwZGF0ZXMu,frameworkIcon_i-logos-vue,p_Ii9kb2NzL3Z1ZS9oZWFkL2FwaS9jb21wb3NhYmxlcy91c2UtaGVhZCI.png","Manage document head tags with useHead(). Set titles, meta tags, scripts, and styles with full TypeScript support and reactive updates."]