添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
爱笑的帽子  ·  ANLOL Arcane - Bauru ...·  1 月前    · 
阳光的甜瓜  ·  Android:BottomNavigati ...·  2 月前    · 
面冷心慈的登山鞋  ·  Ask·  2 月前    · 

Hi guys,

I have added Vue to my Sage 10 theme using “bud-vue”, everything is good in dev mode.

After compiling the assets using “yarn build”, it’s like everything inside <main id="main" class="main"> breaks, showing nothing of its content.

My current devDependencies :

"devDependencies": {
    "@roots/bud": "6.11.0",
    "@roots/bud-babel": "^6.11.0",
    "@roots/bud-tailwindcss": "6.11.0",
    "@roots/bud-vue": "^6.11.0",
    "@roots/sage": "6.11.0",
    "browser-sync": "^2.28.3",
    "browser-sync-webpack-plugin": "^2.3.0"

Here is the app.js:

import domReady from '@roots/sage/client/dom-ready';
import {createApp} from "vue";
import App from "./components/App.vue";
 * Application entrypoint
domReady(async () => {
  // ...
 * @see {@link https://webpack.js.org/api/hot-module-replacement/}
import.meta.webpackHot?.accept(console.error);
createApp({
  // Every vue component has to be imported here in order to be available
  // in every template.
  components: {App}
}).mount("#main");

My App.vue component:

<script setup>
  import { ref, onMounted } from 'vue'
  const props = defineProps({
    msg: {
      type: String,
  // reactive state
  const count = ref(0)
  // functions that mutate state and trigger updates
  function increment() {
    count.value++
  // lifecycle hooks
  onMounted(() => {
    console.log(`The initial count is ${count.value}.`)
</script>
<template>
  <h3>Here is the message prop: {{ props.msg }}</h3>
  <button @click="increment" class="bg-blue-400 px-10 py-3">Count is: {{ count }}</button>
</template>

No modifications to the bud.config.js

The weird thing is that I don’t see any error in the console. Any error would help but there are no error messages.

What am I doing wrong?

Cheers

Hi @luis_troya-as,

Thanks for providing code and some good detail. The only thing that looks unusual there to me is that you’re calling createApp() outside of the domReady handler. It’s possible the behaviour you’re seeing is due to createApp firing before the DOM is ready. In dev mode, your code will be loaded by HMR, which I guess could mean a delay (to get the DOM in order). Perhaps it’s only working by chance in dev.

Just a thought - I may be completely wrong! Try:

import domReady from '@roots/sage/client/dom-ready';
import {createApp} from "vue";
import App from "./components/App.vue";
 * Application entrypoint
domReady(async () => {
  createApp({
    // Every vue component has to be imported here in order to be available
    // in every template.
    components: {App}
  }).mount("#main");
 * @see {@link https://webpack.js.org/api/hot-module-replacement/}
import.meta.webpackHot?.accept(console.error);

After creating the repo, I got the same result but I can see an error this time (not sure if it is related)

EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection.

Hope it helps! Let me know if I can do anything else to help.

Cheers

In your code, createApp only gets fed a list of components and no root component is set, so Vue will mount but render nothing.

If you change your call to this (as per the docs), the application will be mounted and the App.vue component will be loaded automatically:

domReady(async () => {
   createApp(App).mount("#main");

Beware though that mounting on #main directly will clear any PHP/Blade template output provided by @yield('content') inside the <main> element. It might be better to mount on an empty dedicated element (<div id="app"></div>, for example) inside <main></main> instead. Otherwise you have all the database overhead of WordPress querying posts only to have all that content cleared and replaced by Vue when mounting the app.

mensch:

Beware though that mounting on #main directly will clear any PHP/Blade template output provided by @yield('content') inside the <main> element.

That’s not entirely true, this is a strategy I have been using in Laravel for several years. This is how it looks with vue 2:

new Vue({
  el: '#app',

My idea is to use components in demand. For example, in any given page that extends the template, call a component like <Carousel data="someObj">, and it will render only on that page.

if I use the following code as the docs suggest, it will render the component all the time.

domReady(async () => {
   createApp(App).mount("#main");

Does that make sense?

Ah, I can see what you’re doing now - thanks for the repo.

I think the problem you’re running into is that in production, the Vue bundle doesn’t contain the template compiler. Even though <app></app> is present inside #main, the template isn’t compiled, so the App component never initialises.

I haven’t had to bundle the template compiler in Vue 3 before, but you should be able to use vue.runtime.esm-browser.prod.js instead of vue.esm-bundler.js.

I haven’t checked this, but app.vue.runtimeOnly(false) in your bud.config.js seems like it may have the desired effect, and bundle the compiler too.

The more I think about this, the more I wonder if you actually need the compiler if your objective is to…

luis_troya-as:

use components in demand. For example, in any given page that extends the template, call a component like <Carousel data="someObj">, and it will render only on that page.

I hacked together a helper a few weeks ago which could possibly be adapted to avoid the need to include the compiler. You can mountAll(AppComponent, '.css-selector', [ ... plugins ]). Data attributes set on the selected element(s) will automatically be passed as props.

EDIT: I realise I was really vague here, sorry. The idea is to mount multiple ‘root’ Vue components as Apps using a selector string. You can then share state if required using a store like pinia loaded via the uses argument to mountAll(). Instead of <Component> in your blade view, output a <div class="vue-app-component1">, then mountAll(Component1, '.vue-app-component1'). Do this for each component.

May or may not be useful - do what you want with it (MIT).

import { createApp } from 'vue'
import type { Component, Plugin } from 'vue'
export function mountAll(component: Component, selector: string, uses: Plugin[] = []): Component[] {
    var apps: Component[] = []
    if (typeof document.querySelectorAll(selector)[0] !== 'undefined') {
      const els = Array.prototype.slice.call(document.querySelectorAll(selector));
      for (var i in els) {
        const atts = {  ...els[i].dataset }
        const innerHtml = els[i].innerHTML
        delete(atts.vApp)
        if(typeof atts.inner === 'undefined') {
          atts.inner = innerHtml
        const app = createApp(component, atts)
        uses.forEach((plugin: Plugin) => {
          app.use(plugin)
        app.mount(els[i])
        apps.push(app)
    return apps
 luis_troya-as:

That’s not entirely true, this is a strategy I have been using in Laravel for several years. This is how it looks with vue 2:

The reason I mentioned it, is because bud-vue is set up to do this by default, as it only bundles the runtime version of Vue 3 out of the box.

As @talss89 already mentioned, adding app.vue.runtimeOnly(false) to your bud config should enable you to use the strategy you’re familiar with.

talss89:

I haven’t had to bundle the template compiler in Vue 3 before, but you should be able to use vue.runtime.esm-browser.prod.js instead of vue.esm-bundler.js.

I didn’t know that. I thought it was doing a compiling process under the hood. But taking a look at the source code, it makes sense now

The only thing that worries me is that everything under #main breaks because of this error without a console error, failing silently.

talss89:

EDIT: I realise I was really vague here, sorry. The idea is to mount multiple ‘root’ Vue components as Apps using a selector string. You can then share state if required using a store like pinia loaded via the uses argument to mountAll(). Instead of <Component> in your blade view, output a <div class="vue-app-component1">, then mountAll(Component1, '.vue-app-component1'). Do this for each component.

No problem @talss89, I appreciate any help, and that snippet you shared it’s really good. Nice to know more ways how to use the strategy I have in mind.

I think I should fix the problem I have right now in order to iterate new ideas of the best way to use Vue. What do you think @talss89?

If by any chance @talss89 @mensch, you have an example I can take a look at using Vue, I will really appreciate it!

Kind regards,
Luis.

I’ve just cloned your repo, and the template compiler is working in both dev and prod…

I wonder if Bud is reusing some cached fragments between builds. I’ve managed to break the project by removing the runtimeOnly setting, then doing a clean.

Keep your bud.config.js file as-is, then try running yarn run bud clean before running yarn build again. Hopefully that should now work.

Also, prefer app.vue.set('runtimeOnly', false) over app.vue.runtimeOnly(false) - I wasn’t aware of the deprecation!

Hey @talss89, the clean command did trick!

Several hours doing changes without seeing any result, not sure for how many hours I have been using cached :frowning:

But finally, it is working for me.

Thank you so much for the help, ideas and for taking the time to check the repo!

Glad to hear you’re up and running @luis_troya-as!

I think others will run into the same issue you highlighted (needing to bud clean).

I’ve opened a feature request to address this. A fix should be included in Bud 6.12.0, due for release tomorrow (23rd March 2023), thanks to kellymears.