Análise do código-fonte Vue2 do princípio de responsividade de dados + como responder ao princípio de responsividade Vue2 durante o processo de entrevista

Pergunta da entrevista: (O conteúdo em [parte da fonte vermelha] é mais profundo, opcional para falar, a cereja do bolo, se você não consegue entender, não precisa falar sobre isso)

1.Como o Vue2 implementa a capacidade de resposta? (Do raso ao profundo, chega de brincadeiras)

        Em primeiro lugar, um dos princípios básicos do Vue2 para implementar a capacidade de resposta é usar a função Object.defineProperty(). Sua função principal é sequestrar/proxy dados. Como você deseja implementar a capacidade de resposta, você precisa ter gatilhos e respondedores. Os dados são o gatilho. Ou então Object.defineProperty() monitora as alterações de dados, principalmente por meio dos métodos set e get. Quando acessamos os dados, o método get será acionado, como obj.a. Quando modificamos os dados, o método set será acionado, como obj.a=6, para que possamos saber que os dados estão mudando, mas como o valor modificado de set não pode ser capturado por get no tempo, precisamos deixar o valor de retorno de get retorna uma variável, e é uma variável que precisa ser retida. Esperamos que ela exista na próxima vez que visitarmos, mas não queremos declará-la como uma variável global para poluir o meio ambiente, então usamos fechamentos. Nós encapsulamos a função Object.defineProperty() no método de função defineReactive(). Declaramos var tep globalmente dentro da função. Como o valor de retorno de get, os dados são atualizados em tempo real. Neste ponto, podemos capturar as alterações em os dados no tempo. Em outras palavras, os dados podem ser usados ​​como um gatilho.

arrayMethods); Modifique o ponteiro array __proto__ para que ele aponte para arrayMethods e depois para o protótipo Array.prototype. O objetivo disso é primeiro encontrar a função no objeto arrayMethods reescrito ao chamar os sete métodos. Caso contrário, vá para o próximo passo. No primeiro nível, aqui está o uso do conhecimento da cadeia de protótipos. O método de chamada js será pesquisado em toda a cadeia de protótipos, para que a modificação dos dados possa ser monitorada em arrayMethods e processada. É importante notar que o método arrayMethods ainda precisa apontar para Array.prototype, porque além dos sete métodos reescritos, existem muitos outros métodos para arrays, portanto, faça backup dos métodos em Array.prototype com antecedência. Depois de modificar o sete métodos, você precisa restaurar o método apply para sua função original (os sete métodos estão em Depois de monitorar os dados, a função original ainda precisa ser implementada. Se você apenas alterar o ponteiro sem restaurar a função, a alteração será detectado, mas as operações de adição, exclusão e modificação do array não serão realmente feitas), então também há um problema aqui, ou seja, através de arr [2], este tipo de dados modificados com subscrito do array não pode ser detectado. Isso também é um problema clássico do Vue2. No entanto, o objeto proxy usado pelo Vue3 para implementar a capacidade de resposta resolve esse problema. A diferença do Vue2 é que o proxy modifica o proxy. Objeto, em vez do objeto de origem, de modo que, uma vez que os dados mudam, as mudanças podem ser capturado a tempo. Para os métodos no protótipo, após modificar os sete métodos, o método apply precisa ser restaurado à sua função original (os sete métodos ainda precisam realizar a função original após monitorar os dados. Se você apenas alterar o apontamento e não restaurar a função, então as alterações serão detectadas. , mas as operações de adição, exclusão e modificação do array não são realmente feitas), então também há um problema aqui, ou seja, através de arr[2], esse tipo de array dados de modificação de subscrito não podem ser detectados. Este também é um problema clássico do Vue2. No entanto, o objeto proxy usado no Vue3 para implementar a capacidade de resposta resolve esse problema. Ao contrário do Vue2, o proxy modifica o objeto proxy, não o objeto de origem, de modo que uma vez que os dados mudanças, as mudanças podem ser capturadas a tempo. Para os métodos no protótipo, após modificar os sete métodos, o método apply precisa ser restaurado à sua função original (os sete métodos ainda precisam realizar a função original após monitorar os dados. Se você apenas alterar o apontamento e não restaurar a função, então as alterações serão detectadas. , mas as operações de adição, exclusão e modificação do array não são realmente feitas), então também há um problema aqui, ou seja, através de arr[2], esse tipo de array dados de modificação de subscrito não podem ser detectados. Este também é um problema clássico do Vue2. No entanto, o objeto proxy usado no Vue3 para implementar a capacidade de resposta resolve esse problema. Ao contrário do Vue2, o proxy modifica o objeto proxy, não o objeto de origem, de modo que uma vez que os dados mudanças, as mudanças podem ser capturadas a tempo.

