【问题标题】:Integrate Angular and Webpack in an ASP.NET MVC Application [closed]在 ASP.NET MVC 应用程序中集成 Angular 和 Webpack [关闭]
【发布时间】:2017-12-18 14:31:50
【问题描述】:

我正在寻找一篇文章,其中包含我可以遵循的步骤来将 Angular 添加到位于我的解决方案区域中的现有 MVC 应用程序中。我找到了一篇文章,向我展示了如何使用 gulp.js 文件添加 angular,该文件提取必要的 node_modules 并将我的 ts 文件转换为 js 文件。我想改用 webpack 做同样的事情。

目前我的 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 任务。

【问题讨论】:

    标签: asp.net-mvc angular webpack


    【解决方案1】:

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

    查看source code on Github

    1.构建项目

    我们将项目分为两个文件夹:Client 和 Server。这些将位于 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
    
    • Server文件夹中,我们添加了名为Web-Core.sln 的MVC解决方案,所有公共库项目都是用C#编写的。
    • Client 文件夹中只包含前端相关的东西。要构建前端项目,只需调用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;
    }
    

    所以基本上它会尝试在上层解析目录并将所有编译的文件放到Server文件夹中的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 即可,一切就绪。如果您想在每次构建项目时运行它,您可以在构建前或构建后事件中设置它。

    Image source

    5.在视图中引用编译好的 JavaScript 文件。

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

    索引.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 来处理它,如下所示。从技术上讲,它会自动将 JavaScript 文件以唯一 id 注入到我们的视图中。

    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>
    

    并按照package.json 的定义运行npm run build:html。如果运行成功,结果会是这样。

    @{
        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>
    

    二。定义你自己的 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>
    

    三。 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"));
    

    然后在视图中渲染。

    索引.cshtml

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

    这样在浏览器上提供服务时,最后会有唯一的令牌,如果您在脚本包中进行任何更改,它就会有所不同。

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

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


    更新

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

    【讨论】:

    • 这是一个很好的解释。
    • @trungk18 这是一个很好的解释。我会试一试的。
    • 希望还不算太晚,我刚刚创建了一个存储库github.com/trungk18/mvc5-angular-webpack。你可以下载试试。它也可以更容易。
    • @trungk18 嘿,我真的很想把这个赏金奖励给你,因为你应该得到它。我会努力做到这一点。我不明白为什么这些书呆子会搁置我的问题,因为这个答案非常有价值,很多人都可以从中受益。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    相关资源
    最近更新 更多