Vue
1. Понимание мввм 1.MVVM — это сокращение от Model-View-ViewModel. 2. Модель представляет собой модель данных, а бизнес-логика модификации и работы данных также может быть определена в модели. 3.View представляет компонент пользовательского интерфейса, который отвечает за преобразование модели данных в пользовательский интерфейс для отображения. 4. ViewModel отслеживает изменения в данных модели, контролирует поведение представления и обрабатывает взаимодействие с пользователем Простым пониманием является объект, который синхронизирует представление и модель, соединяя модель и представление.
2. Жизненный цикл Vue (11, ключ 8) 1.beforeCreate (перед созданием) до того, как события наблюдения и инициализации данных не начались
2.создано (после создания) Полное наблюдение за данными, работа свойств и методов, события инициализации, свойство $el не отображалось
3.beforeMount (перед загрузкой) вызывается перед началом монтирования, и соответствующая функция рендеринга вызывается в первый раз. Пример завершил следующую конфигурацию: скомпилируйте шаблон, сгенерируйте html из данных и шаблон в данных. Обратите внимание, что в настоящее время html не смонтирован на странице.
4.mounted (после загрузки) Вызывается после того, как el заменяется вновь созданным vm.$el и монтируется на экземпляре. Экземпляр завершил следующую настройку: замените объект DOM, на который указывает атрибут el, скомпилированным выше html-содержимым. Завершите рендеринг html в шаблоне на html-странице. Во время этого процесса выполняется Ajax-взаимодействие.
3. Принцип реализации двусторонней привязки данных в Vue: Object.defineProperty()
Vue реализует три основных объекта двусторонней привязки данных: Наблюдатель (геттер в Object.defineProperty, всякий раз, когда данные изменяются, сеттер срабатывает. В это время Наблюдатель уведомляет подписчика, а подписчик является Наблюдателем) , Watcher (подписчик Watcher) Как мост для связи между Observer и Compile), Compile
Наблюдатель: 1. Добавьте себя к подписчику свойства (dep), когда вы создаете свой экземпляр 2. Он должен иметь сам метод update() 3. Когда свойство изменяет уведомление dep.notice(), оно может вызвать свой собственный метод update() и вызвать связанный обратный вызов в Compile.
Главное, что делает Compile, — разбирает инструкции шаблона, заменяет переменные в шаблоне данными, затем инициализирует и визуализирует представление страницы, привязывает функцию обновления к узлу, соответствующему каждой инструкции, и добавляет подписчиков для мониторинга данных. данные меняются, получайте уведомления, обновляйте представление.
режим истории и режим хеширования В случае со страницей 404 наш внешний интерфейс должен обрабатывать ее самостоятельно. маршруты: [ {путь: '*', компонент: NotFoundComponent} ]
как получить доступ к экземпляру vue beforeRouteEnter (до, от, далее) { следующий (ВМ => { console.log('ВМ', ВМ) }) }
активировано: вызывается при активации компонента поддержки активности, этот хук не вызывается во время рендеринга на стороне сервера
deactivated: вызывается, когда компонент поддержки активности деактивирован, этот хук не вызывается во время рендеринга на стороне сервера.
beforeCreate перед созданием экземпляра созданный экземпляр создан beforeMount перед монтированием смонтированное крепление завершено перед обновлением перед обновлением обновлено обновление завершено до того, как Destory будет уничтожен уничтожено уничтожено
1. перед созданием Этот хук является первым хуком, запускаемым после new Vue (). На текущем этапе данные и методы для данных, методы, вычисляемые и наблюдаемые недоступны.
2. Этот крюк возникает после создания экземпляра, текущая фаза была выполнена данные наблюдения, то есть вы можете использовать данные, изменение данных, данные здесь не для изменения обновленных функций триггера. Может выполнить некоторые исходные данные о приобретении данных, данные не слишком много внимания на запрос, он будет вызывать белый экран слишком долго. Не может взаимодействовать с DOM на этом этапе, если вы хотите, домик доступа можно получить доступ к VM. $ NextTick.
3.перед монтированием Этот хук происходит перед монтированием, до того, как шаблон шаблона был импортирован в компиляцию функции рендеринга. На данном этапе виртуальный Дом создан и вот-вот начнется рендеринг. В этот момент также можно вносить изменения в данные, не запуская обновление.
4. установлен Этот хук происходит после завершения монтирования, на данном этапе монтируется настоящий Дом, данные привязываются в обоих направлениях, к узлу Дома можно получить доступ, а атрибут $ref используется для работы с Домом. Вы также можете отправить запрос в фоновый режим и получить возвращенные данные.
5. перед обновлением Этот хук происходит до обновления, то есть обновляются отзывчивые данные и происходит повторный рендеринг виртуального дома.Вы можете изменить данные на текущем этапе, не вызывая повторного рендеринга.
6.обновлено Этот хук происходит после того, как обновление завершено, и компонент текущего этапа, Дом, завершил обновление. Важно избегать изменения данных в течение этого времени, так как это может привести к бесконечному циклу обновлений.
7. перед уничтожением Этот хук происходит до того, как экземпляр будет уничтожен, и экземпляр можно использовать на текущем этапе.В это время мы можем сделать последние штрихи, например, сбросить таймер.
8.уничтожен Этот хук происходит после уничтожения экземпляра, и в это время остается только оболочка dom. Компоненты были разобраны, привязки данных разобраны, слушатели удалены, а дочерние экземпляры уничтожены.
Недавно я использовал время простоя, чтобы посмотреть исходный код Vue, как раз в этот раз я видел исходный код версии Flow. Стыдно, я не понял с первого раза, поэтому читал Vue файл после упаковки, каждый может себе представить боль этого процесса, поддержки типа нет, и она очень длинная, поэтому у нас долго , Анализ исходного кода VUE — это исходный код версии Flow, который представляет собой код в каталоге SRC в исходном коде, загруженном с Github. Однако перед анализом я хотел бы рассказать о некоторых знаниях, необходимых для чтения исходного кода Vue.После освоения этих знаний я полагаю, что исходный код будет расслаблен.
1. Необходимые очки знаний
Я лично думаю, что, чтобы глубоко понять исходный код Vue, по крайней мере следующие пункты знаний требуются:
function sum(a, b) {
return a + b;
}
function sum(a: number, b:number) {
return a + b;
}
export function renderList (
val: any,
render: (
val: any,
keyOrIndex: string | number,
index?: number
) => VNode
): ?Array<VNode>{
...
}
Val - это тип Any, представляющий собой любой тип KeyorIndex - это строка | Числовой тип, независимо от того, является ли представитель строковым типом или числом, не может быть другим; index?: Number Это мы думаем о регулярном выражении? Значение --- 0 или 1, значение также соответствует, но следует обратить внимание?Расположение после двоеточия или двоеточия - поскольку эти две возможности имеют, вопросительный знак в приведенном выше коде должен следовать за двоеточием В передний, представляющий индекс, может пройти, но если вы прошли, вы должны передать цифровой тип; если вопросительный знак стоит за двоеточием, параметр должен быть передан, но он может быть цифровым типом или пустым. Это все еще слишком жестко? В то же время смысл кода более понятен. Почему ты говоришь это? Ранее я смотрел исходный код Vue.Когда я увидел реализацию режима наблюдателя, поскольку не было никакой неопределенности, я увидел исходный код этой версии Flow, и мне было легко его понять. Конечно, если вы хотите узнать больше деталей, вы можете ознакомиться с этой учебной документацией: Потоковые учебные материалы
1.2 Прототипы и наследование прототипов
Компоненты Vue считают, что мы использовали, и среди компонентов могут иметь подсазылы, это включает в себя сборку отца и сына. Фактически, процесс инициализации компонента такой же, с некоторым способом может быть унаследован. Код Vue Sons реализуется с использованием общих компонентов инициализации кода прототипа наследования. Поэтому, чтобы понять здесь, нужно понять концепцию прототипа JS; не много говорить здесь, но несколько предоставляют учебные материалы для вашей справки: Учебник Liao Xuefeng JS Прототип JS понимает1.3 Object.definePropertyЭтот метод очень эффективен в js, и Vue использует его для реализации функции адаптивных данных. Давайте взглянем на код в Vue, определяющий реактивные данные:
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
.....
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()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
Среди них мы видим использование функции Object.defineProperty, где первый параметр представляет собой устанавливаемый объект, второй параметр представляет собой значение ключа устанавливаемого объекта, а третий параметр представляет собой объект конфигурации, в котором параметры могут быть установлены следующим образом: value: значение соответствующего ключа, не нужно много говорить настраиваемый: можно ли удалить или перенастроить ключ enumerable: можно ли пройти ключ перезаписываемый: можно ли изменить ключ get: функция, вызываемая при получении значения ключа set: функция, вызываемая при установке значения ключа Рассмотрим эти свойства на примере:
let x = {}
x['name'] = 'vue'
console.log(Object.getOwnPropertyDescriptor(x,'name'))
Object.getOwnPropertyDescriptor可以获取对象某个key的描述对象,打印结果如下:
{
value: "vue",
writable: true,
enumerable: true,
configurable: true
}
Как видно из вышеизложенного, свойство, соответствующее ключу, может быть перезаписано (доступно для записи: true), сброшено или удалено (настраиваемое: true) и может быть пройдено (перечислено: true). Итак, давайте изменим эти свойства, такие как configurable, код выглядит следующим образом:
Object.defineProperty(x, 'name', {
configurable: false
})
После успешного выполнения, если вы хотите удалить свойство, например, удалить x['name'], вы обнаружите, что возвращаемое значение является ложным, то есть его нельзя удалить. Что значит перечисляемый? Давайте возьмем пример, чтобы понять, код выглядит следующим образом:
let x = {}
x[1] = 2
x[2] = 4
Object.defineProperty(x, 2, {
enumerable: false
})
for(let key in x){
console.log("key:" + key + "|value:" + x[key])
}
Результат выглядит следующим образом: ключ: 1 | значение: 2 Зачем? Поскольку мы установили 2 как непроходимое, то наш цикл for не может его получить.Конечно, мы все еще можем использовать x[2] для получения значения, соответствующего 2, но мы не можем получить его в цикле for . Какая от этого польза? В исходном коде Vue в классе Observer есть следующая строка кода: определение (значение, 'ob', this);
Вот функция DEF Tool, цель - добавить значение к ключу для __OB__, значение этого, но почему не только значение.ob= Вместо этого это доставит много хлопот? Поскольку программе необходимо пройти через значение и установить его подсодержимое рекурсивно, если значение.__ob__ используется напрямую, оно будет извлечено во время обхода, что, очевидно, не является первоначальным намерением, поэтому функция def использует Object.defineProperty для добавить значение к свойству value, в то время как enumerable имеет значение false. Что касается получения и установки? Это более мощное действие, похожее на добавление прокси-сервера при получении значения объекта и установке значения объекта.Вы можете представить, что вы можете делать в этой прокси-функции, например, уведомлять представление View об обновлении при установке значения. Давайте рассмотрим пример: пусть х = {} Object.defineProperty(x, 1, { получить: функция () { console.log("Вызван геттер!") }, установить: функция (newVal) { console.log("Вызван сеттер! newVal:" + newVal) } })
Вызванный геттер печатается, когда мы обращаемся к x[1], а вызываемый сеттер печатается, когда мы устанавливаем x[1] = 2. Именно таким образом исходный код Vue реализует сбор зависимостей при доступе к свойствам.При установке свойств в исходном коде есть предложение dep.notify, которое является операцией уведомления об обновлении представления.
1.4 Концепция Vnode
Vnode, как следует из названия, виртуальный узел, виртуальный узел, в первую очередь, это не концепция, созданная самой Vue. На самом деле, есть аналогичный проект на Github: Snabbdomy. Я лично думаю, что Vue также должен обратиться к реализации этой библиотеки, потому что эта библиотека содержит полный алгоритм Vnode и DOM Diff, и даже конкретный код реализации чувствует себя немного похоже на эту библиотеку. Зачем использовать vnode? Фактически, главная причина заключается в том, что нативный объект NOD NODE слишком велик. Давайте запустим код:
let dom = document.createElement('div');
for(let key in dom){
console.log(key)
}
打印的结果灰常长! ! !说明这个dom对象节点有点重量级,而我们的html网页经常数以百计个这种dom节点,如果采用之前的Jquery这种方式直接操作dom,性能上确实稍微low一点。所以snabbdom或者Vue中应用了Vnode,Vnode对象啥样呢?看看Vue源码对Vnode的定义:
export default class VNode {
tag: string | void;
data: VNodeData | void;
children: ?Array<VNode>;
text: string | void;
elm: Node | void;
ns: string | void;
context: Component | void; // rendered in this component's scope
key: string | number | void;
componentOptions: VNodeComponentOptions | void;
componentInstance: Component | void; // component instance
parent: VNode | void; // component placeholder node
// strictly internal
raw: boolean; // contains raw HTML? (server only)
isStatic: boolean; // hoisted static node
isRootInsert: boolean; // necessary for enter transition check
isComment: boolean; // empty comment placeholder?
isCloned: boolean; // is a cloned node?
isOnce: boolean; // is a v-once node?
asyncFactory: Function | void; // async component factory function
asyncMeta: Object | void;
isAsyncPlaceholder: boolean;
ssrContext: Object | void;
fnContext: Component | void; // real context vm for functional nodes
fnOptions: ?ComponentOptions; // for SSR caching
fnScopeId: ?string;
....
}
В отличие от этого, многие свойства Vnode объектов сделать меньше, в самом деле, оптические свойства не обязательно являются менее способны к высокой производительности, куда идти, еще один аспект для дифф алгоритмов старого и нового Vnode. На самом деле, здесь есть феномен: На самом деле, даже если есть много модификаций в большинстве случаев, но если смотреть с точки зрения макроса, на самом деле, не так много изменений точки. Например: Например, есть три узла A B C дом Наша деятельность превратится в B C D Если метод изменения Jquery , когда речь идет в первый раз A B, модифицировано еще раз столкнулся B в C, и один раз модифицировано, снова столкнуться С к D, и модифицированный, очевидно , факт, с точки зрения макроса, просто удалить а, D, а затем в конце концов, вы можете добавлять, изменять число снижается, однако эта оптимизация является обязательным условием, что может искать работу с точки зрения макрос. Jquery метод предыдущей модификации модифицируют встретились в первый раз, когда необходимо изменить A B, то код не был выполнен на спине, это невозможно знать позже модифицирован, то есть, не в силах смотреть на проблему с точки зрения глобальной перспективы. Так взгляд на проблеме с глобальным способом является асинхронной, первой модификацией в очередь, то все в группу, чтобы изменить, сделать дифф, на этот раз из статистического смысла действительно может оптимизировать производительность. Это также причина, почему Vue следующий код появляется в исходном коде: queueWatcher (это);
1.5 Функция Carrying.
Что, черт возьми, такое каррирование функций? По сути, это превращение многопараметрической функции в несколько частичных функций для вызова. Например:
function getSum(a,b){
return a+b;
}
Это функция двух аргументов, может вызов Getsum (1,2), чтобы получить результаты напрямую; однако иногда не два параметра могут быть определены, просто хочу пройти значение, в дополнение к повторной передаче на другой Точка времени в то, что мы изменяем функцию:
function getSum(a){
return function(b){
return a+b;
}
}
Так как же нам назвать эту каррированную функцию?
let f = getSum(2)
console.log(f(3))
console.log(getSum(2)(3)) //结果同上
可见,柯里化的效果便是之前必须同时传入两个参数才能调用成功而现在两个参数可以在不同时间点传入。那为毛要这么做嘛?Vue源码是这么应用这个特性的,Vue源码中有一个platform目录,专门存放和平台相关的源码(Vue可以在多平台上运行 比如Weex)。那这些源码中肯定有些操作是和平台相关的,比如会有些以下伪代码所表示的逻辑: if(平台A){ .... }else if(平台B){ .... }
Но если настолько мало написанного неудобным местом, то есть на самом деле, сначала пришло сюда, уже знают, какую ветвь выполненного времени на основе текущего платформенного кода, поэтому теперь необходимо написать код для запуска при проведении провода здесь, когда платформа Будут судить, поэтому всегда чувствуйте немного более скучного избыточного суждения, поэтому Vue Way для решения этой проблемы состоит в том, чтобы применить функцию Curry Curry, функцию, аналогичную следующему утверждению: Функция ... (зависимый от платформы параметр) { Обратная функция (не соответствующие параметры Интернета) { Обработка логики } }
Этот метод используется в ссылках на патч и компиляцию Vue. Когда мы будем говорить об этой части кода, мы рассмотрим ее подробно. Читатели заранее поймут дизайн Vue.
1.6 Макрозадача и микрозадача
Некоторые читатели могут слышать эти два слова впервые, на самом деле это тесно связано с механизмом цикла обработки событий в js. Мы также упоминали выше, что обновление Vue не должно обновлять представление синхронно, как только данные изменяются, поэтому определенно будут проблемы с производительностью, например, в обработчике события сначала this.data = A, а затем this.data = B , если вы хотите отрендерить Twice, думать об этом очень плохо. Исходный код Vue фактически помещает изменения в очередь, и один и тот же наблюдатель не будет повторяться (неважно, если вы не понимаете этих понятий, исходный код сосредоточится на этом позже), а затем обработает обновление логика асинхронно. При реализации асинхронного метода js фактически предоставляет две задачи — макрозадачу и микрозадачу. В чем разница между двумя задачами? Начнем с примера:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
Promise.resolve().then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
Каков результат выполнения приведенного выше кода? Читатели могут подумать об этом, ответ должен быть таким:
script start
script end
promise1
promise2
setTimeout
promise3
promise4
Это можно легко понять так: в цикле событий js есть две очереди, одна называется MacroTask, а другая MircroTask, вы можете видеть, что Macro большая, а Micro маленькая (подумайте о переводе макроэкономики и микроэкономики). Затем большая очередь задач запускает большие задачи — такие как основная программа процесса, обработчик событий, setTimeout и т. д., а маленькая очередь задач выполняет маленькие задачи.В настоящее время читатели могут вспомнить одну — Promise. js всегда сначала выполняет одно выполнение из большой очереди задач, а затем выполняет все маленькие очереди задач и циклически повторяется. Взяв приведенный выше пример программы, во-первых, последняя программа — это большая задача, которая должна быть выполнена первой, а все мелкие задачи должны быть выполнены после выполнения.Промис — это маленькая задача, поэтому выводятся обещания1 и обещания2, а setTimeout — это большая задача, поэтому все задачи выполняются.После маленькой задачи возьмите для выполнения еще одну большую задачу, которая называется setTimeout, которая бросает обещание в очередь маленьких задач, поэтому после выполнения setTimeout выполняются все маленькие очереди задач опять же, так что последние обещания3 и обещания4. Это немного сбивает с толку. Скопируйте приведенный выше образец программы в браузер и запустите его на некоторое время, и тогда вы поймете. Ключ в том, чтобы знать, что приведенная выше программа сама по себе также является большой задачей. Обязательно разберитесь, прежде чем переходить к исходному коду Vue, иначе вы не поймете функцию nextTick во Vue. Порекомендуйте несколько статей (я внимательно их прочитал и получил много пользы) Макрозадача против микрозадачи Понимание макрозадачи и микрозадачи в js Ruan Yifeng Eventloop Понимание
1.7 Алгоритмы рекурсивного программирования
Многие программисты боятся рекурсии, но рекурсия — действительно очень мощный алгоритм. В исходном коде Vue используется множество рекурсивных алгоритмов, таких как алгоритм dom diff, оптимизация ast, генерация объектного кода и т. д. Много, много. И эти рекурсии не так просты, как A->A.Рекурсия в большинстве исходных кодов — это такие сложные рекурсивные вызовы, как A->B->C...->A и так далее. Например, классический алгоритм dom diff в Vue:
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVnode)) {
oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left
} else if (isUndef(oldEndVnode)) {
oldEndVnode = oldCh[--oldEndIdx];
} else if (sameVnode(oldStartVnode, newStartVnode)) {
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
oldStartVnode = oldCh[++oldStartIdx];
newStartVnode = newCh[++newStartIdx];
} else if (sameVnode(oldEndVnode, newEndVnode)) {
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
oldEndVnode = oldCh[--oldEndIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));
oldStartVnode = oldCh[++oldStartIdx];
newEndVnode = newCh[--newEndIdx];
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx];
} else {
if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
if (isUndef(idxInOld)) { // New element
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
} else {
vnodeToMove = oldCh[idxInOld];
if (sameVnode(vnodeToMove, newStartVnode)) {
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue);
oldCh[idxInOld] = undefined;
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);
} else {
// same key but different element. treat as new element
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
}
}
newStartVnode = newCh[++newStartIdx];
}
Приведенный выше код является частью исходного кода для сравнения старых и новых узлов Vnode для обновления дочерних узлов.Вызывающий объект — это функция patchVnode.Мы обнаружили, что эта часть функции снова вызовет patchVnode, а цепочка вызовов такова: patchVnode ->updateChildren->patchVnode. В то же время, даже если рекурсия не применяется напрямую, в процессе компиляции шаблонов в AST (абстрактное синтаксическое дерево) он использует стек для имитации идеи рекурсии, что показывает важность рекурсивного алгоритма. В этом нет ничего удивительного, ведь будь то настоящий dom или vnode, на самом деле это древовидная структура, которая изначально определяется рекурсивно. Мы также подготовим отдельную статью, чтобы поговорить о рекурсии, например, об использовании рекурсии для разбора строк JSON. Надеюсь, читатели обратят на это внимание.
1.8 Базовые знания принципов компиляции
Для некоторых программистов это может оказаться более болезненным, чем рекурсия, но я считаю, что до тех пор, пока читатель внимательно разбирается в этой части кода Vue, это точно будет полезнее, чем чтение учебника по принципу компиляции N раз. Давайте посмотрим на реализацию исходного кода Vue здесь:
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
Приведенный выше код сначала компилирует шаблон в абстрактное синтаксическое дерево ast с помощью функции синтаксического анализа, затем оптимизирует код ast и, наконец, генерирует функцию рендеринга. По сути, это процесс перевода, например, gcc переводит язык C в ассемблер, а Babel переводит ES6 в ES5 и т. д. Здесь процесс очень похож. Vue тоже играл в такую игру, компилируя html шаблона в функцию рендера, что это значит?
<li v-for="record in commits">
<span class="date">{{record.commit.author.date}}</span>
</li>
Например, приведенный выше html, как вы думаете, браузер будет хорошо знать? Очевидно, что это не атрибут v-for html native, если вы запустите приведенный выше код прямо в браузере, вы обнаружите, что {{record.commit.author.date}} показывает прямой выход, v-for, конечно, не работает , будет ли html отображаться внутри (ведь html отказоустойчивый высокий);? Vue, но после компиляции компилятора для генерации некоторых системных функций, которые выполняют html-элемент, браузер знает, удивительный факт, что это всего лишь применение принципа компиляция учебников, часть знаний заполняет эту часть, так как мы будем очень подробно описывать исходный код, просто следуйте взгляду вниз, это определенно будет иметь понимание процесса компиляции. Теперь это может быть так же просто, как понять, что такое AST (абстрактное синтаксическое дерево), например, если судья может писать java, язык C также может писать, js, python и т. д. также может быть (как показано ниже): Джава: если (х > 5) { .... }
python: if x>5: ....
будь осторожен 在使用生命周期时有几点注意事项需要我们牢记。 1.第一点就是上文曾提到的created阶段的ajax请求与mounted请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态。 2.除了beforeCreate和created钩子之外,其他钩子均在服务器端渲染期间不被调用。 3.上文曾提到过,在updated的时候千万不要去修改data里面赋值的数据,否则会导致死循环。 4.Vue的所有生命周期函数都是自动绑定到this的上下文上。所以,你这里使用箭头函数的话,就会出现this指向的父级作用域,就会报错。原因下面源码部分会讲解。
Webpack
Описывает основные функции .webpack. Упакованный интерфейсный модуль Webpack — это пользовательский инструмент. Он в основном используется для упаковки javascript в используемом браузере. Но также можно конвертировать, связывать, упаковывать другие статические ресурсы, включая css, изображение, файл шрифта, шаблон и так далее. Лично я считаю, что его преимущество в простоте использования и в основном имеет общие черты, Также они могут разработать собственный загрузчик плагинов и удовлетворить свои потребности. Здесь максимально подробно можно представить использование некоторых основных функций.
二.运行webpack webpack需要编写一个config文件,然后根据这个文件来执行需要的打包功能。 我们现在来编写一个最简单的config。新建一个文件,命名为webpack-config.js。 config文件实际上就是一个Commonjs的模块。 Содержание следующее:
var webpack = require('webpack'); var path = require('path'); var buildPath = path.resolve(__dirname,"build"); var nodemodulesPath = path.resolve(__dirname,'node_modules');
var config = { //入口文件配置 entry:path.resolve(__dirname,'src/main.js'), решать: { extentions:["","js"]//当requrie的模块找不到时,添加这些后缀 }, //文件导出的配置 выход: { path:buildPath, filename:"app.js" } }
module.exports = config;
1.entry: запись в конфигурационном файле должна быть упакована, можно настроить файл с несколькими записями
2.Output: Объект выходного параметра используется для определения сборки выходных файлов. Который содержит путь и имя файла
3.модуль(загрузчики): Настройте загрузчик для использования. Выполните некоторую обработку файла соответственно. Например, babel-loader может конвертировать файлы es6 в es5. Большинство функций обработки файлов реализует загрузчик. Загрузчик эквивалентен задаче в gulp. Загрузчики могут использоваться для обработки файлов, которые требуются в файлах ввода и на которые ссылаются другими способами. Загрузчик, как правило, представляет собой независимый модуль узла, который необходимо устанавливать отдельно. Конфигурация элемента загрузчика: test: /.(js|jsx)$/,//Обратите внимание, что это регулярное выражение, не добавляйте кавычки, сопоставьте обрабатываемый файл loader: 'eslint-loader', // Используемый загрузчик, "-loader" можно не указывать include: [path.resolve(__dirname, "src/app")],//включить каталог для обработки exclude: [nodeModulesPath]//Исключить каталоги, которые не обрабатываются
конфигурации модуля: модуль: { прелоадеры: [ { тест: /.(js|jsx)/, //正则表达式匹配 .js 和 .jsx 文件 loader: 'babel-loader?optional=runtime&stage=0',//对匹配的文件进行处理的loader exclude: [nodeModulesPath]//排除node module中的文件 } ] }
Основная идея
Вход выход погрузчик плагины модель
webpack.config.js const config = { entry: './file.js' } module.exports = config
Общие сценарии: Разделение приложений и записей сторонних библиотек Но страничные приложения и многостраничные приложения
Старший продвинутый (CDN и использование ресурсов хеш)
Определение загрузчика модуль загрузчика для конвертации исходного кода.
Загрузчики позволяют предварительно обрабатывать файлы при импорте или «загрузке» модулей.
Таким образом, загрузчики похожи на «задачи» в других инструментах сборки и предоставляют мощный способ обработки этапов сборки внешнего интерфейса.
Загрузчики могут преобразовывать файлы с разных языков (например, TypeScript) в JavaScript или преобразовывать встроенные изображения в URL-адреса данных.
Загрузчик даже позволяет импортировать файлы CSS прямо в модули JavaScript!
plugins
Цель плагинов - решить другие вещи, которые не может сделать загрузчик.
vuex
Что такое Vuex.
Vuex — это **модель управления состоянием**, специально разработанная для приложений Vue.js. Он использует централизованное хранилище для управления состоянием всех компонентов приложения и использует соответствующие правила для обеспечения предсказуемого изменения состояния.
Что такое «модель управления государством»?
Во-первых, давайте посмотрим на следующий пример
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
Это государственное применение самоуправления состоит из следующих частей:
- представление, которое декларативно сопоставляет состояние представлениям;
Вот простая иллюстрация идеи «одностороннего потока данных»:
- Действия из разных представлений требуют изменения одного и того же состояния.
Vuex — это библиотека управления состоянием, специально разработанная для Vue.js, позволяющая использовать детальный механизм ответа данных Vue.js для эффективного обновления состояния.
Когда мне следует использовать Vuex?
Vuex может помочь нам управлять общим состоянием и поставляется с большим количеством концепций и фреймворков. Это необходимо сопоставить с краткосрочными и долгосрочными выгодами. Использование Vuex может быть утомительным и избыточным, если вы не планируете разрабатывать большие одностраничные приложения. Это правда — если ваше приложение достаточно простое, вам лучше не использовать Vuex. Простая модель достаточна для хранения необходимого вам. Однако, если вам нужно создать большое одностраничное приложение и вы, вероятно, задумаетесь о том, как лучше управлять состоянием во внешних компонентах, естественным выбором будет Vuex.
const store = new Vuex.Store({
state: {
token: 'xxx12345'
},
mutations: {
changeToken(state, token) {
state.token = token
}
}
})
// 通过mutation去改变token的状态值
store.commit('changeToken', 'xxxx12345555')
再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。 Это простое соглашение делает ваше намерение более очевидным, упрощая интерпретацию изменений состояния в вашем приложении при чтении кода. Кроме того, это дает нам возможность реализовать некоторые инструменты отладки, которые могут записывать каждое изменение состояния и сохранять моментальный снимок состояния. С его помощью мы можем даже добиться опыта отладки, подобного путешествию во времени.
Поскольку хранилище состояний реагирует на состояние для сохранения вызова, простое вычисление нужно только вернуть свойству в компоненте. Изменение триггера — это только представленная мутация в компоненте методов.
основные концепции vuex
State
** Дерево с одним состоянием ** Vuex использует одно дерево состояний — да, один объект содержит все состояния уровня приложения. Пока он существует как «Единый источник данных (SSOT)». Это также означает, что каждое приложение будет содержать только один экземпляр хранилища. Единое дерево состояний позволяет нам напрямую находить любую конкретную часть состояния и легко делать моментальный снимок всего текущего состояния приложения во время отладки.
Деревья с одним состоянием не конфликтуют с модульностью — в последующих главах мы обсудим, как распределять события состояния и изменения состояния по подмодулям.
** Получить состояние Vuex в компоненте Vue **
Итак, как мы покажем это в сборке состояний Vue? Поскольку память состояния Vuex реагирует на чтение из хранилища состояний экземпляра, самый простой способ — вернуться к состоянию при вычислении свойств:
// 创建一个 tree 组件
const trees = {
template: `<div>{{ tree }}</div>`,
computed: {
tree () {
return store.state.tree
}
}
}
Всякий раз, когда изменяется Store.State.Tree, свойства расчета будут изменены, и будет запущен обновленный связанный DOM.
Однако этот шаблон приводит к тому, что компоненты полагаются на синглтоны глобального состояния. В модульной системе сборки вам нужно часто импортировать каждый компонент, который должен использовать состояние, и вам нужно имитировать состояние при тестировании компонентов.
Vuex предоставляет механизм «внедрения» состояния из корневого компонента в каждый дочерний компонент с помощью параметра хранилища (необходимо вызвать Vue.use(Vuex)):
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { trees },
template: `
<div class="app">
<trees></trees>
</div>
`
})
При регистрации параметра хранилища в корневом экземпляре экземпляр хранилища будет внедрен во все дочерние компоненты корневого компонента, и доступ к дочерним компонентам можно будет получить через this.$store . Обновим реализацию деревьев:
const trees = {
template: `<div>{{ tree }}</div>`,
computed: {
count () {
return this.$store.state.tree
}
}
}
Getter
Иногда нам нужно получить какое-то состояние из состояния в хранилище, например фильтрация и подсчет списка:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
Если несколько компонентов должны использовать это свойство, мы либо дублируем функцию, либо извлекаем ее в общую функцию и импортируем в несколько мест — ни один из способов не идеален.
Vuex позволяет нам определять «геттеры» (которые можно рассматривать как вычисляемые свойства хранилища) в хранилище. Как и вычисляемые свойства, возвращаемое значение метода получения кэшируется на основе его зависимостей и пересчитывается только при изменении его зависимостей.
Getter принимает состояние в качестве своего первого параметра:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
** Доступ через свойства ** Геттеры выставляются как объекты store.getters, и вы можете получить доступ к этим значениям как к свойствам:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
Getter 也可以接受其他 getter 作为第二个参数:
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount // -> 1
我们可以很容易地在任何组件中使用它:
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
Обратите внимание, что геттеры кэшируются как часть системы реактивности Vue при доступе через свойства.
** Доступ по методу ** Вы также можете передать аргументы геттеру, позволив геттеру вернуть функцию. Очень полезно, когда вы запрашиваете массивы в хранилище.
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
Обратите внимание, что при доступе к геттеру через метод он будет вызываться каждый раз, и результат не будет кэшироваться.
Mutation
Единственный способ изменить состояние в хранилище Vuex — отправить мутацию. Мутации в Vuex очень похожи на события: каждая мутация имеет строковый тип события (тип) и функцию обратного вызова (обработчик). В этой функции обратного вызова мы фактически изменяем состояние, и она принимает состояние в качестве первого параметра:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
state.count++
}
}
})
Вы не можете вызвать обработчик мутации напрямую. Этот вариант больше похож на регистрацию события: «При срабатывании мутации инкремента типа вызвать эту функцию».
store.commit('increment')
**Отправить полезную нагрузку**
В store.commit можно передать дополнительные параметры, а именно полезную нагрузку мутации:
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
В большинстве случаев полезной нагрузкой должен быть объект, который может содержать несколько полей и записывать мутации более читабельно:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
store.commit({
type: 'increment',
amount: 10
})
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
** Mutation 需遵守 Vue 的响应规则 ** 既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。 Это также означает, что мутации в Vuex также должны подчиняться тем же предостережениям, что и при использовании Vue:
Когда вам нужно добавить новое свойство к объекту, вы должны
state.obj = {...state.obj, newProp: 123} ** Используйте константы вместо типов событий Mutation ** Использование констант вместо типов событий мутации является распространенным шаблоном в различных реализациях Flux. Это позволяет работать таким инструментам, как линтеры, а наличие этих констант в отдельных файлах дает вашим соавторам кода представление о мутациях, содержащихся во всем приложении:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
// 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION] (state) {
// mutate state
}
}
})
Вам решать, использовать константы или нет — это может быть полезно в больших проектах, требующих совместной работы нескольких человек. Но если вам это не нравится, вы можете вообще не делать этого.
** Мутация должна быть синхронизирована функция ** Важным правилом является запоминание мутации должна быть синхронизирована функция. Почему? Пожалуйста, обратитесь к следующим примерам:
mutations: {
someMutation (state) {
api.callAsyncMethod(() => {
state.count++
})
}
}
Теперь представьте, что мы отлаживаем приложение и регистрируем обнаруженную devtool мутацию. Каждая мутация была записана, devtools должен сделать снимок предыдущего состояния и состояния. Однако в приведенном выше примере асинхронные функции обратного вызова мутации делают это невозможным: поскольку при срабатывании мутации функция обратного вызова не вызывается, инструменты разработки не знают, когда функция обратного вызова фактически вызывается — по сути, любое изменение, сделанное в состоянии функции обратного вызова. невозможно отследить.
** Отправить мутацию в компоненте ** Вы можете использовать this.$store.commit('xxx') для отправки мутаций в компонентах или использовать помощник mapMutations для сопоставления методов в компонентах с вызовами store.commit (требуется внедрение хранилища в корневом каталоге).
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
Action
Попробуйте этот урок на scrimba Действие похоже на мутацию, разница в следующем:
Действия вызывают мутации, а не изменения состояния напрямую. Действие может содержать произвольные асинхронные операции. Зарегистрируем простое действие:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
Функция Action принимает объект контекста с теми же методами и свойствами, что и экземпляр хранилища, поэтому вы можете вызвать context.commit, чтобы зафиксировать мутацию, или получить состояние и геттеры через context.state и context.getters. Когда позже мы представим модули, вы увидите, почему объект контекста не является самим экземпляром хранилища.
На практике мы часто используем деструктуризацию параметров ES2015 для упрощения кода (особенно когда нам нужно много раз вызывать commit):
actions: {
increment ({ commit }) {
commit('increment')
}
}
** Распространить действие ** Действия запускаются методом store.dispatch:
store.dispatch('increment') 乍一眼看上去感觉多此一举,我们直接分发 mutation 岂不更方便?实际上并非如此,还记得 mutation 必须同步执行这个限制么? Действие не обязывает! Мы можем выполнять асинхронные операции внутри действия:
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
Действия поддерживают ту же полезную нагрузку и распределение объектов:
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
Давайте рассмотрим более практичный пример корзины покупок, включающий вызов асинхронного API и отправку нескольких мутаций:
actions: {
checkout ({ commit, state }, products) {
// 把当前购物车的物品备份起来
const savedCartItems = [...state.cart.added]
// 发出结账请求,然后乐观地清空购物车
commit(types.CHECKOUT_REQUEST)
// 购物 API 接受一个成功回调和一个失败回调
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失败操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
Обратите внимание, что мы выполняем ряд асинхронных операций и записываем побочные эффекты (т. е. изменения состояния) действия, отправляя мутации.
** Распределить действие в компоненте ** Вы используете this.$store.dispatch('xxx') для отправки действий в вашем компоненте или используете помощник mapActions для сопоставления методов вашего компонента с вызовами store.dispatch (сначала вам нужно внедрить хранилище в корневой узел):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
** Комбинированное действие ** Действия обычно асинхронны, так как же узнать, что действие завершилось? Что еще более важно, как мы можем объединить несколько действий для обработки более сложных асинхронных процессов?
Во-первых, нужно понимать, что store.dispatch может обрабатывать промис, возвращаемый обработчиком сработавшего действия, а store.dispatch по-прежнему возвращает промис:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
Теперь вы можете:
store.dispatch('actionA').then(() => {
// ...
})
在另外一个 action 中也可以:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
Наконец, если мы воспользуемся преимуществами async/await, мы можем составить действия следующим образом:
// Предположим, что getData() и getOtherData() возвращают Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
Store.dispatch может запускать несколько функций действия в разных модулях. В этом случае возвращенный промис будет выполняться только после завершения всех запускающих функций.
Module
Благодаря использованию единого дерева состояний все состояния приложения будут агрегированы в относительно большой объект. Когда приложение становится очень сложным, объект хранилища может сильно раздуться.
Чтобы решить вышеуказанные проблемы, Vuex позволяет нам разделить хранилище на модули. Каждый модуль имеет свое состояние, мутации, действия, геттеры и даже вложенные подмодули — разбитые таким же образом сверху вниз:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
** ** локальный государственный модуль
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
Для Getter внутри модуля статус будет выставлен root в качестве третьего параметра:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
** 命名空间 ** По умолчанию действия, мутации и геттеры внутри модулей регистрируются в глобальном пространстве имен — это позволяет нескольким модулям реагировать на одну и ту же мутацию или действие.
Если вы хотите, чтобы ваш модуль был более инкапсулированным и пригодным для повторного использования, вы можете сделать его модулем с пространством имен, добавив namespaced: true . Когда модуль регистрируется, все его геттеры, действия и мутации автоматически именуются в соответствии с путем, зарегистрированным модулем. Например:
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模块内容(module assets)
state: { ... }, // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模块
modules: {
// 继承父模块的命名空间
myPage: {
state: { ... },
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 进一步嵌套命名空间
posts: {
namespaced: true,
state: { ... },
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
Включить пространства имен геттера и действия будут получать локализованный геттер, отправку и фиксацию. Другими словами, вам не нужно добавлять дополнительный пробел в тот же префикс имени модуля, когда вы используете содержимое модуля (активы модуля). Без изменения кода в модуле после изменения атрибутов пространства имен.
**Доступ к глобальным активам в модуле с пространством имен** Если вы хотите использовать глобальное состояние и геттер, rootState и rootGetter передаются геттеру в качестве третьего и четвертого параметров, а также действию через свойства объекта контекста.
Чтобы отправить действия или зафиксировать мутации в глобальном пространстве имен, передайте { root: true } в качестве третьего параметра для отправки или фиксации.
modules: {
foo: {
namespaced: true,
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
** Зарегистрируйте глобальные действия в модулях именных модулей ** Чтобы зарегистрировать глобальное действие в модуле имен, вы можете добавить root: true и поместите определение действия в обработчике функции. Например:
{
actions: {
someOtherAction ({dispatch}) {
dispatch('someAction')
}
},
modules: {
foo: {
namespaced: true,
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
}
}
}
** Связывание функций с пространствами имен ** При использовании функций mapState, mapGetters, mapActions и mapMutations для привязки модулей с пространством имен может быть неудобно писать:
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
},
methods: {
...mapActions([
'some/nested/module/foo', // -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()
])
}
Для этого вы можете передавать строку имени пространства модуля в качестве первого аргумента вышеуказанной функции, и все привязки будут автоматически иметь этот модуль в качестве контекста. Таким образом, приведенный выше пример можно упростить:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
Кроме того, вы можете создавать вспомогательные функции на основе пространства имен с помощью createNamespacedHelpers. Он возвращает объект с новыми вспомогательными функциями привязки компонентов, привязанными к заданному значению пространства имен:
import { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a,
b: state => state.b
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo',
'bar'
])
}
}
export function createPlugin (options = {}) {
return function (store) {
// 把空间名字添加到插件模块的类型(type)中去
const namespace = options.namespace || ''
store.dispatch(namespace + 'pluginAction')
}
}
myModule
store.registerModule('myModule', {
// ...
})
// 注册嵌套模块nested/myModule
store.registerModule(['nested', 'myModule'], {
// ...
})
之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态。
Функция динамической регистрации модулей позволяет другим плагинам Vue управлять состоянием с помощью Vuex, добавляя новые модули в хранилище.例如,vuex-router-sync 插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。
你也可以使用 store.unregisterModule(moduleName) 来动态卸载模块。 Обратите внимание, что вы не можете выгрузить статические модули (т. е. модули, объявленные при создании хранилища) с помощью этого метода.
** сохранить состояние ** При регистрации нового модуля вы, скорее всего, захотите сохранить предыдущее состояние, например, из приложения, отображаемого сервером. Вы можете заархивировать его с помощью параметра saveState: store.registerModule('a', module, {preservState: true}).
Когда вы устанавливаете saveState: true , модуль будет зарегистрирован, а действия, мутации и геттеры будут добавлены в хранилище, а состояние — нет. Это предполагает, что состояние хранилища уже содержит состояние модуля, и вы не хотите его перезаписывать.
** ** модуль повторно Иногда нам может потребоваться создать несколько экземпляров модуля, например:
Создайте несколько хранилищ, которые используют один и тот же модуль (например, чтобы избежать синглтонов с отслеживанием состояния при рендеринге на сервере, когда параметр runInNewContext имеет значение false или 'once' ) Многократная регистрация одного и того же модуля в магазине Если мы используем чистый объект для объявления состояния модуля, то этот объект состояния будет совместно использоваться по ссылке, что приведет к проблеме загрязнения данных между хранилищами или модулями при изменении объекта состояния.
На самом деле данные в этой сборке и Vue — одна и та же проблема. Таким образом, решение то же самое - использовать функцию для объявления состояния модуля (только поддержка 2.3.0+):
const MyReusableModule = {
state () {
return {
foo: 'bar'
}
},
// mutation, action 和 getter 等等...
}
vue-router
Введение Чтобы изучить vue-router, вы должны сначала узнать, что такое маршрутизация? Почему мы не можем просто использовать оригиналГде ссылка на запись тега? Как использовать vue-маршрутизатор? Каковы общие операции маршрутизации? Эти вопросы и т. д. являются основными вопросами, которые будут обсуждаться в этой статье.
Если вы хотите прочитать больше качественных статей, нажмите на блог GitHub.
2. Что такое vue-маршрутизатор Маршрутизация здесь не относится к аппаратным маршрутизаторам, на которые мы обычно ссылаемся.Маршрутизация здесь — это диспетчер путей SPA (одностраничное приложение). С точки зрения непрофессионала, vue-router — это система управления путями ссылок WebApp. vue-router — официальный плагин маршрутизации для Vue.js, глубоко интегрированный с vue.js и подходящий для создания одностраничных приложений. Одностраничное приложение Vue основано на маршрутизации и компонентах.Маршрутизация используется для установки путей доступа и сопоставления путей и компонентов. В традиционных страничных приложениях некоторые гиперссылки используются для переключения страниц и переходов между ними. В одностраничном приложении vue-router это переключение между путями, то есть переключение компонентов. Суть модуля маршрутизации заключается в установлении отношения отображения между URL-адресом и страницей.
Что касается того, почему мы не можем использовать тег a, это связано с тем, что все одностраничные приложения создаются с помощью Vue (когда ваш проект будет готов к упаковке, когда вы запустите npm run build, будет создана папка dist, которая содержит только статические ресурсы и страница index.html), поэтому вы пишетеЯрлыки не работают, для управления ими нужно использовать vue-router.
В-третьих, принцип реализации vue-router SPA (одностраничное приложение): одностраничное приложение только с одной полной страницей; при загрузке страницы оно не загружает всю страницу, а только обновляет содержимое в указанном контейнере. Одним из основных принципов одностраничного приложения (SPA) является: обновление представления без повторного запроса страницы; vue-router предоставляет два способа реализации одностраничного внешнего интерфейса: режим хэширования и режим истории; в зависимости от того, какой из них используется, зависит по параметру режима.метод.
1. Режим хеширования: Режим хеширования vue-router по умолчанию — использует хэш URL-адреса для имитации полного URL-адреса, поэтому при изменении URL-адреса страница не будет перезагружаться. Хэш (#) — это точка привязки URL-адреса, которая представляет позицию на веб-странице. Если часть после # изменить, браузер прокрутит только до соответствующей позиции и не перезагрузит веб-страницу, что означает, что в URL появляется хэш, но он не будет включен в http запрос и никак не повлияет на бэкэнд, поэтому изменение хеша не приведет к перезагрузке страницы, в то же время каждый раз при изменении части после #, запись будет добавлена в историю доступа браузера, используя кнопку «назад», вы можете вернуться к предыдущей позиции; поэтому режим хеширования отображает разные данные указанной позиции DOM в соответствии с изменением значения привязки в соответствии с различными значения. Принцип хэш-режима заключается в событии onhashchange (отслеживание изменений хэш-значения), которое можно отслеживать на объекте окна.
2. Режим истории: Поскольку режим хеширования будет иметь свой собственный # в URL-адресе, если мы не хотим уродливого хэша, мы можем использовать режим маршрутизации истории, просто добавьте «режим:« история »» при настройке правил маршрутизации, этот режим полностью использует новых методов pushState() и replaceState() в интерфейсе истории html5. Эти два метода применяются к стеку записей браузера.На основе текущих возвратов, перенаправлений и переходов они обеспечивают функцию изменения записи истории. Просто когда они выполняют модификацию, хотя текущий URL-адрес меняется, браузер не сразу отправляет запрос на серверную часть.
// файл main.jsconst router = новый VueRouter({ режим: «история», маршруты: [...] }) Когда вы используете режим истории, URL-адрес похож на обычный URL-адрес, например.yoursite.com/user/id, лучше…Однако, чтобы хорошо играть в этом режиме, ему также нужна поддержка фоновой конфигурации. Поскольку наше приложение является одностраничным клиентским приложением, если фон не настроен должным образом, когда пользователь напрямую обращается к браузеруoursite.com/user/idОн вернет 404, что некрасиво. Итак, вам нужно добавить ресурс-кандидат, который охватывает все ситуации на стороне сервера: если URL-адрес не соответствует ни одному из статических ресурсов, он должен возвращать ту же страницу index.html, от которой зависит ваше приложение.
экспортировать константные маршруты = [ {путь: "/", имя: "homeLink", компонент: Home} {путь: "/register", имя: "registerLink", компонент: Register}, {путь: "/login", имя: "loginLink", компонент: Login}, {путь: "*", перенаправление: "/"}] Здесь установлено, что если URL-адрес введен неправильно или URL-адрес не соответствует каким-либо статическим ресурсам, он автоматически перейдет на Домашнюю страницу.
3. Используйте модуль маршрутизации для перехода на другую страницу Способ 1: изменить адресную строку напрямую
Способ 2: this.$router.push('адрес маршрутизатора')
Путь 3:
Четыре, использование vue-router 1: Загрузите npm i vue-router -S 2: добавлен main.js при импорте VueRouter из «vue-router»; 3: Подключите Vue.use (VueRouter); 4: Создайте и настройте правила маршрутизации для объекта маршрутизации let router = new VueRouter ({routes: [{path: '/ home', component: Home}]}); 5: он передается экземпляру объекта маршрутизации Vue, к маршрутизатору добавляются параметры: маршрутизатор 6: app.vue в сторону ямы
Для конкретной реализации, пожалуйста, смотрите следующий код:
// Представлено в файле main.jsимпортировать Vue из 'vue'; импортировать VueRouter из «vue-router»; //основное тело импортировать приложение из './components/app.vue'; импортировать Home из './components/home.vue' //установить плагин Vue.use(VueRouter);//Подключаем свойства //Создаем объект маршрутизации и настраиваем правила маршрутизации пусть маршрутизатор = новый VueRouter({ маршруты: [ // объект {путь: '/home', компонент: Главная} ] }); // новый запуск Vue новый Vue({ эль: '#приложение', // Сообщаем vue о наших правилах маршрутизации маршрутизатор: маршрутизатор, // может быть сокращено как маршрутизатор рендеринг: с => с (приложение), }) Наконец, не забудьте «оставить яму» в app.vue.
Пять, передача параметров vue-router И декларативная навигация, и программная навигация router.push(...) могут передавать параметры. В этой статье в основном представлен метод передачи параметров первого. Те же правила применяются к программной навигации.
1. Передайте параметры с именем Настройте атрибут имени в файле маршрутизации src/router/index.js.
маршруты: [ { дорожка: '/', имя: 'Привет', компонент: привет } ] Используется в шаблоне (src/App.vue)route.name}}
2 ДЛЯ прохождения через тег Основной синтаксис этого метода металлизации:
значениеСтрока Например, сначала в файле src/App.vue
{{
{ path:'/params/:newsId/:newsTitle', component:Params } 我们需要传递参数是新闻ID(newsId)和新闻标题(newsTitle).所以我们在路由配置文件里制定了这两个值。
<template>
<div>
<h2>{{ msg }}</h2>
<p>新闻ID:{{ $route.params.newsId}}</p>
<p>新闻标题:{{ $route.params.newsTitle}}</p>
</div>
</template>
<script>
export default {
name: 'params',
data () {
return {
msg: 'params page'
}
}
}
</script>
params
- Используется для соответствия пути маршрутизации, а затем пройти параметры запроса ПКС RUTER-LINK LINK QUERY
Соответствующая конфигурация маршрутизации:
{
path: '/query',
name: 'Query',
component: Query
}
Таким образом, мы можем получить параметры:
это.$ route.query.queryId Шесть, подмаршрут конфигурации vue-router (второй маршрут) Реальный интерфейс приложения, обычно представляющий собой комбинацию компонентов, вложенных друг в друга. Точно так же URL каждого сегмента по динамическому пути определенной структуры также может соответствовать слоям вложенных компонентов, например:
изображение 如何实现下图效果(H1页面和H2页面嵌套在主页中)? изображение 1.首先用标签增加了两个新的导航链接
Дома Страница H1 Страница H2 2. Добавьте тег в HelloWorld.vue, чтобы указать место вставки для подшаблона.
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<router-view></router-view>
</div>
</template>
3. Создайте два новых шаблона компонентов H1.vue и H2.vue в каталоге компонентов. Содержимое этих двух файлов похоже. Ниже приведено содержимое страницы H1.vue:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
data() {
return {
msg: 'I am H1 page,Welcome to H1'
}
}
}
</script>
修改router/index.js代码,子路由的写法是在原有的路由配置下加入children字段。
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld,
children: [{path: '/h1', name: 'H1', component: H1},//子路由的<router-view>必须在HelloWorld.vue中出现
{path: '/h2', name: 'H2', component: H2}
]
}
]
Семь, одностраничная мультимаршрутная операция У нас есть более двух областей на странице, и мы управляем содержимым этих областей, настроив js-файл маршрута.
1. Файл App.vue, напишите две новые строки тегов ниже и добавьте несколько стилей CSS.
<template>
<div id="app">
<img src="./assets/logo.png">
<router-link :to="{name:'HelloWorld'}"><h1>H1</h1></router-link>
<router-link :to="{name:'H1'}"><h1>H2</h1></router-link>
<router-view></router-view>
<router-view name="left" style="float:left;width:50%;background-color:#ccc;height:300px;"/>
<router-view name="right" style="float:right;width:50%;background-color:yellowgreen;height:300px;"/>
</div>
</template>
2. Эти три области необходимо настроить в маршрутизации, настройка в основном выполняется в поле компонентов.
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
components: {default: HelloWorld,
left:H1,//显示H1组件内容'I am H1 page,Welcome to H1'
right:H2//显示H2组件内容'I am H2 page,Welcome to H2'
}
},
{
path: '/h1',
name: 'H1',
components: {default: HelloWorld,
left:H2,//显示H2组件内容
right:H1//显示H1组件内容
}
}
]
})
Мы пишем код над двумя путями, по умолчанию это «/», а другой — «/ Привет». В случае компонентов, у которых два пути, мы определили три области отображения содержимого. На последней странице показан следующий рисунок:
изображение Восемь.Разница между роутерами Сначала мы распечатываем два файла console.log:
изображение изображение $route — это «информационный объект маршрутизации», включая путь, параметры, хэш, запрос, полный путь, сопоставление, имя и другие параметры маршрутной информации.
① $route.path Строка, соответствующая пути текущего маршрута, всегда разрешается в абсолютный путь, например "/order".
② $route.params Объект типа "ключ-значение", содержащий динамические фрагменты и фрагменты полного совпадения, Если параметры маршрута отсутствуют, это пустой объект.
3маршрут.запрос.пользователь равен 1, Если параметры запроса отсутствуют, это пустой объект.
④ $ Route.hash. Значение хэша текущего маршрута (без #) или пустой строки, если нет значения хеша.
⑤ $ Marain.fullpath. После завершения URL-анализа, включите полный путь и хеш параметров запроса.
⑥ $ route.matched Объект параметра конфигурации массива содержит все сегменты текущего пути, соответствующие содержащемуся.
$ Общий метод перехода маршрутизатора:
<script>
export default{
methods:{
goToMenu(){
this.$router.go(-1)//跳转到上一次浏览的页面
this.$router.replace('/menu')//指定跳转的地址
this.$router.replace({name:'menuLink'})//指定跳转路由的名字下
this.$router.push('/menu')//通过push进行跳转
this.$router.push({name:'menuLink'})//通过push进行跳转路由的名字下
}
}
}
</script>
Разница между маршрутизатором. Беспроводное:
Прыжок с использованием метода push добавит новую запись в стек истории, и когда мы нажмем кнопку «Назад» в браузере, мы сможем увидеть предыдущую страницу. Использование метода replace не добавит новую запись в историю, а заменит текущую запись истории, то есть после замены веб-страницы, на которую происходит переход, кнопка «назад» не может просмотреть предыдущую страницу. 9. Как настроить страницу 404 Пользователи часто заходят не на ту страницу. Когда пользователь заходит не на ту страницу, мы надеемся дать ему дружественную подсказку. Эту страницу мы часто называем страницей 404. vue-router также предоставляет нам такой механизм.
设置我们的路由配置文件(/src/router/index.js) { дорожка: '' component:Error } 这里的path:''То есть, когда входной адрес не совпадает, автоматически отображается содержимое файла Error.vue
Создайте новый файл Error.Vue в / SRC / компонентах / папке. Просто введите что-нибудь об странице об ошибке.
<template>
<div>
<h2>{{ msg }}</h2>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'Error:404'
}
}
}
</script>
На этом этапе, когда мы произвольно вводим неправильный адрес, он автоматически переходит на страницу 404.
axios
Что такое Аксиос?
Axios — это HTTP-библиотека на основе обещаний, которую можно использовать в браузерах и node.js.
Особенности Аксиос
1. Создайте XMLHttpRequests из браузера
2. Создайте http-запрос из node.js
3. Поддержка обещания API
4. Перехват запросов и ответов
5. Преобразование данных запроса и данных ответа
6. Отменить запрос
7. Автоматически конвертировать данные JSON
8. Защита поддержки клиентов XSRF
Аксиос использовать
Выполнение запроса на получение
axios.get('/user?id=12345')
.then(function (response) {
console.log(response);
})
Выполнить запрос на почту
axios.post('/user', {
name: 'zxm'
})
.then(function (response) {
console.log(response);
})
Интерпретация исходного кода
- lib/axios.js
'use strict';
var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');
// 重点 createInstance 方法
// 先眼熟一个代码 下面讲完工具函数会再具体来讲解 createInstance
function createInstance(defaultConfig) {
// 实例化 Axios
var context = new Axios(defaultConfig);
// 自定义 bind 方法 返回一个函数()=> {Axios.prototype.request.apply(context,args)}
var instance = bind(Axios.prototype.request, context);
// Axios 源码的工具类
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
return instance;
}
// 传入一个默认配置 defaults 配置先不管,后面会有具体的细节
var axios = createInstance(defaults);
// 下面都是为 axios 实例化的对象增加不同的方法。
axios.Axios = Axios;
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
module.exports = axios;
module.exports.default = axios;
module.exports = {
isArray: isArray,
isArrayBuffer: isArrayBuffer,
isBuffer: isBuffer,
isFormData: isFormData,
isArrayBufferView: isArrayBufferView,
isString: isString,
isNumber: isNumber,
isObject: isObject,
isUndefined: isUndefined,
isDate: isDate,
isFile: isFile,
isBlob: isBlob,
isFunction: isFunction,
isStream: isStream,
isURLSearchParams: isURLSearchParams,
isStandardBrowserEnv: isStandardBrowserEnv,
forEach: forEach,
merge: merge,
deepMerge: deepMerge,
extend: extend,
trim: trim
};
// a, b,thisArg 参数都为一个对象
function extend(a, b, thisArg) {
forEach(b, function assignValue(val, key) {
// 如果指定了 thisArg 那么绑定执行上下文到 thisArg
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
'use strict';
var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
// 核心方法 request
Axios.prototype.request = function request(config) {
// ... 单独讲
};
// 合并配置将用户的配置 和默认的配置合并
Axios.prototype.getUri = function getUri(config) {
config = mergeConfig(this.defaults, config);
return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};
// 这个就是给 Axios.prototype 上面增加 delete,get,head,options 方法
// 这样我们就可以使用 axios.get(), axios.post() 等等方法
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
// 都是调用了 this.request 方法
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
module.exports = Axios;
Все вышеперечисленные методы вызываются вызовом метода this.request
Итак, давайте взглянем на этот метод запроса. Лично я думаю, что это суть исходного кода, и это также сложная для понимания часть. Он использует цепной вызов Promise и идею промежуточного программного обеспечения.
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
Axios.prototype.request = function request(config) {
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
// 合并配置
config = mergeConfig(this.defaults, config);
// 请求方式,没有默认为 get
config.method = config.method ? config.method.toLowerCase() : 'get';
// 重点 这个就是拦截器的中间件
var chain = [dispatchRequest, undefined];
// 生成一个 promise 对象
var promise = Promise.resolve(config);
// 将请求前方法置入 chain 数组的前面 一次置入两个 成功的,失败的
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 将请求后的方法置入 chain 数组的后面 一次置入两个 成功的,失败的
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 通过 shift 方法把第一个元素从其中删除,并返回第一个元素。
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
Источник перехватчика InterceptorManager
'use strict';
var utils = require('./../utils');
function InterceptorManager() {
// 存放方法的数组
this.handlers = [];
}
// 通过 use 方法来添加拦截方法
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
// 通过 eject 方法来删除拦截方法
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
// 添加一个 forEach 方法,这就是上述说的 forEach
InterceptorManager.prototype.forEach = function forEach(fn) {
// 里面还是依旧使用了 utils 的 forEach, 不要纠结这些 forEach 的具体代码
// 明白他们干了什么就可以
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
Источник запроса отправки
- lib/ core/ dispatchRequest.js
'use strict';
var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
var combineURLs = require('./../helpers/combineURLs');
// 请求取消时候的方法,暂不看
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// 请求没有取消 执行下面的请求
if (config.baseURL && !isAbsoluteURL(config.url)) {
config.url = combineURLs(config.baseURL, config.url);
}
config.headers = config.headers || {};
// 转换数据
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// 合并配置
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers || {}
);
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
// 这里是重点, 获取请求的方式,下面会将到
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// 难道了请求的数据, 转换 data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
// 失败处理
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
Прочитав так много, мы не увидели, через что отправляется запрос.Теперь давайте посмотрим, какие значения по умолчанию мы передали в методе createInstance начального экземпляра.
var axios = createInstance(defaults);
- lib/ defaults.js
'use strict';
var utils = require('./utils');
var normalizeHeaderName = require('./helpers/normalizeHeaderName');
var DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded'
};
function setContentTypeIfUnset(headers, value) {
if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
headers['Content-Type'] = value;
}
}
// getDefaultAdapter 方法是来获取请求的方式
function getDefaultAdapter() {
var adapter;
// process 是 node 环境的全局变量
if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// 如果是 node 环境那么久通过 node http 的请求方法
adapter = require('./adapters/http');
} else if (typeof XMLHttpRequest !== 'undefined') {
// 如果是浏览器啥的 有 XMLHttpRequest 的就用 XMLHttpRequest
adapter = require('./adapters/xhr');
}
return adapter;
}
var defaults = {
// adapter 就是请求的方法
adapter: getDefaultAdapter(),
// 下面一些请求头,转换数据,请求,详情的数据
// 这也就是为什么我们可以直接拿到请求的数据时一个对象,如果用 ajax 我们拿到的都是 jSON 格式的字符串
// 然后每次都通过 JSON.stringify(data)来处理结果。
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],
/**
* A timeout in milliseconds to abort a request. If set to 0 (default) a
* timeout is not created.
*/
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
}
};
defaults.headers = {
common: {
'Accept': 'application/json, text/plain, */*'
}
};
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
defaults.headers[method] = {};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
module.exports = defaults;
В качестве стека технологий семейства Vue необходимо освоить vue, vuex, vue-Router и axios. Чем глубже вы овладеваете, тем сильнее ваша способность контролировать архитектуру внешнего интерфейса. Я надеюсь, что каждый может изменить ситуацию к лучшему в технологии внешнего интерфейса. !