Então, quando há um gatilho, deve haver um respondente, e a classe Watcher e a classe Dep são usadas (PS: há um resumo mais detalhado e popular na parte inferior) Eles usam principalmente dois pontos: um é a publicação de assinatura modelo, e o outro é obter coletar dependências e acionar dependências no conjunto. A principal função do Watcher é coletar dependências e armazená-las na classe Dep quando a função de obtenção de dados é acionada. A classe Dep é mais como um bloco de notas. Ele armazena as dependências em uma matriz. Quando os dados mudam, o método set será acionado e o método set acionará o método de notificação na classe Dep. A instância Dep percorrerá a lista de notificações e os observadores relacionados às dependências. ), e então o(s) observador(es) irão até o dom para fazer uma atualização da View, completando assim a capacidade de resposta dos dados

        Dicas: As dependências podem parecer um pouco abstratas, mas na verdade são muito simples. Por exemplo, quais dados são usados ​​por uma página ou componente, então quando esses dados são atualizados, as páginas e componentes correspondentes devem detectar alterações, então podem ser disse que esta página ou componente depende desses dados, pois os dados são muito grandes, foi proposto o conceito de watcher, que pode gerenciar os dados em um intervalo menor e evitar o overhead de atualizar a página inteira quando ocorre uma alteração de dados. No observador, atualizações parciais de componentes podem ser executadas para reduzir a sobrecarga.

1. Capacidade de resposta simples de implementação de dados

1. A principal função de Object.defineProperty() é sequestro de dados/proxy de dados

Definição: Defina uma nova propriedade diretamente em um objeto ou modifique uma propriedade existente de um objeto e retorne o objeto.

Valor do atributo: (parte/comumente usado)

① Defina um novo atributo em um objeto, os atributos a e b em obj

var obj ={}

Object.defineProperty(obj,'a',{

value:3

})

Object.defineProperty(obj,'5',{

value:5

})

console.log(obj)

console.log(obj.a,obj.b)

打印结果是:

{a:3,b:5}

3 5

②writable define se um atributo é gravável.Quando o atributo a é definido como gravável: false, obj.a++ não pode incrementar o valor a no objeto.

③ Se um atributo enumerável pode ser enumerado. Quando o atributo b é definido como enumerável: falso, obj.b não pode ser executado em loop através da travessia for ou forEach. É frequentemente usado para definir alguns valores que não desejam ser modificado, como ob responsivo.valor de classe

④obter e definir funções (importante)

Object.defineProperty(obj,'a',{

get(){

console.log(“你试图访问obj的a属性”)

}

set(){

console.log(“你试图改变obj的a属性”)

}

})

Deve-se notar aqui que get e value não podem existir ao mesmo tempo. Se você definir get, precisará excluir o valor: 3. Este é na verdade o sequestro de dados. A função do sequestro é na verdade fazer algo quando o atributo é acessado ou alterado, então há get e set

2.defineReativo()

Antecedentes: ① Quando get é acessado, ele precisa retornar um valor através de return, mas se retornar uma constante, não pode retornar imediatamente após a modificação de set; ② É necessário reter variáveis ​​​​e não poluir o ambiente global. O código é do seguinte modo

var obj ={}

Object.defineProperty(obj,'a',{

get(){

console.log(“你试图访问obj的a属性”)

return 7

}

set(newValue){

console.log(“你试图改变obj的a属性”,newValue)

}

})

console.log(obj.a)

obj.a = 9

console.log(obj.a) // 7

O resultado impresso é:

Você está tentando acessar a propriedade a do obj 7

Você está tentando alterar a propriedade a do obj 9

