A carga do módulo realmente divide JS em muitos módulos para facilitar o desenvolvimento e a manutenção. Portanto, ao carregar muitos módulos JS, é necessário carregar dinamicamente para melhorar a experiência do usuário.
Antes de introduzir a biblioteca de carregamento do módulo, apresentaremos um método.
Carregamento dinâmico do método JS:
A cópia do código é a seguinte:
função loadjs (url, retorno de chamada) {
var node = document.createElement ("script");
node [window.addeventListener? "OnLoad": "onereadyStatechange"] = function () {
if (window.addeventListener || /loaded|complete/i.test(node.readyState)) {
ligar de volta();
node.onReadyStateChange = null;
}
}
node.onerror = function () {};
node.src = url;
var head = document.getElementsByTagName ("Head") [0];
head.InsertBefore (nó, Head.FirstChild); // Antes de inseri -lo no primeiro nó da cabeça, impede que a etiqueta da cabeça no IE6 seja fechada e use o AppendChild para relatar um erro.
}
Como o zhengmei situou a estrutura de massa que escreveu para introduzir o carregamento do módulo, o mais usado no setor é requer.js e Sea.js. Portanto, acho que ele tem uma personalidade forte.
Deixe -me falar sobre o processo de carregamento de Sea.js:
A página chaosan.js é introduzida na etiqueta da cabeça e o objeto SEAJS será obtido.
Também apresente index.js.
O código do index.js é o seguinte:
A cópia do código é a seguinte:
SeaJs.Use (['./ A', 'JQuery'], função (a, $) {
var num = aa;
$ ('#J_a'). Texto (num);
})
A.JS:
A cópia do código é a seguinte:
Definir (função (requer, exporta, módulo) {
var b = requer ('./ b');
var a = function () {
retornar 1 + parseint (bb ());
}
exports.a = a;
})
B.JS:
A cópia do código é a seguinte:
Definir (função (requer, exporta, módulo) {
var c = requer ('./ c');
var b = function () {
retornar 2 + parseint (cc ());
}
exports.b = b;
})
C.JS:
A cópia do código é a seguinte:
Definir (função (requer, exporta, módulo) {
var c = function () {
retornar 3;
}
exports.c = c;
})
Do exposto, podemos ver que o módulo A depende de B e B depende de c.
Quando o programa entra em index.js, o SEAJS chama o método de uso.
A cópia do código é a seguinte:
Seajs.use = função (IDS, retorno de chamada) {
globalmodule._use (IDS, retorno de chamada)
}
Descrição: Quando o GlobalModule é inicializado no SEAJS (quando o Sea.js é introduzido), a instância do módulo var globalModule = novo módulo (util.pageuri, status.compiled)
Nesse momento, IDS -> ['./a','jQuery'], retorno de chamada -> função (a, $) {var num = aa; $ ('#j_a'). Text (num);}
Em seguida, globalmodule._use (IDS, retorno de chamada) será chamado
A cópia do código é a seguinte:
Module.prototype._use = function (IDS, retorno de chamada) {
var uris = resolver (ids, this.uri); // Resolução ['./a','jQuery']
this._load (URIs, function () {// Chame o endereço do módulo A e JQuery analisado [url1, url2] e chame o método _load.
//util.map: Deixe todos os membros de dados executarem a função especificada por vez e retornem uma nova matriz, que é o resultado da execução do retorno de chamada do membro da matriz original
var args = util.map (Uris, função (URI) {
retornar URI? Cachedmodules [URI] ._ COMPILE (): null; // Se houver URL, chame o método _compile.
})
if (retorno de chamada) {callback.apply (null, args)}
})
}
Porque depois de chamar o método _load, duas funções de retorno de chamada aparecerão, então sinalizamos função (a, $) {var num = aa; $ ('#j_a'). Texto (num);} para chamada de chamada1,
Defina esse sinalizador de método de retorno de chamada de retorno de chamada para chamado de retorno de chamada 2.
O método de resolução é resolver o endereço do módulo, para não entrar em detalhes aqui.
Finally, the uris in var uris = resolve(ids, this.uri) was parsed into ['http://localhost/test/SEAJS/a.js','http://localhost/test/SEAJS/lib/juqery/1.7.2/juqery-debug.js'], and the module path resolution has been completed.
E a seguir este._load será executado
A cópia do código é a seguinte:
// O método _load () determinará primeiro quais arquivos de recursos ainda não estão prontos. Se todos os arquivos de recursos estiverem no estado pronto, o retorno de chamada2 será executado.
// Nesse caso, também faremos dependências circulares e executaremos o carregamento em js descarregados
Module.prototype._load = function (URIs, callback2) {
//util.Filter: Deixe todos os membros de dados executarem a função especificada por vez e retornem uma nova matriz. A matriz é um membro que retorna true depois de executar um retorno de chamada do membro da matriz original.
// UNLOOUDEDURIS é uma variedade de módulos que não são compilados
Var UNLOBLEAREDURIS = UTIL.FILTER (URIS, FUNÇÃO (URI) {
// Retorna um membro cuja função de execução O valor booleano é verdadeiro, retorna verdadeiro quando o URI existe e não existe nos Cachemodules variáveis internos ou armazena o valor do status menor que o status.
// Se o valor do status.
Retornar Uri && (! CachedModules [URI] ||
cachedmodules [URI] .Status <status.ready)
});
// Se todos os módulos nos URIs estiverem prontos, execute o retorno de chamada e saia do corpo da função (desta vez o método _Compile do módulo será chamado).
VAR Length = Uncloadeduris.length
if (length === 0) {callback2 () return}
// o número de módulos não carregados
Var permanece = comprimento
// Crie fechamentos e tente carregar módulos que não são carregados
for (var i = 0; i <comprimento; i ++) {
(função (uri) {
// julgar se as informações de armazenamento de URI não existem nos cachedmodules variáveis internos, instanciam um objeto de módulo
var módulo = cachedmodules [URI] ||
(cachedmodules [URI] = novo módulo (URI, status.fetching))
// Se o valor do estado do módulo for maior ou igual a 2, significa que o módulo foi baixado e já existe localmente. Neste momento, OnFetched () é executado
// Caso contrário, Fetch (URI, OnFetched) é chamado para tentar baixar o arquivo de recursos. O ONLOAD será acionado após o download do arquivo de recursos e o método on -itched será executado no Onload.
Module.status> = status.Fethed? OnFetched (): Fetch (Uri, OnFetched)
função onFetted () {
módulo = cachedmodules [URI]
// Quando o valor do estado do módulo é maior ou igual ao status.
if (module.status> = status.Saved) {
// getPurePendências: Obtenha uma matriz de dependência sem dependências circulares
VAR DEPS = getPuredEpendências (módulo)
// se a matriz de dependência não estiver vazia
if (deps.Length) {
// execute o método _load () novamente até que todas as dependências sejam carregadas e o retorno de chamada seja executado após o carregamento de todas as dependências ser concluído
Module.prototype._load (deps, function () {
CB (módulo)
})
}
// Se a matriz de dependência estiver vazia, execute CB (módulo) diretamente
outro {
CB (módulo)
}
}
// Se a aquisição falhar, como 404 ou não cumprir a especificação modular
// Nesse caso, o Module.status será mantido em busca ou busca
outro {
CB ()
}
}
}) (Uncloadeduris [i])
}
// Método CB - Execute o retorno de chamada depois de carregar todos os módulos
função cb (módulo) {
// Se as informações de armazenamento do módulo existirem, modifique o valor do status em suas informações de armazenamento do módulo e modifique -as para status.ready
módulo && (module.status = status.ready)
// retornos de chamada são executados apenas quando todos os módulos são carregados.
--remain === 0 && Callback2 ()
}
}
}
Aqui, o comprimento da matriz de Uncloadeduris é 2, ['http: //localhost/test/seajs/a.js'.http: //localhost/test/seajs/lib/juqery/1.2/juqery-debug.js], SOBLEMENTE, SOBROTIDO DE BROSTAS, SOBROTETOB.JS/SUBROMENTE, SOBREBUG.JS/LIB/Juqery/1.2/Juqery-Debug.js],
Veja http: //localhost/test/seajs/a.js como exemplo
Próximo: Primeiro, um módulo será criado:
A cópia do código é a seguinte:
Cachedmodules ('http: //localhost/test/seajs/a.js') = new Module ('http: //localhost/test/seajs/a.js'.1)
Module.status> = status.Fethed? OnFetched (): Fetch (Uri, OnFetched)
Como o módulo A não é carregado neste momento, o busca (URI, on -itched) será executado a seguir, ou seja, buscar ('http: //localhost/test/seajs/a.js'.onFetched).
A cópia do código é a seguinte:
Função Fetch (URI, OnFetted) {
// Substitua URI pelo novo endereço de solicitação de acordo com as regras no mapa
var requesturi = util.parseMap (URI)
// Primeiro, descubra se o registro de solicitação está contido na lista obtida
if (fetchedlist [requesturi]) {
// No momento, atualize as informações de armazenamento do módulo do URI original para o requesturi redefinido pelo mapa
cachedmodules [URI] = cachedmodules [requesturi]
// executar o OnFetched and Return, o que significa que o módulo foi obtido com sucesso
OnFetched ()
Retornar
}
// Consulta Informações de armazenamento da solicitação de solicitação na lista de obtenção
if (fetchinglist [requesturi]) {
// Adicione o retorno de chamada correspondente ao URI na lista de chamada e retorne
callbacklist [requesturi] .push (onfetched) // Se estiver sendo recuperado, empurre o método de retorno de chamada de este módulo para dentro da matriz e retorne -o.
Retornar
}
// Se os módulos que você estiver tentando obter não aparecerá no FetchedList e FetchingList, adicione suas informações na lista de solicitações e na lista de retorno de chamada, respectivamente
fetchinglist [requesturi] = true
callbacklist [requesturi] = [OnFetched]
// busca isso
Module._fetch (
requesturi,
function () {
fetchedList [requesturi] = true
// Atualiza o status do módulo
// se module.status for igual a status.
var módulo = cachedmodules [URI]
if (module.status === status.fetching) {
Module.status = status.Fethed
}
if (fetchinglist [requesturi]) {
Excluir FetchingList [requesturi]
}
// chama a execução unificada de chamada de chamada de retornos de chamada
if (callbacklist [requesturi]) {
util.foreach (callbacklist [requesturi], função (fn) {
fn () // fn é o método onfeched correspondente ao módulo a.
})
Excluir uma lista de chamada [requesturi]
}
},
config.charset
)
}
Em seguida, o Module._fetch () será executado e chamamos a função de retorno de chamada aqui como chamada de retorno3.
Este método é chamar o método loadjs para baixar dinamicamente o arquivo A.JS. (Como existem um jQuery, dois novos scripts serão criados). Há uma pergunta aqui. Se você criar um script para A e adicioná -lo à cabeça, baixará o arquivo JS. No entanto, no SEAJS, ele não é baixado. Em vez disso, você esperará até que o script para jQuery seja estabelecido e adicionado à cabeça antes de baixá -lo (o Google Depury define um ponto de interrupção e continua mostrando a espera pendente). Isso é para Mao?
(Recommended here: http://ux.sohu.com/topics/50972d9ae7de3e752e0081ff. I'll talk about additional questions here. You may know why we need to use tables to layout less, because when the table is rendered, it needs to calculate multiple times, while the div only needs once. At the same time, the interviewer of Midea e-commerce told me that the table needs to be parsed all Antes de ser exibido, e o DIV será exibido tanto quanto ele analisou.
Depois que o download for bem -sucedido, ele será analisado e executado, e o método define será executado. Aqui, primeiro executaremos o código do módulo a.
Definir (ID, DEPS, function () {}) Análise do método
A cópia do código é a seguinte:
// Definir definição, ID: ID do módulo, DEPS: dependência do módulo, fábrica
Module._define = function (id, deps, fábrica) {
// Resolva dependências // se deps não for do tipo de matriz, a fábrica é uma função
if (! util.esArray (deps) && util.isfunction (fábrica)) {// O corpo da função corresponde regularmente à string requer
DEPS = Util.parseDependências (Factory.ToString ())
}
// Defina meta informações
var meta = {id: id, dependências: deps, fábrica: fábrica}
if (document.attachevent) {
// Obtenha o nó do script atual
var script = util.getCurrentScript ()
// Se existir o nó de script
if (script) {
// Obtenha o endereço URI original
Deriveduri = util.unparseMap (util.getscriptabsolutesrc (script))}
if (! Deriveduri) {
Util.log ('falhou em derivar URI do script interativo para:', Factory.toString (), 'Warn')
}
}
.........
}
Definir primeiro executará um julgamento na fábrica para determinar se é uma função (o motivo é que a definição também pode incluir arquivos e objetos)
Se for uma função, a função será obtida através da fábrica.ToString (), e a dependência de A.JS é comparada regularmente, e a dependência será salva em depósito
Para A.Js, sua dependência é B.JS, então deps é ['./b']
E salve as informações de A.JS var meta = {id: id, dependências: deps, fábrica: fábrica}
Para a.js meta = {id: indefinido, dependências: ['./b'], fábrica: function (xxx) {xxx}}
No navegador do IE 6-9, você pode obter o caminho para a execução do JS. No entanto, em um navegador padrão, isso não é viável, para que você possa atribuir a meta informação ao anonymousmodulemeta = meta.
Em seguida, acione a ONLOAD e o método de chamada de retorno de retorno3 será chamado. Este método de retorno de chamada modificará o valor de status do módulo de retorno de chamada atual (A.JS) e o configurará como Module.status = status.Fetched.
Em seguida, o retorno de chamada correspondente a A.JS na lista de chamada da fila de retorno de chamada será executado uniformemente, isto é, onfticed.
O método on -itched verifica se um módulo A possui módulo dependente. Porque A depende de B, execute _load () de B.Js em qual módulo A depende.
O módulo B será baixado e o método de definição do jQuery será executado primeiro. Porque o jQuery não depende de módulos, após o retorno de chamada onload. CHAMADAS ONFETED Método CB.
Quando B é implementado no mesmo processo que A, o módulo C será baixado. Finalmente, os módulos C, B, A são baixados e executados e, após a conclusão da Onload, o método CB também será chamado (primeiro C, depois B, depois C)
Depois que todos os módulos estiverem prontos, o método de retorno de chamada2 será chamado.
Finalmente, o retorno de chamada é chamado Back2 e o método _Compile dos módulos A e JQuery é executado:
Primeiro, compila o módulo A.JS e execute a função do módulo a. Como a exige (B.JS), ele executará a função do módulo b.
A função do módulo A inicia a execução
A função do módulo B começa a executar
A função do módulo C inicia a execução
A execução da função do módulo C foi concluída
A execução da função do módulo B foi concluída
A execução da função do módulo A foi concluída
Finalmente, execute a função do jQuery.
Após a compilação, execute o retorno de chamada1 e você pode usar objetos A e JQuery.
PS: A versão SEAJS foi atualizada e agora não há método _Compile. (Todo mundo vai ver você mesmo, eu também quero ver)
Vamos falar sobre o processo de compilação do módulo SEAJS.
Primeiro, a compilação de A.JS
A cópia do código é a seguinte:
Module.prototype._compile = function () {
126 var módulo = this
127 // Se o módulo tiver sido compilado, retorne o Module.Exports diretamente
128 if (module.status === status.compilado) {
129 Módulo de retorno.Exports
130}
133 // 1. O arquivo do módulo é 404.
134 // 2. O arquivo do módulo não está escrito com formato de módulo válido.
135 // 3. Outros casos de erro.
136 // Aqui estão algumas exceções para lidar e depois retornará diretamente
137 if (module.status <status.saved &&! HasModifiers (módulo)) {
138 Retornar nulo
139}
140 // altere o status do módulo para compilar, o que significa que o módulo está sendo compilado
141 module.status = status.compiling
142
143 // O uso interno do módulo é um método usado para obter a interface fornecida por outros módulos (chamada submódulos) e operar de forma síncrona
144 Função requer (id) {
145 // Caminho do caminho do módulo de acordo com o ID
146 var uri = resolver (id, module.uri)
147 // Obtenha módulos do cache do módulo (observe que as dependências do submodule como o módulo principal foram baixadas)
148 Var Child = Cachedmodules [URI]
149
150 // Basta retornar nulo quando o URI é inválido.
151 // Se a criança estiver vazia, isso só pode significar que o parâmetro está preenchido incorretamente e depois retorne nulo diretamente
152 If (! Child) {
153 Retornar nulo
154}
155
156 // evita chamadas circulares.
157 // Se o status do submódulo estiver status.Compiling, retorne a criança.Exports diretamente para evitar a compilação repetida de módulos devido a dependências circulares.
158 if (Child.status === status.compiling) {
159 Retorne Child.Exports
160}
161 // aponte para o módulo que chama o módulo atual durante a inicialização. Com base nesta propriedade, você pode obter a pilha de chamadas quando o módulo for inicializado.
162 Child.parent = Módulo
163 // retorna o módulo.Exports da criança compilada
164 Return Child._Compile ()
165}
166 // usado internamente para carregar o módulo de forma assíncrona e executar o retorno de chamada especificado após a conclusão do carregamento.
167 requer.async = function (ids, retorno de chamada) {
168 MODULE._USE (IDS, retorno de chamada)
169}
170 // Use o mecanismo de resolução do caminho dentro do sistema de módulos para analisar e retornar o caminho do módulo. Esta função não carrega o módulo e retorna apenas o caminho absoluto analisado.
171 requer.resolve = function (id) {
172 Resolve de retorno (id, module.uri)
173}
174 // Através desta propriedade, você pode visualizar todos os módulos carregados pelo sistema de módulos.
175 // Em alguns casos, se você precisar recarregar um módulo, poderá obter o URI do módulo e excluir suas informações por excluir requer.cache [URI]. Isso será realizado novamente na próxima vez que você o usar.
176 requer.cache = cachedmodules
177
178 // requer é um método para obter interfaces fornecidas por outros módulos.
179 módulo.Require = requer
180 // Exportações é um objeto que fornece interfaces de módulo para o exterior.
181 Module.Exports = {}
182 Var Factory = Module.Factory
183
184 // Quando a fábrica é uma função, representa o construtor do módulo. Ao executar esse método, você pode obter a interface fornecida pelo módulo para o exterior.
185 if (util.isfunction (fábrica)) {
186 CompileStack.push (módulo)
187 runinmoduleContext (fábrica, módulo)
188 CompileStack.pop ()
189}
190 // Quando a fábrica é do tipo de função, como um objeto, uma string, etc., a interface que representa o módulo é o objeto, uma string e outros valores.
191 // por exemplo: define ({"foo": "bar"});
192 // por exemplo: define ('eu sou um modelo. Meu nome é {{name}}.');
193 else if (fábrica! == indefinido) {
194 Module.Exports = Factory
195}
196
197 // Alterar o status do módulo para compilado, o que significa que o módulo foi compilado
198 module.status = status.compilado
199 // Execute a modificação da interface do módulo, através de Seajs.Modify ()
200 executificadores (módulo)
201 módulo de retorno.exports
202}
A cópia do código é a seguinte:
if (util.isfunction (fábrica)) {
186 CompileStack.push (módulo)
187 runinmoduleContext (fábrica, módulo)
188 CompileStack.pop ()
189}
Aqui está a inicialização do módulo.Export. Método RuninModuleContext:
A cópia do código é a seguinte:
// execute o código do módulo de acordo com o contexto do módulo
489 função runinmoduleContext (fn, módulo) {
490 // Passe em dois parâmetros relacionados ao módulo e ao próprio módulo
491 // As exportações são usadas para expor interfaces
492 // requer é usado para obter módulos dependentes (síncronos) (compilar)
493 var ret = fn (module.require, module.exports, módulo)
494 // suporta a forma de interface de exposição ao valor de retorno, como:
495 // retorna {
496 // fn1: xx
497 //, fn2: xx
498 // ...
499 //}
500 se (ret! == indefinido) {
501 module.exports = ret
502}
503}
Executar o método de função em A.Js e, em seguida, var B = requer ("b.js") será chamado,
O método requisito retornará o valor de retorno do método de compilação de B, e há var c = requer ('c.js') no módulo B.
Neste momento, o método de compilação de C será chamado e, em seguida, a função de C será chamada. Em C, se o objeto for exposto ou o objeto de retorno C é retornado, as exportações do Módulo C serão exportações = c. Ou diretamente module.export = c; Em resumo, o módulo c.export = c será retornado no final; Então var c = módulo c.export = c. No módulo B, você pode usar a variável C para chamar os métodos e propriedades do objeto C no módulo c.
Por analogia, o módulo A pode, eventualmente, chamar as propriedades e métodos do objeto B no módulo b.
Independentemente do módulo, desde que você use o módulo.export = xx, o módulo, outros módulos podem usar o requer ("xx módulo") para chamar vários métodos no módulo XX.
O estado final do módulo se tornará módulo.status = status.compilado.
A cópia do código é a seguinte:
Module.prototype._use = function (IDS, retorno de chamada) {
var uris = resolver (ids, this.uri); // Resolução ['./a','jQuery']
this._load (URIs, function () {// Chame o endereço do módulo A e JQuery analisado [url1, url2] e chame o método _load.
//util.map: Deixe todos os membros de dados executarem a função especificada por vez e retornem uma nova matriz, que é o resultado da execução do retorno de chamada do membro da matriz original
var args = util.map (Uris, função (URI) {
retornar URI? Cachedmodules [URI] ._ COMPILE (): null; // Se houver URL, chame o método _compile.
})
if (retorno de chamada) {callback.apply (null, args)}
})
}
Neste momento, args = [módulo A.Export, módulo jQuery.export];
A cópia do código é a seguinte:
SeaJs.Use (['./ A', 'JQuery'], função (a, $) {
var num = aa;
$ ('#J_a'). Texto (num);
})
Neste momento, A e $ na função são o módulo A.Export e o módulo jQuery.Export.
Como agora estou estudando o código -fonte do jQuery e o design da estrutura do jQuery, compartilho alguma experiência:
JQuery Code, li muitas análises on -line, mas não consigo mais lê -lo enquanto olho para ele. Não é muito significativo, por isso recomendo a análise do código -fonte jQuery da Miaowei em sala de aula.
O design da estrutura JavaScript de Situ Zhengmei é difícil, mas após uma leitura cuidadosa, você se tornará um engenheiro de front-end sênior.
Sugiro aprender e usar o mar de Yu Bo.js, afinal, é feito pelos próprios chineses. Os novos projetos ou reconstruções de nossa empresa serão feitos usando o SEAJS.
Em seguida, é o código -fonte leitura intensiva de terbares modulares e backbone do MVC ou MVVM Angular. Aqui, espero que alguém me dê sugestões sobre quais livros, sites e vídeos para aprender rapidamente.