在ASP.NET MVC应用程序中集成Angular和Webpack

时间:2017-12-18 14:31:50

标签: asp.net-mvc angular webpack

我正在寻找一个记录,其中包含我可以遵循的步骤,将Angular添加到现有的MVC应用程序中,该应用程序位于我的解决方案的某个区域。我找到了一个编写,告诉我如何使用gulp.js文件添加角度,该文件提取必要的node_modules并将我的ts文件转换为js文件。我想用webpack做同样的事情。

enter image description here

目前我的package.json看起来像这样。

{
    "version": "1.0.0",
    "name": "aspnet",
    "private": true,
    "scripts": {},
    "dependencies": {
        "@angular/animations": "4.3.5",
        "@angular/common": "4.3.5",
        "@angular/compiler": "4.3.5",
        "@angular/compiler-cli": "4.3.5",
        "@angular/core": "4.3.5",
        "@angular/forms": "4.3.5",
        "@angular/http": "4.3.5",
        "@angular/platform-browser": "4.3.5",
        "@angular/platform-browser-dynamic": "4.3.5",
        "@angular/platform-server": "4.3.5",
        "@angular/router": "4.3.5",
        "@angular/upgrade": "4.3.5",
        "angular-in-memory-web-api": "0.3.2",
        "bootstrap": "3.3.7",
        "core-js": "2.5.0",
        "ie-shim": "0.1.0",
        "rxjs": "5.4.3",
        "zone.js": "0.8.16",
        "systemjs": "^0.20.18"
    },
    "devDependencies": {
        "gulp": "^3.9.1",
        "gulp-clean": "^0.3.2",
        "gulp-concat": "^2.6.1",
        "gulp-tsc": "~1.3.2",
        "gulp-typescript": "^3.2.2",
        "path": "^0.12.7",
        "typescript": "^2.4.2"
    }
}

This 是我所遵循的使用gulp的写作,这是有效的。

但我更愿意使用webpack而不是gulp任务。

1 个答案:

答案 0 :(得分:14)

我希望这可以帮助您找到想法,因为我们的项目还需要将webpack和ASP.NET MVC集成在一起。注意到这是我自己的提议,所以可能有更好的方法来做到这一点。以下是我们的工作。

检查source code on Github

module 1

<强> 1。构建项目

我们将项目分为两个文件夹:客户端和服务器。这些文件夹将位于 mvc5-angular-webpack 文件夹中,此文件夹将提交到存储库

mvc5-angular-webpack/
├── Server/
│   ├── WebApplication/
│   │     ├── Controllers/
│   │     ├── Scripts/
│   │     ├── Web.config
│   │     ├── Many more folder and file...
│   │        
│   └── Web-Core.sln
│   
├── Client/
    ├── modules
    │     ├── angularModule-1/
    │     │      ├── main.ts
    │     │      ├── app.modules.ts
    │     │      ├── app.component.ts
    │     │      ├── Many more file...
    │     │
    │     ├── angularModule-2/
    │     │      ├── main.ts
    │     │      ├── app.modules.ts
    │     │      ├── app.component.ts
    │     │      ├── Many more file...
    │     ├── polyfill.ts
    │     ├── vendor.ts
    │
    └── build.bat
    └── npm-shrinkwrap.json
    └── package.json
    └── tsconfig.json
    └── tslint.json
    └── webpack.config.js
  • 服务器文件夹中,我们添加了名为Web-Core.sln 的MVC解决方案,所有公共库项目都是用C#编写的。
  • 客户端文件夹中,只包含与前端相关的内容。要构建前端项目,只需调用build.bat即可。我稍后会讨论这个文件内容。在modules模块文件夹中,我们的项目将为每个模块创建每个子文件夹。

我们的网站有一些模块仍在使用纯Razor的服务器端渲染。还有一些用AngularJS和Angular编写的客户端代码模块。

<强> 2。配置webpack

