Why requiring javascript asynchronous loading under my webpack configuration - javascript

I'm really struggling with odd codes.
I want to include some query parameters by $.ajaxPrefilter in all jQuery ajax request.
I found below the code make it if it load correctly synchronous order.
But, following code in entry.js loaded jscode in unpredictable order.Sometimes prefilter.js were preloaded ,the other time, post_ajaxs.js were preloaded.(I inspected server post message,and sometimes lack of data and I checked timing loading using chrome devtools).
require(['prefilter'])
$(function(){
if($("#page_id").length > 0) {
require([
"src/common/post_ajax.js"
]);
}
});
Why is it caused? I'm confused because I first thought require keyword is synchronous loading.
I show partial fragment of webpack.config.js may be related.
entry: {
/webroot/js/entry.js: __dirname+"/src/entry.js"
},
resolve: {
alias: {
"prefilter": __dirname + "/src/common/prefilter.js",
}
},
output: {
path: __dirname + "/webroot/js/",
filename: "[name].js",
chunkFilename: "[hash].[id].js?" + (+new Date()),
publicPath: "/js/"
},
plugins: [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.AggressiveMergingPlugin(),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
d3: "d3"
})
]
I want to enforce prefilter.js preloaded and load post_ajax after it. Please, give me help.Any information I'll appreciate.
Edit:
Here is prefilter.js.
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
options.data = $.param($.extend(originalOptions.data, {'data[extra]':$("#some_id").val() }));
}
});

It might be scope problem because outer required js is global scope and not nested by function curly braces. Webpack might separate dependencies because I test two case using require.ensure nesting $(function{}) and putting require(['prefilter']) into $(function{})'s scope, both works correctly though it is my prediction.
But,more correct answer welcome if you have.

Related

Missing Script generated by Webpack(development) on first page load

the first time i load the login page the login.js script doesn't load , only the shared.js bundle loads (which has bootstrap js/css and toastr css), if i refresh the page everything loads without problems and after that there are no problems.
Pretty much every time i restart my local server i need refresh the login page to be able to login
my webpack config is as follows
export default {
entry: {
shared: [
'./src/3rdparty/bootstrap/bootstrap.min.js',
'./src/3rdparty/bootstrap/bootstrap.min.css',
'./src/3rdparty/toastr/toastr.min.css',
],
login : {
import: './src/pages/login.js',
filename: 'main/login.js',
dependOn: 'shared',
chunkLoading: false,
}
},
output:{
path: fileURLToPath(new URL('public',import.meta.url)),
clean:true,
},
resolve:{
extensions: ['.js']
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
],
mode: 'development'
}
The project has been build using Node 16.17 / Expressjs 4
It works without problems if i change the mode to production
Going to write in case i encounter the same issue again:
after adding the compression library i cannot replicate the issue
another issue that i had was that when i was returning messages from the server with the status 400, it would return a invalid string Ex:
{ok:true, description:"username invalid"
always missing the closing bracket }
this issue has also been resolved by using compression middleware

Webpack - Merging splitted bundles

I have one big bundled JS file which contains scripts that can run at the end of page loading.
In other words - I wanted to reduce the size of the first downloaded JS file by multiple entry points
So I've created two entry points :
module.exports = {
entry: {
a: "./scripts/entry.js",
b: "./scripts/windowEvents.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin("common.js")
],
output: {
path: path.join(__dirname, '/scripts/bundle'),
filename: "[name].entry.js"
},
module: {
loaders: [
]
}
};
So now I have :
a.entry.js
b.entry.js
common.js.entry.js
And my HTML file looks like :
<script src="/scripts/bundle/common.js.entry.js"></script>
<script src="/scripts/bundle/a.entry.js"></script>
//Html renders fast
//bottom page script
<script src="/scripts/bundle/b.entry.js"></script>
But here is the problem :
The first two scripts sections makes 2 requests to the server. I don't want that
Question:
How can I merge the first two bundles into one bundle there will be only one request ? In other words I want to merge a.entry.js & common.js.
Something like that :
<script src="/scripts/bundle/Common_AND_Entry_a.js"></script>
//Html renders fast
//bottom page script
<script src="/scripts/bundle/b.entry.js"></script>
You can give the CommonsChunkPlugin the name of an existing chunk and it will add it to that instead of creating a new file. So in your case it would get the name a (as shown in Explicit vendor chunk):
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "a"
})
],
If you want to change the output filename as well, you can do that with the filename option, which accepts the same placeholders as output.filename. The following produces common_and_a.js and b.entry.js:
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "a",
filename: "common_and_[name].js"
})
],
For all available options see CommonsChunkPlugin - Options
Recently, I ran into this same issue, but time has passed and CommonsChunkPlugin has been deprecated in favour of SplitChunksPlugin.
Currently there is no way to achieve the intended behaviour by using only Webpack's included functionalities, so I wrote a plugin that solves this problem and I'm glad to share it with who may need it!
You can find it HERE.

