Fix Vue compatibility (fix #790)

- 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)
This commit is contained in:
John Hildenbiddle 2020-07-05 03:15:20 -05:00
parent c78cb11292
commit f385e21a1b
4 changed files with 136 additions and 73 deletions

View File

@ -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 {
@ -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'

View File

@ -1,94 +1,111 @@
# 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.
## Basic rendering
*README.md*
````markdown
# Vue guide
`v-for` usage.
```html
<ul>
<li v-for="i in 10">{{ i }}</li>
</ul>
```
<ul>
<li v-for="i in 10">{{ i }}</li>
</ul>
````
You can manually initialize a Vue instance.
*README.md*
Docsify will automatically render basic Vue content that does not require `data`, `methods`, or other instance features.
```markdown
# Vue demo
<button v-on:click.native="this.alert('Hello, World!')">Say Hello</button>
<div id="main">hello {{ msg }}</div>
<ul>
<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 3">{{ i }}</li>
</ul>
## Advanced usage
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
<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>
```
```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:
## Combine Vuep to write playground
<div id="example-1">
<p>{{ message }}</p>
[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.
<button v-on:click="hello">Say Hello</button>
*index.html*
<button v-on:click="counter -= 1">-</button>
{{ counter }}
<button v-on:click="counter += 1">+</button>
</div>
!> 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 playgrounds
[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*
Add vuep markup to a markdown file (e.g. `README.md`):
```markdown
# Vuep
<vuep template="#example-2"></vuep>
<vuep template="#example"></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' }
}
}
@ -96,4 +113,33 @@ You can manually initialize a Vue instance.
</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>

View File

@ -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;
@ -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>

View File

@ -26,9 +26,7 @@ function executeScript() {
return false;
}
setTimeout(_ => {
window.__EXECUTE_RESULT__ = new Function(code)();
}, 0);
new Function(code)();
}
function formatUpdated(html, updated, fn) {
@ -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);
}
}
}
}