Разработка больших приложений


Vue
Webpack
Кирилл Кайсаров
github.com/markuplab
http://vuejs.org
http://webpack.github.io
Минутка ностальгии
Какими были Javascript приложения
в период с 2006 по 2015 год
Write Less / Do More
Эра jQuery
$(element) - Основная структурная единица
1
Все манипуляции ориентированы на DOM ноды
Plugin основная единица экосистемы
2
Большинство популярных решений имели избыточный объем кода
Server Side Rendering
3
Получение отрендеренного html'a один из классических подходов эры
Model View Controller
ЭРА MVVC / MVC / VM / MC / MV / VC / ETC...
Plain JS Object - Основная архитектурная единица
1
new Constructor(), Object.prototype, Object.extend({}), итд...
Client-Side Rendering
2
Избавление сервера от дополнительной работы с шаблонами + гибкость
Триумф AMD
3
Загрузка модулей через AJAX, явные зависимости, и сборка на Java
Утром Data - Вечером HTML
4
Тренд на Dom-First Apps резко изменился на Data-First Apps
Наше время
Компонентная эра
Компоненты как архитектурная единица
1
Теперь модуль это не только Javascript. Все asset'ы внутри.
Триумф CommonJS
2
NPM, Модули для сервера и браузера, browserify, изоморфность.
Реактивное и асинхронное программирование
3
Promise, Generators, NextTick, Pipes
Большая нагрузка на клиента - почва для оптимизаций
4
Ленивая загрузка, разделение приложения на части, benchmarking
Vue.JS

Основные принципы

Построен на принципах реактивного программирования
Основные принципы
1
Vue.nextTick(), Queue, Reactive Directives
Поддерживает концепцию Web Components
2
V-component, Vueify, Component Registry, Custom Directives
Сущность это простой (plain) Javascript объект
3
Template as HTML String, Component as Javascript Object

Единая сущность Vue
Основные принципы
4
Объект содержит в себе $data, $methods, $events итд...
Дружелюбен к модульным системам
5
Простая интеграция с Common.JS и другими модульными системами
Webpack
Основные принципы

Основные принципы

1
Module is Everything
HTML строка, CSS таблица, Javascript функция, нет разницы.
2
Только Javascript? Нет.
Webpack умеет собирать не только Javascript, также графику, шрифты итд...
3
Ленивая / Частичная загрузка из пакета
Важный инструмент для больших приложений без излишних расширений
Основные принципы

4
Ориентирован на Client-Side приложения
Hot Module Replacement, JSONP, и другие браузерные решения
5
AMD / CommonJS / Native
Нет разницы какую систему модулей вы используете
Какие задачи стоят
перед современными
Client-Side приложениями?

Технические требования приложения

Расширяется как в ширину так и в высоту
Приложение состоит из модулей
Приложение может быть интернационализировано
Приложение может быть менять свой внешний вид на разных платформах
Время для котикадинга


/** Module dependencies */
var Vue = require('vue');
/** Define application */
var app = module.exports = new Vue({
el: '#v-app'
});
Точка входа в приложение
doctype html
html
head
title Simple application
body#v-app
script(src='/public/build.js')
Построим HTML Layout
$ sudo npm i webpack -g
Устанавливаем Webpack
module.exports = {
entry: {
app: "./components/app.js"
},
output: {
path: "./public/build",
filename: "app.js"
}
}
Описываем webpack.config.js


$ webpack -p
Hash: f2f8823c3ad505796c0d
Version: webpack 1.4.15
Time: 3230ms
Asset Size Chunks Chunk Names
app.js 59934 0 [emitted] app
+ 63 hidden modules
Собираем наше приложение


Создаем первый компонент
doctype html
html
body#v-app
#v-panel(v-component="panel")
Объявляем в Layout
Регистрируем компонент в нашем приложении
/** Define application */
var app = module.exports = new Vue({
el: '#v-app',
components: {
panel : require('./panel')
}
});


Описываем компонент
/** Define component */
module.exports = {
template: '<div class="v-panel">Я панель по имени {{ firstName }} '
+ 'и фамилии <span v-html="lastName"></span></div>',
data: function(){
return {
firstName: 'Петя'
}
},
created: function() {
this.$set('lastName', 'Петров');
}
};
Создаем первый компонент


Создаем первый компонент

Результат


Капля оптимизации
/** Define component */
module.exports = {
template: require('./template.html'),
data: function(){
return {
firstName: 'Петя'
}
},
created: function() {
this.$set('lastName', 'Петров');
}
};
Создаем первый компонент


/** Define component */
module.exports = {
// Как загрузить HTML в require?!
template: require('./template.html')
};
Опишем webpack.config.js
module.exports = {
module: {
loaders: [
{ test: /\.html$/, loader: "html" }
]
}
};
Создаем первый компонент
Капля оптимизации
Webpack Loaders
Загрузчики это функции которые выполняют трансформацию файлов в модули приложения. Аналогичны browserify transform.


/** Define component assets */
require('./styles.less')
/** Define component */
module.exports = {
template: require('./template.html')
};
Дополним наш webpack.config.js загрузчиком стилей
module: {
loaders: [
{ test: /\.html$/, loader: "html" },
{ test: /\.less$/, loader: "style!css!less" }
]
}
Создаем первый компонент


