Docsify: Dynamic content in Vue components

Created on 14 Mar 2020  路  8Comments  路  Source: docsifyjs/docsify




Bug Report

In the documentation it says that one can embed Vue instances. I tried building an interactive component for our documentation like so:

# Servers

Thanks to [AngrySnout](https://github.com/AngrySnout/SauerTracker) we have excellent statistics for the game.

<div id="server">
    <ul>
        <li v-for="server in servers" :key="server.description"> {{ server.descriptionStyled }} </li>
    </ul>
</div>

<script>
  const API_URL = "https://tomaten.sauertracker.net/api/servers"

  new Vue({
    el: '#server',
    data: () => ({
        servers: []
    }),
    async mounted {}
        console.log("HEY")
        const response = await fetch(API_URL);
        this.servers = await response.json(); 
    }
  })
</script>

However when loading the page the content quickly flashes before getting "rendered away".
I have no idea what sorcery is going on behind the scenes but my best bet is that it's an unsupported feature.

Steps to reproduce

(see above)

What is current behaviour

(see above)

What is the expected behaviour

The app that is rendered on screen should allow for dynamic content.

Other relevant information

  • [x] Bug does still occur when all/other plugins are disabled?
  • Your OS: OSX 10.15.3 (19D76)
  • Browser version: Version 80.0.3987.132 (Official Build) (64-bit)
  • Docsify version: 4.11.2
  • Docsify plugins: search plugin
bug vuejs

Most helpful comment

Actually ,It seems some issues about the Vue supports in docsify.
Currently, when u create ur own Vue instance, you will not get the Vue instance in the mounted hook by using this (this directs to the window obj in docsify now).

the Vue instance was covered in this.__EXECUTE_RESULT__.
there is showing how we execute script in docsify.

  // Execute script
  if (
    this.config.executeScript !== false &&
    typeof window.Vue !== 'undefined' &&
    !executeScript()
  ) {
    setTimeout(_ => {
      const vueVM = window.__EXECUTE_RESULT__;
      vueVM && vueVM.$destroy && vueVM.$destroy();
      window.__EXECUTE_RESULT__ = new window.Vue().$mount('#main');
    }, 0);
  } else {
    this.config.executeScript && executeScript();
  }

Although, I tried to set the root data in a hack way, it seems not work.
I haven't found a way to resolve it yet, I hope those informations could be helpful.

All 8 comments

Sorry I am not familiar with vue so cant really help you here !

Feel Free to investigate and submit the fix.

_Also, try testing with older version and check in which it broke, it will help in investigation_

Actually ,It seems some issues about the Vue supports in docsify.
Currently, when u create ur own Vue instance, you will not get the Vue instance in the mounted hook by using this (this directs to the window obj in docsify now).

the Vue instance was covered in this.__EXECUTE_RESULT__.
there is showing how we execute script in docsify.

  // Execute script
  if (
    this.config.executeScript !== false &&
    typeof window.Vue !== 'undefined' &&
    !executeScript()
  ) {
    setTimeout(_ => {
      const vueVM = window.__EXECUTE_RESULT__;
      vueVM && vueVM.$destroy && vueVM.$destroy();
      window.__EXECUTE_RESULT__ = new window.Vue().$mount('#main');
    }, 0);
  } else {
    this.config.executeScript && executeScript();
  }

Although, I tried to set the root data in a hack way, it seems not work.
I haven't found a way to resolve it yet, I hope those informations could be helpful.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale bot may make it harder to triage issues if they get closed before we see them. Wdyt?

Now that I've realized this, I need to go check all the closed issues.

Regarding the original issue, I think I encountered them. I believe it has to do with double execution of scripts, and I refactored this code in my personal fork. I will make a PR soon...

@Fohlen --

This bug is currently being addressed in #1271. The changes in that PR allow your example to work as expected, albeit with a few minor changes:

<div id="server">
  <ul>
    <li v-for="server in servers" :key="server.description">
      <span v-html="server.descriptionStyled"></span>
    </li>
  </ul>
</div>

<script>
  const API_URL = "https://tomaten.sauertracker.net/api/servers"

  new Vue({
    el: '#server',
    data: () => ({
      servers: []
    }),
    async mounted() {
      console.log("HEY")
      const response = await fetch(API_URL);
      this.servers = await response.json();
    }
  });
</script>

my solution:
set executeScript to false and use a plugin to initial Vue instance

((global, plugin) => {
  global.DocsifyVuePlugin = plugin;
})(window, () => (hook) => {
  let script = null;
  hook.beforeEach(() => {
    if(window.__DOCSIFY_VUE_INSTANCE__ ){
      window.__DOCSIFY_VUE_INSTANCE__.$destroy();
      window.__DOCSIFY_VUE_INSTANCE__ = null;
    }
  });
  hook.afterEach((content, next) => {
    script = null;
    const template = content.replace(/<script>([\s\S]*?)<\/script>/g, ($0, $1) => {
      if(!script) {
        script = $1;
      }
      return '';
    });
    return next(script ? `<div id="docsifyVuePluginInstance">${template}</div>` : content);
  });
  hook.doneEach(() => {
    if(!script) return;
    const vm = window.eval(script);
    script = null;
    if (vm instanceof window.Vue) {
      vm.$mount('#docsifyVuePluginInstance');
      window.__DOCSIFY_VUE_INSTANCE__ = vm;
    }
  })
});


md5-8baa1ee2a43a9209272090f59e220f39




md5-e1ace05ff0bbb946e1fb0e70d600638f


@Fohlen --

Fixed in #1271. The example code you provided has a few issues though. Here's update code that will work as is:

# Servers

Thanks to [AngrySnout](https://github.com/AngrySnout/SauerTracker) we have excellent statistics for the game.

<div id="server">
    <ul>
        <li v-for="server in servers" :key="server.description"><span v-html="server.descriptionStyled"></span></li>
    </ul>
</div>

<script>
  const API_URL = "https://tomaten.sauertracker.net/api/servers"

  new Vue({
    el: '#server',
    data: () => ({
        servers: []
    }),
    mounted() {
      console.log("HEY")
      fetch(API_URL)
        .then(response => response.json())
        .then(data => this.servers = data)
        .catch(err => console.log(err));
    }
  })
</script>
Was this page helpful?
0 / 5 - 0 ratings