Você está tentando acessar a propriedade a do obj 7

explicar:

Embora o valor de a seja modificado e impresso no conjunto, ele não pode ser atualizado para obj.a a tempo, portanto, neste momento, uma variável de transição é necessária para resolver este problema.

solução:

var temp;

Object.defineProperty(obj,'a',{

get(){

console.log(“你试图访问obj的a属性”)

return temp

}

set(newValue){

console.log(“你试图改变obj的a属性”,newValue)

temp = newValue

}

})

Isso pode resolver esse problema e retornar o valor atualizado a tempo. Neste momento, estamos diante de um novo problema. Precisamos definir uma variável que será retida, mas não queremos que ela seja global e polua o ambiente da variável Isto Quando você precisa usar fechamentos, você precisa encapsular uma função defineReactive().

var obj = {}

function defineReactive(data,key,val){

//如果传入的是两个参数值,值等于对象本身值

if (arguments.length == 2) {

val = data[key]

}

Object.defineProperty(data,key,{

//可枚举

enumerable:true,

//可以被配置,比如可以被delete

configurable:true,

//getter

get(){

console.log('访问obj的a属性')

return val;

}

set(newValue){

console.log('修改obj的a属性',newValue)

//当新值和旧值一样的时候直接return无需修改

if(val === newValue) {

return;

}

val = newValue

}

})

}

Desta forma, podemos definir o valor do atributo a do objeto obj através de defineReactive(obj,'a',10), realizando assim uma capacidade de resposta de dados simples. Quando acessarmos e modificarmos os dados, reagiremos. Quando pudermos monitorar alterações nos dados pode realizar as operações necessárias com base nas alterações.

2. Implementar capacidade de resposta para objetos complexos (objetos aninhados em objetos):

fundo:

var obj = {

a:{

    m:{

        n:5

    }

}

b:3

}

Conforme mostrado no código acima, quando um objeto é aninhado dentro de um objeto, os dados internos não podem ser acessados ​​de forma responsiva por meio de obj.amn. O valor n no objeto ainda não implementou a capacidade de resposta. Mesmo que os dados possam ser acessados, eles não podem ser sequestrado ou usado.

1. Defina a classe Observador:

efeito:

Converta um objeto normal em um objeto cujas propriedades em cada nível sejam responsivas (valores que podem ser monitorados).

export default class Observer {

//构造器

constructor(value){

//1.给实例(this,一定要注意,构造函数中的this不是表示类本身,而是实例,也就是this指向的是实 例本身)2.这里的def就是下述1.2中定义的

def(value,'ob',this,false)

console.log("我是Observer构造器",value) //能够在实例中看到ob属性,先标记

//下面就是Observer的作用,将这层的函数转换成响应式

this.walk(value)

}

//遍历

walk(value) {

for (let k of value){

defineReactive(value,k)

}

}

}

1.1 Os objetos geralmente têm muitos níveis. Embora a classe Observer possa converter os dados de cada camada em capacidade de resposta, para garantir que cada camada seja convertida em capacidade de resposta, um atributo chamado __ob__ precisa ser introduzido. Quando uma camada é convertida em capacidade de resposta por Observer Adicione este atributo. Caso contrário, você precisará chamar a classe Observer para convertê-lo em capacidade de resposta. Por analogia, converter um objeto multinível em capacidade de resposta de fora para dentro pode completar toda a capacidade de resposta de um objeto.

//创建observe函数,这里不同Observer类,这里没有r注意区分,他的作用在上述说明了。

function observe(value){

//如果value不是对象,什么都不需要做

if (typeof value != 'object') return;

//定义ob,判断这层是否已经被转换成响应式

var ob

//如果存在ob,将value.ob赋值给ob属性,通过return将其作为observe的返回值返回

if (typeof value.ob !=='undefined') {

ob = value.ob

} else {

ob = new Observer(value)

}

return ob

}

1.2 Crie o arquivo utils.js, que armazena a função def. Sua função é simplificar a função Object.defineProperty() e definir um padrão de dados graváveis ​​​​e excluídos.

export const def = function (obj,key,value,enumerable){

Object.defineProperty(obj,key,{

value,

//是否可被枚举

enumerable,

//是否可写

writable:true,

//是否可被删除

configurable:true

})

}

