Наша команда работает над интерфейсом BILLmanager 6. Дабы вы имели общее представление о проекте до обновления, сообщу, что количество файлов в нем уже перевалило за 67 тысяч. Архитектурно можно выделить два подпроекта: модуль регистрации и основной пользовательский интерфейс. По технологиям основу составляют компоненты, директивы и модули Angular, написанные на TypeScript. Есть несколько компонентов на Web components. Для стилизации мы используем SASS/SCSS и применяем CSS variables, чтобы темизировать приложение без перекомпиляции.
Предпосылки
У всего есть причины, и наши текущие трудности получили свое начало полтора года назад. Тогда только появилась beta Angular 2. У программистов в компании был опыт создания приложений на Angular 1, ReactJS и собственном небольшом фреймворке. Angular 2 на тот момент вобрал в себя плюсы из первой версии и ReactJS. Поэтому и был выбран в силу своей перспективности (как никак Google), успешности Angular 1 и формализации, которую дает TypeScript. Мы не пишем небольшие SPA сайты, которые можно отдать заказчику и забыть, наши приложения живут долго и им нужна постоянная поддержка и развитие. BILLmanager используют провайдеры для продажи хостинга и работы с клиентами. Поэтому и его, и другие продукты ISPsystem, нужно постоянно поддерживать и развивать. В принципе, команда Angular 2 уже тогда везде писала, что теперь будет просто Angular и развитие фреймворка будет происходить эволюционно, что подходит для наших внутренних процессов.
Как я уже писал, проекты у нас большие и долгоживущие. У них сложные конфиги с гибкими настройками под отдельные сборки. А Webpack давно является своего рода стандартом для сборки больших и маленьких проектов в мире frontend, поэтому здесь выбор был однозначным.
В итоге общая часть конфига в проекте выглядела следующий образом:
module.exports = {
context: PATHS.root,
target: 'web',
entry,
resolve: {
extensions: ['.ts', '.js', '.json'],
modules: [PATHS.src, PATHS.node_modules],
},
module: {
rules: [{
test: /\.ts$/,
loaders: [{
loader: 'awesome-typescript-loader',
options: {
transpileOnly: process.env.NODE_ENV !== 'production'
}
},
'angular2-template-loader',
'angular2-router-loader'
],
exclude: [/\.(spec|e2e)\.ts$/],
},
{
test: /\.ts$/,
include: [/\.(spec|e2e)\.ts$/],
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
{
test: /\.json$/,
use: 'json-loader'
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
}],
},
{
test: /\.(eot|woff|woff2|ttf|png|jpg|gif|svg|ico)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
options: {
context: PATHS.assets,
name: '[path][name].[ext]'
},
},
{
test: /\.css$/,
loader: extractSASS.extract({
fallback: 'style-loader',
use: 'css-loader?sourceMap'
}),
exclude: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
},
{
test: /\.css$/,
include: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
use: [{
loader: "raw-loader" // creates style nodes from JS strings
}],
},
{
test: /\.(scss|sass)$/,
loader: extractSASS.extract({
use: [{
loader: "css-loader",
},
{
loader: "sass-loader",
options: {
sourceMap: true,
}
}
],
// use style-loader in development
fallback: "style-loader"
}),
exclude: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
},
{
test: /\.(scss|sass)$/,
use: [{
loader: "raw-loader" // creates style nodes from JS strings
},
{
loader: "sass-loader", // compiles Sass to CSS
options: {
sourceMap: true
}
}
],
include: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
}
]
},
plugins: [
extractSASS,
new webpack.IgnorePlugin(/vertx/),
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/\@angular(\\|\/)core(\\|\/)esm5/,
PATHS.projectPath, // location of your src
{} // a map of your routes
),
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor', 'polyfills'],
// minChunks: Infinity
}),
]
};
Это очень похоже на то, что описано сейчас в документации Angular angular.io/guide/webpack. Наиболее интересной из этого является часть про компиляцию .ts файлов.
{
test: /\.ts$/,
loaders: [{
loader: 'awesome-typescript-loader',
options: {
transpileOnly: process.env.NODE_ENV !== 'production'
}
},
'angular2-template-loader',
'angular2-router-loader'
],
exclude: [/\.(spec|e2e)\.ts$/],
},
{
test: /\.ts$/,
include: [/\.(spec|e2e)\.ts$/],
loaders: ['awesome-typescript-loader', 'angular2-template-loader']
},
Как видите, мы используем лоадер angular2-template-loader и angular2-router-loader для сборки наших компонентов Angular. В официальной документации так и написано. И это крайне странно, так как оба лоадера написаны не командой Angular и лежат в пользовательских репозиториях на GitHub. Одной из причин выбора Angular в качестве основного фреймворка было как раз то, что он работает как комбайн — всё идет «из коробки», в отличие от того же ReactJS. Но тут мы видим, что инструмент, которым будет собираться наш проект, «из коробки» не идет.
Ну да ладно, такой конфиг работал со второй по пятую версию, и причин для беспокойства не было. Хотя нет, была одна. На ng-conf 2017 Brad Green рассказал о попытке сборки Angular приложения с помощью Bazel и Closure. Кто работал с большими проектами на Angular, тот поймет меня — сборки проходят очень долго. Наша первая сборка development режима на пятой версии Angular со вторым webpack занимает больше 4 минут. И стремление разработчиков фреймворка сделать сборки быстрее вполне обосновано. Хотя существует и другой взгляд на эту ситуацию. Как сказал мой коллега:
«Надо же было сделать медленно собирающийся фреймворк, а потом начать его ускорять».
Обновление Angular до 6-ой версии
В нашей компании хорошо понимают, что происходит, когда не обновляешь инструменты. Мы знаем, к чему это может в итоге привести. Революции не проходят безболезненно, и лучше их предвидеть, двигаясь эволюционным путем. Поэтому при появлении новости о выходе стабильной версии Angular 6, мы решили обновить наш проект. Но безболезненно сделать это не получилось.
Как и при обновлении предыдущей версии, мы перешли на сайт с руководством по обновлению update.angular.io. Здесь нас ждал первый сюрприз.
Если не указать пункт “I use ngUpgrade”, то руководство всё равно предложит выполнить команду
ng update @angular/core
.В обновлении предыдущих версий такого не было, а теперь, получается, без CLI и обновиться-то нельзя? Возможно, это баг и он будет исправлен, но сегодня, как и неделю назад, получить четких инструкций по обновлению без CLI не удалось.
Если как и мы, вы все же хотите продолжить обновлять проект, то именно тут начинается наш тернистый путь. Для начала нужно определиться с направлением:
- Установить CLI и обновлять по шагам официального руководства.
- Обновлять пакеты отдельно и редактировать конфиги самостоятельно.
Первый показался нам проще и мы пошли по нему.
Но после установки, обновления CLI и выполнения команды
ng update @angular/core
нас ожидало разочарование.$ ng update @angular/core
Package "@angular/compiler-cli" has an incompatible peer dependency to "typescript" (requires ">=2.7.2 <2.8", would install "2.8.3")
Invalid range: ">=2.3.0 <3.0.0||>=4.0.0"
В issues на GitHub можно найти github.com/angular/angular-cli/issues/10621. На сегодняшний день эту ошибку вроде бы поправили (судя по github.com/angular/devkit/pull/901), но на тот момент мы решили не лезть в дебри утилиты обновления и обновили пакеты вручную.
После обновления пакетов проект перестал запускаться, что, собственно, было ожидаемо. Angular 6 использует Webpack 4 (это можно увидеть, если вы поставите его через CLI). Поэтому на следующем шаге мы обновили Webpack и смежные пакеты до последних версий. Рассказ про обновление Webpack тянет на отдельную статью, поэтому здесь напишу только, что если вы используете extract-text-webpack-plugin, замените его на mini-css-extract-plugin, и это сэкономит вам нервы и силы. Почитать про то, как хорош четвертый webpack, можно здесь, ну и, собственно, статья по миграции.
Помимо обновления Angular и Webpack, нужно обновить RxJS до шестой версии, иначе проект просто не запустится. Это обязательное условие и выполнить его несложно, нужно просто следовать документации по миграции. Существенных трудностей возникнуть не должно, RxJS предоставляет утилиту, которая самостоятельно вносит необходимые изменения в проект.
Тем временем мы возвращаемся к обновлению до Angular 6. Проект по-прежнему не собирается и выдает массу невразумительных ошибок. Здесь самое время обратить внимание на лоадер, которым обрабатываются .ts файлы. У нас используется связка angular2-template-loader и angular2-router-loader. Если зайти в репозиторий angular2-template-loader, то видно, что он не обновлялся уже как год (странно, что в официальной документации нам все еще предлагают использовать его).
Похоже, что проблема в том как этот лоадер обрабатывает наш код. Мы начали искать замену и нашли плагин для Ahead-of-Time (AoT) компиляции @ngtools/webpack. Это не равнозначная замена, так как до этого нами использовалась только JIT-компиляция. Но, с другой стороны, команда Angular уже давно говорит о планах сделать AoT-компиляцию по умолчанию. @ngtools/webpack — официальный инструмент из Angular DevKit, он обновляется постоянно и был переработан под шестую версию фреймворка. Для справедливости замечу, что можно собрать Angular 6 проект и с плагинами angular2-template-loader и angular2-router-loader. Связка этих плагинов может подойти для разработки, но для production сборок лучше их не использовать из-за отсутствия дополнительных проверок исполняемого кода. Именно это не позволило нам легко сразу же отловить все необходимые исправления для перехода на шестую версию.
AOT-компиляция по большому счету отличается тем, что шаблоны компилируются в момент сборки приложения, а не после запуска в браузере пользователя. С одной стороны, это ускоряет работу приложения и позволяет сократить его размер, с другой — несет дополнительные затраты при компиляции и требует больше строгости к написанию компонентов.
Переход на AOT-компиляцию проекта — это отдельная большая задача. Для ее выполнения придется переделать большую часть проекта, потому что AOT предъявляет очень строгие требования к коду и если сразу вы их не соблюдали, то будет сложно. Но есть выход. Можно использовать плагин @ngtools/webpack с JIT-компиляцией. Для этого нужно добавить параметр
skipCodeGeneration=true
в настройки плагина. Обозначу основные моменты, которые пришлось исправить при переходе на @ngtools/webpack плагин:
- В шаблонах все
private
переменные заменены наpublic
. - При наследовании нежелательно наследовать один компонент от другого (с директивами то же самое). В принципе, это логично, но angular2-template-loader — пропускал, а @ngtools/webpack стал ругаться на неправильно созданные модули.
- Если пренебречь рекомендацией выше, то можно получить ошибку при использовании в конструкторе компонента переменных простых типов. Это наиболее странная ошибка. Компонент выглядит следующим образом:
@Component({
selector: '[form-component]',
template: ''
})
export class FormComponent extends BaseComponent implements OnInit {
constructor(
public formService: FormService,
public formFunc: string,
public formParams: Array<any> = []
) {
super();
}
...
В логах видим примерно следующее:
ERROR in : Can't resolve all parameters for FormComponent in form.component.ts: ( [object Object], ?, ?)
Рекомендую все же выполнить правило второе, но если по каким-либо причинам это не получается, вы можете сделать небольшой хак https://stackoverflow.com/a/48748942/4778628, заменив код выше на:
@Component({
selector: '[form-component]',
template: ''
})
export class FormComponent extends BaseComponent implements OnInit {
constructor(
public formService: FormService,
@Inject('') public formFunc: string,
@Inject('') public formParams: Array<any> = []
) {
super();
}
...
К сожалению, на этом ошибки не закончились, и мы получили её в самом плагине компилятора Angular:
[0] building modules ?wds?: Project is running at http://localhost:8080/
?wds?: webpack output is served from /
?wds?: Content not from webpack is served from /home/dsumbaev/DEVELOPMENT/bill-client-front/dist
?wds?: 404s will fallback to /index.html
[0] building modules/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/@ngtools/webpack/src/angular_compiler_plugin.js:509
if (this.done && (request.request.endsWith('.ts')
^
TypeError: Cannot read property 'request' of null
at nmf.hooks.beforeResolve.tapAsync (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/@ngtools/webpack/src/angular_compiler_plugin.js:509:47)
at _fn1 (eval at create (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:27:1)
at Object.resolveWithPaths (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/@ngtools/webpack/src/paths-plugin.js:14:9)
at nmf.hooks.beforeResolve.tapAsync (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/@ngtools/webpack/src/angular_compiler_plugin.js:521:32)
at AsyncSeriesWaterfallHook.eval [as callAsync] (eval at create (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:19:1)
at NormalModuleFactory.create (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/NormalModuleFactory.js:338:28)
at semaphore.acquire (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:494:14)
at Semaphore.acquire (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/util/Semaphore.js:17:4)
at asyncLib.forEach (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:492:15)
at arrayEach (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/neo-async/async.js:2400:9)
at Object.each (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/neo-async/async.js:2835:9)
at Compilation.addModuleDependencies (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:471:12)
at Compilation.processModuleDependencies (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:450:8)
at afterBuild (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:556:15)
at buildModule.err (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:600:11)
at callback (/home/dsumbaev/DEVELOPMENT/bill-client-front/node_modules/webpack/lib/Compilation.js:358:35)
Сначала подумали, что отдаем компилятору пакеты из node_modules, а он не может их обработать, но добавление исключений никак не отразилось на ошибке. Деваться было некуда и поворачивать поздно, поэтому появился небольшой PR в @ngtools/webpack. Эти изменения вошли в версию пакета 6.0.1. После этого сборка прошла успешно и проект запустился!
НО! Оказалось, что не подтянулись все модули кроме основного. Давайте посмотрим на настройку @ngtools/webpack плагина.
new AngularCompilerPlugin({
platform: 0,
sourceMap: true,
tsConfigPath: path.join(PATHS.root, 'tsconfig.json'),
skipCodeGeneration: true,
})
На первый взгляд, все как в документации. Обратите внимание, что параметр entryModule помечен как необязательный. Методом проб и ошибок выяснили, что если параметр не был указан, то сборка не проходила дальше главного модуля, поэтому мы и получали нерабочее приложение. Исправить проблему легко, нужно добавить
entryModule
. new AngularCompilerPlugin({
platform: 0,
entryModule: path.join(PATHS.src, 'apps/client/app/app.module#AppModule'),
sourceMap: true,
tsConfigPath: path.join(PATHS.root, 'tsconfig.json'),
skipCodeGeneration: true,
})
Если помните, в начале я писал, что в проекте у нас два подпроекта, но в entryModule можно указать только один. Здесь нам повезло, так как второе приложение не содержит вложенных модулей. Если же у вас другая ситуация: несколько сложных проектов внутри одного, то вам придется сделать для каждого отдельные конфиги или дождаться прохождения этого PR в репозитории Angular DevKit.
В результате общая часть конфига в проекте стала следующей:
const path = require('path');
const merge = require('webpack-merge');
const webpack = require('webpack');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const {AngularCompilerPlugin} = require('@ngtools/webpack');
const {
PATHS,
PARAMS
} = require('./helpers.js');
const devMode = process.env.NODE_ENV === 'development';
let entry = {
'polyfills': path.join(PATHS.src, 'polyfills.browser.ts'),
'main': path.join(PATHS.projectPath, 'main.ts'),
'extform': path.join(PATHS.apps, 'extform/main.ts'),
'style': path.join(PATHS.assets, 'sass', 'app.sass')
};
PARAMS.themes.forEach(theme => {
entry['themes/' + theme + '/theme'] = path.join(PATHS.themes, theme, 'theme.scss')
});
module.exports = {
context: PATHS.root,
target: 'web',
entry,
resolve: {
extensions: ['.ts', '.js', '.json'],
modules: [PATHS.src, PATHS.node_modules],
},
mode: process.env.NODE_ENV,
stats: 'errors-only',
module: {
rules: [{
test: /\.ts$/,
loader: '@ngtools/webpack',
exclude: [/\.(spec|e2e)\.ts$/, /node_modules/],
},
{
test: /\.ts$/,
loader: 'null-loader',
include: [/\.(spec|e2e)\.ts$/],
},
{
test: /\.json$/,
use: 'json-loader'
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
}],
},
{
test: /\.(eot|woff|woff2|ttf|png|jpg|gif|svg|ico)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
options: {
context: PATHS.assets,
name: '[path][name].[ext]'
},
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
],
exclude: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
},
{
test: /\.css$/,
include: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
use: [{
loader: "raw-loader" // creates style nodes from JS strings
}],
},
{
test: /\.(scss|sass)$/,
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
exclude: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
},
{
test: /\.(scss|sass)$/,
use: [{
loader: "raw-loader" // creates style nodes from JS strings
},
{
loader: "sass-loader", // compiles Sass to CSS
options: {
sourceMap: true
}
}
],
include: [path.join(PATHS.projectPath), path.join(PATHS.src, 'common'), path.join(PATHS.src, 'common-bill')],
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
}
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[hash].css',
}),
new webpack.IgnorePlugin(/vertx/),
new ProgressPlugin(),
new AngularCompilerPlugin({
platform: 0,
entryModule: path.join(PATHS.src, 'apps/client/app/app.module#AppModule'),
sourceMap: true,
tsConfigPath: path.join(PATHS.root, 'tsconfig.json'),
skipCodeGeneration: true,
})
]
};
Заключение
После всех вышеописанных манипуляций мы получили работающее приложение с Angular 6 и собственным Webpack конфигом. В ходе работ было скорректировано 180 файлов проекта, и заняло это около одной недели.
Angular — монолитный фреймворк, и с приходом шестой версии это становится видно еще больше. Теперь не только дополнительные инструменты в виде роутера или библиотеки HTTP-запросов, но и инструменты сборки идут «из коробки». И лучше их не трогать, не вносить изменения, которые не были задуманы разработчиками Angular. Только в этом случае вы сможете легко обновлять проект и, возможно, не будете сталкиваться с необходимостью менять сотни файлов после обновлений. Иначе, вас ожидает нелегкий путь и придется лайкать негативные отзывы по новой версии в море позитива и общей радости окружающих.
Это не значит, что Angular плохой или хороший, просто он требует особого обращения и подойдет не всем. Если вы будете работать с ним через CLI, собирать проект ng утилитой, ей же тестировать и создавать модули и компоненты, то будет вам счастье. В своей команде мы бы тоже хотели так, но, увы и ах, слишком много уже завязано на наш конфиг Webpack. Как говорится в хорошей русской пословице: «Знал бы, где упадешь, — соломки бы подстелил». Еще год назад CLI не был столь обязательным инструментом в Angular проектах, ну а сегодня даже в документации по обновлению нет руководства как обновлять без него.
Комментарии (11)
justboris
16.05.2018 22:08+2Очень интересно, как у вас работал старый конфиг вебпака вот с этой строкой
transpileOnly: !process.env.NODE_ENV === 'production'
Порядок операций такой, что это эквивалентно:
false === 'production'
, то есть всегда false.
Радует, что в финальной версии конфига этой строки уже нет :)
Sumbad Автор
17.05.2018 03:56Да, точно, спасибо поправил :)
Это в ходе экспериментов так выключал/включал дополнительные проверки в попытках ускорить сборку, а обратно для статьи поменять забыл.
devpreview
17.05.2018 08:41@ngtools/webpack работает и с Webpack 3, в моём случае приложение, обновлённое до Angular 6, прекрасно собиралось с Webpack 3. А из-за того что изначально использовался @ngtools/webpack (ещё с беты), каких-то проблем по переходу с Angular 5 на Angular 6 я не увидел.
А вот новый SplitChunksPlugin пришлось осознавать довольно долго.
MooooM
18.05.2018 12:54Спасибо за пост. Сам хотел нечто подобное описать, т.к. был очень схожий болезненный опыт перехода, но на 5ую версию. Проект также работал без CLI и был довольно увесистый.
Мы решили, что наиболее правильным решением будет перейти на CLI, и несмотря на огромное количество убитого времени (неделя боли и страданий) проект стал работать через CLI и даже с AOT компиляцией. Правда это стало последним каплей и следующие проекты мы делали уже на другом фреймворке...
supersmeh
А какие такие изменения, привнесенные в 6-ю версию, заставили вас на него перейти?
Мы вот до сих пор на 4.4 сидим и вроде всё устраивает…
kovalevsky
установка пакетов через ng add, которая без «открой X, вставить туда строчку Y»
ng update, который вас практически избавит от ручного обновления пакетов
RxJS 6.0 и улучшенный tree-shaking
Анимации без полифоллов
Компоненты без ангуляра (например, чтоб шерить их между основным сайтом и лендингом на WP)
это как минимум
LionSoft
А можно об этом поподробнее? Или ссылку скиньте, плз.
kovalevsky
Там не совсем без ангуляра, там идея упаковки компонента со всеми его зависимостями в нативный web component.
Я говорю об Angular Elements, angular.io/guide/elements.
Оно ещё очень сырое, но нас подходит, мы начнем готовить некоторые элементы как раз под описанный выше случай.
Более-менее production-ready это всё будет уже к 7 релизу.
Sumbad Автор
Основных мотиваций для обновления было две:
1. Хотелось ускорить сборку проекта. Уже давно следим за новостями по Webpack 4. В этом видео Sean Larkin рассказывает подробнее. Angular 6 по-умолчанию использует Webpack 4 поэтому решили обновлять совместно. В итоге сейчас проект стал собираться на 40 секунд быстрее (думаю можно будет еще ускорить, но уже приятно).
2. С каждым обновлением команда Angular вносит ряд изменений в фреймворк, которые требуют переработки компонентов. Если затягивать с обновлением, то потом придется менять слишком много кода. Даже в update.angular.io если указать переход через несколько мажорных версии они напишут предупреждение:
MaxKorz
А как вы сидите на 4.4 если плагины собранные под Angular 5.x+ не работают на 4.x? Не говоря уже о breaking changes. Или вы не используете сторонние пакеты? Или намеренно используете устаревшие и не поддерживаемые версии?
supersmeh
«dependencies»: {
"@angular/common": «4.4.6»,
"@angular/compiler": «4.4.6»,
"@angular/core": «4.4.6»,
"@angular/forms": «4.4.6»,
"@angular/http": «4.4.6»,
"@angular/platform-browser": «4.4.6»,
"@angular/platform-browser-dynamic": «4.4.6»,
"@angular/animations": «4.4.6»,
"@angular/router": «4.4.6»,
"@angular/compiler-cli": «4.4.6»,
«angular-in-memory-web-api»: «0.5.1»,
«bootstrap»: «3.3.7»,
«core-js»: «2.5.1»,
«eonasdan-bootstrap-datetimepicker»: «4.17.47»,
«font-awesome»: «4.7.0»,
«ie-shim»: «0.1.0»,
«moment»: «2.15.1»,
«reflect-metadata»: "^0.1.10",
«rxjs»: «5.5.2»,
«zone.js»: «0.8.18»,
«aspnet-prerendering»: «2.0.3»,
«aspnet-webpack»: «2.0.1»,
«file-saver»: «1.3.3»,
«d3»: «4.7.4»
},