How my application is split into chunks
My applications main and only entry chunk contains some common logic that is reused throughout the application such has layouting or helper methods and components related to authentication such as login/registration dialogs. This chunk has react, react-dom, redux, react-router and some other libs from node_modules directory in its dependency tree because they are being used by the authentication components.
All those dependencies are bundled into an app.js file that is always initially loaded. Whenever the user starts to use some part of the application that requires a logged in user - such as dashboards, forms and other app functionality, additional javascript chunks comprising that functionality are loaded. Those additional chunks are automatically generated by webpack because of import() calls distributed throughout the applications code.
When a new user loads the app and is not logged in, app.js is loaded and the user is presented with a login mask. After a user has successfully logged in and is redirected to the main dashboard, the code of the dashboard is asynchronously loaded. This code, automatically built into a dashboard.js chunk by webpack, also depends on a lot of the libraries already baked into the app.js chunk (e.g. react, react-dom etc). Because the dashboard.js chunk does not duplicate those libraries, it is quite small compared to the app.js.
To make good use of browser caching, all of those chunks (in this simplified
example: the entry chunk app.js and the dynamic chunk dashboard.js) have a
[chunkhash] in their name. In comparison to library code from node_modules, both the code of the dynamic chunks and the parts of the app.js that are actual application code change quite a lot.
What i would like to do
I would like to configure webpack in a way that detects which libraries from
node_modules are used in more than 2 (or any configurable number) of chunks and
automatically split those into an additional chunk, vendor.js.
This chunk should contain the library parts of the current app.js that don't change often so the user doesn't often re-download library code.
From what i've understood, something like this can be done with
CommonsChunkPlugin when manually adding lots of entry chunks. Can i also do
this with dynamic code splitting performed with import() calls?
Example
I might use lodash.partialRight somewhere in in dashboard.js. As long as it is only used there, i want it to be bundled with dashboard.js. The moment i also use it in the login/registration process, it should not be bundled into the
app.js the way it is now but should instead automatically be bundled into vendor.js with other libraries that are re-used across chunks.
You'll have to list out the libraries that you want to be added to vendor.js explicitly. https://webpack.github.io/docs/code-splitting.html#split-app-and-vendor-code
The rest of your code and node_modules that are not listed in "vendor" will be split automatically by webpack. So the more libraries you list in
vendor: ["jquery", "underscore", ...],
the larger vendor.js file is but with less frequent re-download from your users.
Related
I'm learning nodejs and vuejs to modify an already created web site.
I installed nodejs, vue and vue-cli and I launched "npm run serve" which apparently start "vue-cli-serve serve"
The problem is that I don't understand what this web server do on files, in this documentation : https://cli.vuejs.org/guide/prototyping.html , it's told:
It automatically infers the entry file in the current directory - the entry can be one of main.js, index.js, App.vue or app.vue. You can also explicitly specify the entry file:
vue serve MyComponent.vue
Ok, but does it run main.js, does it include it into a js file which is the loaded by the index.html on the client broswer.
I see in the browser that the page load a js file named like that: app.23d...js
My question is how this js file is created?
For instance, when the content of main.js is this one:
import './css/icon.css'
Vue.use(VueResource)
Vue.use(VueScrollTo)
what is the output in the app....js file?
It seems it doesn't work at all like php which I usually use on web server
Thank you
Vue uses Webpack to convert your potentially-numerous distinct .js files into bundles with names like app.23d92ab88708...js
From the Webpack documentation:
Concepts
At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph from one or more entry points and then combines every module your project needs into one or more bundles, which are static assets to serve your content from.
I don't suggest trying to get into the details of how Webpack works
This will take a lot of time. If you have an existing Vue project, you are much better off spending your time interpreting that as a Vue project, and accepting that the conversion into the actual app.23d.....js file or files is an automatic process that you do not need to involve yourself in.
It will avoid a colossal waste of time
It won't advance your understanding of how the Vue project works
Whatever you learn about the exact workings of today's Webpack, may be completely wrong about tomorrow's Webpack.
Nevertheless the interface that Webpack provides to you as a Vue programmer will remain constant over future versions.
This is the concept of software abstraction. It is highly advantageous to not have to know how every step of every process works, as long as you know how it is designed to respond to actions you take at a high level.
Horrific thought
I have just re-read your opening sentence:
I'm learning nodejs and vuejs to modify an already created web site.
Please tell me that you are not trying to modify an already created web site where you only have the compiled website available, without the Vue source code? That would be a stupendously painful enterprise.
Base on the Sapper documentation, Rollup bundles Sapper by pages. It should be one chunk per page. However, in our situation, there is more than one chunk per page. Some of the components and utility functions (not all of them, not none of them, but some) got bundled into its individual chunk.
For example with the screenshot below, AdBanner and TextExpander are components within the sapper page, but they got bundled into their own js files rather than being with the sapper page js file index.27bfb559.js.
However, looking at the sapper page js file index.27bfb559.js, SimilarDrugTable is bundled with the sapper page js file.
Essentially, what we want to achieve is one chunk per page like Sapper documentation states.
I just wondering let's say I already have a website. If I would like to inject some react modules into it lets say MODULE-A, MODULE-B, MODULE-C. At some situations, MODULE-A and MODULE-B are on the same page. All modules are sharing react, axios but also they have their own dependencies. what is actually the best approach to prepare build for it?? I actually like create-react-app. Is there any smart way to make modules only load required dependencies? so I can include them separately and if both exist on the page they will not clone dependency in code.
I want to avoid the situation that I have 20 small modules and all of them use react inside built code. So 60% of the code will be duplicated dependencies.
You have not many options
You can leverage code-splitting as referred here https://webpack.js.org/guides/code-splitting/ so let's say you have
ModuleA -> [depA, depB, depC]
ModuleB -> [depA]
ModuleC -> [depC]
now you can code split to 4 javascript files which will be
moduleAbundle.js
mobuleBbundle.js
mobuleCbundle.js
vendors.js
vedors will be concatenation of depA, depB, depC but ofcourse when you will load moduleB and ModuleC on 1 page you will be also loading depB as it's included in vendors.js you will need to think and play a lot with orchestration of this.
But what you can actually do you can think of most critical parts and most shared deps and list them, let's say all of your packages are using react, react-dom, lodash you will make bundle for vendors with only those libraries, any other library will be bundled with module itself and yes you can't 100% elimate "double-loading" but if you will double load library which has several kB it's fine, when it's larger, ship it with vendors.js
or you can have some complex mechanism on backend side when you will exactly specify and orchestrate loaded dependencies because you know which modules are rendered and what are they dependencies. But there are no any ways of on-the-fly bundling and sending only needed and requested dependencies, it would be very slow to do that on-the-fly and you need to flush quick response to the browser not slow it down with additional compilation process. So it's most likely a manual job to correctly prepare and split code in logical chunks which makes sense.
I dont know answer for React but for the future, maybe you can think about using Angular instead, and its ngModule capability into splitting application to multiple independent modules with their own dependences.
https://angular.io/guide/ngmodules
I have deployed a Laravel 5.3 application to Heroku. However, when loading /login, I noticed a very slow page load time. The problem seems to be a very large app.js file: /js/app.js. Here is a screenshot of the Network resource panel in DevTools: screenshot- Network panel. The 3rd resource from the top is the offending file.
I am not sure why this file has gotten so large. here is a link to the repository: https://github.com/AshMenhennett/Salon-Pricing.
I wasn't able to post anymore links, so do let me know if you would like direct links to specific files.
What should I be doing to mitigate this issue?
The most obvious thing you can do is to run npm run prod. This will compile the assets for production use. But in most cases, you must be looking at other solutions beyond running npm run prod. If your production file is too large, you must check your dependencies. Remove unnecessary dependencies and ensure that you don't use a lot of external libraries. For example, if you are using bootstrap, you should rely on Bootstrap's alerts in order to show alerts rather than using a Vue package to show alerts. I admit that sometimes you will need to use an external library to make your website interactive but to achieve that, you will have to sacrifice the performance. So your best bet in order to reduce the app.js file is to use the minimal external dependencies in your package.json.
The second thing you can do is use minimum HTML in your components' templates. A lot of components with heavy HTML/CSS will contribute to a larger app.js file. This is yet another approach that will result in a smaller app.js file.
Lastly, you should consider using Vue's component slots to pass HTML contents to your components. This will leave the HTML in your static files and only javascript data (API calls, props, etc.) will be compiled in the app.js file. This is an effective approach to build a smaller app.js file.
Edit: You can remove JQuery and Bootstrap scripts from the bootstrap.js file and can include these dependencies separately. It is always a good idea to have a few more scripts rather than having a very large script. i.e. browsers do parallel downloading and thus using JQuery and Bootstrap dependencies separately is a good idea.
From the looks of your link you've not created a production version of your assets, and currently all the source maps are in your app.js file, which will be adding a lot of the file size, the css and js output are also not compress/minified either.
Assuming you're using laravel elixir, you just need to run gulp --production and this will remove the source maps, compress the js and css outputs, etc.
For people that are using Laravel Mix you just need to run npm run prod to compress and remove source maps from app.js itself.
You need to load the components asynchronously
Webpack has an awesome feature to create chunks of code. The key to this is to use async components. These components get loaded completely asynchronously whenever the component is present on the page you just loaded.
Let's do it.
In resources/js/app.js
I changed
Vue.component('jobs', require('./pages/employer/jobs/Index.vue').default);
To
Vue.component('jobs', () => import('./pages/employer/jobs/Index.vue'));
and in webpack.mix.js
mix.webpackConfig({
output:{
chunkFilename:'js/vuejs_code_split/[name].js',
}
});
Now by running npm run watch or prod each component file is saved public/js/vuejs_code_split/[name].js
And the main app.js is automatically calling those components when required.
Webpack makes it really easy to bundle things together, and also asynchronously requiring single chunks.
However I can't quite work out how to delay requiring a set of dependencies needed by only a few parts of my app.
Since it's an SPA, I'd like to avoid using a <script> tag and have it load a set of larger (namely brace, esprima, lodash etc) dependencies when the user reaches the relevant parts of the app.
This is similar to the vendor split approach, except I only need the essential modules for navigation, signup, login etc. When using chunks out the box it duplicates these dependencies for each chunk, so they are common dependencies - but I'd like to have more than one common bundle; and be able to load it asynchronously when needed.
This is called code splitting. (Webpack Code Splitting) Take a look at the Webpack site for a "brief" breakdown of how it works. Most of the tutorials out there reference code splitting for critical path rendering, but the concept is the same and should be applicable for your situation.
I would recommend creating an entry for the your dependency bundles, and then reference that withing each module. While you should theoretically be able to just pull in dependencies for each module independent of the "bundled dependencies", I have ran into issues with webpack where dependencies are reinitialized when referenced by separate bundles, which causes issues for dependencies that are meant to be singletons.