Содержание предыдущих двух разделов представило компоненты, от принципа компонентов до применения компонентов, включая реализацию и сценарии использования асинхронных компонентов и функциональных компонентов. Как мы все знаем, компоненты — это то, что проходит через всю концепцию дизайна Vue, а также являются основными идеями, которые определяют нашу разработку, поэтому в следующих нескольких статьях мы вернемся к содержанию компонентов для анализа исходного кода. начиная с часто используемых динамических компонентов, включая принцип встроенных шаблонов, концепция встроенных компонентов будет кратко упомянута в конце, прокладывая путь для будущих статей.
12.1 Динамические компоненты
Я считаю, что большая часть динамических компонентов будет использоваться в процессе разработки.Когда нам нужно переключать состояния между различными компонентами, динамические компоненты вполне могут удовлетворить наши потребности.Ядром которых являетсяcomponentэтикетки иisИспользование свойств.
12.1.1 Базовое использование
Пример представляет собой базовый сценарий использования динамического компонента, когда при нажатии на кнопку открывается вид.this.chooseTabsзначение в компонентеchild1,child2,child3переключаться между.
// vue
<div id="app">
<button @click="changeTabs('child1')">child1</button>
<button @click="changeTabs('child2')">child2</button>
<button @click="changeTabs('child3')">child3</button>
<component :is="chooseTabs">
</component>
</div>
// js
var child1 = {
template: '<div>content1</div>',
}
var child2 = {
template: '<div>content2</div>'
}
var child3 = {
template: '<div>content3</div>'
}
var vm = new Vue({
el: '#app',
components: {
child1,
child2,
child3
},
methods: {
changeTabs(tab) {
this.chooseTabs = tab;
}
}
})
12.1.2 Анализ AST
<component>Интерпретация согласуется с предыдущим содержанием и будет исходить изASTКогда дело доходит до этапа анализа, процесс не будет фокусироваться на каждой детали, а будет конкретно объяснять отличия от предыдущих методов обработки. Чтобы узнать о различиях в разрешении динамических компонентов, обратите внимание наprocessComponentна, потому чтона этикеткеisсвойство существует, оно будет в финалеastударить по деревуcomponentФлаг атрибута.
// 针对动态组件的解析
function processComponent (el) {
var binding;
// 拿到is属性所对应的值
if ((binding = getBindingAttr(el, 'is'))) {
// ast树上多了component的属性
el.component = binding;
}
if (getAndRemoveAttr(el, 'inline-template') != null) {
el.inlineTemplate = true;
}
}
окончательныйastДерево выглядит следующим образом:
12.1.3 функция рендеринга
имеютastдерево, а затемastисполняемый файл генерации дереваrenderфункцию, так как естьcomponentАтрибуты,renderПроцесс генерации функции пойдетgenComponentветвь.
// render函数生成函数
var code = generate(ast, options);
// generate函数的实现
function generate (ast,options) {
var state = new CodegenState(options);
var code = ast ? genElement(ast, state) : '_c("div")';
return {
render: ("with(this){return " + code + "}"),
staticRenderFns: state.staticRenderFns
}
}
function genElement(el, state) {
···
var code;
// 动态组件分支
if (el.component) {
code = genComponent(el.component, el, state);
}
}
Логика обработки динамических компонентов на самом деле очень проста, при отсутствии встроенного флага шаблона (будет описан позже) последующие дочерние узлы получаются для сплайсинга, единственное отличие от обычных компонентов в том, что,_cПервый аргумент больше не указанная строка, а переменная, представляющая компонент.
// 针对动态组件的处理
function genComponent (
componentName,
el,
state
) {
// 拥有inlineTemplate属性时,children为null
var children = el.inlineTemplate ? null : genChildren(el, state, true);
return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")")
}
12.1.4 Сравнение обычных компонентов и динамических компонентов
На самом деле, мы можем сравнивать обычные компоненты и динамические компоненты вrenderРазница в функциях очевидна с первого взгляда.
Функция рендеринга обычного компонента
"with(this){return _c('div',{attrs:{"id":"app"}},[_c('child1',[_v(_s(test))])],1)}"
функции рендеринга в динамической сборке
"with(this){return _c('div',{attrs:{"id":"app"}},[_c(chooseTabs,{tag:"component"})],1)}"
В двух словах, разница между динамическими компонентами и обычными компонентами заключается в следующем:
-
-
astэтап добавленcomponentсвойство, которое является флагом динамического компонента
-
-
- производить
renderфункциональная стадия из-заcomponentНаличие свойства будет выполнятьgenComponentветвь,genComponentДля выполнения функции динамических компонентов будет выполняться специальная обработка.В отличие от обычных компонентов,_cПервый параметр больше не неизменяемая строка, а указанная переменная имени компонента.
- производить
-
-
renderприбытьvnodeПроцесс этапа такой же, как и в обычном компоненте, за исключением того, что строка заменяется переменной, и есть{ tag: 'component' }изdataАтрибуты. примерchooseTabsВ это времяchild1.
-
С помощью функции рендеринга следующий процесс от vnode до реального узла в основном такой же, как и у обычных компонентов с точки зрения процесса и идей.На этом этапе вы можете просмотреть анализ процесса компонента, представленный ранее.
12.1.5 Сомнение
Поскольку мое понимание исходного кода недостаточно глубоко, после прочтения процесса создания динамических компонентов у меня возникает вопрос.Из анализа процесса принцип, ядро динамических компонентов на самом делеisЭто ключевое слово, которое начинается во время компиляции сcomponentсвойство определяет компонент как динамический компонент, в то время какcomponentКажется, что он не имеет особенно большого применения в качестве ярлыка, пока естьisСуществование ключевых слов и установка имени метки компонента на любую пользовательскую метку может добиться эффекта динамических компонентов? (componenta, componentb). Эта строка заканчивается только на{ tag: 'component' }существует в видеvnodeизdataсвойство существует. Означает ли это, что так называемые динамические компоненты обусловлены толькоisодносторонние ограничения? ЭтоcomponentЧто означает этикетка? (Спросите большого парня!!)
12.2 Встроенные шаблоны
Поскольку динамические компоненты имеют в дополнение кisВ качестве передачи по значению вы также можете иметьinline-templateВ качестве конфигурации, исходя из этой предпосылки, можно обосноватьVueПринципы и идеи дизайна встроенных шаблонов в .VueНа официальном сайте есть поразительная фраза, напоминающая намinline-templateОбъем шаблона станет более сложным для понимания. Поэтому рекомендуется использовать его как можно чаще.templateвозможность определять шаблоны вместо встроенных шаблонов. Затем мы используем исходный код, чтобы найти причину, по которой так называемая область видимости сложна для понимания.
Давайте просто скорректируем приведенный выше пример и начнем с точки зрения использования:
// html
<div id="app">
<button @click="changeTabs('child1')">child1</button>
<button @click="changeTabs('child2')">child2</button>
<button @click="changeTabs('child3')">child3</button>
<component :is="chooseTabs" inline-template>
<span>{{test}}</span>
</component>
</div>
// js
var child1 = {
data() {
return {
test: 'content1'
}
}
}
var child2 = {
data() {
return {
test: 'content2'
}
}
}
var child3 = {
data() {
return {
test: 'content3'
}
}
}
var vm = new Vue({
el: '#app',
components: {
child1,
child2,
child3
},
data() {
return {
chooseTabs: 'child1',
}
},
methods: {
changeTabs(tab) {
this.chooseTabs = tab;
}
}
})
Эффект, достигнутый в примере, согласуется с первым примером статьи.Очевидно, что самое большое отличие от предыдущего когниции заключается в том, что среда в родительском компоненте может получить доступ к переменным среды внутри дочернего компонента. На первый взгляд это казалось невероятным. Вспомним предыдущую ситуацию, когда родительские компоненты могут обращаться к дочерним компонентам, есть два основных направления:
- 1. Используя механизм событий, подкомпоненты проходят$emitсобытие, которое информирует родительский компонент о состоянии дочернего компонента, чтобы родитель мог получить доступ к дочернему.
- 2. Используйте слот области, чтобы передать дочернюю переменную черезpropsформа передается родителю, а родитель передаетv-slotСинтаксический сахар для получения, и результат нашего предыдущего анализа заключается в том, что этот метод по существу уведомляет родительский компонент в форме отправки события.
В предыдущем процессе анализа также упоминалось, что родительский компонент не может получить доступ к переменным дочерней среды.Основные причины:Все в родительском шаблоне компилируется в родительской области, все в дочернем шаблоне компилируется в дочерней области.Так что у нас есть основания гадать, не нарушает ли встроенный шаблон этот принцип и позволяет компилировать контент родителя в процессе создания дочернего компонента? Смотрим вниз:
назадastНа этапе синтаксического анализа, как анализировалось ранее, ключом к синтаксическому анализу динамических компонентов являетсяprocessComponentфункциональная параisОбработка, из которых есть ключевой атрибутinline-templateобработки, он будет вastувеличение на деревеinlineTemplateАтрибуты.
// 针对动态组件的解析
function processComponent (el) {
var binding;
// 拿到is属性所对应的值
if ((binding = getBindingAttr(el, 'is'))) {
// ast树上多了component的属性
el.component = binding;
}
// 添加inlineTemplate属性
if (getAndRemoveAttr(el, 'inline-template') != null) {
el.inlineTemplate = true;
}
}
renderСтадия генерации функции обусловленаinlineTemplateПрисутствие,отцаrenderДочерние узлы функцииnull, этот шаг также определяетinline-templateПриведенный ниже шаблон не скомпилирован на этапе родительского компонента., как шаблон передается в процесс компиляции дочернего компонента?Ответ заключается в том, что шаблон существует в виде атрибутов, а значение атрибута получается при достижении дочернего экземпляра.
function genComponent (componentName,el,state) {
// 拥有inlineTemplate属性时,children为null
var children = el.inlineTemplate ? null : genChildren(el, state, true);
return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")")
}
Посмотрим, наконецrenderрезультат функции, где шаблон начинается с{render: function(){···}}Форма существует в родительском компонентеinlineTemplateв свойствах.
"_c('div',{attrs:{"id":"app"}},[_c(chooseTabs,{tag:"component",inlineTemplate:{render:function(){with(this){return _c('span',[_v(_s(test))])}},staticRenderFns:[]}})],1)"
наконецvnodeРезультаты также показывают, что,inlineTemplateОбъект останется в родительском компоненте.dataв свойствах.
// vnode结果
{
data: {
inlineTemplate: {
render: function() {}
},
tag: 'component'
},
tag: "vue-component-1-child1"
}
имеютvnodeПосле этого пришел к решающему заключительному шагу, по словамvnodeПроцесс генерации реальных узлов. Начиная с корневого узла, встречайтеvue-component-1-child1, пройдет процесс инстанцирования и создания подкомпонентов. Прежде чем создавать экземпляры подкомпонентов,inlineTemplateсвойства обрабатываются.
function createComponentInstanceForVnode (vnode,parent) {
// 子组件的默认选项
var options = {
_isComponent: true,
_parentVnode: vnode,
parent: parent
};
var inlineTemplate = vnode.data.inlineTemplate;
// 内联模板的处理,分别拿到render函数和staticRenderFns
if (isDef(inlineTemplate)) {
options.render = inlineTemplate.render;
options.staticRenderFns = inlineTemplate.staticRenderFns;
}
// 执行vue子组件实例化
return new vnode.componentOptions.Ctor(options)
}
Конфигурация параметров по умолчанию для подкомпонентов будет основана наvnodeВверхinlineTemplateшаблон получения атрибутаrenderфункция. На этом этапе анализа вывод ясен.Содержимое встроенного шаблона в конечном итоге анализируется в дочернем компоненте, поэтому неудивительно, что область действия дочернего компонента доступна в шаблоне.
12.3 Встроенные компоненты
Наконец сказатьVueИмеется в виду еще одна концепция, встроенные компоненты, на самом делеvueВ официальной документации перечислены встроенные компоненты, которыеcomponent, transition, transition-group, keep-alive, slot,в<slot>Мы подробно рассмотрели это в разделе игровых автоматов, иcomponentИспользование этого раздела также тратит много места для анализа от использования в принципе. Тем не менее, узналslot,componentПосле этого я начал осознаватьslotа такжеcomponentНе совсем встроенный компонент. **Встроенные компоненты — это компоненты, зарегистрированные глобально на этапе инициализации исходного кода. **а также<slot>а также<component>Он не рассматривается как компонент, поэтому жизненный цикл компонента отсутствует.slotтолько вrenderФункциональное преобразование фазыrenderSlotфункция для обработки иcomponentтолько с помощьюisсобственность будетcreateElementПервый аргумент преобразуется из строки в переменную и все. Итак, вернемся к концепции понимания, ** встроенные компоненты — это компоненты, предоставляемые самим исходным кодом, ** поэтому основное внимание в этой части будет уделено тому, когда регистрируются встроенные компоненты, и в чем разница, когда компиляция. Эта часть является лишь введением, и подробно ей будут посвящены две статьи.keep-alive,transition, transition-groupпринцип реализации.
12.3.1 Компоненты, определяемые конструктором
VueФаза инициализации будет в конструктореcomponentsДобавьте к атрибуту три объекта-компонента, и метод записи каждого объекта-компонента будет таким же, как наш метод записи в процессе пользовательского компонента.renderФункции имеют жизненный цикл, а также определяют различные данные.
// keep-alive组件选项
var KeepAlive = {
render: function() {}
}
// transition 组件选项
var Transition = {
render: function() {}
}
// transition-group 组件选项
var TransitionGroup = {
render: function() {},
methods: {},
···
}
var builtInComponents = {
KeepAlive: KeepAlive
};
var platformComponents = {
Transition: Transition,
TransitionGroup: TransitionGroup
};
// Vue构造器的选项配置,compoents选项合并
extend(Vue.options.components, builtInComponents);
extend(Vue.options.components, platformComponents);
extendМетод, который мы упоминали в начале серии, когда параметры анализа были объединены, объединяет атрибуты объекта с исходным объектом и перезаписывает те же атрибуты.
// 将_from对象合并到to对象,属性相同时,则覆盖to对象的属性
function extend (to, _from) {
for (var key in _from) {
to[key] = _from[key];
}
return to
}
наконецVueКонструктор имеет параметры конфигурации для трех компонентов.
Vue.components = {
keepAlive: {},
transition: {},
transition-group: {},
}
12.3.2 Регистрация встроенных компонентов
Просто иметь определение недостаточно. Компоненты должны использоваться глобально и должны быть зарегистрированы глобально, что на самом делеУглубленный анализ исходного кода Vue — слияние вариантов (ниже)Это было объяснено ясно. В процессе инициализации экземпляра Vue наиболее важным первым шагом является объединение параметров, а параметры класса ресурсов, такие как встроенные компоненты, будут иметь специальную стратегию объединения параметров.Параметры компонента в конечном конструкторе регистрируются в цепочке прототипов экземпляра в видеcompoonentsпараметры (то же самое для директив и фильтров).
// 资源选项
var ASSET_TYPES = [
'component',
'directive',
'filter'
];
// 定义资源合并的策略
ASSET_TYPES.forEach(function (type) {
strats[type + 's'] = mergeAssets; // 定义默认策略
});
function mergeAssets (parentVal,childVal,vm,key) {
var res = Object.create(parentVal || null); // 以parentVal为原型创建一个空对象
if (childVal) {
assertObjectType(key, childVal, vm); // components,filters,directives选项必须为对象
return extend(res, childVal) // 子类选项赋值给空对象
} else {
return res
}
}
Два ключевых шагаvar res = Object.create(parentVal || null);, он начнется сparentValСоздайте пустой объект для прототипа и, наконец, пройтиextendопределяемые пользователемcomponentОпции копируются в пустой объект. После объединения параметров встроенные компоненты также регистрируются глобально.
{
components: {
child1,
__proto__: {
keepAlive: {},
transition: {},
transitionGroup: {}
}
}
}
Наконец, мы видим, что нет встроенного объекта компонентаtemplateшаблон, ноrenderфункции, в дополнение к снижению производительности процесса синтаксического анализа шаблона, я думаю, что важная причина заключается в том, что встроенные компоненты не отображают объекты. Наконец, ждем продолженияkeep-aliveа такжеtransitionПринципиальный анализ, так что следите за обновлениями.
- Углубленный анализ исходного кода Vue — слияние опций (включено)
- Углубленный анализ исходного кода Vue — слияние вариантов (ниже)
- Углубленный анализ исходного кода Vue — прокси данных, связывание дочерних и родительских компонентов
- Углубленный анализ исходного кода Vue — монтирование экземпляра, процесс компиляции
- Углубленный анализ исходного кода Vue — полный процесс рендеринга
- Углубленный анализ исходного кода Vue — основа компонентов
- Углубленный анализ исходного кода Vue — расширенный компонент
- Углубленный анализ исходного кода Vue — построение адаптивной системы (включено)
- Углубленный анализ исходного кода Vue — построение адаптивной системы (посередине)
- Углубленный анализ исходного кода Vue — построение адаптивной системы (ниже)
- Углубленный анализ исходного кода Vue — приходите и реализуйте алгоритм сравнения вместе со мной!
- Углубленный анализ исходного кода Vue — демистификация механизма событий Vue
- Углубленный анализ исходного кода Vue - слоты Vue, все, что вы хотите знать, здесь!
- Углубленный анализ исходного кода Vue — понимаете ли вы синтаксический сахар v-model?