我目前在我的反应应用程序中有3个webpack脚本,一个用于服务器,两个用于客户端。
如何优化代码和构建?我有一种感觉,它可以做得更好,样式,字体加载服务器渲染构建。也许我做的一些事情是不必要的?我是否需要在服务器构建上使用My folder structure
.
├── /build/ # Here webpack builds goes
├── /src/
│ ├── /client/ # React app files
│ ├── /server/ # Server rendering
│ ├── /shared/ # shared react components and modules (in case i need more clients)
├── /bin/ # project utils and helpers
├── /webpack/ # webpack scripts
├── ... # etc.
└── package.json
?
package.json
我的 "scripts": {
"prebuild": "yarn build:clean",
"build": "yarn build:server && yarn build:client",
"build:dll": "webpack --display-chunks --color --config ./webpack/webpack.dll.babel.js",
"build:client": "webpack --config ./webpack/webpack.prod.babel.js --color -p --progress",
"build:server": "webpack --config ./webpack/webpack.server.babel.js --color -p --progress",
"prestart": "yarn install && yarn build",
"start": "cross-env NODE_ENV=production node build/server",
"predev": "cross-env NODE_ENV=development yarn install && yarn build:dll",
"dev": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.server.babel.js && node build/server",
},
脚本如下所示:
// Common Webpack configuration used by webpack.config.development and webpack.config.production
const path = require("path");
const webpack = require("webpack");
const autoprefixer = require("autoprefixer");
const e2c = require("electron-to-chromium");
const GLOBALS = require('../bin/helpers/globals');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const isProd = process.env.NODE_ENV === 'production';
const postcssLoaderOptions = {
plugins: [
autoprefixer({ browsers: e2c.electronToBrowserList("1.4") }),
],
sourceMap: !isProd,
};
GLOBALS['process.env'].__CLIENT__ = true;
module.exports = {
target: 'web', // Make web variables accessible to webpack, e.g. window
output: {
filename: 'js/[name].[hash].js',
chunkFilename: 'js/[name].[hash].chunk.js',
path: path.resolve(process.cwd(), 'build'),
publicPath: "/"
},
resolve: {
modules: ["node_modules"],
alias: {
client: path.resolve(process.cwd(), "src/client"),
shared: path.resolve(process.cwd(), "src/shared"),
server: path.resolve(process.cwd(), "src/server")
},
extensions: [".js", '.jsx', ".json", ".scss"],
mainFields: ["browser", "module", 'jsnext:main', "main"],
},
plugins: [
new webpack.NormalModuleReplacementPlugin(
/\/Bundles.js/,
'./AsyncBundles.js'
),
new webpack.IgnorePlugin(/vertx/),
new webpack.ProvidePlugin({
Promise: 'imports-loader?this=>global!exports-loader?global.Promise!es6-promise',
fetch: "imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch", // fetch API
$: "jquery",
jQuery: "jquery",
}),
new webpack.DefinePlugin(GLOBALS),
new ExtractTextPlugin({
filename: "css/[name].[hash].css",
disable: false,
allChunks: true
})
],
module: {
rules: [
// JavaScript / ES6
{
test: /\.(js|jsx)?$/,
include: [
path.resolve(process.cwd(), "src/client"),
path.resolve(process.cwd(), "src/shared"),
],
exclude: /node_modules/,
use: "babel-loader"
},
// Json
{
test: /\.json$/,
use: 'json-loader',
},
//HTML
{
test: /\.html$/,
include: [
path.resolve(process.cwd(), "src/client"),
],
use: [
{
loader: "html-loader",
options: {
minimize: true
}
}
]
},
// Images
// Inline base64 URLs for <=8k images, direct URLs for the rest
{
test: /\.(png|jpg|jpeg|gif|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: "url-loader",
options: {
limit: 8192,
name: "images/[name].[ext]?[hash]"
}
}
},
// Fonts
{
test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
name: "fonts/[name].[ext]?[hash]",
}
}
},
{
test: /\.(ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'file-loader',
options: {
limit: 10000,
name: "fonts/[name].[ext]?[hash]",
}
}
},
// Styles
{
test: /\.s?css$/,
include: [
path.resolve(process.cwd(), "src/client"),
path.resolve(process.cwd(), "src/shared"),
],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
options: {
sourceMap: true,
modules: true,
importLoaders: 1,
localIdentName: '[local]_[hash:base64:3]'
}
},
{
loader: "postcss-loader",
options: postcssLoaderOptions
},
{
loader: "sass-loader",
options: {
sourceMap: true,
outputStyle: "compressed"
}
}
]
})
},
]
}
};
这是我的webpack脚本:
/**
* WEBPACK DLL GENERATOR
*
* This profile is used to cache webpack's module
* contexts for external library and framework type
* dependencies which will usually not change often enough
* to warrant building them from scratch every time we use
* the webpack process.
*/
const { join } = require('path');
const merge = require("webpack-merge");
const webpack = require('webpack');
const config = require("./webpack.base.babel");
const dllPlugin = require('../bin/dllPlugin');
if (!dllPlugin.enabled) { process.exit(0); }
module.exports = merge(config, {
context: process.cwd(),
entry: dllPlugin.dlls ? dllPlugin.dlls : dllPlugin.entry(),
devtool: 'eval',
output: {
filename: '[name].dll.js',
path: dllPlugin.path,
library: '[name]',
},
plugins: [
new webpack.DllPlugin({ name: '[name]', path: join(dllPlugin.path, '[name].json') }),
],
performance: {
hints: false,
},
});
const path = require('path');
const merge = require("webpack-merge");
const webpack = require("webpack");
const config = require("./webpack.base.babel");
const OfflinePlugin = require('offline-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = merge(config, {
devtool: "nosources-source-map",
// In production, we skip all hot-reloading stuff
entry: [
'babel-polyfill', // Needed for redux-saga es6 generator support
path.join(process.cwd(), 'src/client/app.js'), // Start with app.js
],
performance: {
assetFilter: (assetFilename) => !(/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename)),
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true
}),
new HtmlWebpackPlugin({
template: "src/client/index.html",
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
// Shared code
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
children: true,
minChunks: 2,
async: true,
}),
// Avoid publishing files when compilation fails
new webpack.NoEmitOnErrorsPlugin(),
// Put it in the end to capture all the HtmlWebpackPlugin's
// assets manipulations and do leak its manipulations to HtmlWebpackPlugin
new OfflinePlugin({
relativePaths: false,
publicPath: '/',
// No need to cache .htaccess. See http://mxs.is/googmp,
// this is applied before any match in `caches` section
excludes: ['.htaccess'],
caches: {
main: [':rest:'],
// All chunks marked as `additional`, loaded after main section
// and do not prevent SW to install. Change to `optional` if
// do not want them to be preloaded at all (cached only when first loaded)
additional: ['*.chunk.js'],
},
// Removes warning for about `additional` section usage
safeToUseOptionalCaches: true,
AppCache: false,
}),
]
});
const merge = require("webpack-merge");
const webpack = require("webpack");
const config = require("./webpack.base.babel");
const dllPlugin = require('../bin/dllPlugin');
const path = require('path');
const fs = require('fs');
const cheerio = require('cheerio');
const logger = require('../src/server/middleware/logger');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
/**
* We dynamically generate the HTML content in development so that the different
* DLL Javascript files are loaded in script tags and available to our application.
*/
function templateContent() {
const html = fs.readFileSync(path.join(process.cwd(), 'src/client/index.html')).toString();
if (!dllPlugin.enabled) { return html; }
const doc = cheerio(html);
const body = doc.find('body');
const dllNames = !dllPlugin.dlls ? ['reactDeps'] : Object.keys(dllPlugin.dlls);
dllNames.forEach(dllName => body.append(`<script data-dll='true' src='/${dllName}.dll.js'></script>`));
return doc.toString();
}
/**
* Select which plugins to use to optimize the bundle's handling of
* third party dependencies.
*
* If there is a dllPlugin key on the project's package.json, the
* Webpack DLL Plugin will be used. Otherwise the CommonsChunkPlugin
* will be used.
*
*/
function dependencyHandlers() {
// If the package.json does not have a dllPlugin property, use the CommonsChunkPlugin
if (!dllPlugin.enabled) {
return [
// Shared code
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
children: true,
minChunks: 2,
async: true,
}),
];
}
/**
* If DLLs aren't explicitly defined, we assume all production dependencies listed in package.json
* Reminder: You need to exclude any server side dependencies by listing them in dllConfig.exclude
*/
if (!dllPlugin.dlls) {
const manifestPath = path.join(dllPlugin.path, 'reactDeps.json');
if (!fs.existsSync(manifestPath)) {
logger.error('The DLL manifest is missing. Please run `npm run build:dll`');
process.exit(0);
}
return [
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(manifestPath),
})
];
}
// If DLLs are explicitly defined, we automatically create a DLLReferencePlugin for each of them.
const dllManifests = Object.keys(dllPlugin.dlls).map((name) => path.join(dllPlugin.path, `${name}.json`));
return dllManifests.map((manifestPath) => {
if (!fs.existsSync(path)) {
if (!fs.existsSync(manifestPath)) {
logger.error(`The following Webpack DLL manifest is missing: ${path.basename(manifestPath)}`);
logger.error(`Expected to find it in ${dllPlugin.path}`);
logger.error('Please run: npm run build:dll');
process.exit(0);
}
}
return new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(manifestPath),
});
});
}
const plugins = [
new webpack.LoaderOptionsPlugin({
debug: true,
cache: true
}),
new HtmlWebpackPlugin({
inject: true, // Inject all files that are generated by webpack, e.g. bundle.js
templateContent: templateContent(),
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(), // Tell webpack we want hot reloading
new CircularDependencyPlugin({
exclude: /a\.js|node_modules/, // exclude node_modules
failOnError: false, // show a warning when there is a circular dependency
}),
];
module.exports = merge(config, {
devtool: "source-map",
entry: [
'webpack-hot-middleware/client?reload=true',
'babel-polyfill', // Needed for redux-saga es6 generator support
'react-hot-loader/patch',
path.join(process.cwd(), 'src/client/app.js'), // Start with js/app.js
],
// Don't use hashes in dev mode for better performance
output: {
filename: '[name].js',
chunkFilename: '[name].chunk.js',
},
performance: {
hints: false,
},
plugins: dependencyHandlers().concat(plugins),
});
const path = require("path");
const webpack = require("webpack");
const nodeExternals = require("webpack-node-externals");
const GLOBALS = require('../bin/helpers/globals');
const autoprefixer = require("autoprefixer");
const e2c = require("electron-to-chromium");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const isProd = process.env.NODE_ENV === 'production';
const postcssLoaderOptions = {
plugins: [
autoprefixer({ browsers: e2c.electronToBrowserList("1.4") }),
],
sourceMap: !isProd,
};
GLOBALS['process.env'].__CLIENT__ = false;
module.exports = {
target: "node", // in order to ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
devtool: isProd ? "cheap-module-source-map" : "source-map", // original source (lines only) || original source
node: {
//in order to make webpack disable __dirname/__filename injection
__dirname: false,
__filename: false
},
entry: "./src/server",
output: {
filename: "index.js",
path: path.resolve(process.cwd(), 'build/server'),
},
resolve: {
modules: ["node_modules"],
alias: {
bin: path.resolve(process.cwd(), "bin"),
client: path.resolve(process.cwd(), "src/client"),
shared: path.resolve(process.cwd(), "src/shared"),
server: path.resolve(process.cwd(), "src/server")
},
extensions: [".js", '.jsx', ".json", ".scss"],
},
module: {
rules: [
// JavaScript / ES6
{
test: /\.jsx?$/,
include: [
path.resolve(process.cwd(), "bin"),
path.resolve(process.cwd(), "webpack"),
path.resolve(process.cwd(), "src/client"),
path.resolve(process.cwd(), "src/shared"),
path.resolve(process.cwd(), "src/server"),
],
use: "babel-loader",
},
// Json
{
test: /\.json$/,
use: 'json-loader',
},
// Images
// Inline base64 URLs for <=8k images, direct URLs for the rest
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: {
loader: "url-loader",
options: {
limit: 8192,
name: "images/[name].[ext]?[hash]",
emitFile: false
}
}
},
// Fonts
{
test: /\.(woff|woff2|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: "url-loader",
options: {
limit: 8192,
name: "fonts/[name].[ext]?[hash]",
emitFile: false
}
}
},
// Styles
{
test: /\.s?css$/,
include: [
path.resolve(process.cwd(), "src/client"),
path.resolve(process.cwd(), "src/shared"),
],
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: [
{
loader: "css-loader",
options: {
sourceMap: !isProd,
modules: true,
importLoaders: 1,
localIdentName: '[local]_[hash:base64:3]'
}
},
{
loader: "postcss-loader",
options: postcssLoaderOptions
},
{
loader: "sass-loader",
options: {
sourceMap: !isProd,
outputStyle: "compressed"
}
}
]
})
},
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: isProd,
debug: !isProd,
}),
new webpack.DefinePlugin(GLOBALS),
new webpack.NamedModulesPlugin(),
new ExtractTextPlugin({
filename: "css/[name].[hash].css",
disable: false,
allChunks: true
})
]
};
class EntryManager(models.Manager):
def with_documents(self):
vector = SearchVector('title', weight='A') + \
SearchVector('slug', weight='B') + \
SearchVector('content', weight='C') + \
SearchVector('category', weight='D') + \
SearchVector(StringAgg('tags__slug', delimiter=' ')) + \
SearchVector('chron_date')
if SearchVector('chron_date') == "":
SearchVector('chron_date') == None
if SearchVector('clock') == "":
SearchVector('clock') == None
return self.get_queryset().annotate(document=vector)
如何改善设置?
答案 0 :(得分:0)
我之前使用的一个惊人的例子就是这个:
https://github.com/faceyspacey/universal-demo
你基本上可以获得Next.js的所有功能但没有隐藏的webpack配置。希望能帮助你指明正确的方向。