jos*_*erk 5 webpack html-webpack-plugin webpack-2 extract-text-plugin angular
有没有办法让 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.jsng 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
}
};
Run Code Online (Sandbox Code Playgroud)
我已经通过使用extract-text-webpack-plugin和style-ext-html-webpack-plugin的组合来解决我的难题让我们假设文件夹结构如下:
|- src
|- index.ejs
|- inline.css
|- main.css
|- main.js
Run Code Online (Sandbox Code Playgroud)
main.js包含以下内容:
import _ from 'lodash';
import './inline.css';
import './main.css';
function component() {
const element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'Webpack', '!!!'], ' ');
return element;
}
document.body.appendChild(component());
Run Code Online (Sandbox Code Playgroud)
目的是让 Webpack 生成 dist/index.html 并直接在生成的 index.html 头部中渲染 inline.css。通过 css-loader 进一步加载 main.css。
为了实现这一点,我创建了webpack.config.js,如下所示:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const StyleExtHtmlPlugin = require('style-ext-html-webpack-plugin');
const extractSplashCSS = new ExtractTextPlugin('splash.css');
const extractMainCSS = new ExtractTextPlugin('main.css');
module.exports = {
entry: {
main: './src/main.js'
},
output: {
path: path.join(process.cwd(), 'dist'),
filename: '[name].bundle.js'
},
module: {
rules: [
{
include: [
path.join(process.cwd(), 'src/inline.css')
],
test: /\.css$/,
loaders: extractSplashCSS.extract({
use: 'css-loader'
})
},
{
exclude: [
path.join(process.cwd(), 'src/inline.css')
],
test: /\.css$/,
loaders: extractMainCSS.extract({
use: 'css-loader'
})
}
]
},
plugins: [
extractSplashCSS,
extractMainCSS,
new HtmlWebpackPlugin({
title: 'Hello Webpack 2',
template: 'src/index.ejs',
filename: 'index.html'
}),
new StyleExtHtmlPlugin('splash.css')
]
};
Run Code Online (Sandbox Code Playgroud)
生成的index.html包含inline.css作为样式标签嵌入到index.html的头部:
<html>
<head>
<title>Hello Webpack 2</title>
<style>body {
background-color: lightgrey;
}</style><link href="main.css" rel="stylesheet"></head>
<body>
<p>Webpack 2...</p>
<script type="text/javascript" src="main.bundle.js"></script></body>
</html>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2427 次 |
| 最近记录: |