Akili — javascript фреймворк, который появился под влиянием таких решений как React, Angular, Aurelia и в меньшей степени некоторых других. Целью было объединить все лучшее, что я вижу в них и максимально все упростить.
Нравится React, но отталкивает JSX? Любите Angular, но надоела всякая магия?
Тогда вам стоит попробовать это.
Я убежден, что наилучший способ в чем-то разобраться это практика. Поэтому начну описание сразу же с примеров. Они написаны так, как если бы мы компилировали код с помощью Babel (es2015 + stage-0).
Первые шаги
import Akili from 'akili';
class MyComponent extends Akili.Component {
constructor(el, scope) {
super(el, scope);
scope.example = 'Hello World';
}
}
Akili.component('my-component', MyComponent); // регистрируем компонент
document.addEventListener('DOMContentLoaded', () => {
Akili.init(); // инициализируем фреймворк
});
<body>
<my-component>${ this.example }</my-component>
</body>
Здесь мы создали свой первый компонент, зарегистрировали его и инициализировали приложение. Обычный компонентный подход на первый взгляд, но сразу хотел бы отметить пару моментов.
Во-первых, область видимости компонента разделена от области видимости разметки. То есть, можно спокойно наследовать компоненты и это никак не отразиться на этой самой разметке.
class MySecondComponent extends MyComponent {
constructor(...args) {
super(...args);
this.scope.example = 'Goodbye World';
}
myOwnMethod() {}
}
Akili.component('my-second-component', MySecondComponent)
<body>
<my-component>${ this.example }</my-component>
<my-second-component>${ this.example }</my-second-component>
</body>
За область видимости разметки отвечает свойство компонента scope. Это специальный объект, который вы можете заполнить необходимыми данными и отображать их в шаблонах с помощью выражений вида
${ this.example }
, где this и есть этот самый scope. На самом деле в скобках может быть любое javascript выражение.Во-вторых, области видимости разметки также наследуются. Добавим в scope первого компонента новое значение:
class MyComponent extends Akili.Component {
constructor(el, scope) {
super(el, scope);
scope.example = 'Hello World';
scope.test = 'Test';
}
}
Тогда разметка ниже:
<body>
<my-component>
<b>${ this.example }</b>
<my-second-component>${ this.example } - ${ this.test }</my-second-component>
</my-component>
</body>
После компиляции будет выглядеть как:
<body>
<my-component>
<b>Hello World</b>
<my-second-component>Goodbye World - Test</my-second-component>
</my-component>
</body>
В-третьих, синхронизация логики компонента с его шаблоном происходит путем лишь изменения переменной scope в любой момент времени.
class MyComponent extends Akili.Component {
constructor(...args) {
super(...args);
this.scope.example = 'Hello World';
setTimeout(() => {
this.scope.example = 'Goodbye World';
}, 1000);
}
}
Через секунду значение переменной изменится и в объекте и в шаблоне.
Lifecycle в двух словах, в сравнении с React
.constructor(el, scope)
Прежде всего, поскольку любой компонент это простой javascript класс, будет вызван конструктор. Он получает в аргументы html элемент, к которому будет привязан компонент и объект scope. Здесь вы можете делать с элементом любые изменения, либо отменить компиляцию, в случаи необходимости, вызовом метода .cancel().
.created()
Если компиляция компонента не была отменена, то вы попадаете сюда. Этот метод фактически ничем не отличается от конструктора. В React, похожую функцию выполняет componentWillMount.
.compiled()
Здесь компонент скомпилирован, в шаблонах вместо выражений уже соответствующие значения.
В React это componentDidMount. Вы имеете доступ ко всем родительским элементам, которые к этому моменту гарантированно скомпилированы тоже.
.resolved()
Этот метод, насколько я знаю, не имеет аналогов в известных мне фреймфорках.
Дело в том, что Akili позволяет использовать при компиляции асинхронные операции, если это нужно. К ним относятся некоторые системные и любые кастомные операции. Например, загрузка шаблона компонента из файла:
class MyComponent extends Akili.Component {
static templateUrl = '/my-component.html';
constructor(...args) {
super(...args);
this.scope.example = 'Hello World';
}
}
Или любая асинхронная операция, которую мы выполним сами:
class MyComponent extends Akili.Component {
static templateUrl = '/my-component.html';
constructor(...args) {
super(...args);
this.scope.example = 'Hello World';
}
compiled() {
return new Promise((res) => setTimeout(res, 1000));
}
}
В методе compiled вы можете вернуть промис, тогда resolved будет ждать выполнения ВСЕХ асинхронных операций. При этом сама компиляции будет происходить синхронно.
Другими словами в методе resolved вы можете быть уверены, что скомпилированы абсолютно все дочерние элементы, любого уровня вложенности, в том, числе содержащие какие-либо асинхронные операции.
.removed()
Вызывается при удалении компонента. Аналог — componentDidUnmount.
.changed(key, value)
Вызывается при изменении любого атрибута компонента. Аналог — componentWillReceiveProps.
Это очень важная часть фреймфорка, поэтому опишу ее более подробно в отдельной секции ниже.
Универсальность, изоляция, модульность компонентов
Очень важно, чтобы компонент мог быть полностью изолирован и вообще не зависел от внешних условий. Вот пример такого компонента:
import Akili from 'akili';
class NineComponent extends Akili.Component {
static template = '${ this.str }';
static define() {
Akili.component('nine', NineComponent);
}
constructor(...args) {
super(...args);
this.scope.str = '';
}
compiled() {
this.attrs.hasOwnProperty('str') && this.addNine(this.attrs.str);
}
changed(key, value) {
if(key == 'str') {
this.addNine(value);
}
}
addNine(value) {
this.scope.str = value + '9';
}
}
Добавим его к предыдущим примерам:
import NineComponent from './nine-component';
NineComponent.define();
Akili.component('my-component', MyComponent);
document.addEventListener('DOMContentLoaded', () => {
Akili.init();
});
<body>
<my-component>
<nine str="${ this.example }"></nine>
</my-component>
</body>
Итак, вот что мы получим после компиляции:
<body>
<my-component>
<nine str="Hello World">Hello World9</nine>
</my-component>
</body>
Обратите внимание, NineComponent получился абсолютно обособленным. Он похож на функцию, которая может принимать какие-то аргументы и что-то с ними делать. В данном случаи просто добавляет цифру 9 в конец переданной строки и отображает ее.
Можно провести аналогию между атрибутами в Akili и свойствами в React.
this.attrs => this.props
. Они выполняют одну и туже роль, но есть мелкие различия: В Akili свойство attrs как и scope является Proxy, то есть можно добавить, изменить или удалить атрибут html элемента, делая соответствующие операции с каким-то свойством данного объекта. Свойства объекта attrs синхронизируются с атрибутами элемента.
Вы можете использовать атрибуты для биндинга. В примере выше, если переменная области видимости this.example компонента MyComponent изменится, то будет вызван метод changed у NineComponent. Обратите внимание, мы не сделали для этого ничего особенного. Выражение в атрибуте str ничем не отличается от примеров в начале, где мы просто отображали значение в шаблоне.
Для удобства можно использовать сокращенную версию changed.
class NineComponent extends Akili.Component {
changed(key, value) {
if(key == 'str') {
this.addNine(value);
}
}
}
class NineComponent extends Akili.Component {
changedStr(value) {
this.addNine(value);
}
}
Примеры выше эквиваленты. Чтобы не плодить гору ифов или кэйсов, проще писать сразу нужный метод. Принцип именования прост: changed + название атрибута кэмел кейсом с заглавной буквы.
События
Здесь все просто, добавляем тире после on, а дальше все как обычно. Изменим наш первоначальный пример:
class MyComponent extends Akili.Component {
static events = ['timeout'];
constructor(...args) {
super(...args);
this.scope.example = 'HelloWorld';
this.scope.sayGoodbye = this.sayGoodbye;
}
compiled() {
setTimeout(() => this.attrs.onTimeout.trigger(9), 5000);
}
sayGoodbye(event) {
console.log(event instanceof Event); // true
this.scope.example = 'Goodbye World';
}
}
<body>
<my-component on-timeout="${ console.log(event.detail); // 9 }">
<button on-click="${ this.sayGoodbye(event) }">say goodbye</button>
${ this.example }
</my-component>
</body>
Система событий основана на нативной. В примере выше видно, что вы также можете создавать и вызывать свои кастомные события.
Работа с массивами
class MyComponent extends Akili.Component {
constructor(...args) {
super(...args);
this.scope.data = [];
for (let i = 1; i <= 10; i++) {
this.scope.data.push({ title: 'value' + i });
}
}
}
<my-component>
<for in="${ this.data }">
<loop>${ this.loopIndex } => ${ this.loopKey} => ${ this.loopValue.title }</loop>
</for>
</my-component>
<my-component>
<ul in="${ this.data }">
<li>${ this.loopValue }</li>
</ul>
</my-component>
Дополнительно
Из коробки Akili также имеет роутер, библиотечку для совершения ajax запросов, множество системных компонентов для работы с циклами, формами, возможность прикрутить серверный рендеринг и.т.д, в документации вы можете найти подробное описание.
Данная статься написана чтобы познакомить вас с Akili, я постарался раскрыть в целом какие-то технические моменты, но здесь не уместилась даже пятая часть того, что в себе содержит фреймворк. Гораздо больше информации есть в документации, ну и если будет интерес, то начну раскрывать тему глубже в других статьях.
Фреймворк пока в бете, пробуйте, смотрите )
Комментарии (67)
teslitsky
26.06.2017 20:31+6Если хочется React + Angular не лучше ли взять Vue.js?
PYXRU
26.06.2017 20:38+4<for in="${ this.data }"> <loop>${ this.loopIndex } => ${ this.loopKey} => ${ this.loopValue.title }</loop> </for>
Синтаксис так себе, интуитивно не понятный, по сути скрестили кота с ежом без без поддержки кота и ежа. И вызовы конструктора базового класса, тоже не удобно, нужны обертки. Сверху правы берите Vue и не мучайтесь
IsOrtex
26.06.2017 20:50Весь синтаксис это доллар и фигурные скобки в стиле es6, причем во всех кейсах )
Kroid
26.06.2017 21:25+5Именно! Vue — это то, каким должен был быть ангуляр.
AnneSmith
06.07.2017 13:28-1все популярные фреймворки суть одно и то же: крайне не организованный код и hardcoded html, которые невозможно использовать в другом проекте
графический интерфейс очень прост и имеет простые стандартные элементы и функционал, поэтому кодирование бизнес логики не должно быть таким сложным
unsafePtr
26.06.2017 22:08-2Увидел stage-0 перестал читать дальше. Если stage-0 пускать в продакш, то так далеко не уехать.
justboris
27.06.2017 02:23Подозреваю, что автор просто не в курсе, что
class fields
уже давно не stage-0, а 2. Стадии показываются тут. Stage-2 будет достаточно, чтобы код из статьи собрался.
Да и вообще, набор es2015+stage-0 это очень странно. То есть мы берем 2015, пропускаем 2016, 2017 (которые уже тоже часть стандарта и есть в некоторых браузерах), зато берем всякую дичь из stage-0.
Сам Babel рекомендует использовать babel-preset-env, который будет обновляться по мере добавления новых фич в стандарт, и также будут выпиливаться преобразования для штук, которые уже нативно есть в браузерах (список поддерживаемых браузеров настраивается)
Akuma
26.06.2017 22:21Сайт лежит в 502-й. Видно не ожидали вы местного наплыва :)
По теме: первое впечатление — Vue.js.
Но, честно говоря, мне больше нравится именно React на который я пересел с Ангуляра (первого). В особенности нравится JSX за его 100% совместимость с JS. А все эти прилепленные директивы и циклы через html жутко неудобны, как по мне.
В статье вы сначала пишите, что скрестили Ежа с Коноплей, но потом эта мысль как-то сразу теряется. В чем преимущества перед обоими фреймворками? Их потому и два, что они офигеть какие разные. И не нужно это менять.
Кстати, везде в конструкторе присутствет super(...args);
Нужно этого как-то избегать, либо добавить аналог componentDidMount(), а то забыл поставить — и что будет?IsOrtex
27.06.2017 06:38сайт лежал — обнаружил memory leak в jsdom, буду искать решение.
аналог componentWillMount — created
аналог componentDidMount — compiled
То есть конструктор можно вообще и не писать.
justboris
27.06.2017 02:05Про шаблоны совсем ничего не написано. Как написать такой компонент, который из этого html:
<hello-component name="Tester" description="test user" />
сделает вот такой:
<div> <h1>Hello, tester!<h1> <p>test user</p> </div>
?
IsOrtex
27.06.2017 06:40class HelloComponent extends Akili.Component { static templateUrl = './path/to/template'; }
или
class HelloComponent extends Akili.Component { static template = `<div> <h1>Hello, tester!<h1> <p>test user</p> </div>`; }
justboris
27.06.2017 10:26А что будет, если внутри HelloComponent будет разметка:
<hello-component>Текст внутри</hello-component>
Шаблон ее перезапишет или добавится в конец?
IsOrtex
27.06.2017 13:53перезапишет, но если содержимое нужно куда-то вставить, то можно использовать свойство скоупа .__content
class HelloComponent extends Akili.Component { static template = `<div> <h1>Hello, tester!<h1> <p>${ this.__content }</p> </div>`; }
В приме выше выражение ${ this.__content } заменится на Текст внутри
justboris
27.06.2017 02:13.resolved() Этот метод, насколько я знаю, не имеет аналогов в известных мне фреймфорках.
Не имеет аналогов, не потому что их разработчики не догадались об этом, а потому, что надо что-то показывать, пока компонет загружает свои зависимости (спиннер, например).
В React это делается тривиально
class ComponentWithLoader extends React.Component { state = { loading: false, data: null }; componentDidMount() { this.setState({loading: true}) loadData().then((data) => this.setState({loading: false, data: data})); } render() { if(this.state.loading) { return <Loader /> } return <div>{/*какой-то контент*/}</div> } }
При желании можно завернуть в переиспользуемый компонент, или взять уже готовый.
Тащить это в ядро фреймворка смысла нет.IsOrtex
27.06.2017 06:47Смысл все же есть. Бывают, например, группы компонентов, когда их несколько и они завязаны друг на друге. Например посмотрите реализацию табов.
Родительский компонент может захотеть что-то сделать с дочерними. Метод resolved гарантирует, что в нем все дочерние будут полностью скомпилированы и тогда к ним можно обратиться через систему коммуникации компонентов.justboris
27.06.2017 10:29Посмотрел на реализацию табов, понятнее не стало. В исходном коде нет ни одной асинхронной операции, откуда там resolved нужен, совсем не ясно
IsOrtex
27.06.2017 15:49Дело в том что компиляция идет сверху вниз по дереву, элемент за элементом, поэтому если нужно что-то сделать с дочерними элементами из родительского после его компиляции, то мы также используем resolved. В нем все дочерние компоненты будут гарантировано скомпилированы, в этом его смысл.
В целом, метод resolved используем когда нужен доступ к детям.
Такой лайфцикл позволяет работать с компонентами, почти так как это происходит в DOM:
В примерах ниже левая и правая части эквивалетны:
el.querySelector('input') => component.child('input')
el.querySelectorAll('input') => component.children('input')
el.parentNode => component.parent()
Разница только в том, что все методы справа возвращают не элемент, а компонент.
В компонентах Akili есть много разных методов для взаимодействия друг с другом
class ParentComponent extends Akili.Component { resolved() { let childComponent = this.child('child'); console.log(childComponent.scope.example); // 'Hello World' childComponent.scope.example = 'Goodbye World'; // в разметке значение также поменяется } } class ChildComponent extends Akili.Component { compiled() { scope.example = 'Hello World'; } } Akili.component('parent', ParentComponent); Akili.component('child', ChildComponent);
<parent> <child>${ this.example }</child> </parent>
mayorovp
27.06.2017 15:52childComponent.scope.example = 'Goodbye World';
— про инкапсуляцию, видимо, никто не слышал… Ну нельзя же такие вещи приводить в качестве примера!IsOrtex
27.06.2017 16:18А в чем тут проблема? Компонент это по сути экземпляр класса, а scope это public property.
lega
27.06.2017 17:18+1По хорошему, компонент должен быть «черным ящиком с парой кнопок» с мнимальным интерфейсом, что-б никто не мог внутрях ковырятся, иначе это будет повышать риск хаоса.
vintage
27.06.2017 08:38-2А можно не заниматься ерундой с ручным управлением индикаторами ожидания, воспользовавшись реактивным программированием.
Ваш, код мог бы выглядеть примерно так:
class ComponentWithLoader extends MyComponent { @ $mol_mem() render() { return $mol_http.request( '/data.json' ).json().map( item => <Item item={ item } /> ) } }
justboris
27.06.2017 10:29+1Так а что будет видеть пользователь, пока
data.json
загружается?vintage
27.06.2017 14:02-1Индикатор ожидания, очевидно. Не нравится стандартный, всегда можно запилить свой:
class ComponentWithLoader extends MyComponent { @ $mol_mem() render() { try { return $mol_http.request( '/data.json' ).json().map( item => <Item item={ item } /> ) } catch( error ) { return <StatusIndicator error={ error } /> } } }
justboris
27.06.2017 15:29Что-то это еще больше все запутывает
catch
блок — это про ошибку после загрузки. А в процессе что будет показываться?mayorovp
27.06.2017 15:37У него в фреймворке состояние "в процессе" считается исключительным. Метод
render
вычисляется два раза, первый раз отваливается с исключением, а второй раз запускается автоматически когда запрос выполнится. В этот раз методrequest
не будет делать нового запроса к серверу, а вернет значение, полученное в прошлый раз.
По-своему красивая идея — жаль только метод
request
делает не то как называется.justboris
27.06.2017 15:40Метод с аннотацией
mol_mem
(мемоизация, как я предполагал), вызывается несколько раз и выдает разный результат?
Извините, но мне с таким подходом точно не по пути.
mayorovp
27.06.2017 15:42Ну, по задумке, это мемоизация с автоматической инвалидацией и уведомлениях об изменении. Хотя я тоже считаю, что стоило бы назвать его как-то по-другому.
В Knockout такая штука называется pureComputed, в MobX — computed.
vtvz_ru
27.06.2017 06:35-3Как по мне, jsx шикарен, а сам react невероятно элегантен. Нет ни одной задачи, которой я не смог бы решить с его помощью или с помощью библиотеки, которую можно к нему прикрутить.
В любом случае, библиотека хорошая, "но использовать я ее, конечно, не буду". По крайней мере к реакту у меня доверия всяко больше. Как минимум, ответов на stack overflow больше.
Желаю дальнейших успехов в разработке)
alexandzolotarev
27.06.2017 06:35-1Это всё конечно прекрасно, только вот не совсем понятно зачем всё это. Кто хотел получить Angular + React, получили Vue, иногда мне кажется, что она только для этого и придумана была. Но это, очередной проходной фреймворк.
napa3um
27.06.2017 07:36У вас получился Polymer. Пока чуть менее сложный, но это вы ещё до dom-repeat, обработки массивов, атомарных изменений нескольких полей и оптимизации вычисляемых байндингов не дошли. Ну и без Shadow DOM, HTML-imports и изоляции CSS у вас реализация. А так — один в один «стиль» «фреймворка» (правильнее — библиотеки). Polymer себя позиционирует таки компонентной библиотекой, и вам следует тоже, ибо «простота» и «отсутствие магии» — это и есть отсутствие навязываемой жёсткой структуры проекта, предлагается лишь способ «нарезки» кода на компоненты, а архитектуру из этих компонентов программисту придётся сочинять самостоятельно. Ну и ценность таких библиотек зависит от их заполненности «подходящими» друг к другу как пазлинки готовыми базовыми компонентами, и тут Polymer вас тоже сильно опережает, простой чат (при готовом бэке) можно собрать вообще без кода, только путём связывания атрибутов готовых компонентов в разметке друг с другом (там есть «невизуальные» компоненты типа компонента обеспечивающего роутинг или AJAX-запросы к серверу).
Призываю присоединиться к Polymer, работы там ещё куча :).napa3um
27.06.2017 07:40(Прошу прощения, dom-repeat и массивы у вас поддерживаются, роутинг и аякс-запросы тоже в готовых компонентах предложены, поторопился комментировать — теперь я уверен на 100%, что вы вдохновлялись Полимером :))
mayorovp
27.06.2017 09:14Что-то, как мне кажется, тут была нарушена сама суть понятия "компонент". Компонентом применительно к UI обычно называют обособленную часть интерфейса, обладающую своим [внешним] видом, состоянием и поведением.
Здесь же "шаблонная" часть компонента вытащена наружу, компоненту остались только состояние и поведение. Это уже не компонент получается, а контроллер из старого AngularJS.
napa3um
27.06.2017 09:30Компонент = логика + шаблон + имя. По имени один компонент может использовать в своём шаблоне другие компоненты. Всё нормально тут с компонентностью, вроде. (Упомяну опять «свой любимый» Polymer, в нём благодаря HTML-imports действительно более «железобетонно» «нарезаются» компоненты, HTML является контейнером и для разметки, и для скриптов, и для стилей, которые, конечно, можно разнести и на отдельные файлы, оставив в HTML ссылки на них. Плюс ко всему этому получается проще сборка — tree-shaking становится действительно tree — сборщик-«вулканизатор» напрямую из построенного импортами DOM-дерева получает всю иерархию зависимостей без необходимости анализировать JS-код.)
mayorovp
27.06.2017 09:55Компонент = логика + шаблон + имя.
Покажите мне шаблон у вот этого компонента:
class MyComponent extends Akili.Component { constructor(el, scope) { super(el, scope); scope.example = 'Hello World'; } }
napa3um
27.06.2017 10:09+1Ничего страшного, если шаблон является опциональным аспектом компонента (для организации чего-нибудь типа HOC из Реакта или Behaviors из Полимера, например :)). Компонент не обязан быть визуальным.
i360u
27.06.2017 10:20+2Для решения подобных задач, навскидку, на ум приходят варианты сделать это с помощью нативных кастомных элементов или Polymer 2 или Vue.js ...etc. Что я должен знать о Akili, чтобы рассматривать его в качестве одной из альтернатив? В представленном описании я ничего такого не увидел, что я упускаю?
raveclassic
27.06.2017 10:46+3- C react сравнили, с angular тоже, а по что aurelia обидели?
- Какие проблемы вы решали и решили?
- Какие будут аргументы против фразы "yet another framework"?
- Что с перфомансом, где бенчи?
- Ну и, наконец, где работающий туду-лист?
lega
27.06.2017 10:46Через секунду значение переменной изменится и в объекте и в шаблоне.
Как отслеживается изменения?
Работают и отслеживаются ли «js» выражения в шаблоне?, наподобии${a+b + ' ' + foo(c) + bar()}
Зачем нужен scope если есть this? особенно если оно «смешивается» в шаблоне.
<for in="${ this.data }">
Можно ли сделать вложенный цикл и использовать данные родительского цикла во внутреннем?IsOrtex
27.06.2017 11:44+1Как отслеживается изменения?
Изменения отслеживаются с помощью механизма Proxy.
Работают и отслеживаются ли «js» выражения в шаблоне?
Да, отслеживаются все выражения где есть хоть какая-то переменная скоупа
Зачем нужен scope если есть this?
this в html это и есть scope. Поскольку скоуп был придуман как область видимости разметки, то в ней он является контекстом. В компоненте же есть ссылка на на него, в свойстве scope.
Можно ли сделать вложенный цикл и использовать данные родительского цикла во внутреннем?
Да, каждый скоуп имеет свойство .__parent — ссылку на родительский
Также, подробное описание всех возможностей, включая некоторые тонкости, можно найти в документации.lega
27.06.2017 14:36+1Изменения отслеживаются с помощью механизма Proxy.
ok, оно есть в 72% браузеров, в IE вероятно не появится никогда (в edge оно есть).
this в html это и есть scope.
Выглядит как лишняя сущность, было бы красивее разместить данные в this он бы и был this в html.
Да, отслеживаются все выражения где есть хоть какая-то переменная скоупа
Даже не так — если меняется любая отслеживаемая переменная, то вы делаете полный dirty-checking — проверяете все отслеживаемые переменные. При этом если данные в scope меняются, которые выводятся через функцию ${this.fullName()} — то обновления не происходит, недоработка.
Итого: Идет проверка всего на каждое изменение — повышенная нагрузка, хотя Proxy позволяет реагировать точечно, с другой стороны эта «проверка всего» пропускает некоторые типы биндингов в html — хотя у vue.js такая же проблемма, видимо не сильно напрягает, и третье Proxy не поддерживается больщой долей браузеров, как результат — рановато для боя.
Но для старта неплохо, все фреймворки с чего-то начинали.
Кстати, если при вызове выражения биндингов будете передавать this как параметр вместо подмены this, то они будут работать на прядок быстрее.IsOrtex
27.06.2017 16:02Выглядит как лишняя сущность, было бы красивее разместить данные в this он бы и был this в html.
Не совсем, в выражениях может быть что угодно, хоть ${ console.log('Hello') } хоть ${ window.location.href }
Область видимости по умолчанию весь window. Если нужно изменить это поведение, то достаточно дописать метод Component.parse как вам надо.
Даже не так — если меняется любая отслеживаемая переменная, то вы делаете полный dirty-checking — проверяете все отслеживаемые переменные. При этом если данные в scope меняются, которые выводятся через функцию ${this.fullName()} — то обновления не происходит, недоработка.
Данные которые будут в функции тоже начнут отслеживаются. Слежка происходит не на уровне парсинга этой строки. Тут все гораздо хитрее.
И еще момент, хоть отслеживается и каждое свойство скоупа отдельно, но синхронизация с шаблоном происходит группами. То есть если пройтись циклом и изменить 1000 значений в скоупе, то произойдет всего 1 операция изменения разметки, а не 1000.
lega
27.06.2017 17:12+1Данные которые будут в функции тоже начнут отслеживаются. Слежка происходит не на уровне парсинга этой строки. Тут все гораздо хитрее.
Что-то не работает: http://jsfiddle.net/lega911/kw3f2mx2/ — fullName не обновляется.
Странно, в другом примере у меня было полное сканирование, в этом нет.
Область видимости по умолчанию весь window.
Это не желательно, т.к. в шаблон может попасть всякая бяка из вне.IsOrtex
27.06.2017 18:50-1Да, по поводу биндинга функции в шаблоне вы правы. Я в своем время долго решал какой вариант оставить. Почему-то запомнилось как будто оставил вариант с учетом всех вызовов.
Сейчас прикинул все варианты и вернулся к тому же. Выполнять функцию снова все-таки лучше при изменении аргументов, а не содержимого тела.
IsOrtex
27.06.2017 20:23Спасибо за концентрацию внимания на этом моменте, я добавил возможность биндинга функции в шаблоне и по изменению аргументов и по телу. Теперь ваш пример должен работать.
XeL077
27.06.2017 13:36-1Посмотрел примеры, вы создали странный vue 2.
Это было написано просто для веселья?
Возможно, Вам стоит пересмотреть парадигму разработки, придумать что-то новое, мне это кажется интереснее, чем соединять готовые фреймворки.
ju5tify
27.06.2017 14:06Нравится React, но отталкивает JSX? Любите Angular, но надоела всякая магия?
Vue.js вам в помощь.
Кстати, в Ангуляре (который .io), как по мне, минимум магии. В отличии от оверхайпнутого Реакта.
serf
Как в этом случае добавляются/описываются дополнительные зависимости для MySecondComponent?
Прототайпинг скоупа как раз ангуляровская фишка (точнее обычный JS прототайпинг), от которой лучше отказаться, так же лучше отказаться от любых вещей завязанных на специфику фреймворка, передавать все явном виде через атрибуты. Компоненты по-моему мнению компоненты должны быть независимыми/изолированными.
IsOrtex
1) Не совсем понял, что имеется ввиду под дополнительными зависимостями?
2) Тут согласен, но это скорее просто бонус, разные ситуации бывают. Про то как сделать компоненты модульными я в статье описал.
alexiusp
имеется в виду ангуляровское внедрение зависимостей — Dependency Injection
serf
Верно, вот пример возможной реализации https://habrahabr.ru/company/ncloudtech/blog/321584/#comment_10243970