2. Reescreva a função defineProperty, introduza o atributo observe, implemente chamadas circulares e execute o processamento responsivo de todas as propriedades do objeto.

function defineReactive(data,key,val){

//如果传入的是两个参数值,值等于对象本身值

if (arguments.length == 2) {

val = data[key]

}



// 子元素要进行observe,至此形成了“递归”,这里的递归并不是自己调用自己,而是多个函数、类循环调 用的过程。

let childOb = observe(val)

Object.defineProperty(data,key,{

//可枚举

enumerable:true,

//可以被配置,比如可以被delete

configurable:true,

//getter

get(){

console.log('访问obj的a属性')

return val;

}

set(newValue){

console.log('修改obj的a属性',newValue)

//当新值和旧值一样的时候直接return无需修改

if(val === newValue) {

return;

}

val = newValue

//当设置了新值的时候,这个新值也需要被observe

childOb = observe(newValue)

}

})

}

Resumir:

Primeiro, inicie o processamento reativo através da função observe(obj). Sua função é a entrada e também determinar se a camada é processada reativamente. Se não houver processamento reativo, não haverá atributo __ob__. Neste momento, uma classe irá ser instanciado, que também é novo Observer ., passe obj como parâmetro de valor.

if (typeof value.__ob__!=='undefined') {

ob = value.__ob__

} else {

ob = new Observer(value)

}

Então é a vez da classe Observer desempenhar seu papel. Seu papel é adicionar o atributo __ob__ a esta camada de objetos. O Observer é julgado pelo atributo __ob__ e o Observer define o atributo __ob__. Após defini-lo, uma etapa importante precisa ser feito, ou seja, processar esta camada de dados de forma responsiva.

①def(value,' __ob__ ',this,false) //Defina o atributo __ob__ para indicar que esta camada foi processada responsivamente

②Na função walk, passe todos os dados desta camada para defineReactive() para processamento responsivo por meio de travessia

walk(value) {

for (let k of value){

defineReactive(value,k)

}

}

Se forem dados simples, termina aqui, mas a discussão aqui é sobre objetos aninhados, então vai para o próximo nível. Aqui, os valores dos atributos correspondentes aos k nomes de atributos e objetos, então se trata do Etapa defineReactive().

defineReactive(data,key,val)//O val aqui é o valor do atributo correspondente ao nome do atributo

Passe o valor para a função observe(). Além de determinar se __ob__ existe, a função observe também pode determinar se é um objeto. Se não for um objeto e for retornado diretamente, é equivalente a val não ser processado em de qualquer forma. Mas se for um objeto, isso equivale ao início de uma nova rodada. Aqui, observar retorna ao seu papel original. Não é apenas para identificar se esta camada foi processada de forma reativa, mas também para servir como o função de entrada. Neste ponto, a chamada de loop desempenha um papel. Este também é um lugar inteligente. , porque é diferente da recursão, não há necessidade de se preocupar com o problema do loop infinito com este método de chamada cíclica. A última camada deve ser um valor específico, e o objeto deve ser, em última análise, para a conveniência de armazenar o valor, para que ele retorne após a última camada não ser um objeto. Está de volta, e childOb não é uma condição de julgamento de loop nem nada, então, naturalmente, há sem loop infinito.

deixe filhoOb = observar (val)

Outro ponto a ser observado é que na função set o novo valor também precisa ser testado. O motivo é muito simples. Ele pode ser alterado de um valor simples para um objeto, como b:3 é alterado para b:{b1 :2,b2: 6} Então equivale a aprofundar outro nível, e é definitivamente necessário continuar a fazer o processamento responsivo.

val = novoValor

//Quando um novo valor é definido, esse novo valor também precisa ser observado

filhoOb = observar(novoValor)

Desde então, o processamento responsivo de objetos foi concluído.

3. Processamento responsivo de dados

fundo:

As alterações nos dados da matriz não podem ser monitoradas conforme o esperado

resolver:

Reescreva os sete métodos do array.Esses sete métodos são escritos no protótipo Array, que é Array.prototype.

