Prefácio
Este é o 13º artigo da série de artigos [Dicionário de front-end]. Vou me concentrar no Vue nos próximos 9 artigos. Espero que esses 9 artigos possam aprofundar seu conhecimento sobre o Vue. Claro, a premissa desses artigos é que você tem uma certa base do Vue por padrão. Se você não tem nenhum básico, é recomendável ler a documentação oficial primeiro.
No primeiro artigo, combinarei parte do código-fonte do Vue e do Vuex para ilustrar o processo de injeção do Vuex no ciclo de vida do Vue.
Quando se trata de código-fonte, não é tão difícil quanto você pode imaginar. É quase o mesmo que normalmente escrevemos código de negócios, são todas chamadas de método. Mas a árvore de chamadas do código-fonte será muito mais complicada.
Por que usar Vuex
Com o Vue, inevitavelmente encontraremos dados ou estados compartilhados entre os componentes. O código de negócios do aplicativo se torna cada vez mais complicado e as desvantagens dos métodos de comunicação, como adereços, eventos e barramentos de eventos, tornam-se mais óbvias. Neste momento, precisamos do Vuex. Vuex é uma ferramenta de gerenciamento de estado especialmente projetada para Vue.
A gestão do estado é um meio importante de dissociar os componentes do Vue.
Baseia-se nas ideias básicas de Fluxo e redux, extrai o estado para o mundo inteiro e forma uma loja.
Vuex não limita sua estrutura de código, mas você precisa seguir algumas regras:
-
O estado no nível do aplicativo deve ser concentrado em um único objeto de armazenamento
-
Enviar uma mutação é a única maneira de alterar o estado, e este processo é síncrono
- A lógica assíncrona deve ser encapsulada em ação
O processo de injeção de Vuex no ciclo de vida Vue
Quando instalamos o plugin, sempre usamos Vue.use () para carregar o plugin como o seguinte, mas o que Vue.use () faz?
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
O que Vue.use () faz
Instale o plugin Vue.js. Se o plug-in for um objeto, o método de instalação deve ser fornecido. Se o plugin for uma função, ele será usado como método de instalação. Quando o método de instalação é chamado, o Vue será passado como um parâmetro.
O acima é a explicação do documento oficial.
A seguir, vamos dar uma olhada no que Vue.use () faz a partir da parte do código-fonte.
O código-fonte do Vue chama o método initUse (Vue) no método de entrada initGlobalAPI, este método define o que Vue.use () precisa fazer.
function initGlobalAPI (Vue) {
......
initUse(Vue);
initMixin$1(Vue); // 下面讲 Vue.mixin 会提到
......
}
function initUse (Vue) {
Vue.use = function (plugin) {
var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
/* 判断过这个插件是否已经安装 */
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
var args = toArray(arguments, 1);
args.unshift(this);
/* 判断插件是否有 install 方法 */
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args);
} else if (typeof plugin === 'function') {
plugin.apply(null, args);
}
installedPlugins.push(plugin);
return this
};
}
Este código faz principalmente duas coisas:
-
Um é evitar a instalação repetida do mesmo plugin
- A outra é inicializar o plugin
O método de instalação do plugin
Depois de ler o código-fonte acima, sabemos que o plug-in (Vuex) precisa fornecer um método de instalação. Então, vamos ver se existe esse método no código-fonte do Vuex. O resultado é claro:
/* 暴露给外部的 install 方法 */
function install (_Vue) {
/* 避免重复安装(Vue.use 内部也会检测一次是否重复安装同一个插件)*/
if (Vue && _Vue === Vue) {
{
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
);
}
return
}
Vue = _Vue;
/* 将 vuexInit 混淆进 Vue 的 beforeCreate(Vue2.0) 或 _init 方法(Vue1.0) */
applyMixin(Vue);
}
Este código faz principalmente duas coisas:
-
Uma é evitar que o Vuex seja instalado repetidamente
- A outra é executar o applyMixin, o objetivo é executar o método vuexInit para inicializar o Vuex
A seguir, examinamos o código-fonte do applyMixin (Vue):
/* 将 vuexInit 混淆进 Vue 的 beforeCreate */
function applyMixin (Vue) {
var version = Number(Vue.version.split('.')[0]);
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit });
} else {
/* Vue1.0 的处理逻辑,此处省略 */
......
}
function vuexInit () {
......
}
}
A partir do código-fonte acima, podemos ver que o método Vue.mixin confunde o método vuexInit com o gancho beforeCreate. Devido a essa operação, cada instância de vm chamará o método vuexInit. Então, o que vuexInit faz?
vuexInit ()
Quando usamos Vuex, precisamos passar o armazenamento para a instância Vue.
new Vue({
el: '#app',
store
});
Mas podemos acessar a loja em cada vm, isso requer vuexInit.
function vuexInit () {
const options = this.$options
if (options.store) {
/* 根节点存在 stroe 时 */
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
/* 子组件直接从父组件中获取 $store,这样就保证了所有组件都公用了全局的同一份 store*/
this.$store = options.parent.$store
}
}
Quando o nó raiz existe stroe, atribua diretamente options.store a este. $ Store. Caso contrário, não é o nó raiz e é obtido do $ store do nó pai.
Por meio dessa etapa, podemos acessar a instância Store por meio deste. $ Store em qualquer vm. A seguir, vamos falar sobre Vue.mixin () ao contrário.
Vue.mixin ()
Registre um mixin globalmente, afetando cada instância do Vue criada após o registro. Os autores de plug-ins podem usar mixins para injetar comportamentos personalizados nos componentes. Não recomendado para uso em código de aplicativo.
O método initMixin $ 1 (Vue) é chamado no método de entrada initGlobalAPI de vue:
function initMixin$1 (Vue) {
Vue.mixin = function (mixin) {
this.options = mergeOptions(this.options, mixin);
return this
};
}
O processo do Vuex injetando o ciclo de vida do Vue é provavelmente assim. Se você estiver interessado, pode olhar diretamente no código-fonte do Vuex. A seguir, vamos falar sobre Store.
Loja
Mencionamos acima que vuexInit obterá Store de opções. Então, de onde vem a Store?
Quando usarmos Vuex, definiremos uma instância de Store semelhante à seguinte.
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
Vue.use(Vuex)
const state = {
showState: 0,
}
export default new Vuex.Store({
strict: true,
state,
getters,
})
Não habilite o modo estrito no ambiente de publicação. O modo estrito monitorará profundamente a árvore de estado para detectar mudanças de estado não compatíveis - certifique-se de desligar o modo estrito no ambiente de lançamento para evitar perda de desempenho.
Estado responsivo
Você se importa com o quão responsivo é o estado? Isso é obtido principalmente por meio do método resetStoreVM (this, state) chamado no construtor Store.
Este método serve principalmente para redefinir um objeto _vm privado (uma instância de Vue). Este objeto _vm manterá nossa árvore de estados e armazenará getters de armazenamento por meio de cálculos de atributos. Agora, vamos dar uma olhada em seu processo de implementação em detalhes.
/* 使用 Vue 内部的响应式注册 state */
function resetStoreVM (store, state, hot) {
/* 存放之前的vm对象 */
const oldVm = store._vm
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
/* 通过 Object.defineProperty 方法为 store.getters 定义了 get 方法。当在组件中调用 this.$store.getters.xxx 这个方法的时候,会访问 store._vm[xxx]*/
forEachValue(wrappedGetters, (fn, key) => {
computed[key] = partial(fn, store)
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
const silent = Vue.config.silent
/* 设置 silent 为 true 的目的是为了取消 _vm 的所有日志和警告 */
Vue.config.silent = true
/* 这里new了一个Vue对象,运用Vue内部的响应式实现注册state以及computed*/
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
/* 使能严格模式,Vuex 中对 state 的修改只能在 mutation 的回调函数里 */
if (store.strict) {
enableStrictMode(store)
}
if (oldVm) {
/* 解除旧 vm 的 state 的引用,并销毁这个旧的 _vm 对象 */
if (hot) {
store._withCommit(() => {
oldVm._data.$$state = null
})
}
Vue.nextTick(() => oldVm.$destroy())
}
}
O estado de resposta provavelmente é implementado desta forma, que é o processo de inicialização do método resetStoreVM.
Veja o método de confirmação de Store
Sabemos que o método commit é usado para acionar a mutação.
commit (_type, _payload, _options) {
/* unifyObjectStyle 方法校参 */
const {
type,
payload,
options
} = unifyObjectStyle(_type, _payload, _options)
const mutation = { type, payload }
/* 找到相应的 mutation 方法 */
const entry = this._mutations[type]
if (!entry) {
if (process.env.NODE_ENV !== 'production') {
console.error(`[vuex] unknown mutation type: ${type}`)
}
return
}
/* 执行 mutation 中的方法 */
this._withCommit(() => {
entry.forEach(function commitIterator (handler) {
handler(payload)
})
})
/* 通知所有订阅者,传入当前的 mutation 对象和当前的 state */
this._subscribers.forEach(sub => sub(mutation, this.state))
if (
process.env.NODE_ENV !== 'production' &&
options && options.silent
) {
console.warn(
`[vuex] mutation type: ${type}. Silent option has been removed. ` +
'Use the filter functionality in the vue-devtools'
)
}
}
Este método executa primeiro a verificação de estilo de parâmetro e, em seguida, usa o método _withCommit para executar esta função de processamento de mutação de gatilho em lote. Após a conclusão da execução, notifique todos os _subscribers (função de assinatura) sobre o objeto de mutação desta operação e o estado atual.
Plano de produção de artigos relacionados ao Vue
Recentemente, amigos sempre me perguntaram sobre questões relacionadas ao Vue, então irei publicar 9 artigos relacionados ao Vue a seguir, na esperança de ajudá-lo. Manterei uma atualização em 7 a 10 dias.
-
[Dicionário de front-end] O processo de injeção do Vuex no ciclo de vida do Vue
-
[Dicionário de front-end] Análise do princípio responsivo do Vue
-
[Dicionário de front-end] O processo de patch de VNodes novos e antigos
-
[Dicionário de front-end] Como desenvolver componentes funcionais e fazer upload de npm
-
[Dicionário de front-end] Otimize seu projeto Vue a partir desses aspectos
-
[Dicionário de front-end] Fale sobre o desenvolvimento de roteamento de front-end a partir do design do Vue-Router
-
[Dicionário front-end] Como usar o Webpack corretamente no projeto
-
[Dicionário de front-end] Renderização do servidor Vue
- [Dicionário de front-end] Como escolher entre Axios e Fetch
Eu sugiro que você preste atenção à minha conta oficial, você pode receber os artigos mais recentes o mais rápido possível.
Se você deseja ingressar na comunicação em grupo, também pode adicionar meu wqhhsd do WeChat.