Разработка больших приложений
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 Rendering3 Получение отрендеренного html'a один из классических подходов эры
Model View Controller ЭРА MVVC / MVC / VM / MC / MV / VC / ETC...
Plain JS Object - Основная архитектурная единица1 new Constructor(), Object.prototype, Object.extend({}), итд...
Client-Side Rendering2 Избавление сервера от дополнительной работы с шаблонами + гибкость
Триумф AMD3 Загрузка модулей через AJAX, явные зависимости, и сборка на Java
Утром Data - Вечером HTML4 Тренд на Dom-First Apps резко изменился на Data-First Apps
Наше время Компонентная эра
Компоненты как архитектурная единица1 Теперь модуль это не только Javascript. Все asset'ы внутри.
Триумф CommonJS2 NPM, Модули для сервера и браузера, browserify, изоморфность.
Реактивное и асинхронное программирование3 Promise, Generators, NextTick, Pipes
Большая нагрузка на клиента - почва для оптимизаций4 Ленивая загрузка, разделение приложения на части, benchmarking
Vue.JSОсновные принципы
Построен на принципах реактивного программирования
Основные принципы
1 Vue.nextTick(), Queue, Reactive Directives
Поддерживает концепцию Web Components2 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 EverythingHTML строка, 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 htmlhtml 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: f2f8823c3ad505796c0dVersion: webpack 1.4.15Time: 3230ms Asset Size Chunks Chunk Namesapp.js 59934 0 [emitted] app + 63 hidden modules
Собираем наше приложение
Создаем первый компонент
doctype htmlhtml 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.jsmodule.exports = { module: { loaders: [ { test: /\.html$/, loader: "html" } ] }};
Создаем первый компонентКапля оптимизации
Webpack LoadersЗагрузчики это функции которые выполняюттрансформацию файлов в модули приложения.
Аналогичны browserify transform.http://webpack.github.io/docs/using-loaders.html
/** 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.htmlmodule.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 -> Components2Controller определяется в зависимости от состояния URL3
Схема сбора и запуска приложения
Client App URLState
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 объект.
// Указываем библиотеку для обертки в Promisevar load = require("promise?bluebird!./file.js"); // Модуль не будет загружен пока вы не вызовете функциюload().then(function(module) { // Здесь вы можете работать с вашим модулем});
Контроллеры выделены в отдельные файлы
$ webpackHash: 5fa98cfa191aa5dafdb4Version: webpack 1.5.3Time: 827ms Asset Size Chunks Chunk Namesapp.js 349774 0 [emitted] app1.1.js 287 1 [emitted] 2.2.js 294 2 [emitted] + 78 hidden modules
Создаем контроллеры
Результат
Создаем контроллеры
Указываем элемент в котором расположим контроллер
doctype htmlhtml 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
Другие возможности
Технические требования приложения
Расширяется как в ширину так и в высоту
Приложение состоит из модулей
Приложение может быть интернационализировано
Приложение может быть менять свой внешнийвид на разных платформах
Вопросы???