A ideia é criar um objeto arrayMethods baseado em Array.prototype, que possui métodos. Todos nós conhecemos o conceito da cadeia de protótipos, que é encontrar um método. Se nenhum método correspondente for encontrado neste objeto, ele seguirá o protótipo cadeia. Pesquise abaixo. O protótipo finalmente encontrado para o array é Array.prototype. Aqui precisamos reescrever sete métodos. Ou seja, ao completar essas sete operações, esperamos poder fazer algumas operações customizadas, então alteramos o protótipo cadeia da matriz. Aponte para, primeiro aponte para o objeto arrayMethods que criamos e, em seguida, aponte para ele após o processamento

1. Reescreva o método array

//得到Array.prototype

const arrayPrototype = Array.prototype

// 以Array.prototype为原型创建arrayMethods对象并且暴露出去,为Observer判断所用

export const arrayMethods = Object.create(arrayPrototype)

// 要被改写的七个数组方法

const methodsNeedChange = [

'push',

'pop',

'shift',

'unshift',

'splice',

'sort',

'reverse'

]

methodsNeedChange.forEach(methodName=>{

//备份原有的方法,这步很巧妙,因为改写只有七个方法,原来还有很多方法,并且改写后数组依旧需要有 原来的功能,所以这步必不可少

const original = arrayPrototype[methodName]

//定义新的方法

def(arrayMethods,methodName,function(){

console.log("123")

//这里有需要注意的点就是包裹函数不能写成箭头函数,如果是箭头函数this指向就出问题了,目前this指 向的就是调用者也就是数组,如果是箭头函数也没有arguments了,所以这里必须是这样,这里的this就是 数组的上下文,例如a.push(1,2,3)那么push就是this,(1,2,3)就是arguments或者说参数

//恢复原有功能,通过绑定this的指向,并且调用原有方法,bind不会调用,apply和call会调用

original.apply(this,arguments)

},false)

})

1.1 Reescreva o construtor da função Observer para que ele possa detectar o array

constructor(value){

//1.给实例(this,一定要注意,构造函数中的this不是表示类本身,而是实例,也就是this指向的是实 例本身)2.这里的def就是下述1.2中定义的

def(value,'ob',this,false)

console.log("我是Observer构造器",value) //能够在实例中看到ob属性,先标记

//下面就是Observer的作用,将这层的函数转换成响应式

**判断他是数组还是对象

if (Array.isArray(value){

//如果是数组,就强行将这个数组的原型指向arrayMethods

Object.setPrototypeOf(value,arrayMethods);

//让这个数组变observe

this.observeArray(value)

}else{

this.walk(value)

})

//数组的特殊遍历

observeArray(arr) {

for (let i=0,l=arr.length;i<l;i++){

//逐项进行observe

observe(arr[i])

}

}

}

1.2

methodsNeedChange.forEach(methodName=>{

//备份原有的方法,这步很巧妙,因为改写只有七个方法,原来还有很多方法,并且改写后数组依旧需要有 原来的功能,所以这步必不可少

const original = arrayPrototype[methodName]

//定义新的方法

def(arrayMethods,methodName,function(){

// 把这个数组身上的ob取出来,ob已经被添加了,为什么已经被添加了?因为数组肯定不是最高层,比 如Vue初始化数组的时候有一个data对象,然后将数组对象放在里面,所以Observer在初始化data对象 的时候就为数组对象添加了ob属性,因为数组对象其实也是一个对象,所以会为他加上ob属性,所以能 够拿到

//因为arguments是类数组对象,要将其转换成数组对象

const args = [...arguments]

const ob = this.ob

//有三种方法push\unshift\splice能够插入新项,现在要把插入的新项也要变成observe的,也就是响应 式的

let inserted = []

swith(methodName) {

case 'push':

case 'unshift':

inserted = args

break

case 'splice':

//因为splice方法使用的时候是splice(下标,数量,插入的新项),所以这里取2

inserted = args.slice(2)

break

}

//让新项也变成响应式的

if(inserted){

ob.observeArray(inserted)

}

console.log("123")

//这里有需要注意的点就是包裹函数不能写成箭头函数,如果是箭头函数this指向就出问题了,目前this 指向的就是调用者也就是数组,如果是箭头函数也没有arguments了,所以这里必须是这样,这里的this 就是数组的上下文,例如a.push(1,2,3)那么push就是this,(1,2,3)就是arguments或者说参 数

//恢复原有功能

original.apply(this,arguments)

},false)

})