假设您已经配置了所有typescriptnpm。让我们看看webpack.config.js

中的内容
const webpack = require('webpack')
const path = require('path')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const entryPath = path.resolve(__dirname, 'modules')
const corePath = path.resolve(__dirname, '../Server/WebApplication/Scripts/ng2')
const module1 = `${entryPath}/angularModule-1`
const module2 = `${entryPath}/angularModule-2`

module.exports = (envOptions) => {
    envOptions = envOptions || {};

    const config = {
        entry: {
            'polyfills': `${entryPath}/polyfill.ts`,
            'vendors': `${entryPath}/vendor.ts`,
            'module1': `${module1}/main.ts`,
            'module2': `${module2}/main.ts`            
        },
        output: {
            path: corePath,
            filename: '[name].js',
            sourceMapFilename: "[name].js.map"            
        },
        resolve: {
            extensions: ['.ts', '.js', '.html']
        },
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    loaders: ['awesome-typescript-loader', 'angular2-template-loader']
                },
                {
                    test: /\.html$/,
                    loader: 'raw-loader'
                },
                {
                    test: /\.css$/,
                    loader: 'raw-loader'
                }
            ]
        },
        devtool: 'source-map',
        plugins: [
            new webpack.NoEmitOnErrorsPlugin(),
            new webpack.optimize.CommonsChunkPlugin({
                name: ['vendors', 'polyfills']
            })
        ]
    }

    if (envOptions.MODE === 'prod') {
        config.plugins.push(
            new UglifyJsPlugin()
        )
    }

    return config;
}

所以基本上它会尝试解析上层目录并将所有编译好的文件放在服务器文件夹中的 Scripts / ng2

第3。配置npm脚本

配置webpack后,我们基本上会在构建过程中添加一些脚本来运行。将以下代码添加到package.json文件

"scripts": {
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "dev": "webpack-dev-server --https --open",
    "watch": "webpack --config webpack.config.js --watch",
    "build": "webpack --config webpack.config.js",
    "build:html": "webpack --config webpack-html-plugin.config.js --env.MODE=prod",
    "build:prod": "webpack --config webpack.config.js --env.MODE=prod"    
}

<强> 4。配置build.bat

在Angular 2集成开始时,我们为前端目的创建了一个空Web应用程序项目,并将此项目添加为WebApplication的依赖项。但我们的后端团队后来抱怨前端流程的速度有多慢。因为每次WebApplication构建时,他们都不需要构建前端项目。

build.bat文件的想法是手动运行它以在其计算机上获取最新版本的前端代码。不是每次他们都运行项目。

call npm install --production  --loglevel verbose
echo "Build Angular projects"
npm run build:prod

call将继续,因为某些命令会中止命令行。请参阅here

这里的脚本非常简单。首先,我们运行npm install来恢复所有必要的依赖项。然后按照build:prod之前的定义调用package.json。 Webpack将把我们的Typescript代码捆绑成三个大的JavaScript文件vendors.jspolyfills.jsmodule1.js

我们的团队使用Jenkins进行部署,因此我们的开发人员只需要包含运行build.bat并且我们都已设置好。如果要在每次构建项目时运行它,可以在预构建或构建后事件中进行设置。

pre-build and post-build Image source

<强> 5。在视图中引用已编译的JavaScript文件。

通常我们只返回一个区域中的一个视图,如下所示。 my-angular-app是我们在app.component.ts

中定义的内容

Index.cshtml

<script src="~/Scripts/ng2/polyfills.js"></script>
<script src="~/Scripts/ng2/vendors.js"></script>
<script src="~/Scripts/ng2/module1.js"></script>

<my-angular-app>
    Loading...
</my-angular-app>

HomeController.cs

public ActionResult Module1()
{
    return View();
}

如果我们部署到生产,这有点缺点。因为在编译的JavaScript更新之后,浏览器有时会因为缓存而保留旧版本的文件。我们应该有一种机制来在部署后为文件提供唯一的名称。我们有3种选择。

