2017-11-04 59 views
0

我目前为我的反应应用程序有3个webpack脚本,一个服务器和两个客户端。如何优化我的SSRPS设置?

如何优化代码和构建?我有一种感觉,它可以做得更好,样式,字体加载在服务器渲染构建。也许我做的一些事情是不必要的?我需要在服务器版本上使用ExtractTextPlugin吗?

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", 
    }, 

这里是我的WebPack脚本:

webpack.base.babel.js

// 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.dll。 babel.js

/** 
* 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, 
    }, 
}); 

webpack.prod.babel.js

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, 
    }), 
    ] 
}); 

webpack.dev.babel.js

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), 
}); 

webpack.server.babel.js

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 
    }) 
    ] 
}; 

如何提高我的设置?

回答

0

一个惊人的例子,我以前用过的是这个:

​​

你基本上得到所有的真棒Next.js,但没有一个隐藏的WebPack配置。希望能够指引你朝着正确的方向发展。