Depois de concluir a parte acima, o array pode se tornar responsivo e as operações no array podem ser monitoradas por meio de sete métodos. Porém, há um ponto que precisa de atenção aqui, que também é um problema clássico do vue2, ou seja, se você passa obj. A modificação do método g[3] = 56 não pode ser monitorada porque não pertence a nenhum dos sete métodos.

4. Coleta de dependências

conceito:

Onde os dados precisam ser usados, isso é chamado de dependência. Em minhas palavras, se uma mudança nos dados afetará outros dados para serem alterados de acordo, então posso dizer que outros dados dependem desses dados. Coletar dependências significa coletar essas coisas que serão afetado por mim. dados.

Vue1.x, dependências refinadas, todos os DOMs que usam dados são dependências

Vue2.X, dependências de granulação média, componentes que usam dados são dependências

Recursos do Vue:

Colete dependências em getters e acione dependências em setters

1. Classe Dep e classe Watcher

1.1 Classe de dependência

Função: Encapsular o código de coleta de dependências em uma classe Dep, que é especialmente usada para gerenciar dependências.Cada instância do Observer possui uma instância Dep entre seus membros.

1.2 Classe do observador

Função: Ele é um intermediário, quando os dados mudam, eles são retransmitidos através do Watcher para notificar o componente.

2. Ideias de implementação:

A dependência é o observador. Apenas getters acionados por observadores coletarão dependências. Quais getters acionadores de inspetores serão coletados no Dep.

Dep usa o modelo de publicação-assinatura. Quando os dados mudam, ele percorre a lista de dependências e notifica todos os observadores.

A inteligência da implementação do código: o observador se define globalmente para um local especificado e depois lê os dados.Como os dados são lidos, o getter desses dados será acionado. No getter, você pode fazer com que o inspetor esteja lendo os dados no momento e coletá-lo no dep.

Estrutura do código da classe 3.Dep:

export default class Dep {

constructor() {

//用数组存贮自己的订阅者,subs是英语订阅者的意思,全称subscribes

//这个数组里面放的是Watcher的实例

this.subs = []

}

//添加订阅

addSub(){

this.subs.push(sub)

}

// 添加依赖

depend(){

//Dep.target就是一个我们自己指定的全局的位置,和window.target一样,只要是全局唯一,没有歧义就可以了

if(Dep.target){

this.addSub(Dep.target)

}

}

//通知更新

notify(){

//浅克隆一份

const subs = this.subs.slice()

//遍历通知订阅者

for (let i = 0,l=subs.length; i<l;i++){

subs[i].update()

}

}

}

3.1 Instancie a classe dep no construtor Observer

constructor(value){

**实例化dep,每一个Observer的实例身上都有一个dep,作用就是存储收集的依赖

this.dep = new Dep()

//1.给实例(this,一定要注意,构造函数中的this不是表示类本身,而是实例,也就是this指向的是实 例本身)2.这里的def就是下述1.2中定义的

def(value,'ob',this,false)

console.log("我是Observer构造器",value) //能够在实例中看到ob属性,先标记

//下面就是Observer的作用,将这层的函数转换成响应式

**判断他是数组还是对象

if (Array.isArray(value){

//如果是数组,就强行将这个数组的原型指向arrayMethods

Object.setPrototypeOf(value,arrayMethods);

//让这个数组变observe

this.observeArray(value)

}else{

this.walk(value)

})

//数组的特殊遍历

observeArray(arr) {

for (let i=0,l=arr.length;i<l;i++){

//逐项进行observe

observe(arr[i])

}

}

}

3.2 Instanciação no fechamento defineReactive

