Skip to content

Commit

Permalink
Fix Vue compatibility (fix #790)
Browse files Browse the repository at this point in the history
- Fix inability to instantiate reactive Vue components by 1) handling each child of #main instead of #main itself and 2)  skipping elements that are already Vue instances
- Retain previous behavior of processing basic Vue rendering without the need for a markdown <script> tag.
- Update documentation and add live Vue examples
- Update `index.html` files to include Vue.js and Vuep (CSS+JS)
  • Loading branch information
jhildenbiddle committed Jul 5, 2020
1 parent c78cb11 commit f385e21
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 63 deletions.
3 changes: 3 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/dark.css" title="dark" disabled>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/buble.css" title="buble" disabled>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/pure.css" title="pure" disabled>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/vuep/dist/vuep.css">

<style>
nav.app-nav li ul {
Expand Down Expand Up @@ -95,6 +96,8 @@
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-markdown.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs/components/prism-nginx.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/vuep/dist/vuep.min.js"></script>
<script>
((window.gitter = {}).chat = {}).options = {
room: 'docsifyjs/Lobby'
Expand Down
140 changes: 93 additions & 47 deletions docs/vue.md
Original file line number Diff line number Diff line change
@@ -1,99 +1,145 @@
# Compatible with Vue
# Vue compatibility

You can write Vue components directly in the Markdown file, and it will be parsed. You can use this feature to write vue demo and documentation together.
Docsify allows [Vue.js](https://vuejs.org) components to be added directly to you Markdown files. These components can greatly simplify working with data and adding reactivity to your content.

## Basic usage

Load the Vue in `./index.html`.
To get started, load either the production (minified) or development (unminified) version of Vue in your `index.html`:

```html
<script src="//cdn.jsdelivr.net/npm/vue"></script>
<script src="//cdn.jsdelivr.net/npm/docsify"></script>
<!-- Production (minified) -->
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.min.js"></script>

<!-- Or use the compressed files -->
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
<!-- Development (unminified, with debugging info via console) -->
<script src="//cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
```

Then you can immediately write Vue code at Markdown file. `new Vue({ el: '#main' })` script is executed by default to create instance.

*README.md*
## Basic rendering

````markdown
# Vue guide
Docsify will automatically render basic Vue content that does not require `data`, `methods`, or other instance features.

`v-for` usage.
```markdown
<button v-on:click.native="this.alert('Hello, World!')">Say Hello</button>

```html
<ul>
<li v-for="i in 10">{{ i }}</li>
<li v-for="i in 3">{{ i }}</li>
</ul>
```

The HTML above will render the following:

<button v-on:click="this.alert('Hello, World!')">Say Hello</button>

<ul>
<li v-for="i in 10">{{ i }}</li>
<li v-for="i in 3">{{ i }}</li>
</ul>
````

You can manually initialize a Vue instance.
## Advanced usage

*README.md*
Vue components and templates that require `data`, `methods`, computed properties, lifecycle hooks, etc. require manually creating a new `Vue()` instance within a `<script>` tag in your markdown.

```markdown
# Vue demo
<div id="example-1">
<p>{{ message }}</p>

<button v-on:click="hello">Say Hello</button>

<div id="main">hello {{ msg }}</div>
<button v-on:click="counter -= 1">-</button>
{{ counter }}
<button v-on:click="counter += 1">+</button>
</div>
```

```markdown
<script>
new Vue({
el: '#main',
data: { msg: 'Vue' }
})
el: "#example-1",
data: function() {
counter: 0,
message: "Hello, World!"
},
methods: {
hello: function() {
alert(this.message);
}
}
});
</script>
```

!> In a Markdown file, only the script within the first script tag is executed.
The HTML & JavaScript above will render the following:

<div id="example-1">
<p>{{ message }}</p>

<button v-on:click="hello">Say Hello</button>

<button v-on:click="counter -= 1">-</button>
{{ counter }}
<button v-on:click="counter += 1">+</button>
</div>

## Combine Vuep to write playground
!> Only the first `<script>` tag in a markdown file is executed. If you are working with multiple Vue components, all `Vue` instances must be created within this tag.

[Vuep](https://github.com/QingWei-Li/vuep) is a component for rendering Vue components with live editor and preview. Supports Vue component spec and JSX.
## Vuep playgrounds

*index.html*
[Vuep](https://github.com/QingWei-Li/vuep) is a Vue component that provides a live editor and preview for Vue content. See the [vuep documentation](https://qingwei-li.github.io/vuep/) for details.

Add Vuep CSS and JavaScript to your `index.html`:

```html
<!-- Inject CSS file -->
<!-- Vuep CSS -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/vuep/dist/vuep.css">

<!-- Inject JavaScript file -->
<script src="//cdn.jsdelivr.net/npm/vue"></script>
<script src="//cdn.jsdelivr.net/npm/vuep"></script>
<script src="//cdn.jsdelivr.net/npm/docsify"></script>

<!-- or use the compressed files -->
<script src="//cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<!-- Vuep JavaScript -->
<script src="//cdn.jsdelivr.net/npm/vuep/dist/vuep.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
```

*README.md*
```markdown
# Vuep
Add vuep markup to a markdown file (e.g. `README.md`):

<vuep template="#example"></vuep>
```markdown
<vuep template="#example-2"></vuep>

<script v-pre type="text/x-template" id="example">
<script v-pre type="text/x-template" id="example-2">
<template>
<div>Hello, {{ name }}!</div>
</template>

<script>
module.exports = {
data: function () {
data: function() {
return { name: 'Vue' }
}
}
</script>
</script>
```

?> Example Refer to the [Vuep documentation](https://qingwei-li.github.io/vuep/).
<vuep template="#example-2"></vuep>

<script v-pre type="text/x-template" id="example-2">
<template>
<div>Hello, {{ name }}!</div>
</template>

<script>
module.exports = {
data: function() {
return { name: 'World' }
}
}
</script>
</script>

<script>
new Vue({
el: "#example-1",
data: {
counter: 0,
message: "Hello, World!"
},
methods: {
hello: function() {
alert(this.message);
}
}
});
</script>
3 changes: 3 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<link rel="stylesheet" href="/themes/vue.css" title="vue">
<link rel="stylesheet" href="/themes/dark.css" title="dark" disabled>
<link rel="stylesheet" href="/themes/buble.css" title="buble" disabled>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/vuep/dist/vuep.css">
<style>
nav.app-nav li ul {
min-width: 100px;
Expand Down Expand Up @@ -85,6 +86,8 @@
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-markdown.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-nginx.min.js"></script>
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/vuep/dist/vuep.min.js"></script>
</body>

</html>
43 changes: 27 additions & 16 deletions src/core/render/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ function executeScript() {
return false;
}

setTimeout(_ => {
window.__EXECUTE_RESULT__ = new Function(code)();
}, 0);
new Function(code)();
}

function formatUpdated(html, updated, fn) {
Expand All @@ -48,22 +46,35 @@ function renderMain(html) {
}

this._renderTo('.markdown-section', html);

// Render sidebar with the TOC
!this.config.loadSidebar && this._renderSidebar();

// 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();
// Execute markdown <script>
if (this.config.executeScript || 'Vue' in window) {
executeScript();
}

// Handle Vue content not handled by markdown <script>
if ('Vue' in window) {
const mainElm = document.querySelector('#main') || {};
const childElms = mainElm.children || [];

for (let i = 0, len = childElms.length; i < len; i++) {
const elm = childElms[i];
const isValid = ['SCRIPT'].indexOf(elm.tagName) === -1;
const isVue = Boolean(elm.__vue__ && elm.__vue__._isVue);

if (isValid && !isVue) {
new window.Vue({
mounted: function() {
if (this.$children.length === 0) {
this.$destroy;
}
},
}).$mount(elm);
}
}
}
}

Expand Down

0 comments on commit f385e21

Please sign in to comment.