📜 ⬆️ ⬇️

RequireJS for Vue.js + Asp.NETCore + TypeScript applications

logos


Create a modular application Vue.js + Asp.NETCore + TypeScript on Visual Studio 2017. As a build system, instead of Webpack, we use the TypeScript + Bundler & Minifier compiler (extension to VS2017). Application modules are loaded into runtime by SystemJS or RequireJS. We consider the format of AMD modules (asynchronous module definition), which is understood not only by SystemJS, but also by RequireJS.


I’m warning you right away that Vue.js doesn’t completely support AMD or it contains a bug, so almost hacker trick is used, it’s not suitable for everyone. But I hope this article will allow you to better understand how this world Vue.js.


This article is an addition to the tutorial: Application Vue.js + Asp.NETCore + TypeScript without a Webpack . Where in the examples the format of the SYSTEM modules was used. To bet only on the SystemJS bootloader, as it is, scary. At the time of writing, SystemJS has a release of 0.20, which means it is likely that radical changes in the API, options, etc.


The purpose of using the format of AMD modules and the RequireJS loader is to insure against radical changes in SystemJS, to ensure the possibility of using the more popular RequireJS loader and the format of AMD modules.


The material is designed for those able to cope with VS2017 and familiar with the progressive JavaScript framework Vue.js.


Introduction


Initially, the material that relates to AMD and RequireJS was in the article: Application Vue.js + Asp.NETCore + TypeScript without a Webpack . But it turned out too big, so I had to cut a piece. I believe that for the sake of completeness, it is useful to understand also AMD and RequireJS.


Switch to AMD


The TypeScript compiler assembles all modules into a single output file if the {“module”: “system”} or {“module”: “amd”} option is set. With the option "system" it turned out, we must now try "amd". Without this, RequireJS cannot be applied, because This bootloader only understands the amd format of the modules.


Start application


As a starting point, you can take on TryVue’s github project from the Visual Studio 2017 solution, which was described in the main article. Further actions can be performed in this project, but it is better to create a copy under the name TryVueRequire.


After building the application and bundles, the following files should appear in the wwwroot \ dist directory: main.js, main.css, app-templates.html. Run the application in any way convenient for you (F5, Ctrl-F5 in the VS2017 environment, or outside: dotnet run).


The browser should have something similar to the one shown in the screenshot.


image


I remind you that the browser caches the files of your application, so you need to refresh the page correctly (with a reset of the cache). If you suspect something is wrong with further experiments, reset the browser cache.


Error: vue_3.default is not a constructor


Now in the tsconfig.json file, we change the compiler option to {“module”: “amd”}, reassemble the application and try to start.


You should see the text "loading .." in the browser, and an error in the console if you switch to developer mode (for Chrome, use the F12 key).


By removing some code fragments in wwwroot \ dist \ main.js, you can quickly figure out how to swear and what exactly. I quote excerpts and the main.js file with lines that fail, as well as the error texts:


define("components/Hello", ..., function (...) { ... exports.default = vue_1.default.extend({ ... }); define("components/AppHello", ..., function (...) { ... exports.default = vue_2.default.extend({ ... }); define("index", ..., function (...) { ... var v = new vue_3.default({ ... }); 

 Uncaught (in promise) Error: Cannot read property 'extend' of undefined Uncaught (in promise) Error: vue_3.default is not a constructor 

The first error relates to vue_1.default.extend, vue_2.default.extend. The second error relates to vue_3.default. From the text of the errors, it is clear that vue.js is not defined by the "default" constructor. It is very simple to verify this - delete this default after the point at vue_1, vue_2, vue_3.


The application will work! It remains to figure out what to do with the undefined default property of instances of Vue.js.


Patch for SystemJS


Surely, deleting the "default" from the wwwroot \ dist \ main.js file with your hands after each build of the project is not the most convenient option. Therefore, we will make a patch in the wwwroot \ index.html file, which does the following: loads vue, sets default property for it, loads main.js and starts the application.


 <!-- wwwroot\index.html--> <!--  : --> SystemJS.import('dist/main.js').then(function (m) { SystemJS.import('index'); }); <!--   : --> SystemJS.import('vue').then(function (m) { if (!m.default) { m.default = m; console.warn('HACK: vue.default was undefined'); } SystemJS.import('dist/main.js').then(function (m) { SystemJS.import('index'); }); }); 

The application should earn. After checking this option, save the index.html to memory in index-system.html.


Transition to RequireJS


With amd-format modules, the use of RequireJS differs from SystemJS only in the configuration method and API (aplication program interface).


To switch to RequireJS, changes are made exclusively in the wwwroot \ index.html file. Source files of TypeScript code, as well as the project settings do not touch.


Simple patch option for RequireJS


Before offering another definition of the Vue.js default constructor, let's make a complete analogue of index-system.html using RequireJS. In the file wwwroot \ index.html it is enough to change the load of the script system.js -> require.js. Then configure require.js, download the necessary and run the application. We also repeat the default prescription code in the absence.


 <!-- wwwroot\index.html--> ... <script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> <script> require.config({ paths: { "vue": "https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue", "index": "dist/main" } }); require(['vue'], function (m) { if (m.default === undefined) { m.default = m; console.log('HACK: ' + m.name + '.default was undefined'); } require(['index']); }); </script> ... 

We save wwwroot \ index.html into memory in the index-require.html file in order to implement a more "correct" version of the problem of an undefined default constructor in index.html.


Redefinition of calls to Vue.js


There is another solution to the problem of the absence of the default property for Vue instances. Before defining modules in main.js, we define a small adapter, which closes all calls to "vue" and makes the adjusted export "vue-parent" (the renamed true "vue").


Actually the definition of the adapter:


 define("vue", ["require", "exports", "vue-parent"], function (require, exports, vueParent) { Object.defineProperty(exports, "__esModule", { value: true }); exports.default = vueParent.default || vueParent; }); 

Some of the wwwroot \ index.html text relating to the use of the adapter is shown below. To simplify this example, the text of the adapter is included in index.html. It is more correct to keep it as a separate file vue-stub.js, which should be glued to the beginning of main.js with a concatenator.


 <!-- wwwroot\index.html--> ... <script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> <script> // vue-stub.js define("vue", ["require", "exports", "vue-parent"], function (require, exports, vueParent) { "use strict"; //if (!vueParent.default) console.warn('HACK: vue.default was undefined'); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = vueParent.default || vueParent; }); </script> <script> require.config({ paths: { "vue-parent": "https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue", "index": "./dist/main" } }); require(['index']); </script> ... 

Conclusion


Maybe I do not understand something like that, but the vue.js library in Vue instances does not provide the definition of the "default" property that is expected of it in AMD (asynchronous module definition) modules. Honestly, this is a bug or a feature - I do not know.


In such pies with kittens.


In order to use the amd-format of the modules and RequireJS I had to insert my adapter in which to define default. If anyone knows a more correct way - share.


References:



UPD 03/01/2018:


@mayorovp suggested a more correct solution to the problem with amd-modules: in tsconfig.json add the compiler option {"esModuleInterop": true}.


After that, the adapter is no longer needed, and the use of RequireJS becomes trivial. Therefore, downloading and launching the application will look like this:


 <!-- wwwroot\index.html--> ... <script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.5/require.js"></script> <script> require.config({ paths: { "vue": "https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue", "index": "./dist/main" } }); require(['index']); </script> ... 

All necessary changes are made to the decision VS2017 on github .


')

Source: https://habr.com/ru/post/350056/


All Articles