实战 webpack 4 配置解析四
接上篇: 实战 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 配置,我们将合并我们的开发设置。
TAILWIND 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 文档中 Removing unused CSS with PurgeCSS(用 PurgeCSS 删除没用到的 CSS) 这一部分。有关此提取器如何与 PurgeCSS 配合使用以神奇地使 CSS 变得苗条和整洁的详细信息,请参见下文。
CONFIGURATION 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 的更多信息,请查看 Implementing Critical CSS on your website 这篇文章。
下来是 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
@import
和url()
- 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: Service Workers with Jeremy Keith 的博客作为入门。
配置全部来自 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 配置解析四相关推荐
- 实战 webpack 4 配置解析一
配置 github 仓库:https://github.com/nystudio107/annotated-webpack-4-config 随着Web开发变得越来越复杂,我们需要工具来帮助我们构建现 ...
- webpack源码解析七(optimization)
前言 前面我们写了几篇文章用来介绍webpack源码,跟着官网结合demo把整个webpack配置撸了一遍: webpack源码解析一 webpack源码解析二(html-webpack-plugin ...
- 【Spring Boot实战】源码解析Spring Boot自动配置原理
一.简介 Spring致力于让Java开发更简单,SpringBoot致力于让使用Spring进行Java开发更简单,SpringCloud致力于基于SpringBoot构建微服务生态圈,让微服务开发 ...
- Vue(四):element-ui组件用法、表单验证、图标引入、webpack目录配置指向、export暴露方法
目录 1.element-ui组件用法 2.表单验证(复制的默认样式) 3.图标引入 4.webpack目录配置指向 5.export暴露方法 1.element-ui组件用法 这里先补充一下安装依赖 ...
- Webpack核心概念解析
原文链接:banggan.github.io/2019/05/09/- Webpack核心概念解析 终于忙完了论文,可以愉快的开始学习了,重拾起重学前端.webpack以及Vue的源码解读作为入职前的 ...
- 六、Webpack详解学习笔记——webpack的安装、起步、配置、loader的使用、webpack中配置Vue、plugin的使用、搭建本地服务器、webpack配置的分离
一.认识webpack 什么是webpack? 这个webpack还真不是一两句话可以说清楚的. 我们先看看官方的解释: At its core, webpack is a static module ...
- (超全)Vue.js学习笔记—webpack基础配置(webpack4+babel7+vue2)
webpack基础配置&&单文件组件与vue-loader 参考<Vue,js>实战(梁灏编著) 笔者近期学习Vue.js进阶篇,参考教程流程完成webpack.vue.b ...
- SpringCloud之Eureka实战和架构设计解析
SpringCloud之Eureka实战和架构设计解析 Netflix Eureka(后文简称Eureka)是由Netflix开源的一款基于REST的服务发现组件,包括Eureka Server及Eu ...
- 一字一句的搞懂vue-cli之vue webpack template配置
webpack--神一样的存在.无论写了多少次,再次相见,仍是初见.有的时候开发vue项目,对尤大的vue-cli感激涕零.但是,但是,但是...不是自己的东西,真的很不想折腾.所以,我们就得深入内部 ...
最新文章
- 【谦先生日志】程序员如何优雅地融入新环境
- ML_Logistic_Regression
- C语言实现基数排序Radix sort算法之二(附完整源码)
- 【机器学习】 - 激活函数与交叉熵Sigmoid, Softmax, binary_crossentropy, categorican_crossentropy区别
- vue 断开正在发送的请求_vue 发送请求频繁时取消上一次请求
- 2013.8.4thinkPHp学习
- Java设计模式与实践
- C程序设计(第四版)谭浩强著-学习笔记
- 【小程序】小程序开发工具的主要环境设置
- java的学习内容,附高频面试题合集
- Linux下驱动开发
- 15幅非常有创意的影子摄影作品欣赏
- 基于Stm32f103利用模拟iic驱动LM75A温度传感器
- HTML5终极备忘大全(图片版+文字版)
- Oracle数据库学习的第二天(Oracle的简单操作)
- Maven使用与配置
- 小米手环模拟门禁卡读卡失败_一个手环走天下?可以!
- Memcached单键超1M数据量的拆分设计及测试
- python测试-9-7
- 二分法求利率(非线性方程求解)
热门文章
- 2021年全球汽车天线收入大约1816.1百万美元,预计2028年达到2199.7百万美元,2022至2028期间,年复合增长率CAGR为3.0%
- Unity2D学习笔记Day14:靠近门时弹出对话框并播放录制动画
- 重新编译TensorFlow1.4源代码支持SSE-AVX-FMA指令集 (Python3.5版本)
- 梦幻西游进入游戏显示服务器程序停止工作,win10系统提示“梦幻西游已停止工作”的设置教程...
- 点如何在平面设计中应用
- 在华为写了 13 年代码,都是宝贵的经验!
- 关于数字石油,为什么智慧油田能拉动产业变革?
- AtCoder Beginner Contest 163 A Circle Pond 圆周率
- Lambda表达式效率低
- 小米注册极石汽车商标,是否为小米汽车的最终名称?