Webpack with two common chunks: one exported, one local

I would like to use Webpack in a multi-page application in such a way that some pre-determined dependencies are bundled into a "vendor" chunk and the rest of the dependencies is bundled into a "commons" chunk.
For example, assuming two entry points (effectively representing a different page each), pageA.js and pageB.js contain both this code (in EC6, processed via Babel), followed by their own code:
import $ from 'jquery';
require('bootstrap/dist/css/bootstrap.css');
import angular from 'angular';
import uitree from 'angular-ui-tree';
I'd like jQuery and Bootstrap to be bundled into a "vendor" chunk, and the rest (whatever that is) to be bundled into a "commons" chunk.
The objectives are:
I would like to be able to have another separate build that would be able to rely on that same vendor chunk, without it needing to re-include the vendor libraries (I would explicitly declare that set of vendor libraries, to make it available to any sub-build that needs it).
I would also like not to have to re-process the vendor chunk every time I make a change to a page's script.
Here is the configuration I've tried:
module.exports = {
entry : {
"vendor" : [ "jquery", "bootstrap" ],
"pageA" : "./src/pageA.js",
"pageB" : "./src/pageB.js"
},
output : {
path : path.join(__dirname, "./dest"),
filename : "[name].chunk.js"
},
module : {
loaders : [ {
test : /bootstrap\/js\//,
loader : 'imports?jQuery=jquery'
},
/* ... Some modules for angular and CSS processing ... */
{
test : /\.js?$/,
include : [ path.resolve(__dirname, "./src") ],
loader : 'babel',
query : {
presets : [ 'es2015' ]
}
}
/* ... Some other settings for the fonts ... */ ]
},
plugins : [
new webpack.ProvidePlugin({
$ : "jquery",
jQuery : "jquery"
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap : false,
mangle : false,
compress : false
}),
new CommonsChunkPlugin({
name : "vendor",
minChunks : Infinity
}),
new CommonsChunkPlugin({
name : "commons",
minChunks : 2
})
]
};
With the configuration above, I get jQuery and Bootstrap in vendor.chunk.js, as required, but the commons.chunk.js file is almost empty, all the rest of what's commonly used by pageA.js and pageB.js is then put into pageA.chunk.js and pageB.chunk.js (effectively duplicating that code).
If I swap the order of the last two plugins, commons.chunk.js now contains almost everything (unless what's actually specific to pageA.js and pageB.js), and vendor.chunk.js is almost empty:
plugins : [
// ...,
new CommonsChunkPlugin({
name : "commons",
minChunks : 2
}),
new CommonsChunkPlugin({
name : "vendor",
minChunks : Infinity
})
]
Is there a way to bundle a pre-defined list of libraries (e.g. [ "jquery", "jquery-ui", "bootstrap" ] into one particular chunk (in such a way that it can be used by completely independent scripts) and also have another common chunk for whatever else is in commonly used between the entry points?
The aim of all this would be to be able to build a completely separate piece of code for another page later, and tell it it doesn't need to re-bundle those pre-defined libraries.
Here is a diagram representing what I'm trying to achieve:
I would then use the generated scripts as follows on page A:
<script src="vendor.js"></script>
<script src="common.js"></script>
<script src="pageA.chunk.js"></script>
And on page C (built completely independently from pages A and B):
<script src="vendor.js"></script>
<script src="common2.js"></script>
<script src="pageC.chunk.js"></script>
(I am using Webpack 1.12.14.)
I have tried the solution suggested in the only answer so far. While this makes it indeed possible to separate the vendor chunk from the commons chunk, the vendor chunks (with the same definition) made from two separate builds generally cannot be swapped between each other. This does not make it possible to use only one of those vendor chunks across two builds (even though they are virtually the same).
I was working on something similar. I observed the behaviour you desire by having this configuration:
const entryPath = path.resolve(__dirname, "src", "modules"),
entryPoints = {
vendor: ["babel-polyfill", "react", "react-dom", "react-pure-render", "react-simpletabs", "react-redux", "redux", "redux-thunk"],
a: entryPath + "/a.js",
b: entryPath + "/b.js",
c: entryPath + "/c.js",
d: entryPath + "/d.js",
...
}, plugins = [
new CommonsChunkPlugin({
name: "commons",
filename: "commons.bundle.js",
minChunks: 2,
chunks: Object.keys(entryPoints).filter(key => key !== "vendor")
}),
new CommonsChunkPlugin("vendor", "vendor.bundle.js"),
new ExtractTextPlugin("[name].bundle.css", {
//allChunks: true
})
];
so this code above left in b.bundle.js still some import of React and other React libraries. after I added "fixed-data-table" to the vendor list, that disappeared and the only react source code there was was in vendor.bundle.js I assume that is what you were looking for? (in the end each vendor not listed in vendor list ended up in each own module.bundle.js or in commons.bundle.js if it was re-used in multiple bundles)
Regards
Jonas
As far as your request to be able to use the vendor chunk created in one webpack build with bundles from another build, the only way I have found to do this is through a combination of expose-loader and externals.
In my case, I successfully used this to bundle jQuery (and Bootstrap) into a "common.js" in one build, and then use that jQuery in modules belonging to another build, as follows:
1. Expose jQuery from build A chunk
module: {
loaders: [
{
// export jQuery globals for other modules to use as externals
test: require.resolve('jquery'),
loader: 'expose?$!expose?jQuery'
}
]
}
2. Consume jQuery in build B modules
externals: {
'jquery': 'jQuery'
}
Of course, the drawback to this is that you're bridging between the two sets of bundles by using manually-managed global variables, which you probably started using webpack to avoid :-)
However, I don't know of any other way at this point, given that every webpack build creates its own namespace of "private" IDs internally to reference modules, with no guarantee of stability or global unique-ness.

Error when using webpack with jQuery

I'm just starting to use Webpack, and I have an issue when I try to use jQuery with it. I'm a very newbie with Webpack, so please apologize if my question seems very basic.
Here is my webpack.config.js:
var webpack = require("webpack");
var path = require("path");
var bower_dir = __dirname + "/public/js/bower_components";
var compiled_dir = __dirname + "/public/js/compiled";
module.exports = {
entry: {
signup: compiled_dir + "/signup"
},
plugins: [
new webpack.ProvidePlugin({
$ : "jquery",
jQuery : "jquery",
"window.jQuery" : "jquery",
"root.jQuery" : "jquery"
})
],
resolve: {
alias: {
jquery : bower_dir + "/jquery/src/jquery",
}
},
output: {
path: path.join(__dirname, "public/js/dist"),
filename: "[name].bundle.js"
}
};
As I'm using ProvidePlugin (for jQuery plugins), I have no require("jquery") at the begininng of my file.
However, at execution, I get this output on the console :
TypeError: jQuery.expr is undefined
http://localhost:9000/js/dist/signup.bundle.js
Line 6250
Do you have any clue ?
Thanks in advance.
Solved by removing "resolve" section in webpack.config.js and by installing jquery with npm.
However, if someone could explain me why it works now, I would be very grateful :-)
This seems to be an issue with an earlier/current version of JQuery, according to this issue raised here - https://github.com/webpack/webpack/issues/1066.
To summarize from the above link, JQuery uses RequireJS, which has an implementation of AMD different from that of the actual AMD spec. This might be fixed in later versions. For now, try using the dist folder, which works just fine as well.
Just change this line here -
alias: {
jquery : bower_dir + "/jquery/src/jquery",
}
to this -
alias: {
jquery : bower_dir + "/jquery/dist/jquery",
}
In the meantime, there is a Webpack loader to fix this, and also a tutorial on how to use it, by Alexander O'Mara -
https://github.com/AlexanderOMara/amd-define-factory-patcher-loader
http://alexomara.com/blog/webpack-and-jquery-include-only-the-parts-you-need/

Injecting variables into webpack

I'm trying to inject a variable into each module within my webpack bundle in order to have debugging information for JS errors per file. I've enabled
node: {
__filename: true
}
Current file path in webpack
in my webpack.config, but I would like to inject something like
var filename = 'My filename is: ' + __filename;
into each module before compilation. I've seen the Banner Plugin with the raw option but it seems this would only inject the banner outside of the webpack closure rather than my desired result of injecting script into each module.
I use variables to resolve a couple of variables in my webpack.config.js file:
plugins: [
new webpack.DefinePlugin({
ENVIRONMENT: JSON.stringify(process.env.NODE_ENV || 'development'),
VERSION: JSON.stringify(require('./package.json').version)
})
]
It might not be dynamic enough, but if it is, then you might be able to bypass that loader solution.
Write your own loader:
my_project/my_loaders/filename-loader.js:
module.exports = function(source) {
var injection = 'var __filename = "' + this.resourcePath + '";\n';
return injection + source;
};
Add it to your pipeline and make sure to add also the configuration:
resolveLoader: {
modulesDirectories: ["my_loaders", "node_modules"]
}
See the documentation on how to write a loader.

Categories