EEParker / aspnetcore-vueclimiddleware

Helpers for building single-page applications on ASP.NET MVC Core using Vue Cli or Quasar Cli.
Other
337 stars 65 forks source link

example of setup with vite istead of vuecli? #93

Open zoranpopovic opened 3 years ago

zoranpopovic commented 3 years ago

I think it would be great if there was an example of how to setup with vite instead of vue-cli. it seems that the middleware is general enough that this should be possible.

EEParker commented 3 years ago

I think the only change would be to the regex for whatever the export of Vite shows on the console when the web app is ready. Also, be sure to replace the contents of ClientApp with a Vite app instead of vue-cli.

https://github.com/EEParker/aspnetcore-vueclimiddleware/blob/master/samples/Vue3/Startup.cs#L67

                endpoints.MapToVueCliProxy(
                    "{*path}",
                    new SpaOptions { SourcePath = "ClientApp" },
                    npmScript: (System.Diagnostics.Debugger.IsAttached) ? "serve" : null, // make sure `serve` is the correct command
                    regex: "Compiled successfully", // change this to vite output
                    forceKill: true
                    );
coolhome commented 3 years ago

At the moment without these two tickets I'm not sure how far you can get with this.

The HMR requires websockets to be proxied. You can use something like AspNetCore.Proxy to forward the websockets but there is no way to tell Vite the port to actually run the HMR (runtimePort) and what to use in the HTML (hmr port - need to be the proxy server port).

An alterative approach however is to use Vite's proxy feature and skip this plugin and continue to use AddSpaStaticFiles/UseSpaStaticFiles for production builds. You would need to start the SPA project separately and use those endpoints instead. This approach does require the proxy url to be different than the vite project.

Run this command or use alternative methods for generating a SSL certificate. I used the following msbuild target.

    <Target Name="GetHttpsForLocal" BeforeTargets="Build">
        <Exec WorkingDirectory="$(SpaRoot)" Command="dotnet dev-certs https -ep ./localhost-key.pfx -p dev-passphrase" ContinueOnError="true">
            <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
        </Exec>
        <Error Condition="'$(ErrorCode)' != '0'" Text="Failed to get localhost key" />
    </Target>

vite.config.ts

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import { readFileSync } from 'fs'

const key = readFileSync('localhost-key.pfx');

export default defineConfig({
    plugins: [vue()],
    server: {
        https: {
            pfx: key,
            passphrase: 'dev-passphrase' // required for some reason -- maybe easier way?
        },
        proxy: {
            '^/api/.*': {
                target: 'https://localhost:5001',
                rewrite: (path) => path.replace(/^\/api/, ''),
                changeOrigin: true,
                secure: false
            },
            '^/swagger': {
                target: 'https://localhost:5001',
                changeOrigin: true,
                secure: false                
            }
        }
    }
})
zoranpopovic commented 3 years ago

So does this mean that all traffic goes through vite first, which forwards specific routes to dotnet while serving the vue routes directly? Are swagger supposed to go to dotnet?

EEParker commented 3 years ago

What about vite for production build?

This tool is meant for development only and to facility HMR and other features. Production should serve the static files directly using standard AspNet static file methods.

abezulski commented 2 years ago

I gave it a try and eventually hit a wall.

When using MapToVueCliProxy I got the error message related to the port parameter as it tries to append port to the npmScript command:

info: VueCliMiddleware[0]
      > echo Starting the development server && vite build --mode development "--port" "8080"
info: VueCliMiddleware[0]
      Starting the development server

fail: VueCliMiddleware[0]
      E:\vite-setup\vueapp\node_modules\vite\dist\node\cli.js:426

fail: VueCliMiddleware[0]
                throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);

fail: VueCliMiddleware[0]
                ^

fail: VueCliMiddleware[0]
      CACError: Unknown option `--port`

To accommodate regex changes for Vite (mentioned by @EEParker) I modified my npm script: "build-dev": "echo Starting the development server && vite build --mode development".

I guess one of two things need to happen:

yitzolsberg commented 1 year ago

I've got this mostly working now (albeit with Vue 2) due to the following 2 fixes in Vite:

In vite.config.js:

defineConfig({
    ///....
    server: {
        hmr: {
            clientPort: 44308,
        }
    }
})

package.json:

"scripts": {
    "dev": "vite",
    "serve": "vite preview",
    "build": "vite build"
  }

Program.cs (or Startup.cs):

endpoints.MapToVueCliProxy(
    "{*path}",
    new SpaOptions { SourcePath = "ClientApp" },
    npmScript: isDevelopement ? "dev" : null,
    regex: "ready in .+ ms", //vite 3 doesn't show "running at" text anymore. "\d+ ms" doesn't work because of color formatting
    port: 8080,
    forceKill: true
    );

However, quite often on the first run, a few of the requests from vite for certain components are failing. The requests make it to the vite proxy, but are failing with:

System.Net.Http.HttpRequestException: Failed to proxy the request to http://127.0.0.1:8080/src/XXXXXX.vue?vue&type=style&index=0&scoped=477d7e3e&lang.css, because the request to the proxy target failed. Check that the proxy target server is running and accepting requests to http://127.0.0.1:8080/.

The underlying exception message was 'An error occurred while sending the request.'.Check the InnerException for more details.
 ---> System.Net.Http.HttpRequestException: An error occurred while sending the request.
 ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
 ---> System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.
   --- End of inner exception stack trace ---

etc etc

A refresh of the page and all works fine. I can't work out what the issue is.

kubayubay commented 1 year ago

I have it working just fine and I'm using vite3 with vuejs3 in my .NET 6 project. What I have:

vite.config.ts - I had to set server.host to true because of some VueCliMiddelware was pushing for 127.0.0.1 while vite was expecting localhost and I needed to set server.hmr.protocol to 'ws' for websocket:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    host: true,
    hmr: {
      protocol: 'ws',
      port: 52896
    }
  },
})

Program.cs (or Startup.cs) - like @yitzolsberg said, vite3 prints out ready in [x]ms so you gotta make VueCliMiddleware look for that:

spa.UseVueCli(npmScript: "dev", port: 3000, false, ScriptRunnerType.Npm, "ready in", true);

Hope this helps!