当前位置:网站首页>What is a responsive object? How to create a responsive object?
What is a responsive object? How to create a responsive object?
2022-07-06 23:56:00 【Feather_ seventy-four】
The theme : What is a responsive object ? The creation process of responsive objects ?
What is a responsive object ?
Vue.js The core of implementing responsive is to take advantage of ES5 Of Object.defineProperty. because IE8 And the following browsers do not Object.defineProperty Method , That's why Vue.js Incompatible IE8 And the following browser reasons .
Object.defineProperty
Object.defineProperty(obj, prop, descriptor)
var o = {
};
Object.defineProperty(o, "a", {
value : 37,
writable : true,
enumerable : true,
configurable : true
});
// o With attributes a,a The value of is 37
var bValue = 38;
Object.defineProperty(o, "b", {
get() {
return bValue; },
set(newValue) {
bValue = newValue; },
enumerable : true,
configurable : true
});
// o With attributes b,b The value of is bValue. Now? , Unless we redefine o.b,o.b The value of is always with bValue identical
Object.defineProperty(o, 'c', {
value: 39,
writable: true,
enumerable: false,
configurable: true
})
// When enumerable by false when ,Object.keys(ob) Will not show 'c'
obj Is the object on which the attribute is to be defined .prop Is the name of the attribute to be defined or modified .descriptor Is the attribute descriptor to be defined or modified . Once the object has getter and setter attribute , We can simply call this object Responsive object .get It's a... For properties getter Method , When we access this property, it will trigger getter Method ;set It's a... For properties setter Method , When we modify this attribute, it will trigger setter Method .
that Vue.js Which objects have become responsive objects , It can be analyzed from the source code level .
The creation process of responsive objects
First, let's talk about the mapping of raw data to DOM in , props and data When initializing, you can define responsive objects . props By calling defineReactive() How to put each props Of key Become responsive . data It's through observe() Changes in monitoring data ,Observer Add... To the properties of an object getter and setter.
src/core/instance/init.js
initialization Vue Called when _init Method ,init Methods include initState() Method .
src/core/instance/state.js
(1)initState
This method mainly initializes props、methods、data、computed and wathcer Wait for the property to be initialized .
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {
}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
The key analysis initProps() and initData()
(2)initProps
call defineReactive() How to put each props Of key Become responsive
function initProps (vm: Component, propsOptions: Object) {
const propsData = vm.$options.propsData || {
}
const props = vm._props = {
}
// cache prop keys so that future props updates can iterate using Array
// instead of dynamic object key enumeration.
const keys = vm.$options._propKeys = []
const isRoot = !vm.$parent
// root instance props should be converted
if (!isRoot) {
toggleObserving(false)
}
for (const key in propsOptions) {
keys.push(key)
const value = validateProp(key, propsOptions, propsData, vm)
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
const hyphenatedKey = hyphenate(key)
if (isReservedAttribute(hyphenatedKey) ||
config.isReservedAttr(hyphenatedKey)) {
warn(
`"${
hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
vm
)
}
defineReactive(props, key, value, () => {
if (!isRoot && !isUpdatingChildComponent) {
warn(
`Avoid mutating a prop directly since the value will be ` +
`overwritten whenever the parent component re-renders. ` +
`Instead, use a data or computed property based on the prop's ` +
`value. Prop being mutated: "${
key}"`,
vm
)
}
})
} else {
defineReactive(props, key, value)
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
// instantiation here.
if (!(key in vm)) {
proxy(vm, `_props`, key)
}
}
toggleObserving(true)
}
(3)initData
Traverse all Object.keys(data), stay methods or props If it is defined in, it will alarm . hold _data Agent to vm For instance .observe() The way is to observe data.( The code that goes deep into the principle of responsiveness is basically src/core/observer On )
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {
}
if (!isPlainObject(data)) {
data = {
}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${
key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${
key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
}
1)observe
function :observe Is used to monitor data changes .
observe The function of the method is to give and give VNode Object type data to add a Observer, If it has been added, it will return to , Otherwise, instantiate a Observer Object instances .
Code parsing :observe Method accepts two parameters ,value It's any type , What's coming in is data,asRootData What's coming in is true.
Judge value Whether it is Object type ,value Whether it is VNode example .
instanceofThe role of :AinstanceofB , Judge A Whether it is B Example
And then determine value Is there any __ob__ Attributes and value.__ob__ Whether it is Observer example . Meet the conditions and get it directly ob and return. Otherwise, make the next judgment . Judge shouldObserve.
shouldObserve It is a sign of global definition , Used to decide when to execute new Observer.toggleObserving Methods shouldObserve Value change , The method in initProps Method by judgment isRoot Yes shouldObserve Make changes . once shouldObserve yes false, There is no way to value become Observer Object instances .
export let shouldObserve: boolean = true
export function toggleObserving (value: boolean) {
shouldObserve = value
}
At the same time, judge value No ServerRendering, Are arrays and objects, and objects have extensible properties , We have to judge again value No Vue. That's what makes value become Observer Object instances .
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
2)Observer
function :Observer Is a class , Its function is to add... To the properties of an object getter and setter, Used to rely on collection and distribution of updates
Code parsing :Observer It's a class , It can be understood as an observer . Attributes are value、dep、vmCount, Constructor will retain value, Instantiation dep.
def Method pair Object.defineProperty encapsulate , It's for value add to __ob__ attribute , The value of this attribute points to the current instance . To execute observe() When , After the first definition, the next time it will be directly for the same object return ob.
enumerable?: boolean: As Typescript Definition method when the number of interface attributes is uncertain . Either the parameter does not exist , Either it must conform to the type definition of the parameter .
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
Judge value Is it an array ,observeArray() The way is to traverse value, Then recursively observe . If it is not an array but an object, call walk().walk() Traverse all properties on the object , perform defineReactive() Method . If you just take __ob__ Assign a value to value,__ob__ Will perform defineReactive, It's totally unnecessary , Because I won't modify __ob__. Why? __ob__ Not execute defineReactive? because def Only three parameters were passed ,enumerable yes undefined , therefore !!enumerable yes false, So the traversal will not be executed to ( Prototypes are also not enumerable ).
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that have this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
this.walk(value)
}
}
/** * Walk through all properties and convert them into * getter/setters. This method should only be called when * value type is Object. */
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
/** * Observe a list of Array items. */
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
3)defineReactive
function :defineReactive The function of is to define a responsive object , Add... To the object dynamically getter and setter.
Code parsing :
Accept five parameters : object 、 Object property value 、 Initial value 、 Two optional . Get the object attribute definition property, Get the original property getter and setter, When a property value is also an object , Recursively observe. This ensures that no matter obj How complex is the structure of , All its sub properties can also become responsive objects , So we visit or modify obj A deeply nested property in , Can also trigger getter and setter.
export function defineReactive (
obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}

