1
- import { VueConstructor } from 'vue' ;
1
+ import VueInstance , { VueConstructor } from 'vue' ;
2
2
import { SetupContext } from './types/vue' ;
3
3
import { isWrapper } from './wrappers' ;
4
+ import { SetupHookEvent } from './symbols' ;
4
5
import { setCurrentVM } from './runtimeContext' ;
5
- import { isPlainObject , assert , proxy } from './utils' ;
6
+ import { isPlainObject , assert , proxy , noopFn } from './utils' ;
6
7
import { value } from './functions/state' ;
8
+ import { watch } from './functions/watch' ;
9
+
10
+ let disableSetup = false ;
11
+
12
+ // `cb` should be called right after props get resolved
13
+ function waitPropsResolved ( vm : VueInstance , cb : ( v : VueInstance , props : Record < any , any > ) => void ) {
14
+ const safeRunCb = ( props : Record < any , any > ) => {
15
+ // Running `cb` under the scope of a dep.Target, otherwise the `Observable`
16
+ // in `cb` will be unexpectedly colleced by the current dep.Target.
17
+ const dispose = watch (
18
+ ( ) => {
19
+ cb ( vm , props ) ;
20
+ } ,
21
+ noopFn ,
22
+ { lazy : false , deep : false , flush : 'sync' }
23
+ ) ;
24
+ dispose ( ) ;
25
+ } ;
26
+
27
+ const opts = vm . $options ;
28
+ let methods = opts . methods ;
29
+
30
+ if ( ! methods ) {
31
+ opts . methods = { [ SetupHookEvent ] : noopFn } ;
32
+ // This will get invoked when assigning to `SetupHookEvent` property of vm.
33
+ vm . $once ( SetupHookEvent , ( ) => {
34
+ // restore `opts` object
35
+ delete opts . methods ;
36
+ safeRunCb ( vm . $props ) ;
37
+ } ) ;
38
+ return ;
39
+ }
40
+
41
+ // Find the first method will re resovled.
42
+ // The order will be stable, since we never modify the `methods` object.
43
+ let firstMedthodName : string | undefined ;
44
+ for ( const key in methods ) {
45
+ firstMedthodName = key ;
46
+ break ;
47
+ }
48
+
49
+ // `methods` is an empty object
50
+ if ( ! firstMedthodName ) {
51
+ methods [ SetupHookEvent ] = noopFn ;
52
+ vm . $once ( SetupHookEvent , ( ) => {
53
+ // restore `methods` object
54
+ delete methods ! [ SetupHookEvent ] ;
55
+ safeRunCb ( vm . $props ) ;
56
+ } ) ;
57
+ return ;
58
+ }
59
+
60
+ proxy ( vm , firstMedthodName , {
61
+ set ( val : any ) {
62
+ safeRunCb ( vm . $props ) ;
63
+
64
+ // restore `firstMedthodName` to a noraml property
65
+ Object . defineProperty ( vm , firstMedthodName ! , {
66
+ configurable : true ,
67
+ enumerable : true ,
68
+ writable : true ,
69
+ value : val ,
70
+ } ) ;
71
+ } ,
72
+ } ) ;
73
+ }
7
74
8
75
export function mixin ( Vue : VueConstructor ) {
76
+ // We define the setup hook on prototype,
77
+ // which avoids Object.defineProperty calls for each instance created.
78
+ proxy ( Vue . prototype , SetupHookEvent , {
79
+ get ( ) {
80
+ return 'hook' ;
81
+ } ,
82
+ set ( this : VueInstance ) {
83
+ this . $emit ( SetupHookEvent ) ;
84
+ } ,
85
+ } ) ;
86
+
9
87
Vue . mixin ( {
10
- created : functionApiInit ,
88
+ beforeCreate : functionApiInit ,
11
89
} ) ;
12
90
13
91
/**
14
92
* Vuex init hook, injected into each instances init hooks list.
15
93
*/
16
- function functionApiInit ( this : any ) {
94
+ function functionApiInit ( this : VueInstance ) {
17
95
const vm = this ;
18
96
const { setup } = vm . $options ;
19
- if ( ! setup ) {
97
+
98
+ if ( disableSetup || ! setup ) {
20
99
return ;
21
100
}
101
+
22
102
if ( typeof setup !== 'function' ) {
23
103
if ( process . env . NODE_ENV !== 'production' ) {
24
104
Vue . util . warn (
@@ -29,11 +109,16 @@ export function mixin(Vue: VueConstructor) {
29
109
return ;
30
110
}
31
111
112
+ waitPropsResolved ( vm , initSetup ) ;
113
+ }
114
+
115
+ function initSetup ( vm : VueInstance , props : Record < any , any > = { } ) {
116
+ const setup = vm . $options . setup ! ;
117
+ const ctx = createSetupContext ( vm ) ;
32
118
let binding : any ;
33
119
setCurrentVM ( vm ) ;
34
- const ctx = createContext ( vm ) ;
35
120
try {
36
- binding = setup ( vm . $ props || { } , ctx ) ;
121
+ binding = setup ( props , ctx ) ;
37
122
} catch ( err ) {
38
123
if ( process . env . NODE_ENV !== 'production' ) {
39
124
Vue . util . warn ( `there is an error occuring in "setup"` , vm ) ;
@@ -67,43 +152,27 @@ export function mixin(Vue: VueConstructor) {
67
152
} ) ;
68
153
}
69
154
70
- function createContext ( vm : any ) : SetupContext {
155
+ function createSetupContext ( vm : VueInstance & { [ x : string ] : any } ) : SetupContext {
71
156
const ctx = { } as SetupContext ;
72
- const props = [
73
- // 'el', // has workaround
74
- // 'options',
75
- 'parent' , // confirmed in rfc
76
- 'root' , // confirmed in rfc
77
- // 'children', // very likely
78
- 'refs' , // confirmed in rfc
79
- 'slots' , // confirmed in rfc
80
- // 'scopedSlots', // has workaround
81
- // 'isServer',
82
- // 'ssrContext',
83
- // 'vnode',
84
- 'attrs' , // confirmed in rfc
85
- // 'listeners', // very likely
86
- ] ;
87
- const methodWithoutReturn = [
88
- // 'on', // very likely
89
- // 'once', // very likely
90
- // 'off', // very likely
91
- 'emit' , // confirmed in rfc
92
- // 'forceUpdate',
93
- // 'destroy'
94
- ] ;
157
+ const props = [ 'parent' , 'root' , 'refs' , 'slots' , 'attrs' ] ;
158
+ const methodReturnVoid = [ 'emit' ] ;
95
159
props . forEach ( key => {
96
- proxy ( ctx , key , ( ) => vm [ `$${ key } ` ] , function ( ) {
97
- Vue . util . warn ( `Cannot assign to '${ key } ' because it is a read-only property` , vm ) ;
160
+ proxy ( ctx , key , {
161
+ get : ( ) => vm [ `$${ key } ` ] ,
162
+ set ( ) {
163
+ Vue . util . warn ( `Cannot assign to '${ key } ' because it is a read-only property` , vm ) ;
164
+ } ,
98
165
} ) ;
99
166
} ) ;
100
- methodWithoutReturn . forEach ( key =>
101
- proxy ( ctx , key , ( ) => {
102
- const vmKey = `$${ key } ` ;
103
- return ( ...args : any [ ] ) => {
104
- const fn : Function = vm [ vmKey ] ;
105
- fn . apply ( vm , args ) ;
106
- } ;
167
+ methodReturnVoid . forEach ( key =>
168
+ proxy ( ctx , key , {
169
+ get ( ) {
170
+ const vmKey = `$${ key } ` ;
171
+ return ( ...args : any [ ] ) => {
172
+ const fn : Function = vm [ vmKey ] ;
173
+ fn . apply ( vm , args ) ;
174
+ } ;
175
+ } ,
107
176
} )
108
177
) ;
109
178
if ( process . env . NODE_ENV === 'test' ) {
0 commit comments