Результат template.html
module.exports = "<div class=\"v-panel\">\n Я панель по имени {{ firstName }}
и фамилии\n
<span v-html=\"lastName\"></span>\n</div>\n";
/***/
function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(32)();
exports.push([module.id, "span {\n color: #999;\n}\n", ""]);
/***/ }
Результат styles.less
Создаем первый компонент


Создаем первый компонент

Результат


Технические требования приложения
Расширяется как в ширину так и в высоту
Приложение состоит из модулей
Приложение может быть интернационализировано
Приложение может быть менять свой внешний вид на разных платформах
Добавим немного динамики


Controller - компонент отвечающий за состояние приложения
Содержит в себе все нужные для работы компоненты
1
Является точкой разрыва приложения. App -> Controller -> Components
2
Controller определяется в зависимости от состояния URL
3
Схема сбора и запуска приложения
Client
App
URL
State
Controller
Component
Component
Component
Controller
Controller
Контроллер - место где наше приложение начинает ответвляться от приложения. Именно тут мы можем разделить наше приложение на части.
Для решения этой задачи подойдет require.ensure
представленный в CommonJS спецификации.
Webpack поддерживает этот метод.
http://wiki.commonjs.org/wiki/Modules/Async/A
require.ensure(['increment'], function(require) {
var inc = require('increment').inc;
var a = 1;
inc(a); // 2
});
Синтаксис:


Создадим явную карту наших контроллеров
/** Routes map */
var map = {
main: require('./controllers/main'),
alt: require('./controllers/alt')
};
Создаем контроллеры
Проблематика:
Основной проблематикой клиентских загрузчиков модулей является "неумение" работать с динамическими аргументами require(). В текущих реализациях мы вынуждены указывать имена модулей явно. Node.JS умеет подтягивать модули динамически.


Делаем нашу карту модулей ленивой
/** Routes map */
var map = {
main: require('promise?bluebird!./controllers/main'),
alt: require('promise?bluebird!./controllers/alt')
};
Создаем контроллеры
Альтернативный вариант вызова Webpack Loaders
Для трансформации модулей можно использовать дополнительные строковые параметры внутри require() отделив их через знак "!". В указаном выше случае мы загружаем модуль пропуская его через promise-loader.


Немного о Promise-Loader
Создаем контроллеры
Трансформация этим модулем позволяет сделать отложенный require.ensure и обернуть его в Promise объект.
// Указываем библиотеку для обертки в Promise
var load = require("promise?bluebird!./file.js");
// Модуль не будет загружен пока вы не вызовете функцию
load().then(function(module) {
// Здесь вы можете работать с вашим модулем
});


Контроллеры выделены в отдельные файлы
$ webpack
Hash: 5fa98cfa191aa5dafdb4
Version: webpack 1.5.3
Time: 827ms
Asset Size Chunks Chunk Names
app.js 349774 0 [emitted] app
1.1.js 287 1 [emitted]
2.2.js 294 2 [emitted]
+ 78 hidden modules
Создаем контроллеры


Результат
Создаем контроллеры




Указываем элемент в котором расположим контроллер
doctype html
html
head
title Simple application
body#v-app
#v-panel(v-component="panel")
#v-controller(v-el="controller")
script(src='/public/build.js')
Создаем контроллеры


Загружаем и запускаем контроллер
module.exports = new Vue({
el: '#v-app',
created: function() {
var controller = url.getController();
var load = map[controller]();
load.then(function (module) {
var ctrl = new Vue(module);
ctrl.$mount(this.$$.controller);
}.bind(this));
}
...
});
Создаем контроллеры
Связать управление контроллерами с HTML5 History API
Следующие шаги
1
Состояние приложения можно хранить в this.$data
Спроектировать ленивую загрузку внутри контроллера
2
Актуально для больших контроллеров
3
Как инструмент дополнительной оптимизации
Использовать ApplicationCache и Deduplication Plugin


Технические требования приложения
Расширяется как в ширину так и в высоту
Приложение состоит из модулей
Приложение может быть интернационализировано
Приложение может быть менять свой внешний вид на разных платформах


module.exports = {
template: require('./templates/' + platform + '.html')
};
Динамическая загрузка модулей
- templates
- ios.html
- android.html
- windows.html
Отлично подходит для кросс-платформенных решений
Другие возможности


Динамическая загрузка модулей
Другие возможности
При такой загрузке в сборку попадут все модули из папки ./templates с форматом .html, а также внутри загрузчика появиться карта соответствия имени файла, id'шнику модуля.


Технические требования приложения
Расширяется как в ширину так и в высоту
Приложение состоит из модулей
Приложение может быть интернационализированно
Приложение может быть менять свой внешний вид на разных платформах


var messages = require("json!po!./locale/en_US/LC_MESSAGES/messages.po");
Загрузка интернациональных пакетов
Другие полезные загрузчики
- autoprefixer
- mocha
- handlebars
- 6to5
- file
- json
Другие возможности


Технические требования приложения
Расширяется как в ширину так и в высоту
Приложение состоит из модулей
Приложение может быть интернационализировано
Приложение может быть менять свой внешний вид на разных платформах


Вопросы???

Copy of deck
By social4hyq
Copy of deck
- 859