边栏推荐
- Supersocket 1.6 creates a simple socket server with message length in the header
- Common modification commands of Oracle for tables
- [boutique] Pinia Persistence Based on the plug-in Pinia plugin persist
- 编译logisim
- 【精品】pinia 基于插件pinia-plugin-persist的 持久化
- matplotlib画柱状图并添加数值到图中
- (LeetCode)两数之和
- Asset security issues or constraints on the development of the encryption industry, risk control + compliance has become the key to breaking the platform
- 【CVPR 2022】目标检测SOTA:DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection
- 在Docker中分分钟拥有Oracle EMCC 13.5环境
猜你喜欢

MATLIB reads data from excel table and draws function image

服务器SMP、NUMA、MPP体系学习笔记。

《数字经济全景白皮书》保险数字化篇 重磅发布

leetcode:236. 二叉树的最近公共祖先

Today, I met a senior test developer from Tencent and saw the ceiling of the foundation

Why is bat still addicted to 996 when the four-day working system is being tried out in Britain?

Yaduo Sangu IPO

AVL树到底是什么?

Who said that new consumer brands collapsed? Someone behind me won

The programmer said, "I'm 36 years old, and I don't want to be rolled, let alone cut."
随机推荐
Gradle知识概括
Design a red envelope grabbing system
There are only two TXT cells in the ArrayExpress database. Can you only download the sequencing run matrix from line to ENA?
Gradle knowledge generalization
快手的新生意,还得靠辛巴吆喝?
Experiment 6: installing eve-ng
The programmer said, "I'm 36 years old, and I don't want to be rolled, let alone cut."
The programmer refused the offer because of low salary, HR became angry and netizens exploded
在docker中快速使用各个版本的PostgreSQL数据库
MVC and MVVM
服务器SMP、NUMA、MPP体系学习笔记。
Cas d'essai fonctionnel universel de l'application
app通用功能測試用例
Hydrogen future industry accelerates | the registration channel of 2022 hydrogen energy specialty special new entrepreneurship competition is opened!
达晨史上最大单笔投资,今天IPO了
2022 latest blind box mall complete open source operation source code / docking visa free payment interface / building tutorial
电脑重装系统u盘文件被隐藏要怎么找出来
量子时代计算机怎么保证数据安全?美国公布四项备选加密算法
数据运营平台-数据采集[通俗易懂]
设计一个抢红包系统