diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 60ed5aa53..000000000 --- a/cypress.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "video": false -} diff --git a/docs/configuration.md b/docs/configuration.md index 7a65553a3..7b3b5fcda 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -245,10 +245,10 @@ window.$docsify = { // Custom file name coverpage: 'cover.md', - // mutiple covers + // multiple covers coverpage: ['/', '/zh-cn/'], - // mutiple covers and custom file name + // multiple covers and custom file name coverpage: { '/': 'cover.md', '/zh-cn/': 'cover.md', @@ -410,7 +410,7 @@ window.$docsify = { }; ``` -?> If this options is `false` but you dont want to emojify some specific colons , [Refer this](https://github.com/docsifyjs/docsify/issues/742#issuecomment-586313143) +?> If this options is `false` but you don't want to emojify some specific colons , [Refer this](https://github.com/docsifyjs/docsify/issues/742#issuecomment-586313143) ## mergeNavbar @@ -494,15 +494,15 @@ window.$docsify = { ``` ## crossOriginLinks -- type: `Array` -When `routerMode: 'history'`, you may face the cross-origin issues, See [#1379](https://github.com/docsifyjs/docsify/issues/1379). -In Markdown content, there is a simple way to solve it, see extends Markdown syntax `Cross-Origin link` in [helpers](helpers.md). +- type: `Array` + +When `routerMode: 'history'`, you may face the cross-origin issues, See [#1379](https://github.com/docsifyjs/docsify/issues/1379). +In Markdown content, there is a simple way to solve it, see extends Markdown syntax `Cross-Origin link` in [helpers](helpers.md). + ```js window.$docsify = { - crossOriginLinks:[ - "https://example.com/cross-origin-link", - ], + crossOriginLinks: ['https://example.com/cross-origin-link'], }; ``` @@ -604,7 +604,7 @@ window.$docsify = { }; ``` -Load the right 404 page according to the localisation: +Load the right 404 page according to the localization: ```js window.$docsify = { @@ -629,3 +629,104 @@ window.$docsify = { topMargin: 90, // default: 0 }; ``` + +## vueComponents + +- type: `Object` + +Creates and registers global [Vue components](https://vuejs.org/v2/guide/components.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site. + +```js +window.$docsify = { + vueComponents: { + 'button-counter': { + template: ` + + `, + data() { + return { + count: 0, + }; + }, + }, + }, +}; +``` + +```markdown + +``` + + + + + +## vueGlobalOptions + +- type: `Object` + +Specifies [Vue options](https://vuejs.org/v2/api/#Options-Data) for use with Vue content not explicitly mounted with [vueMounts](#mounting-dom-elements), [vueComponents](#components), or a [markdown script](#markdown-script). Changes to global `data` will persist and be reflected anywhere global references are used. + +```js +window.$docsify = { + vueGlobalOptions: { + data() { + return { + count: 0, + }; + }, + }, +}; +``` + +```markdown +

+ + {{ count }} + +

+``` + + +

+ + {{ count }} + +

+
+ +## vueMounts + +- type: `Object` + +Specifies DOM elements to mount as [Vue instances](https://vuejs.org/v2/guide/instance.html) and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site. + +```js +window.$docsify = { + vueMounts: { + '#counter': { + data() { + return { + count: 0, + }; + }, + }, + }, +}; +``` + +```markdown +
+ + {{ count }} + +
+``` + + + + {{ count }} + + diff --git a/docs/index.html b/docs/index.html index 0693aba3a..0a83d114d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -101,6 +101,62 @@ '/': 'Search', }, }, + vueComponents: { + 'button-counter': { + template: ` + + `, + data() { + return { + count: 0, + }; + }, + }, + }, + vueGlobalOptions: { + data() { + return { + count: 0, + message: 'Hello, World!', + // Fake API response + images: [ + { title: 'Image 1', url: 'https://picsum.photos/150?random=1' }, + { title: 'Image 2', url: 'https://picsum.photos/150?random=2' }, + { title: 'Image 3', url: 'https://picsum.photos/150?random=3' }, + ], + }; + }, + computed: { + timeOfDay() { + const date = new Date(); + const hours = date.getHours(); + + if (hours < 12) { + return 'morning'; + } else if (hours < 18) { + return 'afternoon'; + } else { + return 'evening'; + } + }, + }, + methods: { + hello: function() { + alert(this.message); + }, + }, + }, + vueMounts: { + '#counter': { + data() { + return { + count: 0, + }; + }, + }, + }, plugins: [ function(hook, vm) { hook.beforeEach(function(html) { @@ -163,5 +219,6 @@ })(); + diff --git a/docs/vue.md b/docs/vue.md index 5e62363d0..2e61e6ec5 100644 --- a/docs/vue.md +++ b/docs/vue.md @@ -1,8 +1,8 @@ # Vue compatibility -Docsify allows Vue [v2.x](https://vuejs.org) and [v3.x](https://v3.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. +Docsify allows Vue content to be added directly to you markdown pages. This can greatly simplify working with data and adding reactivity to your site. -To get started, load either the production or development version of Vue in your `index.html`: +To get started, add Vue [2.x](https://vuejs.org) or [3.x](https://v3.vuejs.org) to your `index.html` file. Choose the production version for your live site or the development version for helpful console warnings and [Vue.js devtools](https://github.com/vuejs/vue-devtools) support. #### Vue 2.x @@ -10,7 +10,7 @@ To get started, load either the production or development version of Vue in your - + ``` @@ -20,118 +20,327 @@ To get started, load either the production or development version of Vue in your - + ``` -## Basic rendering +## Template syntax -Docsify will automatically render basic Vue content that does not require `data`, `methods`, or other instance features. +Vue [template syntax](https://vuejs.org/v2/guide/syntax.html) is used to create dynamic content. With no additional configuration, this syntax offers several useful features like support for [JavaScript expressions](https://vuejs.org/v2/guide/syntax.html#Using-JavaScript-Expressions) and Vue [directives](https://vuejs.org/v2/guide/syntax.html#Directives) for loops and conditional rendering. ```markdown + +

Text for GitHub

+ + + + +

2 + 2 = {{ 2 + 2 }}

``` -The HTML above will render the following: + +

Text for GitHub

- + -## Advanced usage +

2 + 2 = {{ 2 + 2 }}

+
-Vue components and templates that require `data`, `methods`, computed properties, lifecycle hooks, etc. require manually creating a new `Vue()` instance within a ` + }, +} ``` -#### Vue 3.x +```markdown +Good {{ timeOfDay }}! +``` + + + +Good {{ timeOfDay }}! + + + +### Methods + +```js +{ + data() { + return { + message: 'Hello, World!' + }; + }, + methods: { + hello() { + alert(this.message); + } + }, +} +``` ```markdown - + }, +}; ``` -The HTML & JavaScript above will render the following: +```markdown +