function defineReactive(data,key,val){

//**在闭包中实例化一个Dep实例

const dep = new Dep()

//如果传入的是两个参数值,值等于对象本身值

if (arguments.length == 2) {

val = data[key]

}



// 子元素要进行observe,至此形成了“递归”,这里的递归并不是自己调用自己,而是多个函数、类循环调 用的过程。

let childOb = observe(val)

Object.defineProperty(data,key,{

//可枚举

enumerable:true,

//可以被配置,比如可以被delete

configurable:true,

//getter

get(){

console.log('访问obj的a属性')

//如果现在处于以来的收集阶段

if (Dep.target) {

dep.depend()

if(childOb){

childOb.dep.depend()

}

}

return val;

}

set(newValue){

console.log('修改obj的a属性',newValue)

//当新值和旧值一样的时候直接return无需修改

if(val === newValue) {

return;

}

val = newValue

//当设置了新值的时候,这个新值也需要被observe

childOb = observe(newValue)

//*发布订阅通知,通知dep

dep.notify()

}

})

}

②A mesma matriz também precisa ser notificada aqui

//让新项也变成响应式的

if(inserted){

ob.observeArray(inserted)

}

ob.dep.notify()

4.Watcher类代码结构:

var uid = 0

export default class Watcher{

constructor(target,expression,callback){

this.id = uid++

this.target = target

this.getter = parsePath(expression)

this.callback = callback

this.value = this.get()

}

update(){ ​ this.run()

}

get(){ ​ //进入依赖收集阶段,让全局的Dep.target设置为Watcher本身,那么就是进入依赖收集

Dep.target = this

const obj = this.target

var value

//只要能够找到就一直找

try{

value = this.getter(obj)

}finally{

Dep.target = null

}

return value

}

run(){

this.getAndInvoke(this.callback)

}

getAndInvoke(){

const value = this.get()

if (value !== this.value || typeof value == 'object'){ ​ const oldValue = this.value

this.value = value

cb.call(this.target,value,oldValue)

}

}

}

4.1 Defina a função de coleção no getter da função defineReactive

get(){

console.log('访问obj的a属性')

//如果现在处于以来的收集阶段

if (Dep.target) {

dep.depend()

if(childOb){

childOb.dep.depend()

}

}

return val;

}

4.2parsePath é como identificar os caracteres abcd da sintaxe de ponto.

function parsePath(str) {

    			var segments = str.split(".")
    
    			return (obj) => {
    					for(let i=0;i<segments.length;i++){
    
    						//如果.的值不存在就返回
    
    						if(!obj) return
                            
                             // 这里obj分别是a:{b:{...}}、b:{c:{...}}、c:                                                        
                             //{f:44,d:55:e:66};
                             //obj[segments[i]]分别是a['b']、b['c']、c['d']
                             //然后return回去的就是需要的结果数值,这里值得注意的是obj是 
                             //一个变量
                             //他在逐层剥壳,直到拿到值为止,因为不能对象不能通过a.d直接 
                             //拿到最底层的值
                             //而是需要一层一层拿下去,有几个点(.)就有几层,通过变量 
                             //obj一层一层循环
                             //逐渐拿到最底层需要的值,函数的巧妙之处在于obj是变量每次都 
                             //更深一层
    						obj = obj[segments[i]]
    
    					}
    
    					return obj
    
    			}	
    
    	}
    
    var fn = parsePath('a.b.c.d')
    
    var v = fn({
    
    	a:{
    
    		b:{
    				c:{
                            f:44,
    						d:55,
                            e:66
    				}
    
    		}
    
    	}
    
    })
    console.log(v) // 55

5.Aplicação da função Watcher

new Watcher(obj,'a.m.n',(val)=>{

console.log('@@@@',val)

})

Desta forma, uma vez modificado o valor de obj.amn, a função arrow será executada. É muito semelhante à função watch que costumamos usar? É assim que o Vue implementa a capacidade de resposta. Este é o princípio da capacidade de resposta.

Por exemplo aqui obj.amn = 66

O console imprimirá automaticamente @@@@ 66

Não é isso que sabemos sobre capacidade de resposta?

Para resumir o processo é:

