{"id":1408,"date":"2025-03-24T08:50:42","date_gmt":"2025-03-23T23:50:42","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=1408"},"modified":"2025-03-24T08:50:42","modified_gmt":"2025-03-23T23:50:42","slug":"%e3%80%90%e4%bf%9d%e5%ad%98%e7%89%88%e3%80%91ruby-on-rails%e3%81%a7javascript%e3%82%92%e4%bd%bf%e3%81%84%e3%81%93%e3%81%aa%e3%81%997%e3%81%a4%e3%81%ae%e5%ae%9f%e8%b7%b5%e3%83%86%e3%82%af%e3%83%8b","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=1408","title":{"rendered":"\u3010\u4fdd\u5b58\u7248\u3011Ruby on Rails\u3067JavaScript\u3092\u4f7f\u3044\u3053\u306a\u30597\u3064\u306e\u5b9f\u8df5\u30c6\u30af\u30cb\u30c3\u30af"},"content":{"rendered":"\n<div class=\"toc\"><br \/>\n<b>Warning<\/b>:  Undefined array key \"is_admin\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>116<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_category_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>121<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>128<\/b><br \/>\n    <div id=\"toc_container\" class=\"sgb-toc--bullets js-smooth-scroll\" data-dialog-title=\"\u76ee\u6b21\">\n      <p class=\"toc_title\">\u76ee\u6b21 <\/p>\n      <ul class=\"toc_list\">  <li class=\"first\">    <a href=\"#i-0\">Ruby on Rails\u306b\u304a\u3051\u308bJavaScript\u306e\u57fa\u790e\u77e5\u8b58<\/a>  <\/li>  <li>    <a href=\"#i-1\">Asset Pipeline\u3068Webpacker\u306e\u9055\u3044\u3068\u4f7f\u3044\u5206\u3051<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-2\">Asset Pipeline\uff08Sprockets\uff09\u306e\u7279\u5fb4<\/a>      <\/li>      <li>        <a href=\"#i-3\">Webpacker\u306e\u7279\u5fb4<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-4\">\u5b9f\u8df5\u7684\u306a\u4f7f\u3044\u5206\u3051\u306e\u6307\u91dd<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-5\">JavaScript\u306e\u8aad\u307f\u8fbc\u307f\u30bf\u30a4\u30df\u30f3\u30b0\u3068\u30bf\u30fc\u30dc\u30ea\u30f3\u30af\u30b9\u306e\u95a2\u4fc2<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-6\">Turbolinks\u306e\u52d5\u4f5c\u539f\u7406<\/a>      <\/li>      <li>        <a href=\"#i-7\">\u3088\u304f\u3042\u308b\u554f\u984c\u3068\u89e3\u6c7a\u7b56<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-8\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-9\">\u30e2\u30c0\u30f3\u306aJavaScript\u958b\u767a\u74b0\u5883\u306e\u69cb\u7bc9\u65b9\u6cd5<\/a>  <\/li>  <li>    <a href=\"#i-10\">Webpacker\u3092\u4f7f\u7528\u3057\u305f\u6700\u65b0\u306e\u958b\u767a\u74b0\u5883\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-11\">1. Webpacker\u306e\u521d\u671f\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-12\">2. \u958b\u767a\u74b0\u5883\u306e\u6700\u9069\u5316\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-13\">3. \u958b\u767a\u74b0\u5883\u7279\u6709\u306e\u8a2d\u5b9a<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-14\">4. \u672c\u756a\u74b0\u5883\u5411\u3051\u306e\u6700\u9069\u5316<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-15\">npm\/yarn\u306b\u3088\u308b\u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-16\">1. \u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u306e\u57fa\u672c\u539f\u5247<\/a>      <\/li>      <li>        <a href=\"#i-17\">2. \u52b9\u7387\u7684\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u624b\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-18\">3. \u30d1\u30c3\u30b1\u30fc\u30b8\u4f9d\u5b58\u95a2\u4fc2\u306e\u6700\u9069\u5316<\/a>      <\/li>      <li>        <a href=\"#i-19\">4. \u958b\u767a\u52b9\u7387\u3092\u9ad8\u3081\u308bnpm scripts<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-20\">5. \u63a8\u5968\u3055\u308c\u308b\u30d1\u30c3\u30b1\u30fc\u30b8\u69cb\u6210<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-21\">\u5b9f\u8df5\u7684\u306aJavaScript\u5b9f\u88c5\u30c6\u30af\u30cb\u30c3\u30af<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-22\">\u975e\u540c\u671f\u901a\u4fe1\uff08Ajax\uff09\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u4f7f\u3044\u5206\u3051<\/a>      <\/li>      <li>        <a href=\"#i-23\">\u30a4\u30f3\u30bf\u30e9\u30af\u30c6\u30a3\u30d6\u306aUI\u5b9f\u88c5\u306e\u305f\u3081\u306e\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0<\/a>      <\/li>      <li>        <a href=\"#i-24\">JavaScript\u306b\u3088\u308b\u30d5\u30a9\u30fc\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-25\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30dd\u30a4\u30f3\u30c8<\/a>      <\/li>      <li>        <a href=\"#i-28\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30c7\u30d0\u30c3\u30b0\u624b\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-31\">\u5b9f\u8df5\u7684\u306a\u30b3\u30fc\u30c9\u8a2d\u8a08\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-34\">\u30e2\u30c0\u30f3\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a\u3078\u306e\u767a\u5c55<\/a>      <\/li>    <\/ul>  <\/li><\/ul>\n      <a href=\"#\" class=\"sgb-toc-button js-toc-button\" rel=\"nofollow\" data-open-dialog=\"true\"><i class=\"fa fa-list\"><\/i><span class=\"sgb-toc-button__text\">\u76ee\u6b21\u3078<\/span><\/a>\n    <\/div><\/div><h2 class=\"wp-block-heading\" id=\"i-0\">Ruby on Rails\u306b\u304a\u3051\u308bJavaScript\u306e\u57fa\u790e\u77e5\u8b58<\/h2>\n\n\n\n<p>Ruby on Rails\u3067JavaScript\u3092\u52b9\u679c\u7684\u306b\u6d3b\u7528\u3059\u308b\u305f\u3081\u306b\u306f\u3001Rails\u306eJavaScript\u7ba1\u7406\u306e\u4ed5\u7d44\u307f\u3092\u7406\u89e3\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3059\u3002\u672c\u7ae0\u3067\u306f\u3001Rails\u306b\u304a\u3051\u308bJavaScript\u5b9f\u88c5\u306e\u57fa\u790e\u3068\u306a\u308b\u91cd\u8981\u306a\u6982\u5ff5\u3068\u5b9f\u8df5\u7684\u306a\u4f7f\u3044\u65b9\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-1\">Asset Pipeline\u3068Webpacker\u306e\u9055\u3044\u3068\u4f7f\u3044\u5206\u3051<\/h2>\n\n\n\n<p>Rails\u306b\u304a\u3051\u308bJavaScript\u30a2\u30bb\u30c3\u30c8\u306e\u7ba1\u7406\u65b9\u6cd5\u306b\u306f\u3001\u5f93\u6765\u306eAsset Pipeline\u3068\u30e2\u30c0\u30f3\u306aWebpacker\u306e2\u3064\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u304c\u3042\u308a\u307e\u3059\u3002\u305d\u308c\u305e\u308c\u306e\u7279\u5fb4\u3068\u9069\u5207\u306a\u4f7f\u3044\u5206\u3051\u3092\u898b\u3066\u3044\u304d\u307e\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">Asset Pipeline\uff08Sprockets\uff09\u306e\u7279\u5fb4<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30b7\u30f3\u30d7\u30eb\u306a\u69cb\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8a2d\u5b9a\u304c\u6700\u5c0f\u9650\u3067\u6e08\u3080<\/li>\n\n\n\n<li>\u5f93\u6765\u306eJavaScript\u958b\u767a\u306b\u9069\u3057\u3066\u3044\u308b<\/li>\n\n\n\n<li>\u30a2\u30bb\u30c3\u30c8\u306e\u81ea\u52d5\u7d50\u5408\u30fb\u5727\u7e2e\u304c\u5bb9\u6613<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u4f7f\u7528\u304c\u63a8\u5968\u3055\u308c\u308b\u30b1\u30fc\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30b7\u30f3\u30d7\u30eb\u306aJavaScript\u30b3\u30fc\u30c9\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408<\/li>\n\n\n\n<li>\u30ec\u30ac\u30b7\u30fc\u306aJavaScript\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408<\/li>\n\n\n\n<li>jQuery\u4f9d\u5b58\u306e\u30b3\u30fc\u30c9\u304c\u591a\u3044\u5834\u5408<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/assets.rb\nRails.application.config.assets.precompile += %w( custom.js )\n\n# app\/assets\/javascripts\/application.js\n\/\/= require jquery\n\/\/= require custom\n\/\/= require_tree .<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-3\">Webpacker\u306e\u7279\u5fb4<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e2\u30c0\u30f3\u306a\u958b\u767a\u74b0\u5883<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ES6\u4ee5\u964d\u306e\u65b0\u3057\u3044\u69cb\u6587\u306e\u30b5\u30dd\u30fc\u30c8<\/li>\n\n\n\n<li>npm\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u76f4\u63a5\u5229\u7528\u304c\u53ef\u80fd<\/li>\n\n\n\n<li>\u30e2\u30b8\u30e5\u30fc\u30eb\u30d0\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u67d4\u8edf\u306a\u8a2d\u5b9a<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u4f7f\u7528\u304c\u63a8\u5968\u3055\u308c\u308b\u30b1\u30fc\u30b9<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30e2\u30c0\u30f3\u306aJavaScript\u958b\u767a\u3092\u884c\u3046\u5834\u5408<\/li>\n\n\n\n<li>React\/Vue.js\u306a\u3069\u306e\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408<\/li>\n\n\n\n<li>npm\u7d4c\u7531\u3067\u306e\u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u304c\u5fc5\u8981\u306a\u5834\u5408<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/webpack\/environment.js\nconst { environment } = require('@rails\/webpacker')\nconst webpack = require('webpack')\n\nenvironment.plugins.append('Provide', new webpack.ProvidePlugin({\n  $: 'jquery',\n  jQuery: 'jquery'\n}))\n\nmodule.exports = environment<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-4\">\u5b9f\u8df5\u7684\u306a\u4f7f\u3044\u5206\u3051\u306e\u6307\u91dd<\/h3>\n\n\n<div id=\"id-da9f3214-1d3e-47cb-9c75-305754a1906b\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u8981\u4ef6<\/th><th>\u63a8\u5968\u3055\u308c\u308b\u9078\u629e\u80a2<\/th><th>\u7406\u7531<\/th><\/tr><\/thead><tbody><tr><td>\u30ec\u30ac\u30b7\u30fc\u30b7\u30b9\u30c6\u30e0\u306e\u4fdd\u5b88<\/td><td>Asset Pipeline<\/td><td>\u65e2\u5b58\u30b3\u30fc\u30c9\u3068\u306e\u4e92\u63db\u6027\u7dad\u6301\u304c\u5bb9\u6613<\/td><\/tr><tr><td>\u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8<\/td><td>Webpacker<\/td><td>\u30e2\u30c0\u30f3\u306a\u958b\u767a\u74b0\u5883\u306e\u6069\u6075\u3092\u53d7\u3051\u3089\u308c\u308b<\/td><\/tr><tr><td>\u30cf\u30a4\u30d6\u30ea\u30c3\u30c9\u30a2\u30d7\u30ed\u30fc\u30c1<\/td><td>\u4e21\u65b9\u306e\u4f75\u7528<\/td><td>\u6bb5\u968e\u7684\u306a\u79fb\u884c\u304c\u53ef\u80fd<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\" id=\"i-5\">JavaScript\u306e\u8aad\u307f\u8fbc\u307f\u30bf\u30a4\u30df\u30f3\u30b0\u3068\u30bf\u30fc\u30dc\u30ea\u30f3\u30af\u30b9\u306e\u95a2\u4fc2<\/h2>\n\n\n\n<p>Turbolinks\u306f\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30da\u30fc\u30b8\u9077\u79fb\u3092\u9ad8\u901f\u5316\u3059\u308b\u6a5f\u80fd\u3067\u3059\u304c\u3001JavaScript\u306e\u5b9f\u884c\u30bf\u30a4\u30df\u30f3\u30b0\u306b\u5927\u304d\u306a\u5f71\u97ff\u3092\u4e0e\u3048\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">Turbolinks\u306e\u52d5\u4f5c\u539f\u7406<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30da\u30fc\u30b8\u9077\u79fb\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>HTML\u3092Ajax\u3067\u53d6\u5f97<\/li>\n\n\n\n<li>body\u30bf\u30b0\u306e\u5185\u5bb9\u306e\u307f\u3092\u7f6e\u63db<\/li>\n\n\n\n<li>JavaScript\u306e\u518d\u5b9f\u884c\u304c\u5fc5\u8981<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30a4\u30d9\u30f3\u30c8\u306e\u7a2e\u985e\u3068\u4f7f\u3044\u5206\u3051<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u57fa\u672c\u7684\u306aTurbolinks\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\ndocument.addEventListener('turbolinks:load', () =&gt; {\n  \/\/ \u30da\u30fc\u30b8\u8aad\u307f\u8fbc\u307f\u6642\u306b\u5b9f\u884c\u3057\u305f\u3044\u51e6\u7406\n  initializeComponents();\n});\n\n\/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u304b\u3089\u306e\u5fa9\u5e30\u6642\ndocument.addEventListener('turbolinks:restore', () =&gt; {\n  \/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u3055\u308c\u305f\u30da\u30fc\u30b8\u304c\u8868\u793a\u3055\u308c\u308b\u6642\u306e\u51e6\u7406\n  restoreState();\n});<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-7\">\u3088\u304f\u3042\u308b\u554f\u984c\u3068\u89e3\u6c7a\u7b56<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>jQuery\u306eready\u51e6\u7406\u304c\u52d5\u4f5c\u3057\u306a\u3044<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ NG\n$(document).ready(() =&gt; {\n  \/\/ \u51e6\u7406\u304c\u5b9f\u884c\u3055\u308c\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308b\n});\n\n\/\/ OK\n$(document).on('turbolinks:load', () =&gt; {\n  \/\/ \u78ba\u5b9f\u306b\u5b9f\u884c\u3055\u308c\u308b\n});<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30b5\u30fc\u30c9\u30d1\u30fc\u30c6\u30a3\u30e9\u30a4\u30d6\u30e9\u30ea\u306e\u521d\u671f\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30b0\u30ed\u30fc\u30d0\u30eb\u306a\u521d\u671f\u5316\u51e6\u7406\u306e\u4f8b\ndocument.addEventListener('turbolinks:load', () =&gt; {\n  \/\/ DatePicker\u306e\u521d\u671f\u5316\n  $('.datepicker').datepicker();\n\n  \/\/ \u30c4\u30fc\u30eb\u30c1\u30c3\u30d7\u306e\u521d\u671f\u5316\n  $('[data-toggle=\"tooltip\"]').tooltip();\n});<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-8\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u306e\u9069\u5207\u306a\u7ba1\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30da\u30fc\u30b8\u9077\u79fb\u6642\u306e\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u9632\u6b62<\/li>\n\n\n\n<li>\u91cd\u8907\u521d\u671f\u5316\u306e\u56de\u907f<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u306e\u9069\u5207\u306a\u7ba1\u7406\nconst initializeComponents = () =&gt; {\n  \/\/ \u65e2\u5b58\u306e\u30ea\u30b9\u30ca\u30fc\u3092\u524a\u9664\n  $('.dynamic-content').off('click');\n\n  \/\/ \u65b0\u3057\u3044\u30ea\u30b9\u30ca\u30fc\u3092\u8ffd\u52a0\n  $('.dynamic-content').on('click', handleClick);\n};\n\ndocument.addEventListener('turbolinks:load', initializeComponents);<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u6761\u4ef6\u4ed8\u304d\u5b9f\u884c\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u7279\u5b9a\u306e\u30da\u30fc\u30b8\u3067\u306e\u307f\u5b9f\u884c\ndocument.addEventListener('turbolinks:load', () =&gt; {\n  if (document.getElementById('specific-component')) {\n    initializeSpecificComponent();\n  }\n});<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u57fa\u790e\u77e5\u8b58\u3092\u62bc\u3055\u3048\u308b\u3053\u3068\u3067\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306eJavaScript\u5b9f\u88c5\u306b\u304a\u3051\u308b\u591a\u304f\u306e\u554f\u984c\u3092\u56de\u907f\u3057\u3001\u52b9\u7387\u7684\u306a\u958b\u767a\u3092\u9032\u3081\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u7ae0\u3067\u306f\u3001\u3053\u308c\u3089\u306e\u77e5\u8b58\u3092\u57fa\u306b\u3001\u3088\u308a\u5b9f\u8df5\u7684\u306a\u958b\u767a\u74b0\u5883\u306e\u69cb\u7bc9\u65b9\u6cd5\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-9\">\u30e2\u30c0\u30f3\u306aJavaScript\u958b\u767a\u74b0\u5883\u306e\u69cb\u7bc9\u65b9\u6cd5<\/h2>\n\n\n\n<p>Ruby on Rails\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306b\u304a\u3044\u3066\u3001\u52b9\u7387\u7684\u306aJavaScript\u958b\u767a\u3092\u884c\u3046\u305f\u3081\u306b\u306f\u3001\u9069\u5207\u306a\u958b\u767a\u74b0\u5883\u306e\u69cb\u7bc9\u304c\u4e0d\u53ef\u6b20\u3067\u3059\u3002\u672c\u7ae0\u3067\u306f\u3001Webpacker\u3092\u4e2d\u5fc3\u3068\u3057\u305f\u30e2\u30c0\u30f3\u306a\u958b\u767a\u74b0\u5883\u306e\u69cb\u7bc9\u65b9\u6cd5\u3068\u3001\u52b9\u7387\u7684\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u306e\u624b\u6cd5\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-10\">Webpacker\u3092\u4f7f\u7528\u3057\u305f\u6700\u65b0\u306e\u958b\u767a\u74b0\u5883\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-11\">1. Webpacker\u306e\u521d\u671f\u8a2d\u5b9a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Webpacker\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nbundle add webpacker\n\n# Webpacker\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\nrails webpacker:install\n\n# React\/Vue.js\u3092\u4f7f\u7528\u3059\u308b\u5834\u5408\u306e\u8ffd\u52a0\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nrails webpacker:install:react\n# \u307e\u305f\u306f\nrails webpacker:install:vue<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-12\">2. \u958b\u767a\u74b0\u5883\u306e\u6700\u9069\u5316\u8a2d\u5b9a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/webpack\/environment.js\nconst { environment } = require('@rails\/webpacker')\n\n\/\/ jQuery\u7d71\u5408\u306e\u4f8b\nconst webpack = require('webpack')\nenvironment.plugins.prepend('Provide',\n  new webpack.ProvidePlugin({\n    $: 'jquery',\n    jQuery: 'jquery',\n    'window.jQuery': 'jquery'\n  })\n)\n\n\/\/ CSS\/SASS\u51e6\u7406\u306e\u8a2d\u5b9a\nconst sassLoader = environment.loaders.get('sass')\nconst sassLoaderConfig = sassLoader.use.find(function(element) {\n  return element.loader == 'sass-loader'\n})\nsassLoaderConfig.options.implementation = require('sass')\n\nmodule.exports = environment<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-13\">3. \u958b\u767a\u74b0\u5883\u7279\u6709\u306e\u8a2d\u5b9a<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/webpack\/development.js\nprocess.env.NODE_ENV = process.env.NODE_ENV || 'development'\nconst environment = require('.\/environment')\n\n\/\/ \u30bd\u30fc\u30b9\u30de\u30c3\u30d7\u306e\u8a2d\u5b9a\nenvironment.config.merge({\n  devtool: 'cheap-module-eval-source-map'\n})\n\n\/\/ Hot Module Replacement\u8a2d\u5b9a\nenvironment.config.merge({\n  devServer: {\n    host: 'localhost',\n    port: 3035,\n    hot: true,\n    headers: {\n      'Access-Control-Allow-Origin': '*'\n    }\n  }\n})\n\nmodule.exports = environment.toWebpackConfig()<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">4. \u672c\u756a\u74b0\u5883\u5411\u3051\u306e\u6700\u9069\u5316<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/webpack\/production.js\nprocess.env.NODE_ENV = process.env.NODE_ENV || 'production'\nconst environment = require('.\/environment')\n\n\/\/ \u5727\u7e2e\u8a2d\u5b9a\nenvironment.config.merge({\n  optimization: {\n    minimize: true,\n    splitChunks: {\n      chunks: 'all'\n    }\n  }\n})\n\nmodule.exports = environment.toWebpackConfig()<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-15\">npm\/yarn\u306b\u3088\u308b\u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-16\">1. \u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u306e\u57fa\u672c\u539f\u5247<\/h3>\n\n\n<div id=\"id-c24bba1c-516c-412c-a57a-33f4485684c6\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u539f\u5247<\/th><th>\u8aac\u660e<\/th><th>\u5b9f\u8df5\u65b9\u6cd5<\/th><\/tr><\/thead><tbody><tr><td>\u30d0\u30fc\u30b8\u30e7\u30f3\u56fa\u5b9a<\/td><td>\u4f9d\u5b58\u95a2\u4fc2\u306e\u4e00\u8cab\u6027\u3092\u4fdd\u3064<\/td><td>package.json\u3067\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u6307\u5b9a<\/td><\/tr><tr><td>\u5b9a\u671f\u7684\u306a\u66f4\u65b0<\/td><td>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3068\u6a5f\u80fd\u306e\u6700\u65b0\u5316<\/td><td>\u66f4\u65b0\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u5c0e\u5165<\/td><\/tr><tr><td>\u30ed\u30c3\u30af\u30d5\u30a1\u30a4\u30eb\u306e\u7ba1\u7406<\/td><td>\u74b0\u5883\u9593\u306e\u4e00\u8cab\u6027\u78ba\u4fdd<\/td><td>yarn.lock\u307e\u305f\u306fpackage-lock.json\u306e\u53b3\u683c\u306a\u7ba1\u7406<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">2. \u52b9\u7387\u7684\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u7ba1\u7406\u624b\u6cd5<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u958b\u767a\u4f9d\u5b58\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nyarn add --dev @babel\/core @babel\/preset-env\nyarn add --dev eslint eslint-config-airbnb\n\n# \u672c\u756a\u74b0\u5883\u7528\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\nyarn add @rails\/ujs @rails\/activestorage\nyarn add lodash axios\n\n# \u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u78ba\u8a8d\nyarn outdated\n\n# \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u8106\u5f31\u6027\u306e\u30c1\u30a7\u30c3\u30af\nyarn audit<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-18\">3. \u30d1\u30c3\u30b1\u30fc\u30b8\u4f9d\u5b58\u95a2\u4fc2\u306e\u6700\u9069\u5316<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\n  \"scripts\": {\n    \"lint\": \"eslint app\/javascript\",\n    \"lint:fix\": \"eslint app\/javascript --fix\",\n    \"test\": \"jest\",\n    \"build\": \"webpack --config webpack.config.js\"\n  },\n  \"dependencies\": {\n    \"@rails\/webpacker\": \"5.4.3\",\n    \"axios\": \"^0.24.0\",\n    \"lodash\": \"^4.17.21\"\n  },\n  \"devDependencies\": {\n    \"@babel\/core\": \"^7.16.0\",\n    \"@babel\/preset-env\": \"^7.16.0\",\n    \"eslint\": \"^8.2.0\",\n    \"webpack-dev-server\": \"^4.5.0\"\n  }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-19\">4. \u958b\u767a\u52b9\u7387\u3092\u9ad8\u3081\u308bnpm scripts<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">{\n  \"scripts\": {\n    \"start\": \"webpack-dev-server --config config\/webpack\/development.js\",\n    \"build:dev\": \"webpack --config config\/webpack\/development.js\",\n    \"build:prod\": \"webpack --config config\/webpack\/production.js\",\n    \"analyze\": \"webpack-bundle-analyzer stats.json\",\n    \"clean\": \"rm -rf public\/packs\",\n    \"lint\": \"eslint app\/javascript --ext .js,.jsx\",\n    \"test\": \"jest\",\n    \"test:watch\": \"jest --watch\"\n  }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-20\">5. \u63a8\u5968\u3055\u308c\u308b\u30d1\u30c3\u30b1\u30fc\u30b8\u69cb\u6210<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">app\/javascript\/\n\u251c\u2500\u2500 packs\/\n\u2502   \u251c\u2500\u2500 application.js\n\u2502   \u2514\u2500\u2500 admin.js\n\u251c\u2500\u2500 components\/\n\u2502   \u251c\u2500\u2500 shared\/\n\u2502   \u2514\u2500\u2500 pages\/\n\u251c\u2500\u2500 utils\/\n\u2502   \u251c\u2500\u2500 api.js\n\u2502   \u2514\u2500\u2500 helpers.js\n\u251c\u2500\u2500 styles\/\n\u2502   \u2514\u2500\u2500 application.scss\n\u2514\u2500\u2500 config\/\n    \u2514\u2500\u2500 endpoints.js<\/pre>\n\n\n\n<p>\u4ee5\u4e0a\u306e\u74b0\u5883\u69cb\u7bc9\u306b\u3088\u308a\u3001\u4ee5\u4e0b\u306e\u30e1\u30ea\u30c3\u30c8\u304c\u5f97\u3089\u308c\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30e2\u30c0\u30f3\u306aJavaScript\u6a5f\u80fd\u306e\u6d3b\u7528<\/li>\n\n\n\n<li>\u52b9\u7387\u7684\u306a\u958b\u767a\u30ef\u30fc\u30af\u30d5\u30ed\u30fc<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u54c1\u8cea\u306e\u7dad\u6301<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306e\u6700\u9069\u5316<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u5411\u4e0a<\/li>\n<\/ol>\n\n\n\n<p>\u6b21\u7ae0\u3067\u306f\u3001\u3053\u306e\u74b0\u5883\u3092\u6d3b\u7528\u3057\u305f\u5b9f\u8df5\u7684\u306aJavaScript\u5b9f\u88c5\u30c6\u30af\u30cb\u30c3\u30af\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-21\">\u5b9f\u8df5\u7684\u306aJavaScript\u5b9f\u88c5\u30c6\u30af\u30cb\u30c3\u30af<\/h2>\n\n\n\n<p>Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306eJavaScript\u5b9f\u88c5\u306b\u304a\u3044\u3066\u3001\u7279\u306b\u91cd\u8981\u306a3\u3064\u306e\u30c6\u30af\u30cb\u30c3\u30af\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-22\">\u975e\u540c\u671f\u901a\u4fe1\uff08Ajax\uff09\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3068\u4f7f\u3044\u5206\u3051<\/h3>\n\n\n\n<p>\u975e\u540c\u671f\u901a\u4fe1\u306e\u5b9f\u88c5\u306b\u306f\u3001\u4e3b\u306b\u4ee5\u4e0b\u306e3\u3064\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u304c\u3042\u308a\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Rails UJS\u306b\u3088\u308b\u5b9f\u88c5<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30ea\u30e2\u30fc\u30c8\u30d5\u30a9\u30fc\u30e0\u306e\u4f5c\u6210\n&lt;%= form_with(model: @article, remote: true) do |f| %&gt;\n  &lt;%= f.text_field :title %&gt;\n  &lt;%= f.submit %&gt;\n&lt;% end %&gt;\n\n\/\/ \u30ec\u30b9\u30dd\u30f3\u30b9\u51e6\u7406\n$(document).on('ajax:success', 'form', function(event) {\n  const [data, status, xhr] = event.detail;\n  \/\/ \u6210\u529f\u6642\u306e\u51e6\u7406\n  showNotification('\u4fdd\u5b58\u3057\u307e\u3057\u305f');\n});<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>Fetch API\u306b\u3088\u308b\u5b9f\u88c5<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ API\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u5b9f\u88c5\nconst api = {\n  async getArticles() {\n    const response = await fetch('\/api\/articles', {\n      headers: {\n        'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n      }\n    });\n    return response.json();\n  },\n\n  async createArticle(data) {\n    const response = await fetch('\/api\/articles', {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application\/json',\n        'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n      },\n      body: JSON.stringify(data)\n    });\n    return response.json();\n  }\n};<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>Axios\u306b\u3088\u308b\u5b9f\u88c5<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ Axios\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7\nconst axios = require('axios');\naxios.defaults.headers.common['X-CSRF-Token'] = document.querySelector('[name=\"csrf-token\"]').content;\n\nconst api = {\n  getArticles: () =&gt; axios.get('\/api\/articles'),\n  createArticle: (data) =&gt; axios.post('\/api\/articles', data)\n};<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-23\">\u30a4\u30f3\u30bf\u30e9\u30af\u30c6\u30a3\u30d6\u306aUI\u5b9f\u88c5\u306e\u305f\u3081\u306e\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30a4\u30d9\u30f3\u30c8\u30c7\u30ea\u30b2\u30fc\u30b7\u30e7\u30f3<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class UIManager {\n  constructor() {\n    this.bindEvents();\n  }\n\n  bindEvents() {\n    document.addEventListener('click', (e) =&gt; {\n      if (e.target.matches('.delete-button')) {\n        this.handleDelete(e);\n      }\n    });\n  }\n\n  handleDelete(event) {\n    event.preventDefault();\n    const id = event.target.dataset.id;\n    if (confirm('\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b\uff1f')) {\n      api.deleteArticle(id);\n    }\n  }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>Turbolinks\u3068\u306e\u9023\u643a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u306e\u521d\u671f\u5316\nclass Component {\n  constructor() {\n    this.initialize();\n  }\n\n  initialize() {\n    \/\/ Turbolinks\u306e\u30da\u30fc\u30b8\u9077\u79fb\u5f8c\u306b\u5b9f\u884c\n    document.addEventListener('turbolinks:load', () =&gt; {\n      this.attachHandlers();\n    });\n\n    \/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u304b\u3089\u306e\u5fa9\u5e30\u6642\u306b\u5b9f\u884c\n    document.addEventListener('turbolinks:restore', () =&gt; {\n      this.restoreState();\n    });\n  }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-24\">JavaScript\u306b\u3088\u308b\u30d5\u30a9\u30fc\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u30b5\u30a4\u30c9\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class FormValidator {\n  constructor(form) {\n    this.form = form;\n    this.initialize();\n  }\n\n  initialize() {\n    this.form.addEventListener('submit', this.handleSubmit.bind(this));\n    this.attachFieldValidators();\n  }\n\n  attachFieldValidators() {\n    const inputs = this.form.querySelectorAll('input, select');\n    inputs.forEach(input =&gt; {\n      input.addEventListener('blur', () =&gt; this.validateField(input));\n    });\n  }\n\n  validateField(field) {\n    const value = field.value.trim();\n    const rules = {\n      required: () =&gt; value !== '',\n      email: () =&gt; \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(value),\n      minLength: (min) =&gt; value.length &gt;= min\n    };\n\n    \/\/ \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u5b9f\u884c\n    let isValid = true;\n    if (field.required &amp;&amp; !rules.required()) {\n      this.showError(field, '\u5fc5\u9808\u9805\u76ee\u3067\u3059');\n      isValid = false;\n    }\n\n    if (field.type === 'email' &amp;&amp; !rules.email()) {\n      this.showError(field, '\u6709\u52b9\u306a\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044');\n      isValid = false;\n    }\n\n    if (isValid) {\n      this.clearError(field);\n    }\n  }\n\n  showError(field, message) {\n    field.classList.add('is-invalid');\n    const errorDiv = field.nextElementSibling || document.createElement('div');\n    errorDiv.className = 'invalid-feedback';\n    errorDiv.textContent = message;\n    if (!field.nextElementSibling) {\n      field.parentNode.insertBefore(errorDiv, field.nextSibling);\n    }\n  }\n\n  clearError(field) {\n    field.classList.remove('is-invalid');\n    const errorDiv = field.nextElementSibling;\n    if (errorDiv?.className === 'invalid-feedback') {\n      errorDiv.remove();\n    }\n  }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u30b5\u30fc\u30d0\u30fc\u30b5\u30a4\u30c9\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3068\u306e\u9023\u643a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30d5\u30a9\u30fc\u30e0\u9001\u4fe1\u306e\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\nconst form = document.querySelector('#article-form');\nform.addEventListener('submit', async (e) =&gt; {\n  e.preventDefault();\n  const formData = new FormData(form);\n\n  try {\n    const response = await api.createArticle(Object.fromEntries(formData));\n    if (response.status === 422) {\n      \/\/ \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30a8\u30e9\u30fc\u306e\u51e6\u7406\n      handleValidationErrors(response.data.errors);\n    } else {\n      \/\/ \u6210\u529f\u6642\u306e\u51e6\u7406\n      window.location.href = response.data.redirect_to;\n    }\n  } catch (error) {\n    console.error('Error:', error);\n    showErrorMessage('\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f');\n  }\n});<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u52b9\u679c\u304c\u5f97\u3089\u308c\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u3088\u308a\u826f\u3044\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u306e\u63d0\u4f9b<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u306e\u65e9\u671f\u767a\u898b\u3068\u9069\u5207\u306a\u51e6\u7406<\/li>\n\n\n\n<li>\u30b3\u30fc\u30c9\u306e\u4fdd\u5b88\u6027\u3068\u518d\u5229\u7528\u6027\u306e\u5411\u4e0a<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306e\u6700\u9069\u5316<\/li>\n<\/ul>\n\n\n\n<p>\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u300c\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30dd\u30a4\u30f3\u30c8\u300d\u306b\u9032\u3093\u3067\u3082\u3088\u308d\u3057\u3044\u3067\u3057\u3087\u3046\u304b\uff1f<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-25\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30dd\u30a4\u30f3\u30c8<\/h3>\n\n\n\n<p>Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306eJavaScript\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u6700\u9069\u5316\u3059\u308b\u305f\u3081\u306e\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-26\">JavaScript\u306e\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\u306b\u3088\u308b\u8868\u793a\u901f\u5ea6\u306e\u6539\u5584<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u7684\u306a\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/packs\/application.js\n\/\/ \u5fc5\u8981\u6700\u5c0f\u9650\u306e\u521d\u671f\u5316\u51e6\u7406\ndocument.addEventListener('DOMContentLoaded', () =&gt; {\n  \/\/ \u30af\u30ea\u30c6\u30a3\u30ab\u30eb\u30d1\u30b9\u306e\u51e6\u7406\u306e\u307f\u3092\u5b9f\u884c\n  initializeCriticalFeatures();\n});\n\n\/\/ \u9045\u5ef6\u8aad\u307f\u8fbc\u307f\u306e\u5b9f\u88c5\nconst loadModule = async (moduleName) =&gt; {\n  try {\n    const module = await import(`..\/modules\/${moduleName}`);\n    return module.default;\n  } catch (error) {\n    console.error(`Failed to load module: ${moduleName}`, error);\n  }\n};\n\n\/\/ \u5fc5\u8981\u306a\u30bf\u30a4\u30df\u30f3\u30b0\u3067\u30e2\u30b8\u30e5\u30fc\u30eb\u3092\u8aad\u307f\u8fbc\u3080\ndocument.querySelector('#editor').addEventListener('click', async () =&gt; {\n  const Editor = await loadModule('rich-text-editor');\n  new Editor('#editor');\n});<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u6761\u4ef6\u4ed8\u304d\u8aad\u307f\u8fbc\u307f\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u7279\u5b9a\u306e\u8981\u7d20\u304c\u5b58\u5728\u3059\u308b\u5834\u5408\u306e\u307f\u8aad\u307f\u8fbc\u307f\nif (document.querySelector('#chart-container')) {\n  import('..\/modules\/chart').then(module =&gt; {\n    const Chart = module.default;\n    new Chart('#chart-container').render();\n  });\n}\n\n\/\/ Intersection Observer\u3092\u4f7f\u7528\u3057\u305f\u9045\u5ef6\u8aad\u307f\u8fbc\u307f\nconst observer = new IntersectionObserver((entries) =&gt; {\n  entries.forEach(entry =&gt; {\n    if (entry.isIntersecting) {\n      loadModule('lazy-component').then(module =&gt; {\n        module.initialize(entry.target);\n      });\n      observer.unobserve(entry.target);\n    }\n  });\n});\n\ndocument.querySelectorAll('.lazy-load').forEach(el =&gt; observer.observe(el));<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-27\">\u30d0\u30f3\u30c9\u30eb\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30b3\u30fc\u30c9\u5206\u5272\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ webpack.config.js\nmodule.exports = {\n  optimization: {\n    splitChunks: {\n      chunks: 'all',\n      maxInitialRequests: 25,\n      minSize: 20000,\n      cacheGroups: {\n        vendor: {\n          test: \/[\\\\\/]node_modules[\\\\\/]\/,\n          name(module) {\n            const packageName = module.context.match(\n              \/[\\\\\/]node_modules[\\\\\/](.*?)([\\\\\/]|$)\/\n            )[1];\n            return `vendor.${packageName.replace('@', '')}`;\n          },\n        },\n      },\n    },\n  },\n};<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u91cd\u8981\u306a\u6700\u9069\u5316\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/webpack\/production.js\nconst environment = require('.\/environment');\nconst TerserPlugin = require('terser-webpack-plugin');\n\nenvironment.config.merge({\n  optimization: {\n    minimize: true,\n    minimizer: [\n      new TerserPlugin({\n        terserOptions: {\n          parse: {\n            ecma: 8,\n          },\n          compress: {\n            ecma: 5,\n            warnings: false,\n            comparisons: false,\n            inline: 2,\n          },\n          mangle: {\n            safari10: true,\n          },\n          output: {\n            ecma: 5,\n            comments: false,\n            ascii_only: true,\n          },\n        },\n        parallel: true,\n        cache: true,\n      }),\n    ],\n  },\n});<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u5b9f\u8df5\u7684\u306a\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/strong><\/li>\n<\/ol>\n\n\n\n<p>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n<div id=\"id-c61f7d55-e6a5-4070-a109-3d4f22fede44\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6700\u9069\u5316\u9805\u76ee<\/th><th>\u5b9f\u88c5\u65b9\u6cd5<\/th><th>\u671f\u5f85\u3055\u308c\u308b\u52b9\u679c<\/th><\/tr><\/thead><tbody><tr><td>Tree Shaking<\/td><td>ES6\u30e2\u30b8\u30e5\u30fc\u30eb\u69cb\u6587\u306e\u4f7f\u7528<\/td><td>\u672a\u4f7f\u7528\u30b3\u30fc\u30c9\u306e\u524a\u9664<\/td><\/tr><tr><td>\u30b3\u30fc\u30c9\u5206\u5272<\/td><td>Dynamic import\u306e\u6d3b\u7528<\/td><td>\u521d\u671f\u30ed\u30fc\u30c9\u6642\u9593\u306e\u77ed\u7e2e<\/td><\/tr><tr><td>\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565<\/td><td>\u30d5\u30a1\u30a4\u30eb\u540d\u3078\u306e\u30cf\u30c3\u30b7\u30e5\u4ed8\u4e0e<\/td><td>\u30d6\u30e9\u30a6\u30b6\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u6700\u9069\u5316<\/td><\/tr><tr><td>\u5727\u7e2e<\/td><td>Gzip\/Brotli\u5727\u7e2e\u306e\u6709\u52b9\u5316<\/td><td>\u8ee2\u9001\u30b5\u30a4\u30ba\u306e\u524a\u6e1b<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u306e\u5b9f\u88c5\nconst performanceMonitor = {\n  measureLoadTime(componentName) {\n    const startTime = performance.now();\n    return {\n      end() {\n        const duration = performance.now() - startTime;\n        console.log(`${componentName} loaded in ${duration}ms`);\n      }\n    };\n  },\n\n  trackBundleSize() {\n    const resources = window.performance.getEntriesByType('resource');\n    resources\n      .filter(r =&gt; r.name.includes('.js'))\n      .forEach(r =&gt; {\n        console.log(`${r.name}: ${r.encodedBodySize \/ 1024}KB`);\n      });\n  }\n};\n\n\/\/ \u4f7f\u7528\u4f8b\nconst monitor = performanceMonitor.measureLoadTime('MainComponent');\n\/\/ \u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u306e\u521d\u671f\u5316\nmonitor.end();<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af\u3092\u9069\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u52b9\u679c\u304c\u671f\u5f85\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u521d\u671f\u8aad\u307f\u8fbc\u307f\u6642\u9593\u306e\u77ed\u7e2e<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u306e\u5411\u4e0a<\/li>\n\n\n\n<li>\u30b5\u30fc\u30d0\u30fc\u30ea\u30bd\u30fc\u30b9\u306e\u52b9\u7387\u7684\u306a\u5229\u7528<\/li>\n\n\n\n<li>\u30e2\u30d0\u30a4\u30eb\u30c7\u30d0\u30a4\u30b9\u3067\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6539\u5584<\/li>\n<\/ul>\n\n\n\n<p>\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u300c\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30c7\u30d0\u30c3\u30b0\u624b\u6cd5\u300d\u306b\u9032\u3093\u3067\u3082\u3088\u308d\u3057\u3044\u3067\u3057\u3087\u3046\u304b\uff1f<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-28\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30c7\u30d0\u30c3\u30b0\u624b\u6cd5<\/h3>\n\n\n\n<p>Rails\u74b0\u5883\u3067\u306eJavaScript\u5b9f\u88c5\u306b\u304a\u3051\u308b\u91cd\u8981\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30c7\u30d0\u30c3\u30b0\u624b\u6cd5\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-29\">\u30af\u30ed\u30b9\u30b5\u30a4\u30c8\u30b9\u30af\u30ea\u30d7\u30c6\u30a3\u30f3\u30b0\u5bfe\u7b56\u306e\u5b9f\u88c5<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u7684\u306aXSS\u5bfe\u7b56<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30a8\u30b9\u30b1\u30fc\u30d7\u51e6\u7406\u306e\u5b9f\u88c5\nconst escapeHTML = (str) =&gt; {\n  const div = document.createElement('div');\n  div.textContent = str;\n  return div.innerHTML;\n};\n\n\/\/ DOMPurify\u3092\u4f7f\u7528\u3057\u305f\u5b89\u5168\u306aHTML\u633f\u5165\nimport DOMPurify from 'dompurify';\n\nfunction setContent(element, content) {\n  element.innerHTML = DOMPurify.sanitize(content, {\n    ALLOWED_TAGS: ['p', 'a', 'b', 'i'],\n    ALLOWED_ATTR: ['href']\n  });\n}\n\n\/\/ CSRF\u30c8\u30fc\u30af\u30f3\u306e\u81ea\u52d5\u4ed8\u4e0e\nconst setupCSRFProtection = () =&gt; {\n  const token = document.querySelector('meta[name=\"csrf-token\"]').content;\n  axios.defaults.headers.common['X-CSRF-Token'] = token;\n};<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Content Security Policy (CSP)\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/initializers\/content_security_policy.rb\nRails.application.config.content_security_policy do |policy|\n  policy.default_src :self\n  policy.script_src  :self, :https\n  policy.style_src   :self, :https\n  policy.connect_src :self, :https\n  policy.img_src     :self, :data, :https\nend\n\n\/\/ nonce\u4ed8\u304d\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u5b9f\u88c5\ndocument.addEventListener('DOMContentLoaded', () =&gt; {\n  const script = document.createElement('script');\n  script.nonce = '&lt;%= request.content_security_policy_nonce %&gt;';\n  script.textContent = 'console.log(\"\u5b89\u5168\u306b\u5b9f\u884c\u3055\u308c\u307e\u3057\u305f\");';\n  document.head.appendChild(script);\n});<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-30\">\u958b\u767a\u8005\u30c4\u30fc\u30eb\u3092\u4f7f\u7528\u3057\u305f\u30c7\u30d0\u30c3\u30b0\u30c6\u30af\u30cb\u30c3\u30af<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u52b9\u7387\u7684\u306a\u30c7\u30d0\u30c3\u30b0\u74b0\u5883\u306e\u69cb\u7bc9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30c7\u30d0\u30c3\u30b0\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u306e\u5b9f\u88c5\nconst Debug = {\n  \/\/ \u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u72b6\u614b\u76e3\u8996\n  observe(obj, property) {\n    let value = obj[property];\n    Object.defineProperty(obj, property, {\n      get() {\n        console.log(`${property}\u304c\u53c2\u7167\u3055\u308c\u307e\u3057\u305f`);\n        return value;\n      },\n      set(newValue) {\n        console.log(`${property}\u304c${newValue}\u306b\u5909\u66f4\u3055\u308c\u307e\u3057\u305f`);\n        value = newValue;\n      }\n    });\n  },\n\n  \/\/ \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6e2c\u5b9a\n  measureTime(name, fn) {\n    console.time(name);\n    const result = fn();\n    console.timeEnd(name);\n    return result;\n  },\n\n  \/\/ \u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u306e\u76e3\u8996\n  trackEvents(element) {\n    const original = element.addEventListener;\n    element.addEventListener = function(type, fn, ...args) {\n      console.log(`\u30a4\u30d9\u30f3\u30c8\u30ea\u30b9\u30ca\u30fc\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f: ${type}`);\n      return original.call(this, type, fn, ...args);\n    };\n  }\n};<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Chrome DevTools\u3092\u6d3b\u7528\u3057\u305f\u30c7\u30d0\u30c3\u30b0\u624b\u6cd5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30c7\u30d0\u30c3\u30b0\u30dd\u30a4\u30f3\u30c8\u306e\u8a2d\u7f6e\nclass ComponentDebugger {\n  constructor(componentName) {\n    this.componentName = componentName;\n    this.debugPoints = new Set();\n  }\n\n  addDebugPoint(methodName, callback) {\n    this.debugPoints.add({\n      name: methodName,\n      callback\n    });\n    console.debug(`\u30c7\u30d0\u30c3\u30b0\u30dd\u30a4\u30f3\u30c8\u304c\u8ffd\u52a0\u3055\u308c\u307e\u3057\u305f: ${methodName}`);\n  }\n\n  logState(state) {\n    console.groupCollapsed(`${this.componentName} State`);\n    console.table(state);\n    console.groupEnd();\n  }\n}\n\n\/\/ \u4f7f\u7528\u4f8b\nconst debugger = new ComponentDebugger('UserForm');\ndebugger.addDebugPoint('submit', (formData) =&gt; {\n  console.log('\u30d5\u30a9\u30fc\u30e0\u30c7\u30fc\u30bf:', formData);\n});<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u5b9f\u8df5\u7684\u306a\u30c7\u30d0\u30c3\u30b0\u30c6\u30af\u30cb\u30c3\u30af<\/strong><\/li>\n<\/ol>\n\n\n\n<p>\u30c7\u30d0\u30c3\u30b0\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n<div id=\"id-7984bfb2-8ff0-4fbc-af89-fc0342de772a\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u624b\u6cd5<\/th><th>\u7528\u9014<\/th><th>\u30b3\u30de\u30f3\u30c9\/\u30c4\u30fc\u30eb<\/th><\/tr><\/thead><tbody><tr><td>\u30d6\u30ec\u30fc\u30af\u30dd\u30a4\u30f3\u30c8<\/td><td>\u30b3\u30fc\u30c9\u306e\u5b9f\u884c\u30d5\u30ed\u30fc\u78ba\u8a8d<\/td><td>debugger\u6587<\/td><\/tr><tr><td>\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u76e3\u8996<\/td><td>API\u901a\u4fe1\u306e\u78ba\u8a8d<\/td><td>Network \u30d1\u30cd\u30eb<\/td><\/tr><tr><td>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5206\u6790<\/td><td>\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306e\u7279\u5b9a<\/td><td>Performance \u30d1\u30cd\u30eb<\/td><\/tr><tr><td>\u30e1\u30e2\u30ea\u30ea\u30fc\u30af\u691c\u51fa<\/td><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u76e3\u8996<\/td><td>Memory \u30d1\u30cd\u30eb<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30c7\u30d0\u30c3\u30b0\u30e2\u30fc\u30c9\u306e\u5b9f\u88c5\nconst DEBUG_MODE = process.env.NODE_ENV !== 'production';\n\nclass DebugLogger {\n  static log(...args) {\n    if (DEBUG_MODE) {\n      console.log('[Debug]', ...args);\n    }\n  }\n\n  static error(...args) {\n    if (DEBUG_MODE) {\n      console.error('[Error]', ...args);\n      console.trace();\n    }\n  }\n}<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3068\u30c7\u30d0\u30c3\u30b0\u624b\u6cd5\u3092\u9069\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u52b9\u679c\u304c\u671f\u5f85\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30ea\u30b9\u30af\u306e\u4f4e\u6e1b<\/li>\n\n\n\n<li>\u52b9\u7387\u7684\u306a\u30d0\u30b0\u691c\u51fa\u3068\u4fee\u6b63<\/li>\n\n\n\n<li>\u958b\u767a\u751f\u7523\u6027\u306e\u5411\u4e0a<\/li>\n\n\n\n<li>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u54c1\u8cea\u5411\u4e0a<\/li>\n<\/ul>\n\n\n\n<p>\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u300c\u5b9f\u8df5\u7684\u306a\u30b3\u30fc\u30c9\u8a2d\u8a08\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u300d\u306b\u9032\u3093\u3067\u3082\u3088\u308d\u3057\u3044\u3067\u3057\u3087\u3046\u304b\uff1f<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-31\">\u5b9f\u8df5\u7684\u306a\u30b3\u30fc\u30c9\u8a2d\u8a08\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0<\/h3>\n\n\n\n<p>Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u304a\u3051\u308bJavaScript\u30b3\u30fc\u30c9\u306e\u8a2d\u8a08\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u306b\u3064\u3044\u3066\u3001\u5b9f\u8df5\u7684\u306a\u30a2\u30d7\u30ed\u30fc\u30c1\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-32\">JavaScript\u30e2\u30b8\u30e5\u30fc\u30eb\u306e\u9069\u5207\u306a\u5206\u5272\u65b9\u6cd5<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e2\u30b8\u30e5\u30fc\u30eb\u8a2d\u8a08\u306e\u57fa\u672c\u539f\u5247<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/modules\/user_management.js\nexport class UserManager {\n  constructor() {\n    this.cache = new Map();\n  }\n\n  async fetchUser(id) {\n    if (this.cache.has(id)) {\n      return this.cache.get(id);\n    }\n\n    const response = await fetch(`\/api\/users\/${id}`);\n    const user = await response.json();\n    this.cache.set(id, user);\n    return user;\n  }\n}\n\n\/\/ app\/javascript\/modules\/notification.js\nexport class NotificationManager {\n  constructor() {\n    this.notifications = [];\n  }\n\n  show(message, type = 'info') {\n    const notification = {\n      id: Date.now(),\n      message,\n      type\n    };\n    this.notifications.push(notification);\n    this.render(notification);\n  }\n\n  render(notification) {\n    const element = this.createNotificationElement(notification);\n    document.body.appendChild(element);\n    setTimeout(() =&gt; element.remove(), 3000);\n  }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u4f9d\u5b58\u95a2\u4fc2\u306e\u7ba1\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/services\/api_client.js\nexport class APIClient {\n  constructor(baseURL) {\n    this.baseURL = baseURL;\n    this.headers = {\n      'Content-Type': 'application\/json',\n      'X-CSRF-Token': document.querySelector('[name=\"csrf-token\"]').content\n    };\n  }\n\n  async get(path) {\n    const response = await fetch(`${this.baseURL}${path}`, {\n      headers: this.headers\n    });\n    return this.handleResponse(response);\n  }\n\n  async post(path, data) {\n    const response = await fetch(`${this.baseURL}${path}`, {\n      method: 'POST',\n      headers: this.headers,\n      body: JSON.stringify(data)\n    });\n    return this.handleResponse(response);\n  }\n}\n\n\/\/ app\/javascript\/repositories\/user_repository.js\nexport class UserRepository {\n  constructor(apiClient) {\n    this.apiClient = apiClient;\n  }\n\n  async find(id) {\n    return this.apiClient.get(`\/users\/${id}`);\n  }\n\n  async update(id, data) {\n    return this.apiClient.post(`\/users\/${id}`, data);\n  }\n}<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-33\">\u30c6\u30b9\u30bf\u30d6\u30eb\u306aJavaScript\u30b3\u30fc\u30c9\u306e\u66f8\u304d\u65b9<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30c6\u30b9\u30c8\u5bb9\u6613\u6027\u3092\u8003\u616e\u3057\u305f\u8a2d\u8a08<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/services\/form_validator.js\nexport class FormValidator {\n  constructor(rules) {\n    this.rules = rules;\n  }\n\n  validate(data) {\n    const errors = {};\n\n    Object.entries(this.rules).forEach(([field, fieldRules]) =&gt; {\n      const value = data[field];\n      const fieldErrors = [];\n\n      fieldRules.forEach(rule =&gt; {\n        if (!rule.test(value)) {\n          fieldErrors.push(rule.message);\n        }\n      });\n\n      if (fieldErrors.length &gt; 0) {\n        errors[field] = fieldErrors;\n      }\n    });\n\n    return {\n      isValid: Object.keys(errors).length === 0,\n      errors\n    };\n  }\n}\n\n\/\/ \u30c6\u30b9\u30c8\u30b3\u30fc\u30c9\u4f8b\ndescribe('FormValidator', () =&gt; {\n  const rules = {\n    email: [\n      {\n        test: value =&gt; \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(value),\n        message: '\u6709\u52b9\u306a\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044'\n      }\n    ]\n  };\n\n  const validator = new FormValidator(rules);\n\n  test('validates email format', () =&gt; {\n    const result = validator.validate({ email: 'invalid-email' });\n    expect(result.isValid).toBe(false);\n    expect(result.errors.email).toBeDefined();\n  });\n});<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30e2\u30c3\u30af\u5316\u53ef\u80fd\u306a\u4f9d\u5b58\u95a2\u4fc2<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/services\/data_service.js\nexport class DataService {\n  constructor(repository, cache) {\n    this.repository = repository;\n    this.cache = cache;\n  }\n\n  async getData(id) {\n    const cachedData = this.cache.get(id);\n    if (cachedData) {\n      return cachedData;\n    }\n\n    const data = await this.repository.find(id);\n    this.cache.set(id, data);\n    return data;\n  }\n}\n\n\/\/ \u30c6\u30b9\u30c8\u30b3\u30fc\u30c9\u4f8b\ndescribe('DataService', () =&gt; {\n  const mockRepository = {\n    find: jest.fn()\n  };\n\n  const mockCache = {\n    get: jest.fn(),\n    set: jest.fn()\n  };\n\n  const service = new DataService(mockRepository, mockCache);\n\n  test('returns cached data when available', async () =&gt; {\n    const cachedData = { id: 1, name: 'Test' };\n    mockCache.get.mockReturnValue(cachedData);\n\n    const result = await service.getData(1);\n    expect(result).toBe(cachedData);\n    expect(mockRepository.find).not.toHaveBeenCalled();\n  });\n});<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u8a2d\u8a08\u539f\u5247\u3068\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u624b\u6cd5\u3092\u9069\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u52b9\u679c\u304c\u671f\u5f85\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30b3\u30fc\u30c9\u306e\u4fdd\u5b88\u6027\u5411\u4e0a<\/li>\n\n\n\n<li>\u30c6\u30b9\u30c8\u306e\u5bb9\u6613\u6027\u5411\u4e0a<\/li>\n\n\n\n<li>\u30d0\u30b0\u306e\u65e9\u671f\u767a\u898b<\/li>\n\n\n\n<li>\u958b\u767a\u52b9\u7387\u306e\u6539\u5584<\/li>\n\n\n\n<li>\u30c1\u30fc\u30e0\u958b\u767a\u306e\u5186\u6ed1\u5316<\/li>\n<\/ul>\n\n\n\n<p>\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u300c\u30e2\u30c0\u30f3\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a\u3078\u306e\u767a\u5c55\u300d\u306b\u9032\u3093\u3067\u3082\u3088\u308d\u3057\u3044\u3067\u3057\u3087\u3046\u304b\uff1f<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-34\">\u30e2\u30c0\u30f3\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a\u3078\u306e\u767a\u5c55<\/h3>\n\n\n\n<p>Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u30e2\u30c0\u30f3\u306a\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u958b\u767a\u624b\u6cd5\u3078\u3068\u9032\u5316\u3055\u305b\u308b\u305f\u3081\u306e\u624b\u9806\u3068\u5b9f\u8df5\u4f8b\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-35\">Vue\u3084React\u3068Rails\u306e\u9023\u643a\u65b9\u6cd5<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Webpacker\u3092\u4f7f\u7528\u3057\u305f\u57fa\u672c\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/packs\/application.js\nimport { createApp } from 'vue'\nimport App from '..\/app.vue'\n\ndocument.addEventListener('DOMContentLoaded', () =&gt; {\n  const app = createApp(App)\n  app.mount('#vue-app')\n})\n\n\/\/ app\/javascript\/components\/Hello.vue\n&lt;template&gt;\n  &lt;div class=\"hello\"&gt;\n    &lt;h1&gt;{{ message }}&lt;\/h1&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\nexport default {\n  data() {\n    return {\n      message: 'Hello from Vue!'\n    }\n  }\n}\n&lt;\/script&gt;<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>API\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/api\/base.js\nexport default class API {\n  static headers() {\n    const csrfToken = document.querySelector('meta[name=\"csrf-token\"]').content;\n    return {\n      'Content-Type': 'application\/json',\n      'X-CSRF-Token': csrfToken\n    };\n  }\n\n  static async get(path) {\n    const response = await fetch(path, {\n      headers: this.headers()\n    });\n    return response.json();\n  }\n\n  static async post(path, data) {\n    const response = await fetch(path, {\n      method: 'POST',\n      headers: this.headers(),\n      body: JSON.stringify(data)\n    });\n    return response.json();\n  }\n}<\/pre>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"i-36\">SPA\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3078\u306e\u6bb5\u968e\u7684\u306a\u79fb\u884c\u624b\u9806<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u6bb5\u968e\u7684\u306a\u79fb\u884c\u6226\u7565<\/strong><\/li>\n<\/ol>\n\n\n<div id=\"id-fbcbb24f-3eaf-4721-ad3d-bafe7a6fc1a2\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30d5\u30a7\u30fc\u30ba<\/th><th>\u5b9f\u88c5\u5185\u5bb9<\/th><th>\u76ee\u7684<\/th><\/tr><\/thead><tbody><tr><td>\u6e96\u5099\u6bb5\u968e<\/td><td>API\u8a2d\u8a08\u3068\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u5316<\/td><td>\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u5206\u96e2\u306e\u57fa\u76e4\u4f5c\u308a<\/td><\/tr><tr><td>\u30d5\u30a7\u30fc\u30ba1<\/td><td>\u7279\u5b9a\u306e\u6a5f\u80fd\u3092SPA\u5316<\/td><td>\u5c0f\u898f\u6a21\u306a\u5b9f\u9a13\u3068\u691c\u8a3c<\/td><\/tr><tr><td>\u30d5\u30a7\u30fc\u30ba2<\/td><td>\u4e3b\u8981\u6a5f\u80fd\u306eSPA\u5316<\/td><td>\u672c\u683c\u7684\u306a\u79fb\u884c\u306e\u958b\u59cb<\/td><\/tr><tr><td>\u30d5\u30a7\u30fc\u30ba3<\/td><td>\u5b8c\u5168\u306aSPA\u5316<\/td><td>\u6700\u7d42\u7684\u306a\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u3078\u306e\u79fb\u884c<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u5b9f\u88c5\u4f8b<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/javascript\/router\/index.js\nimport { createRouter, createWebHistory } from 'vue-router'\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes: [\n    {\n      path: '\/dashboard',\n      component: () =&gt; import('..\/pages\/Dashboard.vue'),\n      meta: { requiresAuth: true }\n    },\n    {\n      path: '\/profile',\n      component: () =&gt; import('..\/pages\/Profile.vue'),\n      meta: { requiresAuth: true }\n    }\n  ]\n})\n\nrouter.beforeEach((to, from, next) =&gt; {\n  if (to.matched.some(record =&gt; record.meta.requiresAuth)) {\n    \/\/ \u8a8d\u8a3c\u30c1\u30a7\u30c3\u30af\u306e\u5b9f\u88c5\n    checkAuth().then(isAuthenticated =&gt; {\n      if (!isAuthenticated) {\n        next({ path: '\/login' })\n      } else {\n        next()\n      }\n    })\n  } else {\n    next()\n  }\n})\n\nexport default router\n\n\/\/ app\/javascript\/store\/index.js\nimport { createStore } from 'vuex'\n\nexport default createStore({\n  state: {\n    user: null,\n    isLoading: false\n  },\n  mutations: {\n    setUser(state, user) {\n      state.user = user\n    },\n    setLoading(state, status) {\n      state.isLoading = status\n    }\n  },\n  actions: {\n    async fetchUser({ commit }) {\n      commit('setLoading', true)\n      try {\n        const user = await API.get('\/api\/user')\n        commit('setUser', user)\n      } finally {\n        commit('setLoading', false)\n      }\n    }\n  }\n})<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u79fb\u884c\u6642\u306e\u6ce8\u610f\u70b9<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ \u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u3068\u306e\u901a\u4fe1\u3092\u62bd\u8c61\u5316\nclass APIService {\n  constructor() {\n    this.baseURL = '\/api\/v1';\n  }\n\n  \/\/ \u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u5171\u901a\u51e6\u7406\n  handleResponse(response) {\n    if (!response.ok) {\n      throw new Error(`API Error: ${response.status}`);\n    }\n    return response.json();\n  }\n\n  \/\/ \u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\n  handleError(error) {\n    console.error('API Error:', error);\n    \/\/ \u30a8\u30e9\u30fc\u901a\u77e5\u306e\u5b9f\u88c5\n    NotificationService.showError(error.message);\n  }\n\n  \/\/ API\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u5b9f\u88c5\n  async request(endpoint, options = {}) {\n    try {\n      const response = await fetch(`${this.baseURL}${endpoint}`, {\n        ...options,\n        headers: {\n          ...options.headers,\n          ...API.headers()\n        }\n      });\n      return this.handleResponse(response);\n    } catch (error) {\n      this.handleError(error);\n      throw error;\n    }\n  }\n}<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u306b\u3088\u308a\u3001\u4ee5\u4e0b\u306e\u30e1\u30ea\u30c3\u30c8\u304c\u5f97\u3089\u308c\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u306e\u5411\u4e0a<\/li>\n\n\n\n<li>\u958b\u767a\u52b9\u7387\u306e\u6539\u5584<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306e\u6700\u9069\u5316<\/li>\n\n\n\n<li>\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u6027\u306e\u5411\u4e0a<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u306e\u78ba\u4fdd<\/li>\n<\/ul>\n\n\n\n<p>\u4ee5\u4e0a\u3067\u3001\u672c\u8a18\u4e8b\u3067\u4e88\u5b9a\u3057\u3066\u3044\u305f\u5168\u3066\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u306e\u57f7\u7b46\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f\u3002\u3053\u308c\u3089\u306e\u5b9f\u88c5\u30c6\u30af\u30cb\u30c3\u30af\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u304a\u3051\u308bJavaScript\u5b9f\u88c5\u306e\u54c1\u8cea\u3092\u5927\u304d\u304f\u5411\u4e0a\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/ &#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":{"0":"post-1408","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-ruby","7":"nothumb"},"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1408","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1408"}],"version-history":[{"count":1,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1408\/revisions"}],"predecessor-version":[{"id":1409,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1408\/revisions\/1409"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1408"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1408"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1408"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}