问题
The Problem
I feel like this should be more straightforward than it is. I need to access all my javascript libraries from the frontend and because I'm integrating it into an old system, I cannot call require("bundle.js"); from the frontend. Everything in the global scope of the bundled files must be accessible from the global scope of the frontend page importing them through the <script> tag.
So I need to change the old:
<script src="js/jquery.js"></script>
<script src="js/silly.js"></script>
<script>
$(silly()); // Some function in silly.js's global scope
</script>
To the new:
<script src="js/bundle.js"></script>
<script>
$(silly()); // Some function in silly.js's global scope
</script>
Things I've tried
expose-loader: This would totally work if I didn't have 100 global variables that I don't want to explicitly tell it to look for.
ProvidePlugin: Only really lets the libraries see the other libraries. I also cannot explicitly write all the globals I need with my current setup (More are added constantly).
What I need
So for more clarity, I need my webpack.config.js to look like one of these options:
// Everything is wrapped in module.exports and other irrelevant things
plugins: [
new StaticLibraryMergerSuperNeatPlugin("js/*.js")
]
// ...
Or:
rules: [
{
test: /\.js$/,
use: [
"neat-merging-cool-loader",
"babel-loader"]
}
]
// ...
Am I going about this wrong?
Is there an obvious solution I am missing?
Tl;Dr:
How do I make globals from my bundled js files, be exposed to the global scope when imported on a frontend html page via <script src="js/bundle.js"></script>?
Btw: If anyone is a webpack legend and knows why this is a bad approach, please post below with a brief explanation so I can fix my life.
回答1:
Here's an example of how I do it in my own site. I'm not sure if it's the only way, or even the best way, but it's clean, simple, and it works for me.
Important side note - Use window["propName"] when declaring things on the window because when you run webpack -p it will uglify any non-strings, so if you define it as window.propName, it can get changed to something like s.c and the rest of your code does not know what it is. Declaring it with bracket notation as a string will force webpack to keep the name intact so you can access it from anywhere with the same name.
site.ts (can be .js, doesn't matter)
/*************************/
/*** JQUERY + JQUERYUI ***/
/*************************/
/* var declaration for typescript - not needed if not using .ts */
declare var $:JQueryStatic; declare var jQuery:JQueryStatic;
window["$"] = window["jQuery"] = require("jquery");
require("jquery-ui/effects/effect-slide");
require("jquery-ui/widgets/autocomplete");
require("jquery-ui/widgets/button");
require("jquery-ui/widgets/datepicker");
require("jquery-ui/widgets/tooltip");
/*************************/
/* END JQUERY + JQUERYUI */
/*************************/
/***************/
/*** ANGULAR ***/
/***************/
/* var declaration for typescript - not needed if not using .ts */
declare var angular:ng.IAngularStatic;
window["angular"] = require("angular");
require("angular-sanitize");
/***************/
/* END ANGULAR */
/***************/
/************************/
/*** MISC THIRD-PARTY ***/
/************************/
window["moment"] = require("moment");
window["saveAs"] = require("FileSaver").saveAs;
window["JSZip"] = require("jszip");
/************************/
/* END MISC THIRD-PARTY */
/************************/
/* var declaration for typescript - not needed if not using .ts */
declare var globals:Globals;
window["globals"] = require("./globals");
Layout.html (loaded on every page)
.....
<script src="/dist/scripts/site.bundle.js"></script>
.....
webpack.config.js
var path = require('path');
var resolve = path.resolve;
var AssetsPlugin = require('assets-webpack-plugin');
var WebpackCleanupPlugin = require("webpack-cleanup-plugin");
'use strict';
var babelOptions = {
"presets": [
[
"es2015",
{
"modules": false
}
],
"es2016"
]
};
module.exports = [{
cache: true,
context: resolve('Scripts'),
devtool: "source-map",
entry: {
site: './site.ts',
},
output: {
path: path.resolve(__dirname, './dist/scripts'),
filename: '[name].bundle.js',
},
module: {
rules: [{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions
},
{
loader: 'ts-loader'
}
]
}, {
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: babelOptions
}
]
}]
},
plugins: [
new AssetsPlugin({ path: path.resolve(__dirname, './dist/assets') }),
new WebpackCleanupPlugin({})
],
}];
回答2:
Note: This is not the ideal scenario but because I have a constant amount of new globals being added, I needed to make a plugin to bundle my javascript for me.
webpack-raw-bundler
This simply stacks your code together to be included on the frontend. Here is my usage example:
Usage
From the old:
<script src="js/jquery.js"></script>
<script src="js/silly.js"></script>
<script>
$(silly()); // Some function in silly.js's global scope
</script>
To the new:
<script src="js/bundle.js"></script>
<script>
$(silly()); // Some function in silly.js's global scope
</script>
Installing to the config
var RawBundlerPlugin = require('webpack-raw-bundler');
module.exports = {
plugins: [
new RawBundlerPlugin({
excludedFilenames: [/angulartics/],
readEncoding: "utf-8",
includeFilePathComments: false,
bundles: [ "vendor.js", "styles.css" ],
"vendor.js": [
'js/*.js'
],
"styles.css": [
'css/bootstrap.css',
'css/edits.css'
]
})
]
}
A Fair Warning:
This should not be your go-to solution, but I had a bad case that made this the easiest option. Using expose-loader and import or window['module'] = require('module.js') is much safer as that is what webpack was built around. However, if you are having some headaches and just want a simple bundler, feel free to use this plugin.
回答3:
If you are using webpack 2.x there is a built-in plugin
You define the global variable and then it can be accessed.
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
"window.Tether": 'tether',
"Tether": 'tether'
}),
...
]
here is my full config
var webpack = require("webpack");
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require("path")
module.exports = {
entry: "./src/entry-js.js",
devtool: 'source-map',
output: {
path: path.join(__dirname, "/public/dist/js"),
publicPath: "/public/",
filename: 'bundle.js',
chunkFilename: 'chunk.[name].[id].js',
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
options: {
presets: ["es2015", "stage-0"]
},
exclude: [
path.resolve(__dirname, "node_modules")
],
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.(scss|sass)$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
"css-loader",
"sass-loader"
]
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
"css-loader",
"less-loader"
]
})
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader:"file-loader",
options: {
limit: 500,
name: "../img/[name].[ext]"
}
}]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [{
loader:"file-loader",
options: {
limit: 500,
name: "../fonts/[name].[ext]"
}
}]
}
]
},
plugins: [
new ExtractTextPlugin({
filename: "../css/bundle.css",
disable: false,
allChunks: true
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
"window.Tether": 'tether',
"Tether": 'tether'
})
]
};
Here is my entry file
/********************
* CSS Libraries *
********************/
// normalize v7
import "../node_modules/normalize.css/normalize.css";
// bootstrap v4.alpha-5
import "../node_modules/bootstrap/scss/bootstrap.scss";
/******************
* CSS Custom *
******************/
import "./css/main.css";
import "./sass/main.scss";
/********************
* JS Libraries *
********************/
//Jquery v3.2.1
import '../node_modules/jquery/src/jquery.js';
import Tether from 'tether';
//Bootstrap v4-alpha-5
import "../node_modules/bootstrap/dist/js/bootstrap.min.js";
import "./js/main.js";
回答4:
I've had the same problem and the best solution I've found is using webpack-concat-plugin.
What it does:
- Concats everything to a single file
- Allows me to specify the resulting file name, including [cache] template to do cache busting
- Adds itself via HtmlWebpackPlugin to the resulting html
The only thing it doesn't do is not-leaking all the globals to the global scope.
来源:https://stackoverflow.com/questions/44724565/expose-javascript-globals-bundled-via-webpack