1 |
|
2 |
|
3 | import Dep from './dep'
|
4 | import VNode from '../vdom/vnode'
|
5 | import { arrayMethods } from './array'
|
6 | import {
|
7 | def,
|
8 | warn,
|
9 | hasOwn,
|
10 | hasProto,
|
11 | isObject,
|
12 | isPlainObject,
|
13 | isPrimitive,
|
14 | isUndef,
|
15 | isValidArrayIndex,
|
16 | isServerRendering
|
17 | } from '../util/index'
|
18 |
|
19 | const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | export let shouldObserve: boolean = true
|
26 |
|
27 | export function toggleObserving (value: boolean) {
|
28 | shouldObserve = value
|
29 | }
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | export class Observer {
|
38 | value: any;
|
39 | dep: Dep;
|
40 | vmCount: number;
|
41 |
|
42 | constructor (value: any) {
|
43 | this.value = value
|
44 | this.dep = new Dep()
|
45 | this.vmCount = 0
|
46 | def(value, '__ob__', this)
|
47 | if (Array.isArray(value)) {
|
48 | if (hasProto) {
|
49 | protoAugment(value, arrayMethods)
|
50 | } else {
|
51 | copyAugment(value, arrayMethods, arrayKeys)
|
52 | }
|
53 | this.observeArray(value)
|
54 | } else {
|
55 | this.walk(value)
|
56 | }
|
57 | }
|
58 |
|
59 | |
60 |
|
61 |
|
62 |
|
63 |
|
64 | walk (obj: Object) {
|
65 | const keys = Object.keys(obj)
|
66 | for (let i = 0; i < keys.length; i++) {
|
67 | defineReactive(obj, keys[i])
|
68 | }
|
69 | }
|
70 |
|
71 | |
72 |
|
73 |
|
74 | observeArray (items: Array<any>) {
|
75 | for (let i = 0, l = items.length; i < l; i++) {
|
76 | observe(items[i])
|
77 | }
|
78 | }
|
79 | }
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | function protoAugment (target, src: Object) {
|
88 |
|
89 | target.__proto__ = src
|
90 |
|
91 | }
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | function copyAugment (target: Object, src: Object, keys: Array<string>) {
|
99 | for (let i = 0, l = keys.length; i < l; i++) {
|
100 | const key = keys[i]
|
101 | def(target, key, src[key])
|
102 | }
|
103 | }
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | export function observe (value: any, asRootData: ?boolean): Observer | void {
|
111 | if (!isObject(value) || value instanceof VNode) {
|
112 | return
|
113 | }
|
114 | let ob: Observer | void
|
115 | if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
|
116 | ob = value.__ob__
|
117 | } else if (
|
118 | shouldObserve &&
|
119 | !isServerRendering() &&
|
120 | (Array.isArray(value) || isPlainObject(value)) &&
|
121 | Object.isExtensible(value) &&
|
122 | !value._isVue
|
123 | ) {
|
124 | ob = new Observer(value)
|
125 | }
|
126 | if (asRootData && ob) {
|
127 | ob.vmCount++
|
128 | }
|
129 | return ob
|
130 | }
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | export function defineReactive (
|
136 | obj: Object,
|
137 | key: string,
|
138 | val: any,
|
139 | customSetter?: ?Function,
|
140 | shallow?: boolean
|
141 | ) {
|
142 | const dep = new Dep()
|
143 |
|
144 | const property = Object.getOwnPropertyDescriptor(obj, key)
|
145 | if (property && property.configurable === false) {
|
146 | return
|
147 | }
|
148 |
|
149 |
|
150 | const getter = property && property.get
|
151 | const setter = property && property.set
|
152 | if ((!getter || setter) && arguments.length === 2) {
|
153 | val = obj[key]
|
154 | }
|
155 |
|
156 | let childOb = !shallow && observe(val)
|
157 | Object.defineProperty(obj, key, {
|
158 | enumerable: true,
|
159 | configurable: true,
|
160 | get: function reactiveGetter () {
|
161 | const value = getter ? getter.call(obj) : val
|
162 | if (Dep.target) {
|
163 | dep.depend()
|
164 | if (childOb) {
|
165 | childOb.dep.depend()
|
166 | if (Array.isArray(value)) {
|
167 | dependArray(value)
|
168 | }
|
169 | }
|
170 | }
|
171 | return value
|
172 | },
|
173 | set: function reactiveSetter (newVal) {
|
174 | const value = getter ? getter.call(obj) : val
|
175 |
|
176 | if (newVal === value || (newVal !== newVal && value !== value)) {
|
177 | return
|
178 | }
|
179 |
|
180 | if (process.env.NODE_ENV !== 'production' && customSetter) {
|
181 | customSetter()
|
182 | }
|
183 |
|
184 | if (getter && !setter) return
|
185 | if (setter) {
|
186 | setter.call(obj, newVal)
|
187 | } else {
|
188 | val = newVal
|
189 | }
|
190 | childOb = !shallow && observe(newVal)
|
191 | dep.notify()
|
192 | }
|
193 | })
|
194 | }
|
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | export function set (target: Array<any> | Object, key: any, val: any): any {
|
202 | if (process.env.NODE_ENV !== 'production' &&
|
203 | (isUndef(target) || isPrimitive(target))
|
204 | ) {
|
205 | warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
|
206 | }
|
207 | if (Array.isArray(target) && isValidArrayIndex(key)) {
|
208 | target.length = Math.max(target.length, key)
|
209 | target.splice(key, 1, val)
|
210 | return val
|
211 | }
|
212 | if (key in target && !(key in Object.prototype)) {
|
213 | target[key] = val
|
214 | return val
|
215 | }
|
216 | const ob = (target: any).__ob__
|
217 | if (target._isVue || (ob && ob.vmCount)) {
|
218 | process.env.NODE_ENV !== 'production' && warn(
|
219 | 'Avoid adding reactive properties to a Vue instance or its root $data ' +
|
220 | 'at runtime - declare it upfront in the data option.'
|
221 | )
|
222 | return val
|
223 | }
|
224 | if (!ob) {
|
225 | target[key] = val
|
226 | return val
|
227 | }
|
228 | defineReactive(ob.value, key, val)
|
229 | ob.dep.notify()
|
230 | return val
|
231 | }
|
232 |
|
233 |
|
234 |
|
235 |
|
236 | export function del (target: Array<any> | Object, key: any) {
|
237 | if (process.env.NODE_ENV !== 'production' &&
|
238 | (isUndef(target) || isPrimitive(target))
|
239 | ) {
|
240 | warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target: any)}`)
|
241 | }
|
242 | if (Array.isArray(target) && isValidArrayIndex(key)) {
|
243 | target.splice(key, 1)
|
244 | return
|
245 | }
|
246 | const ob = (target: any).__ob__
|
247 | if (target._isVue || (ob && ob.vmCount)) {
|
248 | process.env.NODE_ENV !== 'production' && warn(
|
249 | 'Avoid deleting properties on a Vue instance or its root $data ' +
|
250 | '- just set it to null.'
|
251 | )
|
252 | return
|
253 | }
|
254 | if (!hasOwn(target, key)) {
|
255 | return
|
256 | }
|
257 | delete target[key]
|
258 | if (!ob) {
|
259 | return
|
260 | }
|
261 | ob.dep.notify()
|
262 | }
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 | function dependArray (value: Array<any>) {
|
269 | for (let e, i = 0, l = value.length; i < l; i++) {
|
270 | e = value[i]
|
271 | e && e.__ob__ && e.__ob__.dep.depend()
|
272 | if (Array.isArray(e)) {
|
273 | dependArray(e)
|
274 | }
|
275 | }
|
276 | }
|