Je me suis lancé il y a peu dans un projet pour un ami, afin de l’aider à automatiser certaines tâches. Comme je suis une quiche en frontend, je me suis dit que c’était l’occasion d’essayer un framework JavaScript. J’ai mis de côté les deux poids-lourds Angular et React pour jeter mon dévolu sur Vue.js. Pourtant, j’ai galéré comme un veau à avoir un projet qui compile et qui soit fluide.
Je vais donc vous faire un retour d’expérience pour que, dans le cas où vous seriez dans la même situation que moi, vous puissiez moins galérer.
Créer le projet
Comme je l’ai dis, je voulais utiliser .NET Core. Je suis donc parti sur un back Web API et un front utilisant Vue.js et TypeScript. Pour le back, aucun problème. Seulement voilà, même avec Visual Studio 2017, il n’y a pas de template pour créer un front utilisant Vue.js. Bien sûr, il y a Angular et React, mais pas celui que je cherche.
Pas de soucis, on va chercher sur Internet. On tombe sur cette commande pour installer les templates SPA.
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
Il y a bien un template Vue.js, mais celui-ci est complètement dépassé : Webpack 2 (aujourd’hui 4), jQuery 2 (aujourd’hui 3), TypeScript 2 (aujourd’hui 3), etc. Le projet compile, mais dès que je tente de mettre à jour les dépendances NPM, c’est la catastrophe, tout s’effondre. Le code s’exécute, mais je me prends une volée d’erreurs à l’exécution, dont même une recherche Google n’a pas été capable de m’aider.
// Des dépendances pas à jour.
{
"name": "Site",
"private": true,
"version": "0.0.0",
"devDependencies": {
"@types/webpack-env": "^1.13.0",
"aspnet-webpack": "^2.0.1",
"awesome-typescript-loader": "^3.0.0",
"bootstrap": "^3.3.6",
"css-loader": "^0.25.0",
"event-source-polyfill": "^0.0.7",
"extract-text-webpack-plugin": "^2.0.0-rc",
"file-loader": "^0.9.0",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.1.1",
"style-loader": "^0.13.1",
"typescript": "^2.2.1",
"url-loader": "^0.5.7",
"vue": "^2.2.2",
"vue-loader": "^11.1.4",
"vue-property-decorator": "^5.0.1",
"vue-router": "^2.3.0",
"vue-template-compiler": "^2.2.2",
"webpack": "^2.2.0",
"webpack-hot-middleware": "^2.12.2"
}
}
Alors on continue et on tombe sur ce projet. Un nouveau template à installer. Mais celui-ci n’est que du JavaScript pur, ce qui ne m’arrange pas. Et en jetant un œil aux dépendances, elles ne sont pas vraiment plus à jour. Mince.
Je tombe sur cet article qui va sans doute m’aider. Alors je suis les instructions, mais l’article n’est pas clair, je galère, je n’y arrive pas. Je me tape toujours la même exception.
Puis, après encore d’autres recherches, je tombe sur ce projet. Une lueur d’espoir se rallume. Je suis les instructions, je créé un projet pour tester, il marche, les dépendances ne sont pas trop vieilles, je suis sur le bon chemin.
Mon projet ne compile pas !
Je vais donc créer mon projet moi-même et m’inspirer de ce qu’a fait l’auteur du template pour compléter ce qui me manque et ainsi avoir un projet fonctionnel et personnalisé. Je créé d’abord un projet ASP.NET Core vide, que je vais remplir au fur et à mesure. Et, évidemment, je rencontre plusieurs erreurs que je ne voyais pas, même avec le template le plus à jour.
You are using the runtime-only build of Vue where the template compiler is not available.
Une recherche assez rapide m’indique une ligne à rajouter dans le fichier de configuration de Webpack. Un alias
module.exports = {
...
resolve: {
...
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
...
}
...
}
TypeScript : Cannot find module './App'.
Précisons que cette erreur a lieu alors même que le module est accessible et dans le même dossier que le fichier .ts
en cours d’édition. Apparemment, TypeScript n’aime pas trop les fichiers .vue
. On règle se problème en créant un fichier vue-shims.d.ts
, à la racine du projet.
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
Et, comme j’utilise vue-loader
dans sa dernière version, j’ai eu le droit à une belle erreur due à des modifications dans la v15 de ce package.
Vue loader was used without the corresponding plugin. Make sure to inclure VueLoaderPlugin in you wekpack config.
Bon, il faut donc importer ce plugin et puis l’ajouter à la liste correspondante.
const VueLoaderPlugin = require('vue-loader/lib/plugin');
...
module.exports = {
...
plugins: [
new VueLoaderPlugin(),
...
]
...
}
Maintenant, le projet compile et s’exécute, mais survient un problème qui m’embête : le Hot Module Replacement ne fonctionne qu’une seule et unique fois. Ce module permet de recharger les fichiers .vue
(entre autres) au moindre Ctrl + S, au lieu d’un F5 ou d’une recompilation. Pourtant, sur l’autre projet, ça marche. Alors, où est le problème ?
Ce n’est pas le fichier Startup.cs
, puisque le HMR est bien activé.
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
Peut-être qu’en changeant les ports de l’application ?
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:51192/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"vuets": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:51193/"
}
}
}
Décidément, d’où vient ce problème. Aucune des solutions que j’ai trouvé sur Internet n’a fonctionné pour moi. Suis-je condamné à m’en passer ? En désespoir de cause, je modifie mon fichier .csproj
. Sait-on jamais…
Voici celui avant.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<TypeScriptToolsVersion>3.1</TypeScriptToolsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.2.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
Et le voici après.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />
<Exec Command="node node_modules/webpack/bin/webpack.js --env.prod" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="wwwroot\dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
</Project>
Eh bien, aussi bizarre que ça soit, ça marche !
Est-ce un Nuget supprimé ? Est-ce la ligne <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
qui empêchait le HMR de fonctionner ? J’avoue que je n’en sais pas grand chose. Par contre, maintenant, mon projet compile, s’exécute et le HMR est fonctionnel.
En conclusion
Arf, j’ai bien galéré. Autant le back, ça a roulé nickel chrome tout seul, autant le front j’en ai douillé comme pas possible. Et je suis entièrement d’accord avec @SpaceFox sur la folie furieuse de la communauté.
Y’a un moment faut arrêter de délirer. Tous les millions de packages pour une appli simple, chaque mise à jour qui peut potentiellement déprécier la moitié de ton code et donc te forcer à pleins de modification sans aucune valeur ajoutée, les erreurs cryptiques, les documentations pas à jour et donc un code marche chez ton voisin mais plus chez toi, etc.
Voilà, j’espère que si, vous aussi, galérez comme pas permis, j’ai pu vous aider un peu. Bonne journée bien zestueuse.