接上篇: 实战 webpack 4 配置解析三

WEBPACK.PROD.JS 解析

现在让我们看看我们的 webpack.prod.js 配置文件,它包含了我们正在处理项目时用于生产构建的所有设置。它与 webpack.common.js 中的设置合并,形成一个完整的 webpack 配置。

// webpack.prod.js - production builds
const LEGACY_CONFIG = 'legacy';
const MODERN_CONFIG = 'modern';// node modules
const git = require('git-rev-sync');
const glob = require('glob-all');
const merge = require('webpack-merge');
const moment = require('moment');
const path = require('path');
const webpack = require('webpack');// webpack plugins
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CreateSymlinkPlugin = require('create-symlink-webpack-plugin');
const CriticalCssPlugin = require('critical-css-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const PurgecssPlugin = require('purgecss-webpack-plugin');
const SaveRemoteFilePlugin = require('save-remote-file-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const WebappWebpackPlugin = require('webapp-webpack-plugin');
const WhitelisterPlugin = require('purgecss-whitelister');
const WorkboxPlugin = require('workbox-webpack-plugin');// config files
const common = require('./webpack.common.js');
const pkg = require('./package.json');
const settings = require('./webpack.settings.js');

在前面部分,我们还是引入了我们需要的 Node 包,以及我们使用的 webpack 插件。然后我们将 webpack.settings.js 导入设置,以便我们可以访问那里的设置,并将 package.json 作为 pkg 导入,以便访问那里的一些设置。

我们还导入我们的 webpack.common.js 常用 webpack 配置,我们将合并我们的开发设置。

TAIL­WIND EXTRACTOR

这个类是 Tailwind CSS 的自定义 PurgeCSS 提取器,允许在类名中使用特殊字符。

// Custom PurgeCSS extractor for Tailwind that allows special characters in
// class names.
//
// https://github.com/FullHuman/purgecss#extractor
class TailwindExtractor {static extract(content) {return content.match(/[A-Za-z0-9-_:\/]+/g) || [];}
}

这取自 Tailwind CSS 文档中 Remov­ing unused CSS with PurgeC­SS(用 PurgeCSS 删除没用到的 CSS) 这一部分。有关此提取器如何与 PurgeCSS 配合使用以神奇地使 CSS 变得苗条和整洁的详细信息,请参见下文。

CON­FIG­U­RA­TION FUNCTIONS

这是 configureBanner() 的样子:

// Configure file banner
const configureBanner = () => {return {banner: ['/*!',' * @project        ' + settings.name,' * @name           ' + '[filebase]',' * @author         ' + pkg.author.name,' * @build          ' + moment().format('llll') + ' ET',' * @release        ' + git.long() + ' [' + git.branch() + ']',' * @copyright      Copyright (c) ' + moment().format('YYYY') + ' ' + settings.copyright,' *',' */',''].join('\n'),raw: true};
};

这只是为我们构建的每个文件添加了一个包含项目名称,文件名,作者和 git 信息的文件头。

接下来是 configureBundleAnalyzer() 函数:

// Configure Bundle Analyzer
const configureBundleAnalyzer = (buildType) => {if (buildType === LEGACY_CONFIG) {return {analyzerMode: 'static',reportFilename: 'report-legacy.html',};}if (buildType === MODERN_CONFIG) {return {analyzerMode: 'static',reportFilename: 'report-modern.html',};}
};

使用 WebpackBundleAnalyzer 插件为我们的新版和旧版构建生成报告,并且生成一个自包含的交互式 HTML 页面,允许您查看 webpack 打包后的确切内容。


它对保持打包后包的大小很有作用,并且让我确切地了解 webpack 正在构建什么,所以我已经将它作为生产构建过程的一部分。

接下来是 configureCriticalCss()

// Configure Critical CSS
const configureCriticalCss = () => {return (settings.criticalCssConfig.pages.map((row) => {const criticalSrc = settings.urls.critical + row.url;const criticalDest = settings.criticalCssConfig.base + row.template + settings.criticalCssConfig.suffix;let criticalWidth = settings.criticalCssConfig.criticalWidth;let criticalHeight = settings.criticalCssConfig.criticalHeight;// Handle Google AMP templatesif (row.template.indexOf(settings.criticalCssConfig.ampPrefix) !== -1) {criticalWidth = settings.criticalCssConfig.ampCriticalWidth;criticalHeight = settings.criticalCssConfig.ampCriticalHeight;}console.log("source: " + criticalSrc + " dest: " + criticalDest);return new CriticalCssPlugin({base: './',src: criticalSrc,dest: criticalDest,extract: false,inline: false,minify: true,width: criticalWidth,height: criticalHeight,})}));
};

这使用 CriticalCssPlugin 通过我们的 webpack.settings.js 中的settings.criticalCssConfig.pages 进行分块,为我们的网站生成 CriticalCSS。

需要注意的是,如果传入的页面在任何位置的名字都包含settings.criticalCssConfig.ampPrefix 的值,则会通过传入非常大的高度为整个网页(而不仅仅是上面的折叠内容)生成 CriticalCSS。

我不会在这里详细介绍 CriticalCSS;有关 CriticalCSS 的更多信息,请查看 Imple­ment­ing Crit­i­cal CSS on your web­site 这篇文章。

下来是 configureCleanWebpack()

// Configure Clean webpack
const configureCleanWebpack = () => {return {root: path.resolve(__dirname, settings.paths.dist.base),verbose: true,dry: false};
};

这只是使用 CleanWebpackPlugin 删除构建目录。目录配置会从我们的 webpack.settings.js 中读取 settings.paths.dist.base

接下来是 configureHtml()

// Configure Html webpack
const configureHtml = () => {return {templateContent: '',filename: 'webapp.html',inject: false,};
};

这将 HtmlWebpackPlugin 与 WebappWebpackPlugin(见下文)结合使用,为我们的 favicons 生成 HTML。请注意,我们在 templateContent 中传入一个空字符串,以便输出只是 WebappWebpackPlugin 的原始输出。

接下来是 configureImageLoader()

// Configure Image loader
const configureImageLoader = (buildType) => {if (buildType === LEGACY_CONFIG) {return {test: /\.(png|jpe?g|gif|svg|webp)$/i,use: [{loader: 'file-loader',options: {name: 'img/[name].[hash].[ext]'}}]};}if (buildType === MODERN_CONFIG) {return {test: /\.(png|jpe?g|gif|svg|webp)$/i,use: [{loader: 'file-loader',options: {name: 'img/[name].[hash].[ext]'}},{loader: 'img-loader',options: {plugins: [require('imagemin-gifsicle')({interlaced: true,}),require('imagemin-mozjpeg')({progressive: true,arithmetic: false,}),require('imagemin-optipng')({optimizationLevel: 5,}),require('imagemin-svgo')({plugins: [{convertPathData: false},]}),]}}]};}
};

我们传入 buildType,以便我们可以返回不同的结果,具体取决于它是旧版构建还是新版构建。在这种情况下,我们通过一系列的图像优化处理图像,通过 img-loader 进行新版构建。

我们只对新版本执行此操作,因为花费时间同时对新版本和旧版本做图像优化是没有必要的(图像对于两者都是相同的)。

需要强调的是,这仅适用于我们的 webpack 构建中包含的图像;许多其他图像将来自其他地方(CMS系统,资产管理系统等)。

要让 webpack 优化图像,请将其导入 JavaScript:

import Icon from './icon.png';

有关详细信息,请查看webpack文档的“加载图像”部分。

下面是我们的 configureOptimization()

// Configure optimization
const configureOptimization = (buildType) => {if (buildType === LEGACY_CONFIG) {return {splitChunks: {cacheGroups: {default: false,common: false,styles: {name: settings.vars.cssName,test: /\.(pcss|css|vue)$/,chunks: 'all',enforce: true}}},minimizer: [new TerserPlugin(configureTerser()),new OptimizeCSSAssetsPlugin({cssProcessorOptions: {map: {inline: false,annotation: true,},safe: true,discardComments: true},})]};}if (buildType === MODERN_CONFIG) {return {minimizer: [new TerserPlugin(configureTerser()),]};}
};

这是我们配置 webpack 生产优化的地方。对于旧版构建(两次没有任何意义),我们使用 MiniCssExtractPlugin 将项目范围内使用的所有 CSS 提取到单个文件中。如果您以前使用过 webpack,那么过去可能已经使用了ExtractTextPlugin 来执行此操作;现在不需要了。

然后,我们还使用 OptimizeCSSAssetsPlugin 通过删除重复规则来优化生成的CSS,并通过 cssnano 最小化CSS。

最后,我们将 JavaScript 最小化插件设置为 TerserPlugin;这是因为 UglifyJsPlugin 不再支持最小化 ES2015+ JavaScript。由于我们正在生成新版的 ES2015+ 包,所以我们需要它。

接下来是 configurePostcssLoader()

// Configure Postcss loader
const configurePostcssLoader = (buildType) => {if (buildType === LEGACY_CONFIG) {return {test: /\.(pcss|css)$/,use: [MiniCssExtractPlugin.loader,{loader: 'css-loader',options: {importLoaders: 2,sourceMap: true}},{loader: 'resolve-url-loader'},{loader: 'postcss-loader',options: {sourceMap: true}}]};}// Don't generate CSS for the modern config in productionif (buildType === MODERN_CONFIG) {return {test: /\.(pcss|css)$/,loader: 'ignore-loader'};}
};

这看起来非常类似于 configurePostcssLoader() 的 dev 版本,除了对于我们的最终加载器,我们使用 MiniCssExtractPlugin.loader 将我们所有的 CSS 提取到一个文件中。

我们只对旧版构建执行此操作,因为对每个构建执行它没有任何意义(CSS是相同的)。我们使用 ignore-loader 进行新版构建,因此我们的 .css 和 .pcss 文件存在一个加载器,但它什么也没做。

如之前所述,我们使用 PostCSS 处理所有 CSS,包括 Tailwind CSS。我认为它是CSS的 Babel,因为它将各种高级 CSS 功能编译成您的浏览器可以理解的普通旧CSS。

同样,重要的是要注意,对于 webpack 加载器,它们按照它们列出的相反顺序进行处理:

  • postcss-loader - 将文件加载并处理为 PostCSS
  • resolve-url-loader 将 CSS 中的任何 url() 重写为相对公共路径
  • css-loader - 解析我们所有的CSS @importurl()
  • MiniCssExtractPlugin.loader - 提取所有的生产环境 CSS 到一个文件中

由于这是一个生产版本,我们使用 MiniCssExtractPlugin.loader 提取所有使用的CSS,并将其保存到单个 .css 文件中。CSS也被最小化,并针对生产进行了优化。

我们包含这个告诉 webpack 引入了 CSS:

import styles from '../css/app.pcss';

这在webpack文档的 Loading CSS 部分中有详细讨论。

我们从 App.js 入口点开始;将此视为 PostCSS 的切入点。app.pcss 文件 @import 我们项目使用的所有CSS;稍后将详细介绍。

接下来是 configurePurgeCss()

// Configure PurgeCSS
const configurePurgeCss = () => {let paths = [];// Configure whitelist pathsfor (const [key, value] of Object.entries(settings.purgeCssConfig.paths)) {paths.push(path.join(__dirname, value));}return {paths: glob.sync(paths),whitelist: WhitelisterPlugin(settings.purgeCssConfig.whitelist),whitelistPatterns: settings.purgeCssConfig.whitelistPatterns,extractors: [{extractor: TailwindExtractor,extensions: settings.purgeCssConfig.extensions}]};
};

Tailwind CSS 是一个出色的实用程序优先的CSS框架,允许快速原型设计,因为在本地开发中,您很少需要实际编写任何 CSS。相反,您只需使用提供的实用程序CSS类。

缺点是生成的CSS可能有点大。这就是 PurgeCSS 的用武之地。它将解析所有HTML/模板/Vue/任何文件,并删除任何未使用的 CSS。

节省的空间可能很大; Tailwind CSS 和 PurgeCSS 是天作之合。我们在 Adam Wathan 播客——Tailwind CSS 实用程序这篇文章中深入探讨了这个问题。

它遍历 settings.purgeCssConfig.paths 中的所有路径 globs,寻找要保留的 CSS 规则;未找到的任何 CSS 规则都会从我们生成的 CSS 构建中删除。

我们还使用 WhitelisterPlugin,当我们知道我们不希望某些 CSS 被剥离时,可以轻松地将整个文件或全局列入白名单。与我们的 settings.purgeCssConfig.whitelist 匹配的所有文件中的 CSS 规则都列入白名单,并且永远不会从生成的构建中剥离。

接下来是 configureTerser()

// Configure terser
const configureTerser = () => {return {cache: true,parallel: true,sourceMap: true};
};

这只是配置 TerserPlugin 使用的一些设置,可以最小化我们的旧版和新代 JavaScript 代码。

接下来是 configureWebApp()

// Configure Webapp webpack
const configureWebapp = () => {return {logo: settings.webappConfig.logo,prefix: settings.webappConfig.prefix,cache: false,inject: 'force',favicons: {appName: pkg.name,appDescription: pkg.description,developerName: pkg.author.name,developerURL: pkg.author.url,path: settings.paths.dist.base,}};
};

这使用 WebappWebpackPlugin 以无数种格式生成我们所有的网站 favicon,以及我们的 webapp manifest.json和其他 PWA 细节。

它与 HtmlWebpackPlugin 结合使用,还可以输出一个 webapp.html 文件,其中包含指向所有生成的 favicons 和相关文件的链接,以包含在我们的 HTML 页面的 <head></head> 中。

接下来是 configureWorkbox()

// Configure Workbox service worker
const configureWorkbox = () => {let config = settings.workboxConfig;return config;
};

我们使用 Google 的 WorkboxWebpackPlugin 为我们的网站生成 Service Worker。解释 Service Worker 这超出了本文的内容范围,但您可以查看 Going Offline: Ser­vice Work­ers with Jere­my Kei­th 的博客作为入门。

配置全部来自 webpack.settings.js 中的 settings.workboxConfig 。除了预先缓存现代构建 manifest.json 中的所有资源之外,我们还包括一个 workbox-catch-handler.js 来配置它以使用回退响应 catch-all 路由。

// fallback URLs
const FALLBACK_HTML_URL = '/offline.html';
const FALLBACK_IMAGE_URL = '/offline.svg';// This "catch" handler is triggered when any of the other routes fail to
// generate a response.
// https://developers.google.com/web/tools/workbox/guides/advanced-recipes#provide_a_fallback_response_to_a_route
workbox.routing.setCatchHandler(({event, request, url}) => {// Use event, request, and url to figure out how to respond.// One approach would be to use request.destination, see// https://medium.com/dev-channel/service-worker-caching-strategies-based-on-request-types-57411dd7652cswitch (request.destination) {case 'document':return caches.match(FALLBACK_HTML_URL);break;case 'image':return caches.match(FALLBACK_IMAGE_URL);break;default:// If we don't have a fallback, just return an error response.return Response.error();}
});// Use a stale-while-revalidate strategy for all other requests.
workbox.routing.setDefaultHandler(workbox.strategies.staleWhileRevalidate()
);

MODULE.EXPORTS

最后,module.exports 使用 webpack-merge 将 webpack.common.js 中的common.legacyConfig与我们的生产旧版配置合并,并将 common.modernConfig 与我们的生产新版配置合并:

// Production module exports
module.exports = [merge(common.legacyConfig,{output: {filename: path.join('./js', '[name]-legacy.[chunkhash].js'),},mode: 'production',devtool: 'source-map',optimization: configureOptimization(LEGACY_CONFIG),module: {rules: [configurePostcssLoader(LEGACY_CONFIG),configureImageLoader(LEGACY_CONFIG),],},plugins: [new CleanWebpackPlugin(settings.paths.dist.clean,configureCleanWebpack()),new MiniCssExtractPlugin({path: path.resolve(__dirname, settings.paths.dist.base),filename: path.join('./css', '[name].[chunkhash].css'),}),new PurgecssPlugin(configurePurgeCss()),new webpack.BannerPlugin(configureBanner()),new HtmlWebpackPlugin(configureHtml()),new WebappWebpackPlugin(configureWebapp()),new CreateSymlinkPlugin(settings.createSymlinkConfig,true),new SaveRemoteFilePlugin(settings.saveRemoteFileConfig),new BundleAnalyzerPlugin(configureBundleAnalyzer(LEGACY_CONFIG),),].concat(configureCriticalCss())}),merge(common.modernConfig,{output: {filename: path.join('./js', '[name].[chunkhash].js'),},mode: 'production',devtool: 'source-map',optimization: configureOptimization(MODERN_CONFIG),module: {rules: [configurePostcssLoader(MODERN_CONFIG),configureImageLoader(MODERN_CONFIG),],},plugins: [new webpack.optimize.ModuleConcatenationPlugin(),new webpack.BannerPlugin(configureBanner()),new ImageminWebpWebpackPlugin(),new WorkboxPlugin.GenerateSW(configureWorkbox()),new BundleAnalyzerPlugin(configureBundleAnalyzer(MODERN_CONFIG),),]}),
];

通过在 module.exports 中返回一个数组,我们告诉 webpack 我们需要完成多个编译:一个用于我们的旧版构建,另一个用于我们的新版构建。

请注意,对于旧版构建,我们将处理后的 JavaScript 输出为 [name]-legacy.[hash].js,而新版构建将其输出为 [name].[hash].js

通过将 mode 设置为 'production',我们告诉 webpack 这是一个生产版本。这样可以实现适合生产构建的许多设置。

通过将 devtool 设置为 'source-map',我们要求将 CSS/JavaScript 的 .map 生成为单独的 .map 文件。这使我们更容易调试实时生产网站,还无需添加资源的文件大小。

这里使用了几个我们尚未涉及的 webpack 插件:

  • CreateSymlinkPlugin - 这是我创建的一个插件,允许创建符号链接作为构建过程的一部分。我使用它将生成的 favicon.ico 符号链接到 /favicon.ico,因为许多Web浏览器在Web根目录中查找。
  • SaveRemoteFilePlugin - 这是我创建的插件,用于下载远程文件并将其作为 webpack 构建过程的一部分发出。我用它来下载并在本地提供 Google 的analytics.js
  • ImageminWebpWebpackPlugin - 此插件创建项目导入的所有 JPEG 和PNG 文件的 .webp 变体。

就这样,我们现在为我们的项目提供了一个很好的生产构建,包括所有的花里胡哨。

TAILWIND CSS & POSTCSS CONFIG

为了使 webpack 正确构建 Tailwind CSS 和我们的其他 CSS,我们需要做一些设置。感谢我的伙伴乔纳森梅尔维尔(Jonathan Melville)在构建这方面的工作。首先我们需要一个 postcss.config.js 文件:

module.exports = {plugins: [require('postcss-import'),require('postcss-extend'),require('postcss-simple-vars'),require('postcss-nested-ancestors'),require('postcss-nested'),require('postcss-hexrgba'),require('autoprefixer'),require('tailwindcss')('./tailwind.config.js')]
};

这可以存储在项目根目录下;PostCSS 将在构建过程中自动查找它,并应用我们指定的 PostCSS 插件。请注意,这里我们包含 tailwind.config.js 文件的位置,以使其成为构建过程的一部分。

最后,我们的 CSS 入口点 app.pcss 看起来像这样:

/*** app.css** The entry point for the css.**//*** This injects Tailwind's base styles, which is a combination of* Normalize.css and some additional base styles.** You can see the styles here:* https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css*/@import "tailwindcss/preflight";/*** This injects any component classes registered by plugins.**/
@import 'tailwindcss/components';/*** Here we add custom component classes; stuff we want loaded* *before* the utilities so that the utilities can still* override them.**/
@import './components/global.pcss';
@import './components/typography.pcss';
@import './components/webfonts.pcss';/*** This injects all of Tailwind's utility classes, generated based on your* config file.**/
@import 'tailwindcss/utilities';/*** Include styles for individual pages**/
@import './pages/homepage.pcss';/*** Include vendor css.**/@import 'vendor.pcss';

显然,定制它以包含您用于自定义 CSS 的任何组件/页面。

POST-BUILD PROJECT TREE

这是我们的项目树在构建后的样子:

├── example.env
├── package.json
├── postcss.config.js
├── src
│   ├── css
│   │   ├── app.pcss
│   │   ├── components
│   │   │   ├── global.pcss
│   │   │   ├── typography.pcss
│   │   │   └── webfonts.pcss
│   │   ├── pages
│   │   │   └── homepage.pcss
│   │   └── vendor.pcss
│   ├── fonts
│   ├── img
│   │   └── favicon-src.png
│   ├── js
│   │   ├── app.js
│   │   └── workbox-catch-handler.js
│   └── vue
│       └── Confetti.vue
├── tailwind.config.js
├── templates
├── web
│   ├── dist
│   │   ├── criticalcss
│   │   │   └── index_critical.min.css
│   │   ├── css
│   │   │   ├── styles.d833997e3e3f91af64e7.css
│   │   │   └── styles.d833997e3e3f91af64e7.css.map
│   │   ├── img
│   │   │   └── favicons
│   │   │       ├── android-chrome-144x144.png
│   │   │       ├── android-chrome-192x192.png
│   │   │       ├── android-chrome-256x256.png
│   │   │       ├── android-chrome-36x36.png
│   │   │       ├── android-chrome-384x384.png
│   │   │       ├── android-chrome-48x48.png
│   │   │       ├── android-chrome-512x512.png
│   │   │       ├── android-chrome-72x72.png
│   │   │       ├── android-chrome-96x96.png
│   │   │       ├── apple-touch-icon-114x114.png
│   │   │       ├── apple-touch-icon-120x120.png
│   │   │       ├── apple-touch-icon-144x144.png
│   │   │       ├── apple-touch-icon-152x152.png
│   │   │       ├── apple-touch-icon-167x167.png
│   │   │       ├── apple-touch-icon-180x180.png
│   │   │       ├── apple-touch-icon-57x57.png
│   │   │       ├── apple-touch-icon-60x60.png
│   │   │       ├── apple-touch-icon-72x72.png
│   │   │       ├── apple-touch-icon-76x76.png
│   │   │       ├── apple-touch-icon.png
│   │   │       ├── apple-touch-icon-precomposed.png
│   │   │       ├── apple-touch-startup-image-1182x2208.png
│   │   │       ├── apple-touch-startup-image-1242x2148.png
│   │   │       ├── apple-touch-startup-image-1496x2048.png
│   │   │       ├── apple-touch-startup-image-1536x2008.png
│   │   │       ├── apple-touch-startup-image-320x460.png
│   │   │       ├── apple-touch-startup-image-640x1096.png
│   │   │       ├── apple-touch-startup-image-640x920.png
│   │   │       ├── apple-touch-startup-image-748x1024.png
│   │   │       ├── apple-touch-startup-image-750x1294.png
│   │   │       ├── apple-touch-startup-image-768x1004.png
│   │   │       ├── browserconfig.xml
│   │   │       ├── coast-228x228.png
│   │   │       ├── favicon-16x16.png
│   │   │       ├── favicon-32x32.png
│   │   │       ├── favicon.ico
│   │   │       ├── firefox_app_128x128.png
│   │   │       ├── firefox_app_512x512.png
│   │   │       ├── firefox_app_60x60.png
│   │   │       ├── manifest.json
│   │   │       ├── manifest.webapp
│   │   │       ├── mstile-144x144.png
│   │   │       ├── mstile-150x150.png
│   │   │       ├── mstile-310x150.png
│   │   │       ├── mstile-310x310.png
│   │   │       ├── mstile-70x70.png
│   │   │       ├── yandex-browser-50x50.png
│   │   │       └── yandex-browser-manifest.json
│   │   ├── js
│   │   │   ├── analytics.45eff9ff7d6c7c1e3c3d4184fdbbed90.js
│   │   │   ├── app.30334b5124fa6e221464.js
│   │   │   ├── app.30334b5124fa6e221464.js.map
│   │   │   ├── app-legacy.560ef247e6649c0c24d0.js
│   │   │   ├── app-legacy.560ef247e6649c0c24d0.js.map
│   │   │   ├── confetti.1152197f8c58a1b40b34.js
│   │   │   ├── confetti.1152197f8c58a1b40b34.js.map
│   │   │   ├── confetti-legacy.8e9093b414ea8aed46e5.js
│   │   │   ├── confetti-legacy.8e9093b414ea8aed46e5.js.map
│   │   │   ├── precache-manifest.f774c437974257fc8026ca1bc693655c.js
│   │   │   ├── styles-legacy.d833997e3e3f91af64e7.js
│   │   │   ├── styles-legacy.d833997e3e3f91af64e7.js.map
│   │   │   ├── vendors~confetti~vue.03b9213ce186db5518ea.js
│   │   │   ├── vendors~confetti~vue.03b9213ce186db5518ea.js.map
│   │   │   ├── vendors~confetti~vue-legacy.e31223849ab7fea17bb8.js
│   │   │   ├── vendors~confetti~vue-legacy.e31223849ab7fea17bb8.js.map
│   │   │   └── workbox-catch-handler.js
│   │   ├── manifest.json
│   │   ├── manifest-legacy.json
│   │   ├── report-legacy.html
│   │   ├── report-modern.html
│   │   ├── webapp.html
│   │   └── workbox-catch-handler.js
│   ├── favicon.ico -> dist/img/favicons/favicon.ico
│   ├── index.php
│   ├── offline.html
│   ├── offline.svg
│   └── sw.js
├── webpack.common.js
├── webpack.dev.js
├── webpack.prod.js
├── webpack.settings.js
└── yarn.lock

INJECTING SCRIPT & CSS TAGS IN YOUR HTML

使用此处显示的 webpack 配置,<script><style> 标记不会作为生成构建的一部分注入到HTML中。该设置使用 Craft CMS,它具有模板系统,我们使用Twigpack 插件注入标签。

如果您没有使用 Craft CMS 或具有模板引擎的系统,并且希望将这些标记注入到HTML 中,那么您将需要使用 HtmlWebpackPlugin 为您执行此操作。这个插件已经包含在内,你只需要添加一个配置来告诉它将标签注入你的HTML。

CRAFT CMS 3 INTEGRATION WITH THE TWIGPACK PLUGIN

如果您不使用 Craft CMS 3,则可以安全地跳过此部分。它只是提供了一些有用的集成信息。

我写了一个名为 Twigpack 的免费插件,可以很容易地将我们精美的 webpack 构建设置与Craft CMS 3集成。

它处理访问 manifest.json 文件以将入口点注入到 Twig 模板中,它甚至处理用于执行旧版/新版模块注入,异步 CSS 加载以及更多内容的模式。它将使这里介绍的 webpack 4 配置非常简单。为了包括 CSS,我这样干:

<!--# if expr="$HTTP_COOKIE=/critical\-css\=1/" -->{{ craft.twigpack.includeCssModule("styles.css", false) }}
<!--# else --><script>Cookie.set("critical-css", '1', { expires: "7D", secure: true });</script>{{ craft.twigpack.includeCriticalCssTags() }}{{ craft.twigpack.includeCssModule("styles.css", true) }}{{ craft.twigpack.includeCssRelPreloadPolyfill() }}
<!--# endif -->

<!--# --> HTML 注释是 Nginx Servier Side Includes 指令。此模式是如果设置了critical-css cookie,用户已经在过去7天内访问过我们的网站,那么他们的浏览器应该有网站 CSS 缓存,我们通常只是提供网站 CSS。

如果没有设置 critical-css cookie,我们通过 Tiny Cookie 设置 cookie,包括我们的 Critical CSS,并异步加载站点 CSS。有关 Critical CSS 的详细信息,请参阅网站上的实施关键 CSS 文章。

要提供我们的 JavaScript,我们只需:

{{ craft.twigpack.includeSafariNomoduleFix() }}
{{ craft.twigpack.includeJsModule("app.js", true) }}

第二个 true 参数告诉它将 JavaScript async 作为模块加载,因此生成的 HTML 如下所示:

<script>
!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();
</script>
<script type="module" src="http://example.test/dist/js/app.273e88e73566fecf20de.js"></script>
<script nomodule src="http://example.test/dist/js/app-legacy.95d36ead9190c0571578.js"></script>

有关详细信息,请参阅 Twigpack 文档。

这是我使用的完整 config/twigpack.php 文件;请注意,它具有在我的 Homestead VM 内部运行的本地设置。您的设置可能不同:


return [// Global settings'*' => [// If `devMode` is on, use webpack-dev-server to all for HMR (hot module reloading)'useDevServer' => false,// The JavaScript entry from the manifest.json to inject on Twig error pages'errorEntry' => '',// Manifest file names'manifest' => ['legacy' => 'manifest-legacy.json','modern' => 'manifest.json',],// Public server config'server' => ['manifestPath' => '/dist/','publicPath' => '/',],// webpack-dev-server config'devServer' => ['manifestPath' => 'http://localhost:8080/','publicPath' => 'http://localhost:8080/',],// Local files config'localFiles' => ['basePath' => '@webroot/','criticalPrefix' => 'dist/criticalcss/','criticalSuffix' => '_critical.min.css',],],// Live (production) environment'live' => [],// Staging (pre-production) environment'staging' => [],// Local (development) environment'local' => [// If `devMode` is on, use webpack-dev-server to all for HMR (hot module reloading)'useDevServer' => true,// The JavaScript entry from the manifest.json to inject on Twig error pages'errorEntry' => 'app.js',// webpack-dev-server config'devServer' => ['manifestPath' => 'http://localhost:8080/','publicPath' => 'http://192.168.10.10:8080/',],],
];

WRAPPING UP!

嗯,这是一个很深的坑!当我第一次开始钻研 webpack 时,我很快意识到它是一个非常强大的工具,具有非常强大的功能。你走多远取决于你想要潜水多远。

有关此处所示内容的完整源代码,请查看 annotated-webpack-4-config github 仓库。希望这对你有所帮助,享受你的旅程,并建立一些令人敬畏的东西!

实战 webpack 4 配置解析四相关推荐

  1. 实战 webpack 4 配置解析一

    配置 github 仓库:https://github.com/nystudio107/annotated-webpack-4-config 随着Web开发变得越来越复杂,我们需要工具来帮助我们构建现 ...

  2. webpack源码解析七(optimization)

    前言 前面我们写了几篇文章用来介绍webpack源码,跟着官网结合demo把整个webpack配置撸了一遍: webpack源码解析一 webpack源码解析二(html-webpack-plugin ...

  3. 【Spring Boot实战】源码解析Spring Boot自动配置原理

    一.简介 Spring致力于让Java开发更简单,SpringBoot致力于让使用Spring进行Java开发更简单,SpringCloud致力于基于SpringBoot构建微服务生态圈,让微服务开发 ...

  4. Vue(四):element-ui组件用法、表单验证、图标引入、webpack目录配置指向、export暴露方法

    目录 1.element-ui组件用法 2.表单验证(复制的默认样式) 3.图标引入 4.webpack目录配置指向 5.export暴露方法 1.element-ui组件用法 这里先补充一下安装依赖 ...

  5. Webpack核心概念解析

    原文链接:banggan.github.io/2019/05/09/- Webpack核心概念解析 终于忙完了论文,可以愉快的开始学习了,重拾起重学前端.webpack以及Vue的源码解读作为入职前的 ...

  6. 六、Webpack详解学习笔记——webpack的安装、起步、配置、loader的使用、webpack中配置Vue、plugin的使用、搭建本地服务器、webpack配置的分离

    一.认识webpack 什么是webpack? 这个webpack还真不是一两句话可以说清楚的. 我们先看看官方的解释: At its core, webpack is a static module ...

  7. (超全)Vue.js学习笔记—webpack基础配置(webpack4+babel7+vue2)

    webpack基础配置&&单文件组件与vue-loader 参考<Vue,js>实战(梁灏编著) 笔者近期学习Vue.js进阶篇,参考教程流程完成webpack.vue.b ...

  8. SpringCloud之Eureka实战和架构设计解析

    SpringCloud之Eureka实战和架构设计解析 Netflix Eureka(后文简称Eureka)是由Netflix开源的一款基于REST的服务发现组件,包括Eureka Server及Eu ...

  9. 一字一句的搞懂vue-cli之vue webpack template配置

    webpack--神一样的存在.无论写了多少次,再次相见,仍是初见.有的时候开发vue项目,对尤大的vue-cli感激涕零.但是,但是,但是...不是自己的东西,真的很不想折腾.所以,我们就得深入内部 ...

最新文章

  1. 【谦先生日志】程序员如何优雅地融入新环境
  2. ML_Logistic_Regression
  3. C语言实现基数排序Radix sort算法之二(附完整源码)
  4. 【机器学习】 - 激活函数与交叉熵Sigmoid, Softmax, binary_crossentropy, categorican_crossentropy区别
  5. vue 断开正在发送的请求_vue 发送请求频繁时取消上一次请求
  6. 2013.8.4thinkPHp学习
  7. Java设计模式与实践
  8. C程序设计(第四版)谭浩强著-学习笔记
  9. 【小程序】小程序开发工具的主要环境设置
  10. java的学习内容,附高频面试题合集
  11. Linux下驱动开发
  12. 15幅非常有创意的影子摄影作品欣赏
  13. 基于Stm32f103利用模拟iic驱动LM75A温度传感器
  14. HTML5终极备忘大全(图片版+文字版)
  15. Oracle数据库学习的第二天(Oracle的简单操作)
  16. Maven使用与配置
  17. 小米手环模拟门禁卡读卡失败_一个手环走天下?可以!
  18. Memcached单键超1M数据量的拆分设计及测试
  19. python测试-9-7
  20. 二分法求利率(非线性方程求解)

热门文章

  1. 2021年全球汽车天线收入大约1816.1百万美元,预计2028年达到2199.7百万美元,2022至2028期间,年复合增长率CAGR为3.0%
  2. Unity2D学习笔记Day14:靠近门时弹出对话框并播放录制动画
  3. 重新编译TensorFlow1.4源代码支持SSE-AVX-FMA指令集 (Python3.5版本)
  4. 梦幻西游进入游戏显示服务器程序停止工作,win10系统提示“梦幻西游已停止工作”的设置教程...
  5. 点如何在平面设计中应用
  6. 在华为写了 13 年代码,都是宝贵的经验!
  7. 关于数字石油,为什么智慧油田能拉动产业变革?
  8. AtCoder Beginner Contest 163 A Circle Pond 圆周率
  9. Lambda表达式效率低
  10. 小米注册极石汽车商标,是否为小米汽车的最终名称?