@@ -7,13 +7,12 @@ import { callHook } from '../init/lifecycle';
7
7
import { getAndActive , sticky } from '../event/sidebar' ;
8
8
import { getPath , isAbsolutePath } from '../router/util' ;
9
9
import { isMobile , inBrowser } from '../util/env' ;
10
- import { isPrimitive , merge } from '../util/core' ;
10
+ import { isPrimitive } from '../util/core' ;
11
11
import { scrollActiveSidebar } from '../event/scroll' ;
12
12
import { Compiler } from './compiler' ;
13
13
import * as tpl from './tpl' ;
14
14
import { prerenderEmbed } from './embed' ;
15
-
16
- let vueGlobalData ;
15
+ import * as vue from './vue' ;
17
16
18
17
function executeScript ( ) {
19
18
const script = dom
@@ -45,35 +44,14 @@ function formatUpdated(html, updated, fn) {
45
44
function renderMain ( html ) {
46
45
const docsifyConfig = this . config ;
47
46
const markdownElm = dom . find ( '.markdown-section' ) ;
48
- const vueVersion =
49
- 'Vue' in window &&
50
- window . Vue . version &&
51
- Number ( window . Vue . version . charAt ( 0 ) ) ;
52
-
53
- const isMountedVue = elm => {
54
- const isVue2 = Boolean ( elm . __vue__ && elm . __vue__ . _isVue ) ;
55
- const isVue3 = Boolean ( elm . _vnode && elm . _vnode . __v_skip ) ;
56
-
57
- return isVue2 || isVue3 ;
58
- } ;
59
47
60
48
if ( ! html ) {
61
49
html = '<h1>404 - Not found</h1>' ;
62
50
}
63
51
52
+ // Unmount vue
64
53
if ( 'Vue' in window ) {
65
- const mountedElms = dom
66
- . findAll ( '.markdown-section > *' )
67
- . filter ( elm => isMountedVue ( elm ) ) ;
68
-
69
- // Destroy/unmount existing Vue instances
70
- for ( const mountedElm of mountedElms ) {
71
- if ( vueVersion === 2 ) {
72
- mountedElm . __vue__ . $destroy ( ) ;
73
- } else if ( vueVersion === 3 ) {
74
- mountedElm . __vue_app__ . unmount ( ) ;
75
- }
76
- }
54
+ vue . unmountVueInst ( markdownElm , '.markdown-section > *' ) ;
77
55
}
78
56
79
57
this . _renderTo ( markdownElm , html ) ;
@@ -89,130 +67,9 @@ function renderMain(html) {
89
67
executeScript ( ) ;
90
68
}
91
69
92
- // Handle Vue content not mounted by markdown <script>
70
+ // Mount vue
93
71
if ( 'Vue' in window ) {
94
- const vueMountData = [ ] ;
95
- const vueComponentNames = Object . keys ( docsifyConfig . vueComponents || { } ) ;
96
-
97
- // Register global vueComponents
98
- if ( vueVersion === 2 && vueComponentNames . length ) {
99
- vueComponentNames . forEach ( name => {
100
- const isNotRegistered = ! window . Vue . options . components [ name ] ;
101
-
102
- if ( isNotRegistered ) {
103
- window . Vue . component ( name , docsifyConfig . vueComponents [ name ] ) ;
104
- }
105
- } ) ;
106
- }
107
-
108
- // Store global data() return value as shared data object
109
- if (
110
- ! vueGlobalData &&
111
- docsifyConfig . vueGlobalOptions &&
112
- typeof docsifyConfig . vueGlobalOptions . data === 'function'
113
- ) {
114
- vueGlobalData = docsifyConfig . vueGlobalOptions . data ( ) ;
115
- }
116
-
117
- // vueMounts
118
- vueMountData . push (
119
- ...Object . keys ( docsifyConfig . vueMounts || { } )
120
- . map ( cssSelector => [
121
- dom . find ( markdownElm , cssSelector ) ,
122
- docsifyConfig . vueMounts [ cssSelector ] ,
123
- ] )
124
- . filter ( ( [ elm , vueConfig ] ) => elm )
125
- ) ;
126
-
127
- // Template syntax, vueComponents, vueGlobalOptions
128
- const reHasBraces = / { { 2 } [ ^ { } ] * } { 2 } / ;
129
- // Matches Vue full and shorthand syntax as attributes in HTML tags.
130
- //
131
- // Full syntax examples:
132
- // v-foo, v-foo[bar], v-foo-bar, v-foo:bar-baz.prop
133
- //
134
- // Shorthand syntax examples:
135
- // @foo , @foo.bar, @foo.bar.baz, @[foo], :foo, :[foo]
136
- //
137
- // Markup examples:
138
- // <div v-html>{{ html }}</div>
139
- // <div v-text="msg"></div>
140
- // <div v-bind:text-content.prop="text">
141
- // <button v-on:click="doThis"></button>
142
- // <button v-on:click.once="doThis"></button>
143
- // <button v-on:[event]="doThis"></button>
144
- // <button @click.stop.prevent="doThis">
145
- // <a :href="url">
146
- // <a :[key]="url">
147
- const reHasDirective = / < [ ^ > / ] + \s ( [ @ : ] | v - ) [ \w - : . [ \] ] + [ = > \s ] / ;
148
-
149
- vueMountData . push (
150
- ...dom
151
- . findAll ( '.markdown-section > *' )
152
- // Remove duplicates
153
- . filter ( elm => ! vueMountData . some ( ( [ e , c ] ) => e === elm ) )
154
- // Detect Vue content
155
- . filter ( elm => {
156
- const isVueMount =
157
- // is a component
158
- elm . tagName . toLowerCase ( ) in ( docsifyConfig . vueComponents || { } ) ||
159
- // has a component(s)
160
- elm . querySelector ( vueComponentNames . join ( ',' ) || null ) ||
161
- // has curly braces
162
- reHasBraces . test ( elm . outerHTML ) ||
163
- // has content directive
164
- reHasDirective . test ( elm . outerHTML ) ;
165
-
166
- return isVueMount ;
167
- } )
168
- . map ( elm => {
169
- // Clone global configuration
170
- const vueConfig = merge ( { } , docsifyConfig . vueGlobalOptions || { } ) ;
171
-
172
- // Replace vueGlobalOptions data() return value with shared data object.
173
- // This provides a global store for all Vue instances that receive
174
- // vueGlobalOptions as their configuration.
175
- if ( vueGlobalData ) {
176
- vueConfig . data = function ( ) {
177
- return vueGlobalData ;
178
- } ;
179
- }
180
-
181
- return [ elm , vueConfig ] ;
182
- } )
183
- ) ;
184
-
185
- // Mount
186
- for ( const [ mountElm , vueConfig ] of vueMountData ) {
187
- const isVueAttr = 'data-isvue' ;
188
- const isSkipElm =
189
- // Is an invalid tag
190
- mountElm . matches ( 'pre, script' ) ||
191
- // Is a mounted instance
192
- isMountedVue ( mountElm ) ||
193
- // Has mounted instance(s)
194
- mountElm . querySelector ( `[${ isVueAttr } ]` ) ;
195
-
196
- if ( ! isSkipElm ) {
197
- mountElm . setAttribute ( isVueAttr , '' ) ;
198
-
199
- if ( vueVersion === 2 ) {
200
- vueConfig . el = undefined ;
201
- new window . Vue ( vueConfig ) . $mount ( mountElm ) ;
202
- } else if ( vueVersion === 3 ) {
203
- const app = window . Vue . createApp ( vueConfig ) ;
204
-
205
- // Register global vueComponents
206
- vueComponentNames . forEach ( name => {
207
- const config = docsifyConfig . vueComponents [ name ] ;
208
-
209
- app . component ( name , config ) ;
210
- } ) ;
211
-
212
- app . mount ( mountElm ) ;
213
- }
214
- }
215
- }
72
+ vue . mountVueInst ( docsifyConfig , markdownElm , '.markdown-section > *' ) ;
216
73
}
217
74
}
218
75
@@ -246,6 +103,7 @@ export function renderMixin(proto) {
246
103
247
104
proto . _renderSidebar = function ( text ) {
248
105
const { maxLevel, subMaxLevel, loadSidebar, hideSidebar } = this . config ;
106
+ const sidebarElm = dom . find ( '.sidebar-nav' ) ;
249
107
250
108
if ( hideSidebar ) {
251
109
// FIXME : better styling solution
@@ -260,6 +118,11 @@ export function renderMixin(proto) {
260
118
return null ;
261
119
}
262
120
121
+ // Unmount vue
122
+ if ( 'Vue' in window ) {
123
+ vue . unmountVueInst ( sidebarElm , '.sidebar-nav > *' ) ;
124
+ }
125
+
263
126
this . _renderTo ( '.sidebar-nav' , this . compiler . sidebar ( text , maxLevel ) ) ;
264
127
const activeEl = getAndActive ( this . router , '.sidebar-nav' , true , true ) ;
265
128
if ( loadSidebar && activeEl ) {
@@ -269,6 +132,10 @@ export function renderMixin(proto) {
269
132
// Reset toc
270
133
this . compiler . subSidebar ( ) ;
271
134
}
135
+ // Mount vue
136
+ if ( 'Vue' in window ) {
137
+ vue . mountVueInst ( this . config , sidebarElm , '.sidebar-nav > *' ) ;
138
+ }
272
139
273
140
// Bind event
274
141
this . _bindEventOnRendered ( activeEl ) ;
0 commit comments