<强>我。 html-webpack-plugin

如果我们使用纯前端项目,webpack提供html-webpack-plugin来处理它,如下所示。从技术上讲,它会使用唯一ID自动将JavaScript文件注入我们的视图中。

webpack-html-webpack-plugin.config.js

...
const HtmlWebpackPlugin = require('html-webpack-plugin');
const viewPath = path.resolve(
    __dirname,
    "../Server/WebApplication/Views/Home"
  );
...
entry: {
    polyfills: `${entryPath}/polyfill.ts`,
    vendors: `${entryPath}/vendor.ts`,
    module1: `${module1}/main.ts`      
},
output: {
    path: corePath,
    filename: "[name].[hash].js",
    sourceMapFilename: "[name].[hash].js.map"
}
....
plugins: [,
    ...,
    new HtmlWebpackPlugin({
        template: viewPath + "/loader.cshtml",
        filename: viewPath + "/Module1.cshtml",
        inject: false
    })
]

在指定视图的同一文件夹中,我们创建了一个名为loader.cshtml

的cshtml文件

loader.cshtml

<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>  
    <script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>  
<% } %> 

<my-angular-app>
    Loading...
</my-angular-app>

npm run build:html上的定义运行package.json。如果它成功运行,结果将如下所示。

build html plugin

network html plugin

@{
    ViewBag.Title = "Module 1";
}
<script src="../../Scripts/ng2/vendors.1470de344a0f2260b700.js"></script>
<script src="../../Scripts/ng2/vendors.1470de344a0f2260b700.js"></script>
<script src="../../Scripts/ng2/module1.1470de344a0f2260b700.js"></script>

<my-angular-app>Loading....</my-angular-app>

<强> II。定义了您自己的JavaScript版本

在我们的ASP.NET MVC项目中有点不同,因为我们使用了多个Angular应用程序。因此,我们在类中定义了一个版本,并在加载JS时将其附加到文件的末尾。通过这样做,我们将确保将最新的内容加载到浏览器中。但它不属于这个问题,所以我不会再进一步​​了。基本上,它看起来像下面的代码。

<script src="@string.Format("{0}?v={1}", "~/Scripts/ng2/polyfills.js", VersionHelper.CurrentVersion())</script>
<script src="@string.Format("{0}?v={1}", "~/Scripts/ng2/vendors.js", VersionHelper.CurrentVersion())</script>
<script src="@string.Format("{0}?v={1}", "~/Scripts/ng2/module1.js", VersionHelper.CurrentVersion())</script>

在浏览器上提供时,它看起来像

<script src="~/Scripts/ng2/polyfills.js?v=1.1.0"></script>
<script src="~/Scripts/ng2/vendors.js?v=1.1.0"></script>
<script src="~/Scripts/ng2/module1.js?v=1.1.0"></script>

<强> III。 ASP.NET Bundling

在Zyllem,我们的团队没有使用它,因为我们的JavaScript文件在模型中配置并稍后将其呈现给视图。

您可以在项目中打开App_Start\BundleConfig.cs并配置捆绑包。假设名称为module1

bundles.Add(new ScriptBundle("~/bundles/module1").Include(
                "~/Scripts/ng2/polyfills.js",
                "~/Scripts/ng2/vendors.js"
                "~/Scripts/ng2/module1.js"));

通过这样做在视图内部渲染。

Index.cshtml

@Scripts.Render("~/bundles/module1")

因此,当在浏览器上提供服务时,它将在末尾具有唯一标记,并且它是不同的如果您在脚本包中进行任何更改。

<script src="/bundles/module1?v=2PbUw0z_gh_Ocp_Vz13rdmmBSG6utLJAXm2lThxYAow1"></script>

如果一切正常,您将看到以下屏幕截图。

module 1

module 2

<强>更新

在Github上添加存储库https://github.com/trungk18/mvc5-angular-webpack