+ + {{ count }} + +

+``` - + +

+ + {{ count }} + +

+
+ +Notice the behavior when multiple global counters are rendered: + + +

+ + {{ count }} + +

+
+ +Changes made to one counter affect the both counters. This is because both instances reference the same global `count` value. Now, navigate to a new page and return to this section to see how changes made to global data persist between page loads. + +## Mounts + +Use `vueMounts` to specify DOM elements to mount as [Vue instances](https://vuejs.org/v2/guide/instance.html) and their associated options. Mount elements are specified using a [CSS selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) as the key with an object containing Vue options as their value. Docsify will mount the first matching element in the main content area each time a new page is loaded. Mount element `data` is unique for each instance and will not persist as users navigate the site. + +```js +window.$docsify = { + vueMounts: { + '#counter': { + data() { + return { + count: 0, + }; + }, + }, + }, +}; +``` -
-

{{ message }}

+```markdown +
+ + {{ count }} + +
+``` - + + + {{ count }} + + + +## Components + +Use `vueComponents` to create and register global [Vue components](https://vuejs.org/v2/guide/components.html). Components are specified using the component name as the key with an object containing Vue options as the value. Component `data` is unique for each instance and will not persist as users navigate the site. + +```js +window.$docsify = { + vueComponents: { + 'button-counter': { + template: ` + + `, + data() { + return { + count: 0, + }; + }, + }, + }, +}; +``` - - {{ counter }} - -
+```markdown + + +``` - + + + + + +## Markdown script -!> Only the first ` +``` + +```html + + +``` + +## Technical Notes + +- Docsify processes Vue content in the following order on each page load: + 1. Execute markdown ` - `, - }, - vue3: { - markdown: ` -
- +
+

---

+ + {{ counter }} +
+ +
+

---