Por exemplo, quando criamos uma página ou componente, quando a página ou componente encontra uma expressão de interpolação ou vincula um valor dinâmico quando é renderizada pela primeira vez, um inspetor será instanciado. Como pode ser visto na estrutura de código do inspetor, O O método get será executado durante o processo de instanciação. O gatilho de get pode coletar dependências. Na função get, podemos ver se (Dep.target) adiciona a dependência se ela existir, o que é equivalente à visualização desta página ou componente (dom ) A atualização vai depender desses dados, e existe mais de um dado, são muitos, então as dependências coletadas aqui serão armazenadas no Dep. Dep é uma classe que criamos, e vamos instanciar um dep para essa dinâmica data.Sua função é armazenar quais dados precisam ser confiáveis ​​ao atualizar o dom? Então, quando os dados forem atualizados, o método dep.notify() será acionado, o que acionará um dep que instanciamos. Como mencionado antes, instanciaremos um dep para cada dado dinâmico. Este dep é usado para armazenar a lista de dependências. , e dep.notify dependerá circularmente do inspetor na notificação da lista e, em seguida, o inspetor atualizará o dom a tempo de acordo com a função de retorno de chamada. O método de atualização é escrito na função de retorno de chamada da instância do inspetor. Por exemplo, depois a atualização, preciso que seja um Display+3, isso completa a atualização da view, que também é a sutileza do Vue. Na função get, as dependências são coletadas através do watcher e armazenadas na lista dep. Então quando a função set é atualizada, o loop dep notifica o observador dependente e, em seguida, o observador segue o retorno de chamada. O DOM atualiza a visualização

A propósito, este é o modelo de publicação de assinatura. Assim como assinamos uma determinada conta pública (Watcher), o WeChat armazenará nossa lista de assinaturas (Dep). Assim que uma conta pública for atualizada aqui, o WeChat a receberá primeiro. (esta é a função de retorno de chamada cb do Wacther, ela também pode não fazer nada) envie-a para nós. Como centro de envio e recebimento, o WeChat nos ajuda a resolver muitos problemas. Os assinantes só precisam seguir as contas públicas nas quais estão interessados, e as contas públicas só precisam enviar novos conteúdos. O WeChat é usado como roteamento para enviar e receber, e a lógica de processamento também é explicada aqui no WeChat. O acoplamento é particularmente propício à manutenção e atualizações de código. Só precisamos realizar nossas próprias tarefas. Parece que o WeChat não tem nenhuma função, mas na verdade há um relacionamento de tabela muito complicado entre contas oficiais e seguidores, que é processado e compartilhado pelo WeChat. Sem a pressão dos dois, eles só precisam se concentrar no conteúdo.

O Watcher aqui é muito próximo da função watch que normalmente usamos. A função watch que normalmente usamos é na verdade uma instância do watcher. Por exemplo, às vezes há alguns dados não monitorados no componente e os elementos dom estão intimamente relacionados a eles. desta vez, uma função de observação será escrita para monitorar alterações neste valor. Quando detectar alterações, ela será processada por meio da função de retorno de chamada cb que escrevemos para atualizar a visualização em tempo hábil. Há também uma profundidade mais comumente usada: atributo verdadeiro que é na verdade nossa função de observador. Se for verdadeiro, faremos um loop e monitoraremos todas as propriedades neste objeto. Se for falso, quando as propriedades dentro do objeto, como obj.a, mudarem, ele não poderá ser detectado porque a memória O endereço de obj não foi alterado. A propósito, por que nenhuma alteração foi encontrada? Porque o objeto armazena o endereço de memória na forma de pares de valores-chave. Conforme mostrado na figura, o valor 0x123 correspondente ao nome da variável obj é seu endereço de memória, e os dados reais são armazenados no endereço de memória apontando para a memória heap. Em seguida, modifique o heap Os dados internos e o endereço de memória não serão alterados, portanto não podem ser detectados.

 Vídeo de referência:

[Vale do Silício] Princípio de capacidade de resposta de dados da análise de código-fonte Vue

Artigo de referência:

[Entusiastas da hegemonia] Resumo de aprendizagem dos princípios responsivos do Vue no front end 0 1: princípios básicos

imagem de referência:

[Vale do Silício] Princípio de capacidade de resposta de dados da análise de código-fonte Vue

[Pouco mais de cinco yuans restantes no meu bolso] tipos básicos de dados e métodos de armazenamento de objetos

Acho que você gosta

Origin blog.csdn.net/weixin_54515240/article/details/129947441
Recomendado
Clasificación