有没有办法让 Webpack 将已编译的SCSS → CSS文件添加到我的 Angular 项目的index.html头作为内联样式标记?
目标是设置Angular 忙于引导时显示的“正在加载...”页面的样式。为了避免 FOUC,需要将生成的 CSS 文件内联注入,作为我 index.html 头部的样式标记。这样,一旦加载了 index.html,我们就不需要等待另一个网络资源加载来查看我们的预引导样式。
这也可能是将 index.html 页面内的小徽标作为 base64 数据 URI 内联的一种有价值的方法。
该项目是使用 Angular CLI 创建的,并将 Angular 4 与 Webpack 2 一起使用。我使用 webpack.config.js 弹出了 Webpack 配置并对 webpack.config.js进行ng eject
了少量修改。我几乎只是从配置中删除了 LESS 和 Stylus 支持。
这是我的 webpack.config.js 供参考:
const path = require('path');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const ProvidePlugin = require('webpack/lib/ProvidePlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const autoprefixer = require('autoprefixer');
const postcssUrl = require('postcss-url');
const {NoEmitOnErrorsPlugin, LoaderOptionsPlugin} = require('webpack');
const {GlobCopyWebpackPlugin, BaseHrefWebpackPlugin} = require('@angular/cli/plugins/webpack');
const {CommonsChunkPlugin} = require('webpack').optimize;
const {AotPlugin} = require('@ngtools/webpack');
const nodeModules = path.join(process.cwd(), 'node_modules');
const entryPoints = ["inline", "polyfills", "sw-register", "styles", "twbs", "vendor", "main"];
const baseHref = "";
const deployUrl = "";
module.exports = {
devtool: "source-map",
devServer: {
port: 4200,
host: "0.0.0.0",
historyApiFallback: true
},
resolve: {
extensions: [
".ts",
".js"
],
modules: [
"./node_modules"
]
},
resolveLoader: {
modules: [
"./node_modules"
]
},
entry: {
main: [
"./src/main.ts"
],
polyfills: [
"./src/polyfills.ts"
],
styles: [
"./src/styles/styles.scss",
"./src/styles/vendor.scss"
],
twbs: 'bootstrap-loader'
},
output: {
path: path.join(process.cwd(), "dist"),
filename: "[name].bundle.js",
chunkFilename: "[id].chunk.js"
},
module: {
rules: [
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader",
exclude: [
/node_modules/
]
},
{
test: /\.json$/,
loader: "json-loader"
},
{
test: /\.html$/,
loader: "raw-loader"
},
{
test: /\.(eot|svg)$/,
loader: "file-loader?name=[name].[hash:20].[ext]"
},
{
test: /\.(jpg|png|gif|otf|ttf|woff|woff2|cur|ani)$/,
loader: "url-loader?name=[name].[hash:20].[ext]&limit=10000"
},
{
exclude: [
path.join(process.cwd(), "src/styles/styles.scss"),
path.join(process.cwd(), "src/styles/vendor.scss")
],
test: /\.css$/,
loaders: [
"exports-loader?module.exports.toString()",
"css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
"postcss-loader"
]
},
{
exclude: [
path.join(process.cwd(), "src/styles/styles.scss"),
path.join(process.cwd(), "src/styles/vendor.scss")
],
test: /\.scss$/,
loaders: [
"exports-loader?module.exports.toString()",
"css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
"postcss-loader",
"sass-loader"
]
},
{
include: [
path.join(process.cwd(), "src/styles/styles.scss"),
path.join(process.cwd(), "src/styles/vendor.scss")
],
test: /\.css$/,
loaders: ExtractTextPlugin.extract({
use: [
"css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
"postcss-loader"
],
fallback: "style-loader",
publicPath: ""
})
},
{
include: [
path.join(process.cwd(), "src/styles/styles.scss"),
path.join(process.cwd(), "src/styles/vendor.scss")
],
test: /\.scss$/,
loaders: ExtractTextPlugin.extract({
use: [
"css-loader?{\"sourceMap\":false,\"importLoaders\":1}",
"postcss-loader",
"sass-loader"
],
fallback: "style-loader",
publicPath: ""
})
},
{
test: /\.ts$/,
loader: "@ngtools/webpack"
}
]
},
plugins: [
new ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery",
Tether: "tether",
"window.Tether": "tether",
Tooltip: "exports-loader?Tooltip!bootstrap/js/dist/tooltip",
Alert: "exports-loader?Alert!bootstrap/js/dist/alert",
Button: "exports-loader?Button!bootstrap/js/dist/button",
Carousel: "exports-loader?Carousel!bootstrap/js/dist/carousel",
Collapse: "exports-loader?Collapse!bootstrap/js/dist/collapse",
Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
Modal: "exports-loader?Modal!bootstrap/js/dist/modal",
Popover: "exports-loader?Popover!bootstrap/js/dist/popover",
Scrollspy: "exports-loader?Scrollspy!bootstrap/js/dist/scrollspy",
Tab: "exports-loader?Tab!bootstrap/js/dist/tab",
Util: "exports-loader?Util!bootstrap/js/dist/util"
}),
new NoEmitOnErrorsPlugin(),
new GlobCopyWebpackPlugin({
patterns: [
"assets",
"favicon.ico"
],
globOptions: {
"cwd": "./src",
"dot": true,
"ignore": "**/.gitkeep"
}
}),
new ProgressPlugin(),
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "./index.html",
hash: false,
inject: true,
compile: true,
favicon: false,
minify: false,
cache: true,
showErrors: true,
chunks: "all",
excludeChunks: [],
title: "Webpack App",
xhtml: true,
chunksSortMode: function sort(left, right) {
let leftIndex = entryPoints.indexOf(left.names[0]);
let rightindex = entryPoints.indexOf(right.names[0]);
if (leftIndex > rightindex) {
return 1;
}
else if (leftIndex < rightindex) {
return -1;
}
else {
return 0;
}
}
}),
new BaseHrefWebpackPlugin({}),
new CommonsChunkPlugin({
name: "inline",
minChunks: null
}),
new CommonsChunkPlugin({
name: "vendor",
minChunks: (module) => module.resource && module.resource.startsWith(nodeModules),
chunks: [
"main"
]
}),
new ExtractTextPlugin({
filename: "[name].bundle.css",
disable: true
}),
new LoaderOptionsPlugin({
sourceMap: false,
options: {
postcss: [
autoprefixer(),
postcssUrl({
url: (URL) => {
// Only convert root relative URLs, which CSS-Loader won't process into require().
if (!URL.startsWith('/') || URL.startsWith('//')) {
return URL;
}
if (deployUrl.match(/:\/\//)) {
// If deployUrl contains a scheme, ignore baseHref use deployUrl as is.
return `${deployUrl.replace(/\/$/, '')}${URL}`;
}
else if (baseHref.match(/:\/\//)) {
// If baseHref contains a scheme, include it as is.
return baseHref.replace(/\/$/, '') +
`/${deployUrl}/${URL}`.replace(/\/\/+/g, '/');
}
else {
// Join together base-href, deploy-url and the original URL.
// Also dedupe multiple slashes into single ones.
return `/${baseHref}/${deployUrl}/${URL}`.replace(/\/\/+/g, '/');
}
}
})
],
sassLoader: {
sourceMap: false,
includePaths: []
},
context: ""
}
}),
new AotPlugin({
mainPath: "main.ts",
hostReplacementPaths: {
"environments/environment.ts": "environments/environment.ts"
},
exclude: [],
tsConfigPath: "src/tsconfig.app.json",
skipCodeGeneration: true
})
],
node: {
fs: "empty",
global: true,
crypto: "empty",
tls: "empty",
net: "empty",
process: true,
module: false,
clearImmediate: false,
setImmediate: false
}
};