1. Кратко скажите Vue
- Vue — это градиентная структура, добавляйте функции в соответствии с вашими потребностями.
- Драйвер данных Vue принимает режим mvvm, m — это уровень данных, v — уровень представления, а vm — планировщик.
- Одностраничное приложение SPA, всего одна страница, быстрая скорость загрузки
- Компонентный и многоразовый
Итак, каковы его недостатки?
- Нижний слой vue2 является адаптивным на основе Object.defineProperty, сам этот API не поддерживает браузеры IE8 и ниже.
- Врожденный недостаток csr, проблема производительности первого экрана (белый экран)
- Поскольку краулеры поисковых систем, такие как Baidu, не могут сканировать контент в js, spa по своей сути не имеет SEO-оптимизации (кукловод Google довольно хорош, и этот инструмент также используется для предварительного рендеринга нижнего слоя)
2. Разница между vuehereact
Прежде всего, вы должны говорить об одних и тех же моментах: оба являются фреймворками MVVM, представлениями, управляемыми данными, и никаких споров. Если он отличается, его можно разделить на следующие пункты:
-
Vue – это полный набор фреймворков, поддерживаемых официальными лицами. Основная библиотека в основном поддерживается только Богом Ю Юйси, а React поддерживается бессовестной книгой (многие библиотеки поддерживаются сообществом). Какое-то время многие люди подвергали сомнению последующая ремонтопригодность Vue. , похоже, не является проблемой.
-
Vue проста в использовании, продвинутая рамочная рамка. В нерный, вы можете немного изучить, и вы можете использовать его в вашем проекте. Вам не обязательно нужно учиться всю Vue одновременно, чтобы использовать его. Реагировать, я Боюсь, что если вы это сделаете, вы столкнетесь с проектом, является беспомощным.
-
Синтаксически vue не ограничивает вас в написании страниц в es6+ полной форме js.Вы можете максимально разделить представления и логику js, уменьшив отвратительную вложенность, к которой многие люди не привыкли реагировать-jsx.Ведь как front-end разработчики, они больше привыкли к чистому html.
-
Многие говорят, что react подходит для крупных проектов, что для чего подходит, vue легкий, подходит для малых и средних мобильных проектов, на самом деле, я хочу сказать, что люди, которые так говорят, вовсе не вынуждены , vue может полностью справляться со сложными крупномасштабными приложениями, даже если вы не очень хорошо научитесь реагировать, вещи, которые вы пишете, могут быть совсем не так хороши, как написанные vue.В конце концов, vue может следовать официальной документации, и кто-то поможет вам его отрегулировать, а реагировать более лениво и свободно, и можно свободно играть.
-
Популярность Vue в Китае, очевидно, выше, чем у React, что во многом связано с его многочисленными грамматиками, в том числе программным мышлением, которые больше соответствуют мышлению китайцев.
1. Что такое мввм
Ядром MVVM является数据驱动то есть ViewModel,ViewModelэто вид и модель关系映射.
MVVM по существу основан на операциях数据работать视图работатьDOM, с помощью MVVM无需直接Чтобы манипулировать DOM, разработчикам нужно всего лишь написатьViewModelимеют业务, делая вид полностью реализованным自动化.
2. Что такое одностраничный SPA, каковы его преимущества и недостатки
SPA(одностраничное приложение), т.е.一个web项目Сразу只有一个页面(То есть файл HTML, преобразование содержимого HTML осуществляется с помощью механизма маршрутизации.
только на веб-страницах初始化Время加载соответствующийHTML、JavaScript 和 CSS. как только страница加载完成, СПА不会потому что пользователь操作И на странице重新加载或跳转; вместо этого используйте路由机制Внедрить HTML内容Преобразование пользовательского интерфейса и взаимодействия с пользователем без перезагрузки страницы.
преимущество:
-
用户体验好、快, изменения содержимого не требуют перезагрузки всей страницы, что позволяет избежать ненужных переходов и повторного рендеринга; - Исходя из вышеизложенного, СПА является относительно
服务器压力小; - передний и задний
职责分离,架构清晰, фронтенд отвечает за логику взаимодействия, а бэкенд — за обработку данных;
недостаток:
-
初次加载耗时多: чтобы реализовать функцию и эффект отображения одностраничного веб-приложения, JavaScript и CSS должны загружаться одинаково при загрузке страницы, а некоторые страницы загружаются по требованию;
前进后退路由管理: поскольку одностраничное приложение отображает весь контент на одной странице, функции браузера вперед и назад нельзя использовать, и для всех переключений страниц необходимо установить управление стеком само по себе;
SEO 难度较大Поскольку весь контент динамически заменен и отображается на странице, она имеет естественную слабость в SEO.
3. Жизненный цикл
3-1 Основные понятия
Каков жизненный цикл vue?
Vue 实例от创建прибыть销毁из过程, это жизненный цикл.
Уведомление: Браузер имеет 8 хуков, ноnodeПри выполнении рендеринга на стороне сервера толькоbeforeCreateа такжеcreated
-
beforeCreateда
new Vue()срабатывает после第一个Хуки, данные и методы для данных, методы, вычисляемые и наблюдаемые недоступны на текущем этапе. Может сделать блокировку страницы. При вводе маршрута мы можем судить, есть ли у нас разрешение на вход, безопасно ли входить, полны ли переносимые параметры и безопасны ли параметры. При использовании этой функции ловушки она не позволяет странице судить и экономит создание экземпляра Vue. -
createdпроизошло в
实例创建完成后, текущая фаза завершена数据观测, то есть можно использовать данные, менять данные, здесь更改данные不会вызыватьupdatedфункция. Вы можете сделать начальный сбор данных на текущем этапе无法а такжеDomпровести交互(Потому что Дом еще не создан), если надо, то можно пройтиvm.$nextTickпосетить Дом. -
beforeMountпроизошло в
挂载之前, прежде чем шаблон шаблона будет импортирован в функцию рендеринга для компиляции. пока текущий этап虚拟Domуже创建完成, собирается начать рендеринг. В этот момент также можно вносить изменения в данные, не запуская обновление. -
mountedпроизошло в
挂载完成后, на текущем этапе真实изDomМонтаж завершен, данные заполнены双向绑定,Могу访问в Дом节点, используйте свойство $refs для работы с Dom. -
beforeUpdateпроизошло в
更新之前То есть данные ответа обновляются, а виртуальный дом срабатывает до того, как виртуальный DOM отображается, вы можете изменить данные на текущем этапе и не вызывать рендеринга. -
updatedпроизошло в
更新完成之后, компонент текущего этапа Дом был обновлен. Важно избегать изменения данных в течение этого времени, так как это может привести к бесконечному циклу обновлений. -
beforeDestroyпроизошло в
实例销毁之前, экземпляр можно использовать полностью на текущем этапе, а мы можем в это время сделать последние штрихи, такие как сброс таймера и уничтожение повторного мониторинга родительского компонента дочернему компоненту.beforeDestroy(){Bus.$off("saveTheme")} -
destroyedПроисходит после уничтожения экземпляра, и в это время остается только оболочка dom. Компоненты были разобраны, привязки данных разобраны, слушатели удалены, а дочерние экземпляры уничтожены.
3-2 Последовательность вызова жизненного цикла
- Компоненты вызываются в порядке сначала родителя, а затем ребенок
- Порядок, в котором выполняется рендеринг, сначала дочерний, затем родительский.
- Операция уничтожения компонента является первым родителем, а затем дочерним
- Разрушение осуществляется в порядке сначала сына, а затем отца.
Загрузить процесс рендерингародитель beforeCreate->родитель создан->родитель beforeMount->дочерний элемент beforeCreate->дочерний элемент создан->дочерний элемент beforeMount->дочерний элемент смонтирован->родительский элемент
Процесс обновления подкомпонентародитель до обновления-> дочерний перед обновлением-> дочерний обновлен-> родительский обновлен
Процесс обновления родительского компонентародитель перед обновлением -> родитель обновлен
процесс разрушенияродитель перед уничтожением-> дочерний перед уничтожением-> дочерний элемент уничтожен-> родитель уничтожен
3-3 Какова роль жизненного цикла vue
В его жизненном цикле есть несколько обработчиков событий, давайте控制Процесс экземпляра Vue больше清晰.
3-4 Какие хуки будут срабатывать при загрузке первой страницы
Хуки beforeCreate, created, beforeMount, mount срабатывают, когда страница загружается в первый раз.
3-5 Какие сценарии подходят для каждого цикла
- beforecreate : здесь вы можете добавить событие загрузки, которое будет запускаться при загрузке экземпляра.
- created : Сюда пишется событие завершения инициализации.Если здесь заканчивается событие загрузки, сюда же подходят асинхронные запросы для вызова
- Установлен: Установите элемент и получите узел DOM
- update : Если данные обрабатываются единообразно, напишите здесь соответствующую функцию
- beforeDestroy : можно очистить таймер
- nextTick: работайте с домом сразу после обновления данных.
4. Разница между v-show и v-if
v-if
- является истинным условным рендерингом, так как он гарантирует, что
事件监听器а также子组件должным образом销毁和重建; - Слишком
惰性: если условие ложно при начальном рендеринге, ничего не делайте — блок условий не начнет рендеринг, пока условие не станет истинным в первый раз.
v-show
Независимо от начального состояния элемент всегда будет отображаться и просто переключаться на основе свойства CSS display.
так:
- v-if работает во время выполнения
很少改变条件,不需要Сценарии с частыми переключениями условий; - v-show подходит для
非常频繁Сценарии переключения условий.
5. Односторонний поток данных в Vue
задний план:
Все реквизиты формируют отношения между их родительскими и дочерними реквизитами.单向下行绑定: обновления родительских реквизитов передаются дочерним компонентам, но не наоборот. Это предотвратит подкомпоненты от意外改变Состояние родительского компонента, которое может вызвать путаницу в потоке данных вашего приложения.
Каждый раз, когда родительский компонент обновляется, все реквизиты в дочернем компоненте будут обновляться до последнего значения. Это означает, что вы не должны изменять свойства внутри дочернего компонента. Если вы это сделаете, Vue выдаст предупреждение в консоли браузера. Когда подкомпоненты хотят изменить, они могут только передать$emitраспространять один自定义事件, после того как родительский компонент получит его, родительский компонент修改.
Существует два распространенных сценария попытки изменить реквизит:
- Это свойство используется для передачи начального значения;
- Затем этот дочерний компонент хочет использовать его в качестве локальных данных.
Во втором случае лучше определить свойства и локальные данные в качестве их начального значения:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter//定义本地的data属性接收prop初始值
}
}
Это свойство передается как необработанное значение и должно быть преобразовано.
В этом случае лучше определить вычисляемое свойство, используя значение этой опоры.
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
6. В каком жизненном цикле следует вызывать асинхронный запрос?
官方实例Асинхронный запрос находится вmountedВызывается в жизненном цикле и может фактически вызываться в созданном жизненном цикле.
Я рекомендую вызывать асинхронные запросы в созданной функции ловушки, которая имеет следующие преимущества:
- могу
更快получать服务端数据,减少время загрузки страницы; -
ssrНе поддерживает функции beforeMount и монтируемые хуки, поэтому их размещение в файле created способствует согласованности;
7. Каковы методы взаимодействия компонентов Vue2.x?
7-1 Связь между родительским и дочерним компонентами
- Родитель -> дочерний реквизит; промоутер (EMIT) -> родитель (вкл.)
- Получить экземпляр родительского и дочернего компонентовДети, такие как: напишите непосредственно в методах методов дочернего компонента: this.$parent.show()//show - это метод, определенный в родительском компоненте.
- Ссылка (при использовании в обычном элементе DOM ссылка указывает на элемент DOM; при использовании в подкомпоненте ссылка указывает на экземпляр компонента), например, установка на метку импортированного подкомпонента: ref="comA" , а затем в данных метода или подкомпонента this.$refs.comA.titles
- Предоставить и внедрить официально не рекомендуется, но они обычно используются при написании библиотек компонентов.Компоненты-предки предоставляют переменные через провайдер, а затем внедряют переменные в компоненты-потомки через inject
7-2 Компонентная связь Brother
- Шина событий реализует межкомпонентную связь: Vue.prototype.$bus = new Vue
- Vuex
7-3 Межуровневая связь между компонентами
- Vuex
- attrs,listeners
- Обеспечить, ввести
7-4 Использование
1. Реквизит отца и сына, на
// Подсборка
<template>
<header>
<h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
</header>
</template>
<script>
export default {
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值”
}
}
}
</script>
// родительский компонент
<template>
<div id="app">
<Header @titleChanged="updateTitle" ></Header>//与子组件titleChanged自定义事件保持一致
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./Header"
export default {
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
},
components:{
Header
}
}
</script>
2. родитель/$дети и ссылка
// Дочерний компонент
export default {
data () {
return {
title: 'a组件'
}
},
methods: {
sayHello () {
alert('Hello');
}
}
}
// родительский компонент
<template>
<A ref="comA"></A>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // a组件
comA.sayHello(); // 弹窗
}
}
</script>
3.attrs, слушатели
атрибуты: содержит父作用域середина不被 propМесто识别(и получает) привязки атрибутов (кроме class и style ). Когда компонент не объявляет никаких реквизитов, все привязки родительской области (кроме class и style) включаются сюда и могут быть переданы внутренним компонентам через v-bind="$attrs" . Обычно используется с опцией inheritAttrs.
listeners: : содержит прослушиватели событий v-on в родительской области (без декоратора .native). Его можно передать внутренним компонентам через v-on="$listeners"
// index.vue
<template>
<div>
<h2>浪里行舟</h2>
<child-com1 :foo="foo" :boo="boo" :coo="coo" :doo="doo" title="前端工匠"></child-com1>
</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
components: { childCom1 },
data() {
return {
foo: "Javascript",
boo: "Html",
coo: "CSS",
doo: "Vue"
};
}
};
</script>
// childCom1.vue
<template class="border">
<div>
<p>foo: {{ foo }}</p>
<p>childCom1的$attrs: {{ $attrs }}</p>
<child-com2 v-bind="$attrs"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
components: {
childCom2
},
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: {
foo: String // foo作为props属性绑定
},
created() {
console.log(this.$attrs); // 父组件中的属性,且不在当前组件props中的属性。{ "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
}
};
</script>
// childCom2.vue
<template>
<div class="border">
<p>boo: {{ boo }}</p>
<p>childCom2: {{ $attrs }}</p>
<child-com3 v-bind="$attrs"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
boo: String
},
created() {
console.log(this.$attrs); // / 父组件中的属性,且不在当前组件props中的属性。{"coo": "CSS", "doo": "Vue", "title": "前端工匠" }
}
};
</script>
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
}
};
</script>
4. Использование «Предоставь и введи»:
родительский компонент
<template>
<div id="app">
</div>
</template>
<script>
export default {
data () {
return {
datas: [
{
id: 1,
label: '产品一'
}
]
}
},
provide {
return {
datas: this.datas
}
}
}
</script>
Подсборка
<template>
<div>
<ul>
<li v-for="(item, index) in datas" :key="index">
{{ item.label }}
</li>
</ul>
</div>
</template>
<script>
export default {
inject: ['datas']
}
</script>
8. Что такое ССР
ССР это
服务端渲染, то есть поставить Vue на стороне клиента标签РендерингHTMLРабота выполняется на сервере, тоhtml直接返回клиенту.
Преимущества и недостатки рендеринга SSR на стороне сервера следующие:
(1) Преимущества рендеринга на стороне сервера:
-
лучшее SEO: поскольку содержимое страницы SPA получается через Ajax, и сканер поисковой системы не ждет асинхронного завершения Ajax перед сканированием содержимого страницы, поэтому в
SPAсредний да抓取不到страница черезAjaxполученный内容;а такжеSSRнепосредственно服务端вернуть已经渲染好страница (данные уже включены в страницу), поэтому сканер поисковой системы может просканировать отображаемую страницу; -
Более быстрое время поступления контента(над сгибом загружается быстрее):
SPAМогу等待Все Vue скомпилированоjsдокумент都下载完成后,才开始сделать страницу渲染, загрузка файла и т. д. занимают определенное время, поэтому первый рендеринг экрана занимает определенное время; SSR напрямую рендерит страницу с сервера и напрямую возвращает отображение, не дожидаясь загрузки js-файлов и повторного рендеринга, поэтому SSR имеет более быстрое время поступления контента;
(2) Недостатки рендеринга на стороне сервера:
-
Больше условий развития: например, рендеринг на стороне сервера поддерживает только
beforCreateа такжеcreatedДве функции-ловушки, из-за которых некоторые внешние библиотеки расширений требуют специальной обработки для запуска в приложениях, отображаемых на сервере; и, в отличие от полностью статических одностраничных приложений SPA, которые можно развернуть на любом статическом файловом сервере, приложения, отображаемые на сервере. находиться в операционной среде сервера Node.js; -
большая нагрузка на сервер: рендеринг полного приложения в Node.js, очевидно, будет более интенсивно использовать ЦП, чем сервер, который просто обслуживает статические файлы (интенсивность ЦП - интенсивно ЦП), поэтому, если вы планируете использовать его в среде с высоким трафиком (высокий трафик), Будьте готовы к соответствующей нагрузке на сервер и разумно используйте стратегию кэширования.
9. Вью-маршрутизация
9-1 Каковы режимы маршрутизации vue-router?
vue-router имеет 3 режима маршрутизации:hash,history,abstract, соответствующий исходный код выглядит следующим образом:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
Описание режима маршрутизации выглядит следующим образом:
-
hash: использовать хэш URL для маршрутизации. служба поддержки
所有浏览器, включая браузеры, не поддерживающие HTML5 History Api; -
history: зависит от HTML5 History API и конфигурации сервера. Дополнительные сведения см. в разделе Режим истории HTML5;
-
abstract: поддерживать всех
JavaScriptСреда выполнения, такая как серверная часть Node.js. Маршруты автоматически переводятся в этот режим, если API браузера не найден.
9-2 Принцип реализации хэш-маршрутизации и маршрутизации истории
(1) Принцип реализации хеш-режима
Ранняя реализация внешней маршрутизации была основана наlocation.hashбыть реализованным. Принцип реализации очень прост: значением location.hash является содержимое после # в URL.
Например, для следующего веб-сайта его значение location.hash равно'#search':
https://www.word.com#search
Реализация режима хеш-маршрутизации в основном основана на следующих особенностях:
- Хэш-значение в URL просто
客户端типа状态, то есть при отправке на сервер发出请求时, хэш-часть不会被发送; - хэш-значение
改变, будет отображаться в браузере访问历史середина增加Один记录. Следовательно, мы можем управлять переключением хэша через кнопки браузера назад и вперед; - Вы можете передать тег a и установить атрибут href.Когда пользователь щелкнет этот тег, хеш-значение URL-адреса изменится, или используйте JavaScript, чтобы присвоить значение loaction.hash, чтобы изменить хэш-значение URL-адреса;
- мы можем использовать
hashchangeСобытия для прослушивания изменений в хэш-значении для перехода (рендеринга) страницы.
(2) Принцип реализации режима истории
HTML5 предоставляетHistory APIДля внесения изменений в URL наиболее важными API являются следующие два:
- history.pushState() //Новая история
- история.repalceState(). // заменить историю
Эти два API можно найти в不进行刷新на случай, если,操作браузер历史纪录. Единственное отличие состоит в том, что в первом случае добавляется новая запись истории, а во втором — прямая замена текущей записи истории, как показано ниже:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
Реализация режима маршрутизации по истории в основном основана на наличии следующих особенностей:
- PushState и repalceState — два API для работы и реализации изменений URL-адресов;
- мы можем использовать
popstateСобытия для прослушивания изменений в URL-адресе, чтобы перейти (рендерить) страницу; - history.pushState() или history.replaceState() не будут вызывать событие popstate, тогда нам нужно вручную вызвать переход на страницу (рендеринг).
10. Что делает ключ в Vue?
ключ для Vuevnodeиз唯一标记, через этот ключ нашdiffоперация может更准确、更快速.
Процесс сравнения Vue можно обобщить следующим образом:
oldChа такжеnewChу каждого есть两个头尾ПеременныеoldStartIndex、oldEndIndexа такжеnewStartIndex、newEndIndex, они будут новыми узлами, а старые узлы будут两两对比, то есть всего4 способа сравнить: newStartIndex и oldStartIndex, newEndIndex и oldEndIndex, newStartIndex и oldEndIndex, newEndIndex и oldStartIndex, если вышеуказанные 4 сравнения都没匹配, если установленоkey, будет использовать ключ再进行Сравните, в процессе сравнения обход будет往中间靠, сравнение заканчивается, как только StartIdx > EndIdx указывает, что хотя бы один из oldCh и newCh был пройден.
Итак, роль ключа в Vue: ключ — это единственная метка для vnode в Vue, с помощью этого ключа наша операция сравнения может быть более точной и быстрой.
-
Это более точно, потому что ключ не используется повторно на месте.В функции того же узла a.key === сравнение b.key повторного использования на месте можно избежать. Так будет точнее.
-
Быстрее: используйте уникальность ключа для создания объекта карты для получения соответствующего узла, что быстрее, чем метод обхода.Исходный код выглядит следующим образом:
function createKeyToOldIdx (children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
Ссылка 1:Какая польза от :key в Vue2.0 v-for?
11. Принцип реализации виртуального DOM
Принцип реализации виртуального DOM в основном включает следующие три части:
- Смоделируйте настоящее дерево DOM с помощью объектов JavaScript и абстрагируйте настоящий DOM;
- алгоритм diff — сравнивает различия двух виртуальных DOM-деревьев;
- алгоритм pach — применяет разницу двух виртуальных DOM-объектов к реальному DOM-дереву.
Нажмите здесь, чтобы узнать подробности
13. Преимущества и недостатки виртуального DOM
преимущество:
-
Гарантированная нижняя граница производительности: виртуальный DOM фреймворка должен адаптироваться к любым операциям, которые могут быть сгенерированы API верхнего уровня, некоторые из его
DOM 操作Реализация должна быть普适, поэтому его производительность не оптимальна, но это намного лучше, чем грубое манипулирование DOM, так что виртуальный DOM фреймворка может, по крайней мере, гарантировать, что вы不需要手动优化, еще можно提供还不错из性能, то есть нижний предел гарантированной производительности; -
Никаких ручных манипуляций с DOM: нам больше не нужно вручную манипулировать DOM, а нужно только написать логику кода View-Model.Фреймворк будет связывать виртуальный DOM и данные в обоих направлениях, чтобы помочь нам обновлять представление предсказуемым образом, значительно улучшая нашу эффективность разработки;
-
Кроссплатформенность: виртуальный DOM по сути является объектом JavaScript, а DOM тесно связан с платформой.В отличие от этого, виртуальный DOM может выполнять более удобные кроссплатформенные операции, такие как серверный рендеринг, разработка weex и т. д.
недостаток:
- Невозможно оптимизировать: хотя виртуальный DOM + разумная оптимизация достаточны для удовлетворения требований к производительности большинства приложений, в некоторых приложениях с чрезвычайно высокими требованиями к производительности виртуальный DOM не может быть оптимизирован до предела.
14. Сравнение плюсов и минусов Proxy и Object.defineProperty
Преимущества прокси заключаются в следующем:
- Прокси может напрямую слушать объекты, а не свойства;
- Прокси может напрямую отслеживать изменения в массиве;
- Прокси имеет целых 13 методов перехвата, не ограничиваясь apply, ownKeys, deleteProperty, has и т.д. Object.defineProperty не имеет;
- Proxy возвращает новый объект, мы можем использовать новый объект только для достижения цели, а Object.defineProperty может только просматривать свойства объекта и изменять их напрямую;
- В качестве нового стандарта Proxy будет подвергаться постоянной оптимизации производительности производителями браузеров, что является бонусом производительности легендарного нового стандарта;
Преимущества Object.defineProperty заключаются в следующем:
- Хорошая совместимость, поддержка IE9, а у Proxy есть проблемы с совместимостью с браузером, и его нельзя сгладить с помощью полифилла.
14-1 Итак, какие функции может выполнять Proxy?
Прокси — это новая функция в ES6, ее можно использовать для настройки работы объекта.
let p = new Proxy(target, handler)
- цель представляет потребность
添加代理Объект - обработчик используется для настройки
对象中из操作, например, его можно использовать для настройки функции set или get.
Реализуем ответ данных через Proxy:
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver) {
setBind(value, property)
return Reflect.set(target, property, value)
}
}
return new Proxy(obj, handler)
}
let obj = { a: 1 }
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
p.a = 2 // 监听到属性a改变为2
p.a // 'a' = 2
В приведенном выше коде путем настройки функций set и get логика нашей функции вставляется в исходную логику, реализующую уведомление при чтении или записи любого свойства объекта.
Конечно, это простая версия адаптивной реализации. Если вам нужно реализовать адаптивный ответ в Vue, вам нужно собирать зависимости в get и распространять обновления в наборе. Причина, по которой Vue 3.0 использует Proxy для замены исходного API заключается в том, что прокси-серверу не нужен слой Слой рекурсивно добавляет прокси-сервер для каждого атрибута, и вышеперечисленные операции могут быть выполнены одновременно Производительность лучше, а исходная реализация имеет некоторые обновления данных, которые нельзя отслеживать, но прокси-сервер может отлично отслеживать любые изменения данных, единственный недостаток - совместимость с браузером не очень хорошая.
15. Как платформа Vue реализует мониторинг объектов и массивов?
Платформа Vue просматривает массивы и рекурсивно просматривает объекты, так что Object.defineProperty() также может отслеживать объекты и массивы (операции некоторых методов).
vue2:
数组это использоватьobject.definePropertyпереопределить массив每一项, метод, который может вызвать изменение массива,pop 、 push 、 shift 、 unshift 、 splice 、 sort 、 reverseЭти семь, пока выполнение этих методов изменяет содержимое массива, содержимое обновляется
-
используется, чтобы
函数劫持Путь,重写Изменен метод массива, а именно прототип массива изменен на свой, когда пользователь вызывает некоторые методы массива, он использует свой метод, а затем уведомляет представление об обновлении (по сути, обновление вызывается на оригинальный метод, метод данных). -
Элементы массива могут быть
对象, затем наблюдайте за каждым элементом в массиве
vue3:
Вместо этого используйте прокси, чтобы напрямую отслеживать изменения в массиве объектов.
16. Как Vue реализует двустороннюю привязку данных
Двусторонняя привязка данных Vue в основном относится к: изменения данных обновляют представление, изменения представления обновляют данные.
Когда содержимое поля ввода изменяется, данные в Data изменяются синхронно. т.е. Вид => Изменение данных. Когда данные в Data изменяются, содержимое текстового узла изменяется синхронно. т. е. Данные => Просмотр изменений.
Среди них изменения представления для обновления данных, что может быть достигнуто с помощью мониторинга событий, поэтому работа двусторонней привязки данных Vue в основном заключается в том, как обновлять представление в соответствии с изменениями данных.
Vue в основном реализует двустороннюю привязку данных с помощью следующих 4 шагов.:
-
реализовать прослушивательНаблюдатель просматривает объекты данных, включая свойства объектов подсвойств, используя
Object.defineProperty()добавить к атрибутамsetterа такжеgetter. В этом случае присвоение значения этому объекту вызовет установщик, после чего вы сможете отслеживать изменения данных. -
реализовать парсерКомпиляция анализирует директивы шаблона Vue и преобразует
变量заменены на数据, а затем инициализировать визуализированный вид страницы с каждым指令соответствующий узел绑定更新函数, добавьте подписчика для мониторинга данных. После изменения данных получите уведомление и вызовите функцию обновления для обновления данных. -
внедрить подписчикаПодписчик Watcher является связующим звеном между Observer и Compile.Основная задача подписаться на сообщение об изменении значения свойства в Observer.При получении сообщения об изменении значения свойства срабатывает соответствующая функция обновления в парсере Compile.
-
внедрить подписчикаПодписчик Dep принимает шаблон проектирования «публикация-подписка», который используется для сбора наблюдателей подписчиков и унифицированного управления наблюдателем-слушателем и наблюдателем-подписчиком.
17. Принцип v-модели
Директива v-model создает двустороннюю привязку данных к таким элементам, как ввод формы, текстовое поле, выбор и т. д.语法糖, который внутренне использует разные свойства и выдает разные события для разных элементов ввода:
- использование элементов text и textarea
valueсвойства иinputмероприятие; - флажок и использование радио
checkedсвойства иchangeмероприятие; - выберите поля имеют значение как
propи воляchangeкак событие.
Возьмите элемент формы ввода в качестве примера:
<input v-model='something'>
эквивалентно
<input :value="something" @input="something = $event.target.value">
18. Почему данные в компоненте — это функция?
Почему данные в компоненте должны быть функцией, а затем возвращать объект, тогда как в новом экземпляре Vue данные могут быть напрямую объектом?
// data
data() {
return {
message: "子组件",
childName:this.name
}
}
// new Vue
new Vue({
el: '#app',
router,
template: '<App/>',
components: {App}
})
Если компонент используется повторно несколько раз, будет создано несколько экземпляров, которые, по сути, используют один и тот же конструктор.
Если данные являются объектом, объект является ссылочным типом и будет влиять на все экземпляры, поэтому, чтобы гарантировать, что данные не конфликтуют между различными экземплярами компонента, данные должны быть функцией.
Экземпляр нового Vue не будет использоваться повторно, поэтому нет проблем со ссылками на объекты.
19. Расскажите о своих мыслях о сохранении активности
Keep-alive — это Vue内置один из组件, который может быть包含s компонент保留状态,避免重新渲染, который имеет следующие характеристики:
-
Обычно используется в сочетании с маршрутизацией и динамическими компонентами для кэширования компонентов;
-
поставка
includeа такжеexcludeсвойства, поддерживают как строки, так и регулярные выражения, включают только имя匹配компоненты будут缓存, исключить означает любое имя匹配компоненты不会被缓存, где исключить优先级чем включить高; -
Соответствует двум функциям ловушек
activatedа такжеdeactivated, когда компонент激活Когда активирована функция хука, когда компонент активирован移除, деактивированная функция хука срабатывает.
Жизненный цикл keep-alive
- активировано: при первом входе на страницу создается последовательность триггера хука->монтируется->активируется
- деактивировано: деактивировано будет срабатывать при выходе со страницы, и только активировано будет срабатывать при повторном переходе вперед или назад
20. Может ли родительский компонент контролировать жизненный цикл дочернего компонента?
Например, есть родительские компоненты Parent и дочерние компоненты Child, Если родительский компонент прослушивает монтирование дочернего компонента, он будет выполнять некоторую логическую обработку, которая может быть достигнута следующим методом записи:
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
Приведенное выше необходимо вручную инициировать событие родительского компонента через $emit.Более простой способ можно отслеживать через @hook, когда родительский компонент ссылается на дочерний компонент, как показано ниже:
// Parent.vue
<Child @hook:mounted="doSomething" ></Child>
doSomething() {
console.log('父组件监听到 mounted 钩子函数 ...');
},
// Child.vue
mounted(){
console.log('子组件触发 mounted 钩子函数 ...');
},
// Приведенная выше последовательность вывода:
// Дочерний компонент запускает функцию установленного хука ...
// Родительский компонент слушает смонтированную функцию ловушки ...
Разумеется, метод @hook может прослушивать не только смонтированные, но и другие события жизненного цикла, такие как создание, обновление и т. д.
21. Если вы присваиваете значение элементу массива напрямую, может ли Vue обнаружить изменение?
Из-за ограничений JavaScript Vue не может обнаружить изменения в следующих массивах:
- Когда вы напрямую устанавливаете элемент массива по индексу, например:
vm.items[indexOfItem] = newValue - Когда вы изменяете длину массива, например:
vm.items.length = newLength
Для решения первой проблемы Vue предоставляет следующие методы действий:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set (псевдоним для Vue.set)
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
Для решения второй проблемы Vue предоставляет следующие методы действий:
// Array.prototype.splice
vm.items.splice(newLength)
22. Как отслеживать изменения массива в vue2.x
использовал函数劫持Путь,重写Методы массива, Vue будетdataсередина数组Проведенный原型链重写, указывает на自己定义из数组原型方法. Таким образом, при вызове массива API можно получить уведомление об обновлении зависимостей. Если массив содержит ссылочные типы, ссылочные типы в массиве будут снова рекурсивно проходиться для мониторинга. Таким образом реализуется мониторинг изменений массива.
22. Давайте поговорим об алгоритмах diff рендереров Vue2.x и Vue3.x соответственно.
Проще говоря, алгоритм diff имеет следующий процесс
Сравните на том же уровне, затем сравните дочерние узлы, сначала определите ситуацию, что у одной стороны есть дочерние узлы, а у другой нет дочерних узлов (если у новых дочерних узлов нет дочерних узлов, удалите старые дочерние узлы)
Сравните случай, когда есть дочерние узлы (основной diff) рекурсивно сравните дочерние узлы
обычныйDiff两个树Временная сложность — O(n^3), но на самом деле мы редко используем кросс-уровневый мобильный DOM, поэтому Vue будет оптимизировать Diff, от O(n^3) -> O(n), только когда только старый и новые дочерние узлы являются более дочерними узлами Алгоритм Diff необходимо использовать со сравнением основного уровня.
Vue2Основной алгоритм Diff использует双端比较В то же время сравните два конца старого и нового дочерних элементов, найдите повторно используемый узел с помощью значения ключа, а затем выполните связанные операции. По сравнению с алгоритмом React Diff, в том же случае он может уменьшить количество движущихся узлов, уменьшить ненужные потери производительности и быть более элегантным.
Vue3.xАлгоритм ivi и алгоритм inferno используются для справки для определения типа виртуального узла при его создании, а битовые операции используются для определения типа виртуального узла в процессе монтирования/исправления. объединены для повышения производительности Vue2.x был улучшен. Алгоритм также использует идею динамического программирования для решения самой длинной рекурсивной подпоследовательности.
23. Принцип компиляции шаблона Vue
Проще говоря, процесс компиляции Vue — это процесс преобразования шаблона в функцию рендеринга. Он будет проходить следующие этапы:
- Сгенерировать АСТ
- Оптимизация деревьев
- codegen
первый解析模版, сгенерироватьASTСинтаксическое дерево (форма объекта JavaScript, описывающая весь шаблон).
использовать много正则пара выражений模板провести解析,встретить标签,文本всегда执行соответствующий钩子связанная обработка.
Данные Vue адаптивны, но не все данные в шаблоне адаптивны. есть некоторые данные首次渲染позже不会Опять таки变化,соответствующийDOMИ не изменится. Тогда процесс оптимизации深度遍历Дерево AST, согласно соответствующим условиям树节点провести标记. эти отмечены节点(статический узел) мы можем跳过Их比对, Сыграл значительную роль в оптимизации шаблона во время выполнения.
Последним шагом компиляции является преобразование оптимизированного дерева AST.转换для可执行из代码.
24. Вычисление и просмотр
computed:
- вычислено
计算属性, то есть расчетное значение, которое больше используется для расчета значения сцены - вычислено
缓存性, значение вычисляемого будет закэшировано после выполнения геттера, только после того, как значение атрибута, от которого он зависит, изменится, будет снова вызван соответствующий геттер для вычисления при следующем получении значения вычисленного - Computed подходит для вычислительных сценариев, требующих относительной производительности.
watch:
- Больше роли «наблюдения», похожего на обратный вызов мониторинга некоторых данных, используемых для наблюдения.
props$emitили本组件Значение , когда данные изменяются для выполнения обратного вызова для последующих операций - Без кеширования, значение при повторном рендеринге страницы
不变化также执行
резюме:
- Когда мы хотим выполнить численные расчеты и полагаться на другие данные, то проектируем эти данные как вычисляемые.
- Если вам нужно что-то сделать при изменении определенных данных, используйте часы, чтобы наблюдать за этим изменением данных.
25.nextTick
существует下次 DOM 更新循环结束之后воплощать в жизнь延迟回调. Код внутри будет ждать, покаdom更新以后Выполнить снова.
<template>
<section>
<div ref="hello">
<h1>Hello World ~</h1>
</div>
<el-button type="danger" @click="get">点击</el-button>
</section>
</template>
<script>
export default {
methods: {
get() {
}
},
mounted() {
console.log(333);
console.log(this.$refs['hello']);
this.$nextTick(() => {
console.log(444);
console.log(this.$refs['hello']);
});
},
created() {
console.log(111);
console.log(this.$refs['hello']);
this.$nextTick(() => {
console.log(222);
console.log(this.$refs['hello']);
});
}
}
</script>
Нажмите здесь, чтобы узнать подробности
26. Принцип отзывчивости Vue
26-1 Принцип отзывчивых данных Vue2.x
Когда Vue инициализирует данные, он будет использоватьObject.definePropertyпереопределить данные в所有属性, когда страница使用Когда соответствующие атрибуты, первый依赖收集(собрать наблюдатель текущего компонента), если возникает свойство变化Могу通知связанные зависимости更新操作(опубликовать подписаться)
конкретный процесс:
- Первое использование Vue
initDataИнициализировать пользователя, переданного в参数 - затем используйте
new Observerна данных观测 - Если данные являются
对象类型позвонюthis.walk(value)Обработка объекта, используемого внутриdefineeReactiveСвойства объекта Loop определяют отзывчивые изменения, ядром является использованиеObject.definePropertyПереопределить данные.
26-2 Принцип отзывчивых данных Vue3.x
Vue3.x использует прокси вместо Object.defineProperty. Поскольку прокси может直接监听Изменения объектов и массивов, а также до 13 методов перехвата. И в качестве нового стандарта производители браузеров сосредоточатся на постоянной оптимизации производительности.
Прокси будет проксировать только первый слой объекта, так как же Vue3 решает эту проблему?
судить о текущемReflect.getВозвращаемое значениеObject, если да то проходитеreactiveМетод действует как прокси, таким образом реализуя наблюдение за глубиной.
При мониторинге массива get/set может срабатывать несколько раз, так как же предотвратить множественные триггеры?
мы можем судитьkeyЭто актуально被代理对象targetсобственные свойства, о нем также можно судить旧值а также新值Равен ли он, только при выполнении одного из двух вышеперечисленных условий возможно выполнениеtrigger.
27. Могут ли при использовании вычисляемых свойств имя функции и данные в источнике данных иметь одно и то же имя?
Не может иметь одно и то же имя, потому что независимо от того, является ли это вычисляемым свойством или данными или свойствами, они будут смонтированы вvm实例, так что ни один из трех не имеет одинакового имени
28. Как решить проблему, когда изображения статических ресурсов становятся недействительными после упаковки vue
оказатьсяconfig/index.jsфайл конфигурации, найтиbuildв упакованном объектеassetsPublicPathЗначение свойства по умолчанию равно/, изменить на./Достаточно
29. Как решить проблему, когда src для img, динамически устанавливаемый vue, не действует?
потому что динамически добавляетсяsrcЭто когда做静态资源обработано, не сделано编译, поэтому добавьте require.
<img :src="require('../../../assets/images/xxx.png')" />
30. Как оптимизировать использование vue для рендеринга больших объемов данных? Скажи свои мысли!
- идеи
- Загрузка локальных данных по запросу, виртуальный список, бесконечное раскрывающееся обновление
- js выполняет асинхронную обработку: разделение задач, реализация обработки временных интервалов, аналогичная React Fiber, запись времени при каждом выполнении, settimeout или requestAnimation будут отложены до следующего временного интервала, если он превышает определенное время выполнения, обычно временной интервал 16 мс
- Большой объем чистых отображаемых данных, нет необходимости отслеживать изменения
object.freezeЗаморозить (можно использовать фиктивный список,Object.freezeЗаморозить объект, Object.preventExtention предотвращает расширение объекта, чтобы предотвратить добавление vue get и set к каждому объекту, но недостатком является то, что он не может ответить) - Когда объем данных велик, может выполняться пейджинг. Переворачивайте страницы, чтобы запросить 10-20 фрагментов данных за раз
- Object.freez
подходят некоторыеbig dataбизнес-сценарий. Особенно при работе с менеджментом часто бывают некоторые超大数据量изtable, или диаграмма с n данными, наиболее очевидным ощущением от использования такого большого объема данных является карта. Но на самом деле во многих случаях эти данные фактически не нужно менять оперативно, в это время можно использовать метод Object.freeze, который может冻结一个对象(обратите внимание, что это не специфичный для vue API).
Когда вы передаете обычный объект JavaScript параметру данных экземпляра Vue, Vue будет проходить по всем свойствам объекта и использовать Object.defineProperty для преобразования всех этих свойств в геттеры/сеттеры, которые позволяют Vue отслеживать зависимости в свойствах. Уведомлять об изменениях при доступе и изменении.
После использования Object.freeze вы можете не только уменьшитьobserverзатраты могут быть значительно снижены内存开销.
Как использовать:
this.item = Object.freeze(Object.assign({}, this.item))
30. пользовательская директива vue
Сначала посмотрите на Vue, есть много встроенных команд.
Например:
- v-bind: привязка атрибутов, которая привязывает данные к атрибутам элементов HTML.
- v-html и v-text привязывают данные к атрибутам элементов HTML, так же, как innerHTML и innerText
- v-on: связывать события HTML-элемента
Итак, что касается директив, мы можем резюмировать следующие моменты:
- инструкция написана на
HTML 属性местный,<input v-model='name' type='text' /> - Команды
v-начало. - За правой частью выражения команды обычно может следовать значение v-if = false
Кастомная директива Vue, случай 1
Например: нам нужна директива, написанная на элементе HTML-формы, и чтобы она автоматически получала фокус при загрузке в DOM.
// 和自定义过滤器一样,我们这里定义的是全局指令
Vue.directive('focus',{
inserted(el) {
el.focus()
}
})
<div id='app'>
<input type="text">
<input type="text" v-focus placeholder="我有v-focus,所以,我获取了焦点">
</div>
Резюмируя несколько моментов:
- использовать
Vue.directive()создать новый全局指令, (директива используется для атрибутов элемента HTML) - Vue.directive('focus')
第一个参数фокус指令名, когда объявлено имя инструкции, нет необходимости добавлять v- - В элементах HTML, использующих директивы,
<input type="text" v-focus placeholder="我有v-focus,所以,我获取了焦点"/>Нам нужно добавить v-. - Vue.directive('focus',{})
第二个参数Является对象, объект内部имеетinserted()функция, функция имеетelэтот параметр. - el Этот параметр указывает на привязку данной инструкции
DOM元素, вот тот, что сзадиplaceholderизinput, эль эквивалентноdocument.getElementById('el.id') - годный к употреблению
$(el)Простое подключение jQuery
жизненный цикл инструкций
С директивой нам нужно:
- добавить команду
- Определите вставленную функцию во втором параметре команды
- Используйте эту директиву для элемента, который должен получить фокус.
Когда инструкция привязана к элементу, внутри инструкции фактически есть пять функций событий жизненного цикла.
-
bind(){}по указанию绑定в HTML元素Срабатывает при поднятии. Вызывается только один раз. -
inserted()Когда директива связана с этимHTML元素вставить в父元素Запускается, когда родительский элемент поднят (здесь родительский элементdiv#app) Но нет никакой гарантии, что родительский элемент был вставлен в документ DOM. -
updated()компонентаVNode更新звонил когда. -
componentUpdateVNode компонента, в котором находится инструкция, и его дочерние VNode全部更新后передача. -
unbind: Директивы и элементы解绑при вызове только один раз
Объявление периодических функций для директив Vue
Vue.directive('gqs',{
bind() {
// 当指令绑定到 HTML 元素上时触发.**只调用一次**
console.log('bind triggerd')
},
inserted() {
// 当绑定了指令的这个HTML元素插入到父元素上时触发(在这里父元素是 `div#app`)**.但不保证,父元素已经插入了 DOM 文档.**
console.log('inserted triggerd')
},
updated() {
// 所在组件的`VNode`更新时调用.
console.log('updated triggerd')
},
componentUpdated() {
// 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
console.log('componentUpdated triggerd')
},
unbind() {
// 只调用一次,指令与元素解绑时调用.
console.log('unbind triggerd')
}
})
HTML
<div id='app' v-gqs></div>
结果:
bind triggerd
inserted triggerd
Было обнаружено, что по умолчанию запускаются только функции цикла привязки и вставленного объявления.
Итак, когда срабатывают остальные три?
<div id='app' >
<p v-gqs v-if="show">v-if是删除或者新建dom元素,它会触发unbind指令声明周期吗?</p>
<button @click="show=!show">toggle</button>
</div>
Когда элемент, к которому привязана директива, уничтожается, запускается событие unbind директивы. (Новый и отображаемый, по-прежнему вызывает привязку и вставку)
отвязать триггер.gif
<p v-gqs v-show="show2">v-show设置元素的display:block|none,会触发componentUpdated事件</p>
<button @click="show2=!show2">toggle-v-show</button>
- Удаление элемента из DOM вызывает unbind(). ---> просто удалить.
- Запускается, когда элемент настройки отображения скрыт и отображается componentUpdated() ---> block | none Запускает оба.
31. Что такое процесс монтирования экземпляра vue
32. В чем разница между компонентом и плагином
-
Компонент — это бизнес-модуль, используемый для создания вашего приложения, и его целью является App.vue.
-
Плагины — это функциональные модули, используемые для улучшения вашего стека технологий и ориентированные на сам Vue.
33. С какими проблемами (ямами) можно столкнуться в процессе использования vue
34. Что происходит, когда новое свойство динамически добавляется к данным vue
Согласно определению официальной документации:
если в实例创建之后Добавить новое属性к экземпляру, он не запускает обновление представления.
Вью не позволяет已经创建из实例начальство动态添加новый根级响应式属性(реактивное свойство корневого уровня).
Однако его можно использоватьVue.set(object, key, value)метод для добавления свойств ответа к вложенным объектам.
35. Как решить медленную скорость загрузки первого экрана SPA
- Сжато Gzip
- Ленивая загрузка с маршрутами
- Используйте свойство externals в веб-пакете, чтобы отделить файлы библиотеки, которые не нужно упаковывать, и уменьшить размер упакованного проекта.
- Рендеринг с SSR
36. mixin
多个实例Ссылаясь на то же или подобное方法或属性И т. д., этот повторяющийся контент можно извлечь как js миксинов, экспортировать и внедрить через атрибут mixins в файле vue, на который нужно ссылаться.当前实例из其他内容провестиmerge.
Миксин может содержать任意组件选项. тот же жизненный цикл,混入对象было бы лучше, чем组件из先执行.
// Выставляем два объекта-примеси
export const mixinsTest1 = {
methods: {
hello1() {
console.log("hello1");
}
},
created() {
this.hello1();
},
}
export const mixinsTest2 = {
methods:{
hello2(){
console.log("hello2");
}
},
created() {
this.hello2();
},
}
<template>
<div>
home
</div>
</template>
<script>
import {mixinsTest1,mixinsTest2} from '../util/test.js'
export default {
name: "Home",
data () {
return {
};
},
created(){
console.log("1212");
},
mixins:[mixinsTest2,mixinsTest1] // 先调用哪个mixins对象,就先执行哪个
}
</script>
hello2
hello1
1212
37. В чем суть vue
- управляемый даннымиСосредоточьтесь на слое View. Это позволяет разработчикам сохранить процесс манипулирования DOM и нужно только изменить данные.
- Принцип отклика компонентаИзменения данных (модели) приводят к автоматическому обновлению представления (представления).
- составнойРасширяйте элементы HTML, чтобы инкапсулировать повторно используемый код.
38. Какие модификаторы обычно используются в vue
- v-model: .trim .number
- v-on: .stop .prevent
39. Может ли v-on связывать несколько методов?
<input v-model="msg" type="text" v-on="{input:a, focus:b}"/>
40. Понимание компиляции шаблонов
41. Что такое axios и как прервать запрос axios
пример официальной документации axios
var CancelToken = axios.CancelToken;
var cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function (c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// 取消请求
cancel();
в реальном проекте
1.Сначала нужно ввести аксиос отдельно, настроенный аксиос-файлhttp.jsэкспорт по умолчанию не включаетCancelToken
import axios from 'axios'
2.Определите cancelArr для хранения нескольких функций отмены и получите индекс для выполнения, когда запрос прерывается по отдельности:this.cancelArr[key]
data() {
return {
//用于存放 cancel 函数
cancelArr:[]
}
}
3.Axios.CancelToken можно использовать только после отдельного введения axios
let CancelToken = axios.CancelToken
let cancel;
let _this = this;
this.axios.post('upload', obj, {
onUploadProgress: progress => {
//处理进度
},
cancelToken:new CancelToken(function excutor(c){
cancel = c;
//一个处理函数接受一个cancel函数作为参数
_this.cancelArr.push(cancel)
//在已定义的cancelArr数组中放入cancel函数
console.log(_this.cancelArr);
//上传多个文件时,每增加一个上传文件,cancelArr数组中会多一个cancel函数
})
})
4.Отменить один запрос на основе индекса
cancelRequest(key){
//获取到要中止请求的索引后,执行cancel
this.cancelArrkey
},
5.Отменить несколько запросов
for(let i in this.cancelArr){
//遍历 cancelArr,逐个执行
this.cancelArri
}
42. Как внедрить scss?
Установите scss-зависимости:
npm install sass-loader --save-dev npm install node-sass --save-dev
Измените файл webpack.base.conf.js в папке сборки и добавьте конфигурацию в правила под модулем следующим образом:
{ test: /\.scss$/, loaders: ['style', 'css', 'sass'] }
заявление:
在vue文件中应用scss时,需要在style样式标签上添加lang="scss",即<style lang="scss">。
43. Какой из часов и созданных выполняется первым в vue
Немедленное действие в часах позволит слушателю выполнить расчет мониторинга, когда будет объявлено начальное значение, в противном случае созданное будет выполнено первым.
44. В чем разница между созданным и активированным в vue
created(): вызывается сразу после создания экземпляра. На этом шаге экземпляр завершил следующую настройку: наблюдатель данных, работа со свойствами и методами, обратный вызов события наблюдения/события. Однако фаза монтирования еще не началась, и свойство $el еще недоступно.
activated(): находится в настройках маршрутизации<keep-alive></keep-alive>Когда этот жизненный цикл происходит. Вызывается при активации компонента, который кэшируется функцией проверки активности.
45. Почему не рекомендуется использовать случайные числа или индексы для ключей в v-for?
Потому что при вставке или удалении данных будет меняться индекс привязки клавиш следующих данных, что приведет к повторному рендерингу, а эффективность снизится.
46. Как импортировать компоненты партиями
Как использовать динамические компоненты
<keep-alive>
<component :is="isWhich"></component>
</keep-alive>
使用标签保存状态,即切换组件再次回来依然是原来的样子,页面不会刷新,若不需要可以去掉。
通过事件改变is绑定的isWhich值即可切换成不同的组件,isWhich的值为组件名称。
47. Как сбросить данные в vue
используемые сцены:
Например, есть форма.После успешной отправки формы я надеюсь, что компонент будет восстановлен в исходное состояние, а данные data будут сброшены.
использоватьObject.assign(),vm.$dataВы можете получить данные в текущем состоянии,vm.$options.dataДанные могут быть получены в компоненте состояния инициализации
Установить значение по умолчанию данных данных в исходном состоянии и непосредственно при сбросеobject.assign(this.$data, this.$options.data())
проиллюстрировать:
- this.$data получает данные в текущем состоянии
- this.$options.data() получает данные в начальном состоянии компонента (т.е. начальное значение по умолчанию)
- Если вы хотите изменить только значение определенного атрибута данных, вы можете
this[属性名] = this.$options.data()[属性名],Такие какthis.message = this.$options.data().message
48. Как сохранить HTML-комментарии в шаблоне, когда vue отрисовывает шаблон
<template comments>
...
</template>
49. Назначение и принцип атрибута style plus scoped
- использовать: Предотвратить глобальное загрязнение CSS с тем же именем
-
принцип: добавить к ярлыку
v-data-somethingатрибут, а затем добавьте соответствующий селектору[v-data-something], то есть селектор атрибута CSS, чтобы завершить метод выбора, аналогичный области
50. Как настроить фавикон в проекте vue
-
Поместите изображение фавикона в статическую папку
-
Затем в index.html добавьте:
<link rel="shortcut icon" type="image/x-icon" href="static/favicon.ico">
51. модуль babel-polyfill
BabelКонвертировать только по умолчанию新的JavaScript句法(синтаксис) без преобразования新的API,НапримерIterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promiseи другие глобальные объекты, а также некоторые определенные в全局对象метод выше (например,Object.assign) не будет перекодирован.
Например, ES6 добавил метод Array.from для объектов Array. Babel не будет перекодировать этот метод. Если вы хотите, чтобы этот метод работал, вы должны использоватьbabel-polyfill, который обеспечивает прокладку для текущей среды.
52. В чем разница между e.target и e.currentTarget при передаче $event в событии vue
- currentTarget: элемент, к которому привязано событие
- target: элемент, активируемый мышью
53. Как vue реализует принудительное обновление компонентов
принудительный повторный рендеринг
this.$forceUpdate()
Принудительное обновление компонента
//模版上绑定key
<SomeComponent :key="theKey"/>
//选项里绑定data
data(){
return{
theKey:0
}
}
//刷新key达到刷新组件的目的
theKey++;
54. Что делать, если vue привязывает пользовательские события к компонентам неправильно
Добавьте модификатор .native
55. Что происходит, когда имя свойства vue совпадает с именем метода метода
Ошибка "Метод 'xxx' уже определен как свойство данных"
Приоритет имени ключа:props > data > methods, эти трое нельзя назвать повторно
56. Что будет, если имя переменной vue начинается с атрибутов _, $
После создания экземпляра вы можете передатьvm.$dataдоступ原始数据对象. Экземпляр Vue также проксирует все свойства объекта данных, поэтому доступvm.aЭквивалент доступаvm.$data.a.
к_или$свойства в начале不会Создан Vue代理, так как они могут быть совместимы с Vue内置свойства, методы API冲突. можно использоватьvm.$data._propertyПуть访问эти свойства.
57. Сообщается об ошибке 404 после того, как локальная разработка проекта vue завершена и развернута на сервере.
Используется режим истории, и серверная часть не настраивает связанные ресурсы.
58. Модификатор формы Vue.lazy
В поле ввода v-model по умолчанию синхронизирует данные поля ввода в событии ввода (кроме метода ввода китайский ввод), использование модификатора .lazy изменит его на синхронизацию в событии изменения (аналогично ленивой загрузке) :
<div id="app1">
<input type="text" v-model.lazy="message">
<p>{{message}}</p>
</div>
<script>
var app1 = new Vue({ el: '#app1', data: { message:'测试' } });
</script>
В это время сообщение обновляется не в режиме реального времени, а вне в фокусе или нажмите Enterвремя обновить.
59. Почему vue требует, чтобы шаблоны компонентов имели только один корневой элемент
Алгоритм diff требует, чтобы patchVnode в patch.js в исходном коде также проходился в соответствии с древовидной структурой.
60. На какие проблемы следует обратить внимание при использовании этого в vue
Функция ловушки жизненного цикла не может использовать стрелочные функции, иначе она не может указывать на экземпляр vue.
61. <template></template>Какая польза
Обертывание и вложение других элементов делает элемент региональным и имеет три характеристики:
- Скрытый: не отображается на странице
- Произвольно: может быть написано в любом месте на странице
- Недействительность: без обертки корневого элемента любой HTML-контент недействителен.
62. Какова функция написания параметра имени в компоненте
- Когда проект использует поддержку активности, его можно сопоставить с именем компонента.
缓存过滤 - Когда DOM используется как рекурсивный компонент, он должен вызывать свое собственное имя.
- Отображается в инструменте отладки vue-devtools
组见名称Он определяется именем компонента в vue.
63. Как проверяются реквизиты
- Используйте базовые типы, такие как Number, для одного типа
- Массивы для нескольких типов
- При необходимости установите для параметра require значение true
- установить значение по умолчанию
- Настройки объекта и массива по умолчанию соответствуют заводским функциям.
- Пользовательский валидатор функции проверки.
64. vue управление разрешениями, кнопки и маршрутизация
65. Как вы делите структуру и компоненты больших проектов
- Каталог представлений хранит компоненты маршрутизации первого уровня, то есть компоненты представления.
- Каталог компонентов для хранения компонентов
- Store хранит файлы, связанные с vuex
- В каталоге маршрутизатора хранятся файлы, связанные с маршрутизацией.
- Пока каталог для инструментов файла JS
- Каталог API хранит инкапсулированную логику для взаимодействия с серверной частью.
- Активы хранят статические файлы
66. Новые возможности vue3.0
67. Компоненты высшего порядка
68. Что такое vue-loader
解析和转换файлы .vue,提取Выведите сценарий кода логики, стиль кода стиля и шаблон HTML-шаблона, а затем передайте их соответствующему загрузчику для обработки.
69. Как перехватить сообщение об ошибке компонента vue
//通过这个钩子来捕获
Vue.config.errorHandler = (err, vm, info) => {
console.log('进来啦~');
}
export default {
created() {
let a = null;
if(a.length > 1) {
// ...
}
}
};
70. Как настроить кроссдоменность
vue.config.js
module.exports = {
//相当于webpack-dev-server, 对本地服务器进行配置
devSever: {
proxy: {
'/api': {
target: ' http://localhost:3000 ', //需要跨域的目标url ,我这里是自己本地起的一个服务端口
changeOrigin: true, // 将基于名称的虚拟托管网站的选项,如果不配置,请求会报404
ws: true,
pathRewrite: {
' ^/api ': '' //若请求的路径在目标url下但不在/api 下,则将其转换成空
}
}
}
}
}
пример:
this.$axios.post('/api/list').then(res =>{
console.log(res)
})
71. Как настроить страницу 404 в vue-router
настраиватьpath: '*', и вставьте последний
72. Как vue-router реагирует на изменение параметров маршрутизации
Зачем реагировать на изменения параметров?
- переключать маршруты,
路由参数случилось变化,но页面数据а также未Он обновляется вовремя, и его необходимо принудительно обновить, прежде чем он изменится. - Когда разные маршруты отображают один и тот же компонент (повторное использование компонентов более эффективно, чем уничтожение и воссоздание), после переключения маршрутов функции жизненного цикла текущего компонента не будут вызываться снова.
решение:
Используйте часы, чтобы слушать
watch: {
$route(to, from){
if(to != from) {
console.log("监听到路由变化,做出相应的处理");
}
}
}
Добавить ключ к компоненту просмотра маршрутизатора
<router-view :key="$route.fullPath"></router-view>
$route.fullPathURL-адрес, проанализированный после завершения, включая информацию о параметрах запроса и хеш-полный путь.
73. При переходе на новый маршрут лицо должно прокручиваться вверх или сохранять исходное положение прокрутки
在路由实例中配置
scrollBehavior(ro,form,savedPosition){
//滚动到顶部
return {x:0,y:0}
//保持原先的滚动位置
return {selector:false}
}
74. Отложенная загрузка маршрута
- require
{ path: '/home', name: 'home', component: resolve => require(['@/components/home'],resolve) }
- import
//打包后的js文件名为ImportFuncDemo
const Home = () => import(webpackChunkName: 'ImportFuncDemo', '@/components/home')
- require.ensure
//打包后的js文件名为demo
{ path: '/home', name: 'home', component: r => require.ensure([], () => r(require('@/components/home')), 'demo') },
75. MVVM
Полное имя:Model-View-ViewModel, Модель представляет数据模型层, просмотр означает视图层, ViewModel — это слой представления и модели.桥梁,数据绑定прибытьviewModelслой и автоматически отображать на странице,视图变化уведомлятьviewModelДанные обновления слоя.
76. Принцип привязки событий в vue
Какие существуют типы привязок событий?
-
собственная привязка событийСвязывание нативных событий dom с помощью
addEventListenerвыполнить. -
Привязка событий компонента, событие привязки компонента использует
$onметод .
-
普通元素из原生Привязка событий осуществляется через@clickграница -
组件из原生Привязка событий осуществляется через@click.nativeДля привязки в компонентеnativeOnэквивалентно вкл. -
组件из自定义событие прошло@clickсвязанный, через$onметод достижения, должен быть$emitдля запуска.
Объясните разницу между ними:
-
Натив: Например, пишем событие на нативной ноте.
<div @click="getData"></div>, прямой триггер – это собственное событие клика. -
Компонент: теперь мы настроили компонент и хотим записать события в компонент,
<BtnGroup @click="getName" @click.native="getData"></BtnGroup>, в настоящее время для запуска собственного события click getData необходимо использовать модификатор.native, потому что непосредственно с помощью@clickполучен от дочернего компонентаemitПриходит событие getName, так что конфликта не будет.
let compiler = require('vue-template-compiler'); // vue loader中的包
let r1 = compiler.compile('<div @click="fn()"></div>'); // 给普通标签绑定click事件
// 给组件绑定一个事件,有两种绑定方法
// 一种@click.native,这个绑定的就是原生事件
// 另一种@click,这个绑定的就是组件自定义事件
let r2 = compiler.compile('<my-component @click.native="fn" @click="fn1"></mycomponent>');
console.log(r1.render); // {on:{click}}
console.log(r2.render); // {nativeOn:{click},on:{click}}
// 为什么组件要加native?因为组件最终会把nativeOn属性放到on的属性中去,这个on会单独处理
// 组件中的nativeOn 等价于 普通元素on,组件on会单独处理
77. Почему Vue использует асинхронный рендеринг
Вью в更新DOMкогда异步执行Да, пока侦听к данным变化,Буду开启Один队列,а также缓冲все происходит в одном и том же цикле событий数据变更, если так жеwatcherодеяло多次触发, будет только推入прибыть队列середина一次, это в缓冲удалено, когда重复数据для уменьшения不必要из计算和DOM操作очень важно.
Затем, в следующем такте цикла событий, Vue очищает очередь и выполняет фактическую (дедублированную) работу, Vue внутренне пытается использовать нативные для асинхронных очередей.Promise.then、MutationObserver和setImmediate, если среда выполнения не поддерживает его,setTimeout(fn, 0)заменять.
описывать
Для того, почему Vue использует асинхронный рендеринг, это просто для提升性能, так как асинхронные обновления не используются, в每次更新данные будут выполняться на текущем компоненте重新渲染, из соображений производительности Vue будет本轮数据После обновления перейти на асинхронный更新视图, например, давайте повторно обновим значение в методе.
this.msg = 1;
this.msg = 2;
this.msg = 3;
На самом деле нам нужно только последнее обновление, а это значит, что первые три обновления DOM можно опустить, и нам нужно только дождаться изменения всех состояний перед рендерингом, чтобы уменьшить некоторые потери производительности.
Проблема рендеринга предельно ясна. В конце концов, только однократный рендеринг определенно потребляет меньше производительности, чем рендеринг после модификации. Здесь нам также необходимо рассмотреть связанные с этим проблемы асинхронных очередей обновлений, предполагая, что мы сейчас выполняем соответствующую обработку, чтобы каждый Только один реальный рендеринг DOM для данных обновления, давайте рассмотрим оптимизацию производительности асинхронной очереди обновлений.
Предполагая, что это очередь синхронных обновлений, this.msg=1, эти вещи примерно произойдут:
msg值更新 -> 触发setter -> 触发Watcher的update -> 重新调用 render -> 生成新的vdom -> dom-diff -> dom更新
Обновление дома здесь не渲染(т. е. последовательность шагов, таких как компоновка, рисование, компоновка и т. д.), но обновление内存серединаDOM树结构, затем запустите this.msg=2 и повторите шаги, описанные выше. Третье обновление после этого также вызовет тот же процесс. Когда начнется рендеринг, в последнем дереве DOM будет существовать только завершение обновления 3. Взгляните отсюда. первые 2 операции над msg и его внутренняя обработка Vue — все бесполезные операции, и их можно оптимизировать.
Если это асинхронная очередь обновления, то будет следующая ситуация:
Запуск this.msg=1 не сразу выполняет вышеописанный процесс, а сохраняет в очереди все наблюдатели, которые зависят от msg, что может быть похоже на это[Watcher1, Watcher2...]При запуске this.msg = 2 в очереди также будет храниться зависимый от msg Watcher, Vue内部Сможет сделать去重判断, после этой операции можно считать, что данные очереди не изменились, и третье обновление — тоже вышеописанный процесс.
Конечно, вы не можете работать только с msg, вы также можете работать с другим атрибутом в компоненте, например, this.otherMsg=othermessage, что также добавит Watcher, который зависит от otherMsg, в очередь асинхронных обновлений, потому что есть повторения Судя по операции, этот наблюдатель будет существовать в очереди только один раз.После выполнения этой асинхронной задачи он перейдет к следующему процессу выполнения задачи.Фактически, он должен пройти каждый наблюдатель в очереди асинхронного обновления, инициировать его обновление и затем позвоните еще раз.render -> new vdom -> dom-diff -> dom更新Дождитесь процесса, но по сравнению с синхронной очередью обновления, независимо от того, сколько операций с сообщениями выполняется, Vue будет повторно вызывать реальный процесс обновления только один раз внутри.
Итак, при асинхронном обновлении очередь не сохраняется
渲染成本, но спасаетVue内部计算及DOM树操作的成本, В любом случае, действительно оказал только один раз.
Кроме того, компонент фактически использует VirtualDOM для рендеринга, то есть компонент фактически не заботится о том, какое состояние изменилось, ему нужно только один раз вычислить, чтобы узнать, какие узлы необходимо обновить, то есть если N состояний изменены , на самом деле нужно только отправить сигнал для обновления DOM до последней версии, если мы обновляем несколько значений.
this.msg = 1;
this.age = 2;
this.name = 3;
Здесь мы трижды изменяем три состояния, но на самом деле Vue отрендерит его только один раз, потому что VIrtualDOM нужно всего лишь обновить DOM всего компонента до последнего, ему все равно, от какого именно сигнала исходит обновление. изданный.
Чтобы добиться этого, нам нужно渲染操作перенесено на所有положение дел都修改完成, для этого необходимо лишь перенести операцию рендеринга на конец текущего витка цикла событий или на следующий виток цикла событий, то есть только в本轮事件循环из最后,Ждать前面更新状态все предложения执行完之后,执行一次Операция рендеринга может игнорировать предыдущий синтаксис обновления состояния, независимо от того, сколько операторов для обновления состояния было написано ранее, его можно отобразить только один раз в конце.
Откладывание рендеринга до конца текущего цикла событий будет намного быстрее, чем откладывание рендеринга до следующего раунда, поэтому Vue предпочтительно откладывает операцию рендеринга до конца текущего цикла событий.Если среда выполнения не поддерживает это, он будет быть понижена до следующего раунда. Механизм обнаружения изменений Vue (установщик) определяет, что он неизбежно будет отправлять сигнал рендеринга каждый раз, когда состояние изменяется, но Vue проверит, существует ли задача уже в очереди после получения сигнала, чтобы гарантировать, что очередь не Есть повторы.Если очередь не существует, добавьте операцию рендеринга в очередь, а затем задержите выполнение всех операций рендеринга в очереди асинхронно и очистите очередь.При повторном изменении состояния в одном и том же событии цикл, он не будет повторяться.Добавьте ту же операцию рендеринга в очередь, поэтому, когда мы используем Vue, обновление DOM после изменения состояния происходит асинхронно.
когда данные变化будет вызван позжеnotifyметод, будетwatcherпройти, позвонитьupdateМетод уведомляет наблюдателя об обновлении, в это время наблюдатель не будет выполняться сразу, а будет вызываться в обновлении.queueWatcherМетод помещает наблюдателя в очередь, и queueWatcher будет выполнять дедупликацию в соответствии с наблюдателем.Если несколько атрибутов зависят от наблюдателя, если в очереди нет наблюдателя, наблюдатель будет добавлен в очередь, а затем наблюдатель будет быть добавленным в очередь.$nextTickДобавьте метод flushSchedulerQueue в очередь выполнения метода (этот метод инициирует выполнение всех обратных вызовов в очереди буфера), а затем$nextTickДобавлен обратный вызов метода$nextTickОчередь выполнения поддерживается в методе, flushSchedulerQueue начинает запускать метод before, который на самом деле является beforeUpdate, а затем watcher.run() начинает фактически выполнять наблюдателя.После выполнения страницы рендеринг завершен, и обновленный хук вызывается после завершения обновления.
$nextTick
Выше мы говорили о том, почему Vue использует асинхронный рендеринг. Если у нас есть требование в настоящее время, нам нужно получить элементы DOM страницы после завершения рендеринга страницы. Поскольку рендеринг является асинхронным, мы не можем получить это напрямую. синхронно в определенном методе. стоит, так что естьvm.$nextTickметод в Vue$nextTickМетод задерживает выполнение обратного вызова после следующего цикла обновления DOM, то есть отложенный обратный вызов выполняется после завершения следующего цикла обновления DOM.Используйте этот метод сразу после изменения данных для получения обновленного DOM. Проще говоря, когда данные обновляются, функция обратного вызова выполняется после рендеринга в DOM.
Продемонстрируйте на простом примере$nextTickФункционал метода, в первую очередь нужно знать, что Vue выполняется асинхронно при обновлении DOM, то есть не блокирует выполнение кода при обновлении данных, и не начнет выполняться код очереди асинхронных задач, пока не закончится выполнение кода в стеке выполнения.Поэтому при обновлении данных компонент не будет отрисовываться сразу.В это время значение, полученное после получения DOM-структуры, по-прежнему старое значение.$nextTickФункция обратного вызова, установленная в методе, будет выполнена после завершения рендеринга компонента, а значение, полученное после получения структуры DOM, будет новым значением.
<!DOCTYPE html>
<html>
<head>
<title>Vue</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'Vue'
},
template:`
<div>
<div ref="msgElement">{{msg}}</div>
<button @click="updateMsg">updateMsg</button>
</div>
`,
methods:{
updateMsg: function(){
this.msg = "Update";
console.log("DOM未更新:", this.$refs.msgElement.innerHTML)
this.$nextTick(() => {
console.log("DOM已更新:", this.$refs.msgElement.innerHTML)
})
}
},
})
</script>
</html>
Асинхронный механизм#
Js является однопоточным, который вводит синхронный блокирующий и асинхронный неблокирующий режимы выполнения и поддерживаетEvent Loop, цикл событий представляет собой执行模型, в разных местах есть разные реализации, браузеры и NodeJS реализуют свои собственные циклы событий, основанные на разных технологиях. Цикл событий браузера находится вHTML5В спецификации четко определено, что цикл событий NodeJS основан наlibuvосуществленный.
в браузереEvent LoopЗависит от执行栈стек выполнения,后台线程Фоновые темы,宏队列Очередь макрозадач,微队列Состав очереди микрозадач.
-
стек выполнениятолько что в
主线程воплощать в жизнь同步任务из数据结构, вызов функции формирует стек кадров. -
фоновая нитьэто реализация браузера для
setTimeout、setInterval、XMLHttpRequestи так далее в потоке выполнения. -
очередь макросов,Немного
异步任务Обратные вызовы будут по очереди входить в очередь макросов, ожидая последующих вызовов, включая setTimeout, setInterval, setImmediate(Node), requestAnimationFrame, рендеринг пользовательского интерфейса, ввод-вывод и другие операции. -
микроочередь,другие
异步任务Обратные вызовы по очереди будут поступать в микроочередь, ожидая последующих вызовов, включая такие операции, как Promise, process.nextTick(Node), Object.observe и MutationObserver.
При выполнении Js выполняется следующий процесс:
- Во-первых, код в стеке выполнения выполняется синхронно, а асинхронные задачи в этих кодах добавляются в фоновый поток.
- После выполнения кода синхронизации в стеке выполнения стек выполнения очищается и сканируется микроочередь.
- Выполняется удаление задач первой команды микроочередей в стек выполнения, в случае исключения микроочередей из очереди.
- После завершения выполнения стека выполнения задачи микроочереди продолжают удаляться из очереди и выполняться до тех пор, пока не будут выполнены все задачи микроочереди.
- После того, как последняя задача микроочереди удалена из очереди и введена в стек выполнения, задача в микроочереди пуста.Когда задача стека выполнения завершена, начните сканировать микроочередь, чтобы она была пустой, продолжайте сканировать задачу очереди макросов, удалите очередь макросов и поместите его в стек выполнения. Если микро-очередь пуста, просканируйте очередь макросов и выполните ее вне очереди. Постоянно туда-сюда....
Пример#
// Step 1
console.log(1);
// Step 2
setTimeout(() => {
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
}, 0);
// Step 3
new Promise((resolve, reject) => {
console.log(4);
resolve();
}).then(() => {
console.log(5);
})
// Step 4
setTimeout(() => {
console.log(6);
}, 0);
// Step 5
console.log(7);
// Step N
// ...
// Result
/*
1
4
7
5
2
3
6
*/
анализ #
Узнав об очереди выполнения асинхронных задач, вернитесь к$nextTickМетод, когда обновление пользовательских данных, Vue будет поддерживать缓冲队列, для всех операций рендеринга компонента и DOM, которые будут обновляться, данные будут обработаны в определенной стратегии и добавлены в очередь буфера, а затем будут добавлены в очередь буфера.$nextTickДобавить метод в очередь выполнения методаflushSchedulerQueueметод (этот метод инициирует выполнение всех обратных вызовов в очереди буфера), а затем$nextTickДобавлен обратный вызов метода$nextTickОчередь выполнения, поддерживаемая в методе, будет выполняться первой при запуске асинхронно смонтированной очереди выполнения.flushSchedulerQueueметод для обработки задачи рендеринга DOM, а затем выполнить его$nextTickПостроена задача, которую можно реализовать$nextTickВизуализированная структура DOM получается в методе.
В процессе тестирования я обнаружил очень интересное явление, в приведенном выше примере при добавлении двух кнопок результат нажатия кнопки updateMsg равен 3 2 1, а текущий результат нажатия кнопки updateMsgTest равен 2 3 1.
<!DOCTYPE html>
<html>
<head>
<title>Vue</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: '#app',
data: {
msg: 'Vue'
},
template:`
<div>
<div ref="msgElement">{{msg}}</div>
<button @click="updateMsg">updateMsg</button>
<button @click="updateMsgTest">updateMsgTest</button>
</div>
`,
methods:{
updateMsg: function(){
this.msg = "Update";
setTimeout(() => console.log(1))
Promise.resolve().then(() => console.log(2))
this.$nextTick(() => {
console.log(3)
})
},
updateMsgTest: function(){
setTimeout(() => console.log(1))
Promise.resolve().then(() => console.log(2))
this.$nextTick(() => {
console.log(3)
})
}
},
})
</script>
</html>
Предполагая, что объект Promise в среде выполнения полностью поддерживается, можно использовать setTimeout для выполнения очереди макросов в конце, но использовать$nextTickМетоды и самоопределяемые экземпляры Promise имеют проблему порядка выполнения. Хотя все они являются задачами микроочередей, конкретная реализация в Vue приводит к тому, что порядок выполнения отличается. Во-первых, давайте посмотрим непосредственно$nextTickИсходный код метода, с добавленными комментариями к ключевым местам, обратите внимание, что это исходный код версии Vue2.4.2, на более позднем этапе$nextTickМетоды могут быть изменены.
/**
* Defer a task to execute it asynchronously.
*/
var nextTick = (function () {
// 闭包 内部变量
var callbacks = []; // 执行队列
var pending = false; // 标识,用以判断在某个事件循环中是否为第一次加入,第一次加入的时候才触发异步执行的队列挂载
var timerFunc; // 以何种方法执行挂载异步执行队列,这里假设Promise是完全支持的
function nextTickHandler () { // 异步挂载的执行任务,触发时就已经正式准备开始执行异步任务了
pending = false; // 标识置false
var copies = callbacks.slice(0); // 创建副本
callbacks.length = 0; // 执行队列置空
for (var i = 0; i < copies.length; i++) {
copies[i](); // 执行
}
}
// 如果支持promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve();
var logError = function (err) { console.error(err); };
timerFunc = function () {
p.then(nextTickHandler).catch(logError); // 挂载异步任务队列
if (isIOS) { setTimeout(noop); }
};
} else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
var counter = 1;
var observer = new MutationObserver(nextTickHandler);
var textNode = document.createTextNode(String(counter));
observer.observe(textNode, {
characterData: true
});
timerFunc = function () {
counter = (counter + 1) % 2;
textNode.data = String(counter);
};
} else {
// fallback to setTimeout
/* istanbul ignore next */
timerFunc = function () {
setTimeout(nextTickHandler, 0);
};
}
return function queueNextTick (cb, ctx) { // nextTick方法真正导出的方法
var _resolve;
callbacks.push(function () { // 添加到执行队列中 并加入异常处理
if (cb) {
try {
cb.call(ctx);
} catch (e) {
handleError(e, ctx, 'nextTick');
}
} else if (_resolve) {
_resolve(ctx);
}
});
//判断在当前事件循环中是否为第一次加入,若是第一次加入则置标识为true并执行timerFunc函数用以挂载执行队列到Promise
// 这个标识在执行队列中的任务将要执行时便置为false并创建执行队列的副本去运行执行队列中的任务,参见nextTickHandler函数的实现
// 在当前事件循环中置标识true并挂载,然后再次调用nextTick方法时只是将任务加入到执行队列中,直到挂载的异步任务触发,便置标识为false然后执行任务,再次调用nextTick方法时就是同样的执行方式然后不断如此往复
if (!pending) {
pending = true;
timerFunc();
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(function (resolve, reject) {
_resolve = resolve;
})
}
}
})();
Возвращаясь к вопросу, просто поднятому, когда операция DOM обновляется, она будет срабатываться первым$nextTickОбратный вызов метода, ключ к решению этой проблемы заключается в том, кто первым подключает асинхронную задачу к объекту Promise.
Сначала отладьте метод, запускаемый кнопкой updateMsg с обновлением данных.Точка останова установлена в строке 715 Vue.js, версия 2.4.2.Вы можете наблюдать первое выполнение при просмотре стека вызовов и входящих параметров.$nextTickФактически метод вызывается из-за обновления данныхnextTick(flushSchedulerQueue)оператор, то есть когда this.msg = "Update"; выполняется в первый раз$nextTickметод, в настоящее время$nextTickОчередь задач в методе сначала добавит в очередь метод flushSchedulerQueue и смонтирует его$nextTickВыполнение метода ставится в очередь объекта Promise, а затем настраиваетсяPromise.resolve().then(() => console.log(2))Монтирование оператора, при выполнении задач в очереди микрозадач, первая задача, смонтированная в Promise, будет выполняться первой. В это время эта задача является текущей очередью выполнения. В этой очереди есть два метода. Во-первых, будет запущен метод flushSchedulerQueue , Чтобы запустить операцию рендеринга DOM компонента, выполните console.log(3), а затем выполните задачу второй микроочереди () => console.log(2).В это время микрозадача очередь пуста, а затем перейдите к Очередь задач макроса выполняет console.log(1).
Далее отладить метод, срабатывающий по кнопке updateMsgTest без обновления данных.Точка останова ставится на ту же позицию.В это время обновления данных нет,тогда первый триггер$nextTicМетод k является самоопределяемой функцией обратного вызова, тогда в это время$nextTickОчередь выполнения метода будет смонтирована на объекте Promise.Очевидно, что обратный вызов Promise для вывода 2, определенный сам по себе, был смонтирован ранее, поэтому процесс выполнения метода, привязанного к этой кнопке, заключается в том, чтобы сначала выполнить console.log( 2), затем выполнить$nextTickОчередь выполнения закрытия метода.В данный момент в очереди выполнения находится только одна callback-функция console.log(3).В это время очередь микрозадач очищается, а затем переходите в очередь макрозадач для выполнения console.log(1).
Проще говоря, это вопрос о том, кто первым монтирует объект Promise.$nextTickКогда метод используется, очередь выполнения, поддерживаемая внутри его замыкания, будет смонтирована в объекте Promise.Когда данные будут обновлены, Vue сначала выполнит его.$nextTickметод, а затем смонтировать очередь выполнения к объекту Promise.На самом деле, после понимания модели цикла событий Js, обновление данных также рассматривается как$nextTickвызов метода и понять$nextTickМетод будет выполнять все отправленные обратные вызовы одновременно, чтобы вы могли понять порядок выполнения.Ниже приведена минимальная демонстрация метода $nextTick.
var nextTick = (function(){
var pending = false;
const callback = [];
var p = Promise.resolve();
var handler = function(){
pending = true;
callback.forEach(fn => fn());
}
var timerFunc = function(){
p.then(handler);
}
return function queueNextTick(fn){
callback.push(() => fn());
if(!pending){
pending = true;
timerFunc();
}
}
})();
(function(){
nextTick(() => console.log("触发DOM渲染队列的方法")); // 注释 / 取消注释 来查看效果
setTimeout(() => console.log(1))
Promise.resolve().then(() => console.log(2))
nextTick(() => {
console.log(3)
})
})();