The original link
With the maturity of Angular,React,Vue, and other front-end technologies, Web development has changed dramatically over the past few years, from building Web pages to building applications, and from rendering markup on the server to rendering directly in the browser. For the most part, this has led to increased browser-based usage. But as developers move to front-end development, many are asking, should we still use ASP.NET?
Single-page apps, or SPAs for short, are not new. But SPA is now eating into the Web development world in a variety of ways. For some sites, such as Facebook and Twitter, switching to a SPA can be really useful, which is why some SPA frameworks come directly from the companies responsible for those sites. Developers around the world are realizing and implementing SPAs for their websites, especially for enterprise applications.
If you know me, you know I love front-end development, but the term “SPA” gives me the creeps. I prefer to see front-end frameworks like Angular,React, and Vue as a way to scale up your site. By building a giant SPA, we seem to be going back to the way we built whole applications back in Visual Basic 5.
I see these frameworks as a way to build islands of functionality, focusing on what front-end code does best — the user interface. If you want to use SPA instead of all functionality in your site, you’ll have to rely on other methods for SEO and simulating login to the URL to make it look like a website. Unless you’re building the next Twitter or Facebook, there’s no need.
Use the SPA framework in APS.NET Core
If you’re new to APS.NET Core, you probably don’t know that there are several ways to use the SPA framework:
- Microsoft has middleware built in for Angular and React
- Put the SPA in a separate folder and build it into the ASP.NET Core project
- Integrate the SPA framework directly into the project
Let’s discuss each of these approaches.
SPA middleware
Microsoft provides middleware support for using SPA. The middleware takes a hands-off approach, just keeping the SPA in a subfolder and working directly on its own. Here’s how it works.
How to use middleware
In Visual Studio, you can directly select the project template, as shown below:
This is done by placing an entire SPA project in a ClientApp folder, as shown below:
In ASP.NET Core 3, SPA works with middleware, specifying a folder (optionally named) in code by integrating this middleware as the last step in the Startup configuration:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... if (! env .IsDevelopment()) { app.UseSpaStaticFiles(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller}/{action=Index}/{id? } "); }); app.UseSpa(spa => { // To learn more about options for serving an Angular SPA from ASP.NET Core, // see https://go.microsoft.com/fwlink/?linkid=864501 spa.Options.SourcePath = "ClientApp"; if (env.IsDevelopment()) { spa.UseAngularCliServer(npmScript: "start"); }}); }Copy the code
There are two method calls: UseSpaStaticFiles and UseSpa. These methods enable the ability to call angular-CLI provided functionality to generate index.html. In the example above, you can see that NPM start is automatically executed during development.
The middleware works in much the same way with React. Currently, other frameworks (Vue,Backbone, etc.) are not supported. The other limitation is that while there is an older version of middleware available for ASP.NET Core 2.0, the new version only supports ASP.NET Core 2.1 and above.
Use SPA as a subfolder for ASP.NET Core
Middleware is a good choice if you don’t need to use other Web pages. However, I think it’s more common for most developers to integrate ASP.NET Core into traditional MVC applications. You can keep SPA as a separate folder and treat it as a subproject without much integration between the two projects.
How do I use SPA subfolders
For example, suppose you have an Angular project called ClientApp, as shown below:
Since this directory has its own package.json, you can build it using the CLI. You can even develop them separately without referencing the ASP.NET Core project, but because I often add SPAs to existing Razor views, I usually run them together.
One problem is that the output directory of a SPA project usually needs to be modified. You can use SpaStaticFiles middleware to solve this problem, but I usually modify the SPA configuration file to change the output to the wwwroot directory. For example, here’s my angular.json file (using Vue CLI) :
{
...
"projects": {
"ng-core": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
...
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "../wwwroot/client",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
...
Copy the code
In this case, I just tell the Vue CLI to output the results to wwwroot, so I don’t need to configure ASP.NET Core. Angular and React support this output if you don’t want to use SpaStaticFiles middleware.
By including more subfolders, you can put multiple SPA projects into a single ASP.NET Core project. But there are limitations, too, involving dependency management. By doing this, each project will have its own node_modules directory, which can take a long time to build.
Since the output directory is configured, you don’t need to specify the include directory when publishing the project, but you do need to tell MSBuild (.csproj) to build the SPA project before publishing. Here’s a tip to add Target:
<Target Name="client-app" BeforeTargets="ComputeFilesToPublish">
<Exec Command="npm install" WorkingDirectory="ClientApp">
</Exec>
<Exec Command="npm run build" WorkingDirectory="ClientApp ">
</Exec>
</Target>
Copy the code
Target points out that it needs to consider some dependencies before releasing. Two execs are the way to execute the CLI in the ClientApp directory. This is only done at release time, so it won’t affect your development process.
The biggest drawback comes from Visual Studio rather than ASP.NET Core. Because project.json is in a subfolder, not the root of the project, the Task Runner Explorer cannot find it. That means you have to run it alone. If you’re using Visual Studio Code, Rider, or some other IDE, you’re probably already used to opening the terminal and manually building, so this won’t be too much of a problem.
Other limitations related to the above mentioned, multiple package.json needs to be managed. That’s what bothers me the most. Most of my projects use NPM to manage dependencies, and managing multiple package.json files is frustrating. That’s why I usually use the last approach — fully integrating SPA into ASP.NET Core projects.
Fully integrate SPA into ASP.NET Core
If you already use a build step, why not have them work together? This means that you need to build your SPA project every time you build ASP.NET Core, and you can still rely on build watchers and other devices to accelerate development. Build and release can be combined to make testing and production release as simple as possible.
How to integrate SPA directly
Contains the following steps:
- Merge the NPM configuration
- Move the configuration to the root of the project
Merge the NPM configuration
To put the SPA into the APS.NET Core project, either move package.json to the project root or combine them. For example, you already have a package.json file to help you import front-end dependencies:
{
"version": "1.0.0"."name": "mypackage"."private": true."dependencies": {
"jquery": "3.3.1"."bootstrap": "This"}}Copy the code
Unfortunately, most SPA projects have a lot of dependencies and introduce other configuration elements. So, after importing all the required content for an Angular project, it might look like this:
{
"version": "1.0.0"."name": "mypackage"."scripts": {
"ng": "ng"."start": "ng serve"."build": "ng build"."test": "ng test"."lint": "ng lint"."e2e": "ng e2e"
},
"private": true."dependencies": {
"jquery": "3.3.1"."bootstrap": "This"."@angular/animations": "^ 6.1.0"."@angular/common": "^ 6.1.0"."@angular/compiler": "^ 6.1.0"."@angular/core": "^ 6.1.0"."@angular/forms": "^ 6.1.0"."@angular/http": "^ 6.1.0"."@angular/platform-browser": "^ 6.1.0"."@angular/platform-browser-dynamic": "^ 6.1.0"."@angular/router": "^ 6.1.0"."core-js": "^ 2.5.4." "."rxjs": "~ 6.2.0"."zone.js": "~ 0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~ 0.8.0"."@angular/cli": "~ 6.2.9"."@angular/compiler-cli": "^ 6.1.0"."@angular/language-service": "^ 6.1.0"."@types/jasmine": "~ 2.8.8"."@types/jasminewd2": "~ 2.0.3"."@types/node": "~ 8.9.4"."codelyzer": "~ 4.3.0"."jasmine-core": "~ 2.99.1"."jasmine-spec-reporter": "~ 2"."karma": "~ 3.0.0"."karma-chrome-launcher": "~ 2.2.0." "."karma-coverage-istanbul-reporter": "~ 2.0.1." "."karma-jasmine": "~ 1.1.2"."karma-jasmine-html-reporter": "^ 0.2.2"."protractor": "~ 5.4.0"."ts-node": "~ 7.0.0." "."tslint": "~ 5.11.0"."typescript": "~ 2.9.2." "}}Copy the code
Once this is done, you need to manually remove node_modules (in the main project as well as in the client project) and reinstall all packages:
`>npm install’
Now that THE NPM merge is done, it’s time to move the configuration
Mobile configuration
To move the configuration file (not the source code) to the root of the project. Depending on which framework you are using, you will have a set of configuration files. Presents, for example, there are presents. Json, tsconfig. Json and tslint. Json.
Because these files have been moved, you need to change all relative directories in the configuration file. For example, in angular.json, you need to change the path starting with SRC/to ClientApp/ SRC /:
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "version": 1, "newProjectRoot": "projects", "projects": { "ng-core": { "root": "", "sourceRoot": "ClientApp/src", "projectType": "application", "prefix": "app", "schematics": {... }, "architect": { "build": { "builder": "@angular-devkit/build-angular:browser", "options": { "outputPath": "wwwroot/client", "index": "ClientApp/src/index.html", "main": "ClientApp/src/main.ts", "polyfills": "ClientApp/src/polyfills.ts", "tsConfig": "ClientApp/src/tsconfig.app.json", "assets": [ "ClientApp/src/favicon.ico", "ClientApp/src/assets" ], "styles": [ "ClientApp/src/styles.css" ], "scripts": [] }, "configurations": { "production": { "fileReplacements": [ { "replace": "ClientApp/src/environments/environment.ts", "with": "ClientApp/src/environments/environment.prod.ts" } ], "optimization": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true } } }, "serve": { ... }, }, }, }, "defaultProject": "ng-core" }Copy the code
Now, you need to build your project, and the easiest way to do this is:
>ng build
Copy the code
If successful, the SPA should already have a role in the project. If you are using Visual Studio, you can use the Task Runner Explorer to help you get started.
Since the code is rebuilt whenever anything changes, you can bind it to Project Open so that the Project continues to build while you write the code.
Now you have all the work you need for development. However, if you want MSBuild to publish your project for you, you need to make some changes. As with the previous integration, you also need to add Target to.csproj:
<Target Name="client-app" BeforeTargets="ComputeFilesToPublish">
<Exec Command="npm install" WorkingDirectory="ClientApp">
</Exec>
<Exec Command="npm run build" WorkingDirectory="ClientApp ">
</Exec>
</Target>
Copy the code
Which way to choose
Many decisions depend on suitability, not technology or best practice. If the way you’re doing is working well, stick with it. In general, I prefer a fully integrated approach. My main reason is that when I add a SPA (or more than one), I rarely want to build the entire application. I want the Web to do what it does well, and to add new content when I need to add more control, a better user experience, and tighter user interaction.
Also, I don’t just use ASP.NET Core to develop an API. Many times it’s the right thing to do to generate a view on the server, whether it’s for data transfer security (such as sending total data rather than potentially valuable data), improving cache support or even using a layout.
These ideas are based on using the SPA framework (Angular,Vue,React, etc.) as a way to build functional modules. If you’re setting up a dominant SPA and think that’s the right way to go, good luck with that too. Although most of the time that’s not my choice. Of course, I’m just another developer. I could be wrong.