+ {{ counter }}
`, }, }; - for (const vueURL of vueURLs) { - const vueVersion = vueURL.match(/vue(\d+)/)[1]; // vue2|vue3 - const vueData = testData[`vue${vueVersion}`]; + return config; + } - for (const executeScript of ['unspecified', true]) { - test(`handles Vue v${vueVersion}.x advanced usage when executeScript is ${executeScript}`, async () => { - const docsifyInitConfig = { - markdown: { - homepage: vueData.markdown, - }, - scriptURLs: vueURL, - }; + // Tests + // --------------------------------------------------------------------------- + for (const vueURL of vueURLs) { + const vueVersion = Number(vueURL.match(/vue(\d+)/)[1]); // 2|3 - if (executeScript !== 'unspecified') { - docsifyInitConfig.config = { - executeScript, - }; - } + describe(`Vue v${vueVersion}`, function() { + for (const executeScript of [true, undefined]) { + test(`renders content when executeScript is ${executeScript}`, async () => { + const docsifyInitConfig = getSharedConfig(); + + docsifyInitConfig.config.executeScript = executeScript; + docsifyInitConfig.scriptURLs = vueURL; await docsifyInit(docsifyInitConfig); - await expect(page).toEqualText('#test span', '0'); - await page.click('#test button'); - await expect(page).toEqualText('#test span', '1'); + // Static + await expect(page).toEqualText('#vuefor', '12345'); + await expect(page).toEqualText('#vuecomponent', '0'); + await expect(page).toEqualText( + '#vueglobaloptions p', + 'vueglobaloptions' + ); + await expect(page).toEqualText('#vueglobaloptions span', '0'); + await expect(page).toEqualText('#vuemounts p', 'vuemounts'); + await expect(page).toEqualText('#vuemounts span', '0'); + await expect(page).toEqualText('#vuescript p', 'vuescript'); + await expect(page).toEqualText('#vuescript span', '0'); + + // Reactive + await page.click('#vuecomponent'); + await expect(page).toEqualText('#vuecomponent', '1'); + await page.click('#vueglobaloptions button'); + await expect(page).toEqualText('#vueglobaloptions span', '1'); + await page.click('#vuemounts button'); + await expect(page).toEqualText('#vuemounts span', '1'); + await page.click('#vuescript button'); + await expect(page).toEqualText('#vuescript span', '1'); }); } - test(`handles Vue v${vueVersion}.x advanced usage when executeScript is false`, async () => { - const docsifyInitConfig = { - config: { - executeScript: false, - }, - markdown: { - homepage: vueData.markdown, - }, - scriptURLs: vueURL, - }; + test(`ignores content when Vue is not present`, async () => { + const docsifyInitConfig = getSharedConfig(); await docsifyInit(docsifyInitConfig); + await page.evaluate(() => { + return 'Vue' in window === false; + }); + await expect(page).toEqualText('#vuefor', '{{ i }}'); + await expect(page).toEqualText('#vuecomponent', '---'); + await expect(page).toEqualText('#vueglobaloptions p', '---'); + await expect(page).toEqualText('#vuemounts p', '---'); + await expect(page).toEqualText('#vuescript p', '---'); + }); + + test(`ignores content when vueComponents, vueMounts, and vueGlobalOptions are undefined`, async () => { + const docsifyInitConfig = getSharedConfig(); + + docsifyInitConfig.config.vueComponents = undefined; + docsifyInitConfig.config.vueGlobalOptions = undefined; + docsifyInitConfig.config.vueMounts = undefined; + docsifyInitConfig.scriptURLs = vueURL; - const textContent = await page.textContent('#test span'); - expect(textContent).toBe(''); + await docsifyInit(docsifyInitConfig); + await expect(page).toEqualText('#vuefor', '{{ i }}'); + await expect(page).toEqualText('#vuecomponent', '---'); + await expect(page).toEqualText('#vueglobaloptions p', '---'); + await expect(page).toEqualText('#vuemounts p', '---'); + await expect(page).toEqualText('#vuescript p', 'vuescript'); }); - } - }); + + test(`ignores content when vueGlobalOptions is undefined`, async () => { + const docsifyInitConfig = getSharedConfig(); + + docsifyInitConfig.config.vueGlobalOptions = undefined; + docsifyInitConfig.scriptURLs = vueURL; + + await docsifyInit(docsifyInitConfig); + await expect(page).toEqualText('#vuefor', '12345'); + await expect(page).toEqualText('#vuecomponent', '0'); + expect(await page.innerText('#vueglobaloptions p')).toBe(''); + await expect(page).toEqualText('#vuemounts p', 'vuemounts'); + await expect(page).toEqualText('#vuescript p', 'vuescript'); + }); + + test(`ignores content when vueMounts is undefined`, async () => { + const docsifyInitConfig = getSharedConfig(); + + docsifyInitConfig.config.vueMounts['#vuemounts'] = undefined; + docsifyInitConfig.scriptURLs = vueURL; + + await docsifyInit(docsifyInitConfig); + await expect(page).toEqualText('#vuefor', '12345'); + await expect(page).toEqualText('#vuecomponent', '0'); + await expect(page).toEqualText( + '#vueglobaloptions p', + 'vueglobaloptions' + ); + await expect(page).toEqualText('#vuemounts p', 'vueglobaloptions'); + await expect(page).toEqualText('#vuescript p', 'vuescript'); + }); + + test(`ignores