Leitura recomendada:
Implementar a ligação de dados bidirecional JS muito simples
O MVVM é um modelo de desenvolvimento muito popular para o front-end da Web. O uso do MVVM pode fazer com que nosso código se concentre mais em lidar com a lógica de negócios, em vez de se preocupar com as operações da DOM. Atualmente, as famosas estruturas do MVVM incluem Vue, Avalon, React etc. Essas estruturas têm suas próprias vantagens, mas a idéia de implementação é aproximadamente a mesma: ligação de dados + atualização de exibição. Por curiosidade e vontade de lutar, também escrevi a biblioteca MVVM mais simples (MVVM.JS) nessa direção, com um total de mais de 2.000 linhas de código. A nomeação e o uso de instruções são semelhantes ao Vue. Aqui vou compartilhar os princípios de implementação e minhas idéias de organização de código.
Classificação de idéias
O MVVM é conceitualmente um padrão que realmente separa as visualizações da lógica de dados, e o ViewModel é o foco de todo o padrão. Para implementar o ViewModel, você precisa associar o modelo de dados (modelo) e a exibição (exibir). Toda a ideia de implementação pode ser simplesmente resumida em 5 pontos:
Implementar um compilador para digitalizar e extrair instruções para cada nó de um elemento;
Implemente um analisador para analisar as instruções sobre o elemento, que pode atualizar a intenção da instrução para o DOM através de uma função de atualização (um módulo que pode ser especificamente responsável pela exibição de atualização pode ser necessário no meio). Por exemplo, ao analisar o nó <p vs-toque = "insshow"> </p>, primeiro obtenha o valor do iSshow no modelo e depois altere o node.style.display de acordo com o ISSHOW para controlar a tela e o esconderijo dos elementos;
A implementação de um observador pode vincular a função de atualização de cada instrução em analisador aos campos correspondentes ao modelo;
Implementar um observador para monitorar a alteração do valor de todos os campos do objeto e, uma vez que a alteração ocorre, o valor mais recente pode ser obtido e o retorno de chamada de notificação pode ser acionado;
Use o Observer para estabelecer um monitoramento do modelo no observador. Quando um valor no modelo muda, o monitoramento é acionado. Depois que o observador obtém o novo valor, ele chama a função de atualização associada na etapa 2, que pode alcançar o objetivo de atualizar a visualização enquanto os dados mudam.
Exemplo de efeito
Primeiro, vamos dar uma olhada no exemplo final de uso, que é semelhante à instanciação de outras estruturas do MVVM:
<div id = "Mobile-list"> <h1 v-text = "title"> </h1> <ul> <li v-for = "item em marcas"> <b v-text = "item.name"> </b> <span v-show = "showRank"> rank: {{item.rank}} </span> </li> document.querySelector('#mobile-list');var vm = new MVVM(element, {'title' : 'Mobile List','showRank': true,'brands' : [{'name': 'Apple', 'rank': 1},{'name': 'Galaxy', 'rank': 2},{'name': 'OPPO', 'rank': 3}]}); vm.set ('title', 'Top 3 Mobile Rank List'); // => <H1> Top 3 Lista de classificação móvel </h1>Divisão de módulos
Dividi o MVVM em cinco módulos para implementar: compilador do módulo de compilação, analisador, atualizador de atualização do módulo de atualização, observador do módulo de assinatura de dados e observador do módulo de escuta de dados. O processo pode ser descrito brevemente como: Depois que o compilador compila o comando, as informações de instrução são entregues ao analisador para análise. O analisador atualiza o valor inicial e assina o Observador para alterações nos dados. O observador monitora as alterações de dados e, em seguida, o alimenta de volta ao vigia. O observador notifica o atualizador do resultado da alteração e encontra a função de atualização correspondente para atualizar a visualização.
O processo acima é mostrado na figura:
A seguir, é apresentada uma descrição dos princípios básicos da implementação desses cinco módulos (o código é publicado apenas nas partes principais, vá ao meu github para a implementação completa)
1. Compilar compilador de módulos
A responsabilidade do compilador é principalmente digitalizar e extrair instruções para cada nó do elemento. Como o processo de compilação e análise atravessará toda a árvore do nó muitas vezes, a fim de melhorar a eficiência da compilação, no construtor MVVM, primeiro converta o elemento em uma cópia dos fragmentos de documentos. O objeto de compilação é esse fragmento de documento e não deve ser um elemento de destino. Depois que todos os nós são compilados, o fragmento de documento é adicionado de volta ao nó real original.
VM.complieElement implementa a extração de digitalização e instrução de todos os nós do elemento:
vm.compileElement = function (fragmento, root) {var node, childNodes = fragment.childnodes; // verifique o nó filho para (var i = 0; i <childnodes.lengn; i ++) {node = childNodes [i]; if (this.hasdirivective (node) {this); Digitalize o nó infantil do nó infantil if (node.childnodes.length) {this.compileElement (node, false);}} // verifique os nós filhos if (root) if (root) {this.compileallnodes ();}}O método vm.compileallnodes compilará cada nó neste. $ UNCOMPILENODES (deixe as informações de instrução para analisar). Depois de compilar um nó, ele será removido da fila de cache. Ao mesmo tempo, verifique isso. $ UNCOMPILENODES.LENDO quando comprimento === 0, significa que toda a compilação é concluída. Você pode anexar fragmentos de documentos ao nó real.
2. Analisador de análise de instrução
Quando o compilador compilador extrai as instruções de cada nó, ele pode ser enviado ao analisador para análise. Cada instrução tem um método de análise diferente. Todas as instruções só precisam fazer duas coisas: uma é atualizar o valor dos dados para a visualização (estado inicial) e o outro é assinar a função de atualização no monitoramento de alterações do modelo. Aqui, usamos a análise V-Text como exemplo para descrever o método de análise geral de uma diretiva:
parser.parsevText = function (nó, modelo) {// Obtenha o valor inicial definido no modelo var text = this. {node.textContent = last; // updater.updatenodeTextContent (nó, texto);});}3. Observador do módulo de assinatura de dados
No exemplo anterior, o observador fornece um método de relógio para se inscrever nas alterações de dados. Um parâmetro é o modelo de campo do modelo e o outro é a função de retorno de chamada. A função de retorno de chamada é acionada através do observador. O novo valor último e o valor antigo antigo são passados no parâmetro. Depois que o observador recebe o novo valor, ele pode encontrar o retorno de chamada correspondente (função de atualização) do modelo para atualizar a visualização. As funções de modelo e atualização são um relacionamento um para muitos, ou seja, um modelo pode ter tantas funções de retorno de chamada (funções de atualização) que o lidam, por exemplo: as instruções v-text = "title" e v-html = "title" compartilham um campo de modelo de dados.
Adicionar assinatura de dados ao Watcher.watch A implementação do método é:
watchper.watch = function (campo, retorno de chamada, contexto) {var de chamada = this. [];} // retornos de chamada de cache [campo] .push ([retorno de chamada, contexto]);}Quando o campo do campo do modelo de dados muda, o observador aciona todos os retornos de chamada na matriz de cache que se inscreveram no campo.
4. Observador do módulo de monitoramento de dados
Observador é a base principal de toda a implementação do MVVM. Eu li um artigo dizendo que oo (object.observe) acenderá a revolução da ligação de dados e trará enorme influência ao front-end. Infelizmente, o rascunho do ES7 abandonou OO! Atualmente, não há suporte para o navegador! Felizmente, também há objeto.DefineProperty que pode simular um observador simples, interceptando os descritores de acesso (Get and Set) das propriedades do objeto:
// Interceptar os métodos GET e Set da propriedade Prop do objeto Object.DefineProperty (objeto, prop, {get: function () {return this.getValue (object, prop);}, set: function (newvalue) {var OldValue = this.getValue (object); if (newvalue! this.TriggerChange (prop, newValue, OldValue);}}});Depois, há outra pergunta: como monitorar operações de matriz (push, shift, etc.)? Todas as estruturas do MVVM são implementadas reescrevendo o protótipo da matriz:
observer.rewRiteArrayMethods = function (array) {var self = this; var ArrayProto = Array.prototype; var ArrayMethods = Object.Create (ArrayProto); var Métodos = 'push | shift | triturff | splice | classificação | reverse'.split (' | '); métods.ForEach (function (método) {object.DefineProperty (Arraymethods, método, function () {var i = argumentos.Length; var original = ArrayProto [método]; var args = new Array (i) (i-) {args [i] = argsuments [i];} var = método); resultado de retorno;});}); Array .__ proto__ = Arraymethods;}Este método de implementação é referenciado da VUE. Eu acho que é usado muito bem, mas o atributo de comprimento da matriz não pode ser ouvido; portanto, no MVVM, a operação do Array.Length deve ser evitada
5. Veja o atualizador do módulo de atualização
O Updater é o mais fácil entre os cinco módulos, você só precisa ser responsável pela função de atualização correspondente a cada instrução. Após uma série de lançamento e deixar o resultado final para o atualizador para atualizações de visualização ou evento, por exemplo, a função de atualização do V-Text é:
updater.updateNodeTextContent = function (nó, texto) {node.textContent = text;}Função de atualização de V-Bind: Style:
updater.updatenodestySyle = function (nó, propriedade, valor) {node.style [propperty] = value;}Implementação da ligação de dados bidirecional
A ligação de dados bidirecionais dos elementos de formulário é um dos maiores recursos do MVVM:
De fato, o princípio da implementação dessa função mágico também é muito simples. Há apenas duas coisas a fazer: uma é atualizar o valor do formulário quando os dados mudarem, e o outro é atualizar os dados quando o valor do formulário mudar, para que o valor dos dados esteja ligado ao valor do formulário.
Alterações de dados para atualizar os valores do formulário podem ser feitas facilmente usando o módulo Observador mencionado acima:
watchter.watch (modelo, função (último, antigo) {input.value = last;}); 'Para atualizar os dados nas alterações do formulário, você só precisa ouvir os eventos do formulário em tempo real e atualizar os campos correspondentes do modelo de dados:
var modelo = this. $ modelo; input.addeventListenr ('alteração', function () {model [campo] = this.value;}); 'Outros formulários Rádio, Caixa de seleção e Select são o mesmo princípio.
O exposto acima, todo o processo e as idéias básicas de implementação de cada módulo foram explicadas. A primeira vez que publiquei um artigo na comunidade, minhas habilidades de expressão de idiomas não são muito boas. Se houver palavras erradas e coisas ruins escritas, espero que todos possam criticá -las e corrigi -las!
Conclusão
Estou tentando esse MVVM.JS simples porque usei o Vue.js no meu projeto de estrutura, mas acabei de usar o sistema de instruções. Muitas funções foram usadas apenas cerca de um quarto. Eu pensei que seria suficiente apenas implementar a ligação de dados e a refrescamento de visualização. Como resultado, não encontrei uma biblioteca de JavaScript, então criei uma roda assim.
Embora as funções e a estabilidade sejam muito menos do que as estruturas populares do MVVM, como o VUE, e a implementação do código pode ser relativamente difícil, muito conhecimento foi adicionado ao construir essa roda ~ o progresso está no lançamento!
Atualmente, meu MVVM.JS implementa apenas a função mais básica. Continuarei a melhorar e fortalecê -lo no futuro. Se você estiver interessado, discuta e melhore juntos ~