یکی از مشکلاتی را که حین کار با AngularJS 2.0 به کرات شاهدش خواهید بود، کش شدن تک اسکریپتهای ماژولهای آن است. برای مثال فایل ts ایی را تغییر میدهید؛ به فایل js معادل آن کامپایل میشود. چون برنامه ماژولار است و این ماژول پیشتر توسط مرورگر بارگذاری شدهاست، بار دیگر نسبت به دریافت مجدد آن اقدام نمیکند. همچنین با ارائهی نگارش RC، دیگر خبری از فایلهای bundle این مجموعه نیست و اینبار اگر تبادلات شبکهی بین سرور و برنامه را مرور کنید، به چند صد رفت و برگشت، برای دریافت فایلهای JS کتابخانههای مرتبط خواهید رسید که اصلا بهینه نیست. در این قسمت قصد داریم، یک
Gulp Task را ایجاد کنیم تا تمام اسکریپتهای موجود را با هم یکی کرده و توزیع برنامه را سادهتر کند؛ به همراه بالا رفتن سرعت کار با این سیستم، بدون نیازی به توزیع تک تک فایلهای js نهایی، که شاید صدها فایل باشند.
نصب پیشنیازهای کار با Gulp و TypeScript
فایل package.json
در قسمت اول این سری معرفی شد. دراینجا قسمت devDependencies آنرا به نحو ذیل تکمیل کنید:
"devDependencies": {
"typescript": "^1.8.10",
"gulp": "^3.9.1",
"path": "^0.12.7",
"gulp-clean": "^0.3.2",
"fs": "^0.0.2",
"gulp-concat": "^2.6.0",
"gulp-typescript": "^2.13.1",
"gulp-tsc": "^1.1.5",
"del": "^2.2.0",
"gulp-autoprefixer": "^3.1.0",
"gulp-cssnano": "^2.0.0",
"gulp-html-replace": "^1.5.4",
"gulp-htmlmin": "^1.0.5",
"gulp-uglify": "^1.5.3",
"merge-stream": "^1.0.0",
"systemjs-builder": "^0.15.16",
"typings": "^0.8.1"
},
به این ترتیب، پس از ذخیرهی فایل و یا کلیک راست بر روی نام فایل و انتخاب گزینهی restore packages، وابستگیهایی مانند gulp، gulp-typescript و یک سری فشرده ساز CSS و HTML دریافت خواهند شد.
نکتهی مهم آن
systemjs-builder است. این کتابخانه کار کامپایل
systemjs.config.js را به یک تک اسکریپت انجام میدهد. به این ترتیب مشکل صدها بار رفت و برگشت به سرور، برای دریافت وابستگیهای AngularJS 2.0، به طور کامل برطرف میشود.
افزودن فایل gulpfile.js به پروژه
یا یک فایل جدید جاوا اسکریپتی را به نام gulpfile.js به ریشهی پروژه اضافه کنید و یا از منوی project -> add new item نیز میتوانید گزینهی gulp configuration file را در VS 2015 انتخاب نمائید. محتوای این فایل را به نحو ذیل تغییر دهید:
var gulp = require("gulp"),
concat = require("gulp-concat"),
tsc = require("gulp-typescript"),
jsMinify = require("gulp-uglify"),
cssPrefixer = require("gulp-autoprefixer"),
cssMinify = require("gulp-cssnano"),
del = require("del"),
merge = require("merge-stream"),
minifyHTML = require('gulp-htmlmin'),
SystemBuilder = require("systemjs-builder");
var appFolder = "./app";
var outFolder = "wwwroot";
gulp.task("clean", () => {
return del(outFolder);
});
gulp.task("shims", () => {
return gulp.src([
"node_modules/es6-shim/es6-shim.js",
"node_modules/zone.js/dist/zone.js",
"node_modules/reflect-metadata/Reflect.js"
])
.pipe(concat("shims.js"))
.pipe(jsMinify())
.pipe(gulp.dest(outFolder + "/js/"));
});
gulp.task("tsc", () => {
var tsProject = tsc.createProject("./tsconfig.json");
var tsResult = gulp.src([
appFolder + "/**/*.ts"
])
.pipe(tsc(tsProject), undefined, tsc.reporter.fullReporter());
return tsResult.js.pipe(gulp.dest("build/"));
});
gulp.task("system-build", ["tsc"], () => {
var builder = new SystemBuilder();
return builder.loadConfig("systemjs.config.js")
.then(() => builder.buildStatic(appFolder, outFolder + "/js/bundle.js"))
.then(() => del("build"));
});
gulp.task("buildAndMinify", ["system-build"], () => {
var bundle = gulp.src(outFolder + "/js/bundle.js")
.pipe(jsMinify())
.pipe(gulp.dest(outFolder + "/js/"));
var css = gulp.src(outFolder + "/css/styles.css")
.pipe(cssMinify())
.pipe(gulp.dest(outFolder + "/css/"));
return merge(bundle, css);
});
gulp.task("favicon", function () {
return gulp.src("./app/favicon.ico")
.pipe(gulp.dest(outFolder));
});
gulp.task("css", function () {
return gulp.src(appFolder + "/**/*.css")
.pipe(cssPrefixer())
.pipe(cssMinify())
.pipe(gulp.dest(outFolder));
});
gulp.task("templates", function () {
return gulp.src(appFolder + "/**/*.html")
.pipe(minifyHTML())
.pipe(gulp.dest(outFolder));
});
gulp.task("assets", ["templates", "css", "favicon"], function () {
return gulp.src(appFolder + "/**/*.png")
.pipe(gulp.dest(outFolder));
});
gulp.task("otherScriptsAndStyles", () => {
gulp.src([
"jquery/dist/jquery.*js",
"bootstrap/dist/js/bootstrap*.js"
], {
cwd: "node_modules/**"
})
.pipe(gulp.dest(outFolder + "/js/"));
gulp.src([
"node_modules/bootstrap/dist/css/bootstrap.css"
]).pipe(cssMinify()).pipe(gulp.dest(outFolder + "/css/"));
gulp.src([
"node_modules/bootstrap/fonts/*.*"
]).pipe(gulp.dest(outFolder + "/fonts/"));
});
//gulp.task("watch.tsc", ["tsc"], function () {
// return gulp.watch(appFolder + "/**/*.ts", ["tsc"]);
//});
//gulp.task("watch", ["watch.tsc"]);
gulp.task("default", [
"shims",
"buildAndMinify",
"assets",
"otherScriptsAndStyles"
//,"watch"
]);
توضیحات
در این فایل فرض شدهاست که خروجی نهایی برنامه قرار است در پوشهای به نام wwwroot کپی شود و پوشهی اصلی برنامه، همان پوشهای به نام app، در ریشهی پروژه است.
var appFolder = "./app";
var outFolder = "wwwroot";
سپس در اینجا یک سری task کامپایل و کپی کردن فایلها تهیه شدهاند:
1) وظیفهی clean، کار تمیز کردن پوشهی نهایی خروجی برنامه را انجام میدهد (حذف تمام فایلهای آن).
2) وظیفهی shims، کار بسته بندی، یکی کردن و فشرده کردن سه اسکریپت es6-shim.js، zone.js و Reflect.js را انجام میدهد. سپس تک فایل حاصل را به نام shims.js، در پوشهی wwwroot/js کپی میکند.
3) وظیفهی tsc، یکبار دیگر کامپایلر TypeScript را اجرا میکند تا مطمئن شویم با آخرین نگارش فایلهای js برنامه کار میکنیم.
4) وظیفهی system-build، کار پردازش خودکار مداخل فایل
systemjs.config.js را انجام میدهد. در آخرین نگارش ارائه شدهی AngularJS 2.0، بجای ذکر مداخل مورد نیاز آن، این تک فایل systemjs.config.js را به صفحه پیوست میکنیم تا اسکریپتهای لازم را (چند صد عدد)، به صورت خودکار بارگذاری کند. برای یکی کردن این چند صد عدد اسکریپت، از کتابخانهی
SystemBuilder آن کمک گرفته و کار کامپایل نهایی را انجام میدهیم. خروجی تمام این فایلها، به همراه کلیه فایلهای js حاصل از کامپایل فایلهای TypeScript برنامه، در فایلی به نام bundle.js کپی شدهی در پوشهی wwwroot/js نوشته میشود. بنابراین دیگر نیازی نیست تا فایلهای js پوشهی app و همچنین فایلهای js وابستگیهای AngularJS 2.0 را توزیع کنیم. تک فایل bundle.js، حاوی تمام اینها است.
5) وظیفهی buildAndMinify کار اجرای وظیفهی system-bulder را به همراه فشرده سازی تک فایل bundle.js، به عهده دارد. به علاوه اگر در پوشهی css آن نیز فایل styles.css موجود باشد، آن را فشرده میکند.
6) در ادامه یک سری وظیفهی کپی کردن منابع برنامه را مشاهده میکنید. مانند favicon که کار کپی کردن این آیکن را به پوشهی wwwroot انجام میدهد. وظیفهی css، فایلهای css موجود در پوشههای برنامه را به wwwroot و زیر پوشههای آن کپی میکند. وظیفهی templates، کار کپی کردن فایلهای html قالبهای کامپوننتها را بر عهده دارد. وظیفهی assets، کار کپی کردن فایلهای png را انجام میدهد.
7) وظیفهی otherScriptsAndStyles یک سری css و js ثالث را به پوشهی wwwroot کپی میکند؛ مانند فایلهای بوت استرپ و جیکوئری.
8) وظیفهی default، کار اجرای تمام این وظایف را با هم به عهده دارد.
اکنون اگر بر روی gulpfile.js کلیک راست کنید، گزینهی task runner explorer ظاهر خواهد شد. آنرا انتخاب کنید:
بر روی وظیفهی default کلیک راست کرده و آنرا اجرا کنید. پس از مدتی پوشهی جدید wwwroot ساخته شده و فایلهای نهایی برنامه به آن کپی میشوند.
اصلاح فایل index.html و یا Views\Shared\_Layout.cshtml
اکنون که تمام فایلهای مورد نیاز پروژه در پوشهی wwwroot کپی شدهاند، نیاز است فایل index.html را به نحو ذیل تغییر داد:
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
<link href="~/wwwroot/css/bootstrap.css" rel="stylesheet" />
<link href="~/wwwroot/app.component.css" rel="stylesheet" />
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="~/wwwroot/js/shims.js"></script>
</head>
<body>
<div>
@RenderBody()
<pm-app>Loading App...</pm-app>
</div>
<script src="~/wwwroot/js/jquery/dist/jquery.min.js"></script>
<script src="~/wwwroot/js/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="~/wwwroot/js/bundle.js"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
همانطور که مشاهده میکنید، اینبار دیگر خبری از systemjs.config.js و وابستگیهای آن نیست.
اسکریپتهای shims که برای مرورگرهای قدیمیتر درنظر گرفته شدهاند، به تک فایل wwwroot/js/shims.js منتقل شدهاند.
تمام اسکریپتهای AngularJS 2.0 و وابستگیهای آن به همراه تمام اسکریپتهای برنامهی خودمان، به تک فایل wwwroot/js/bundle.js منتقل شدهاند.
اکنون اگر برنامه را اجرا کنید، سرعت آن با قبل قابل مقایسه نیست! اینبار دیگر نه نیازی به بارگذاری تمام وابستگیهای AngularJS 2.0 به صورت مجزا توسط systemjs.config.js وجود دارد و نه به ازای مشاهدهی هر صفحهای، یکبار قرار است فایل js کامپوننت آن بارگذاری شود. تمام اینها داخل فایل wwwroot/js/bundle.js قرار گرفتهاند و تنها یکبار بارگذاری میشوند.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MVC5Angular2.part12.zip خلاصهی بحث
با نوشتن یک Gulp Task جدید میتوان بر اساس فایل systemjs.config.js، تمام اسکریپتهای دخیل در اجرای برنامه را به صورت خودکار یافته و به صورت یک تک فایل نهایی، بسته بندی و توزیع کرد.