Rails7+shakapacker应用中Vue3未加载无控制台报错问题
问题背景
基于Rails 7与shakapacker新建Shopify应用项目,需要在.slim模板文件中使用Vue组件,但Vue未在应用中成功加载,全程无任何报错抛出。
现有配置
- Vue loader配置:
config/webpack/rules/vue.js
const { VueLoaderPlugin } = require('vue-loader') module.exports = { module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' } ] }, plugins: [ new VueLoaderPlugin() ], resolve: { extensions: [ '.vue' ] } }
- webpack主配置:
config/webpack/webpack.config.js
const { webpackConfig, merge } = require('shakapacker') const vueConfig = require('./rules/vue') module.exports = merge(vueConfig, webpackConfig)
- 入口pack文件:
app/javascript/packs/application.js
import HelloWorld from '../components/HelloWorld' import { createApp } from 'vue' const app = createApp({ el: '#app' }) app.component('helloworld', HelloWorld) document.addEventListener('DOMContentLoaded', () => { app })
- 示例组件:
app/javascript/components/HelloWorld.vue
<template> <h1>Hello world</h1> </template> <script> export default { name: 'HelloWorld' } </script>
- 布局文件:
app/views/layouts/embedded_app.slim
doctype html html[lang="en"] head meta[charset="utf-8"] - application_name = ShopifyApp.configuration.application_name title = application_name = stylesheet_link_tag "application", "data-turbo-track": "reload" = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' = csrf_meta_tags body #app .wrapper main[role="main"] = yield = content_tag(:div, nil, id: 'shopify-app-init', data: { api_key: ShopifyApp.configuration.api_key, shop_origin: @shop_origin || (@current_shopify_session.shop if @current_shopify_session), host: @host, debug: Rails.env.development? })
- 首页视图:
app/views/home/index.slim,尝试渲染HelloWorld组件
helloworld
现象描述
配置完成后页面无内容展示,控制台无报错。曾在Vue实例的created钩子中添加console.log('ok')测试日志,修改后控制台仍无输出,已确认DOMContentLoaded事件可正常触发,无法判断Vue应用是否完成渲染。所有配置对照shakapacker文档编写,不确定是否存在配置错误。应用通过Ngrok隧道在Shopify测试店铺中渲染,已排除该因素影响。
根因与修复
1. Vue 3初始化逻辑错误(核心问题)
Vue 3的createApp不再支持Vue 2的el配置项,必须显式调用mount()方法完成应用挂载。现有代码在DOMContentLoaded回调中仅引用了app变量,没有执行挂载操作,Vue应用根本未启动,自然不会产生日志或渲染内容。
修复后的入口文件:
// app/javascript/packs/application.js import HelloWorld from '../components/HelloWorld' import { createApp } from 'vue' document.addEventListener('DOMContentLoaded', () => { const app = createApp({}) app.component('helloworld', HelloWorld) // 显式挂载到DOM节点 app.mount('#app') })
2. webpack resolve配置覆盖默认规则
现有vue规则中的resolve.extensions仅配置了.vue,会覆盖shakapacker默认的扩展名解析逻辑,导致.js等格式文件无法被正常识别。修改config/webpack/rules/vue.js,补充默认扩展名:
const { VueLoaderPlugin } = require('vue-loader') module.exports = { module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' } ] }, plugins: [ new VueLoaderPlugin() ], resolve: { extensions: ['.vue', '.js', '.jsx', '.ts', '.tsx', '.json'] } }
3. 依赖版本校验
如果修改后仍有问题,检查本地依赖版本:vue需为3.x版本,vue-loader需为16.x及以上版本,版本不匹配会导致编译异常无输出。
验证
修改完成后重启shakapacker开发服务,刷新页面即可看到HelloWorld组件渲染的Hello world文本,自定义调试日志也会正常输出。现有slim模板中helloworld标签位于#app节点内部,符合Vue挂载范围要求,无需调整。
内容的提问来源于stack exchange,提问作者cercxtrova

