如何在 Angular + Webpack + Heroku 应用程序中加载应用程序之前包含 CSS

Posted

技术标签:

【中文标题】如何在 Angular + Webpack + Heroku 应用程序中加载应用程序之前包含 CSS【英文标题】:How To Include CSS Before App Load in Angular + Webpack + Heroku Application 【发布时间】:2017-09-11 04:53:58 【问题描述】:

我目前有一个使用 webpack 并部署到 Heroku 的 Angular (v4.0.1) 应用程序。我有一个加载微调器设置为在应用程序加载时显示在页面上。目前我已经对其进行了设置,以便它在本地工作,但是由于某种原因,当我部署到 heroku 时,加载微调器(或者更具体地说是用于旋转加载微调器的 CSS)似乎没有被拉入。

我已经尝试了许多可能的修复方法,但我很难弄清楚我需要更改哪些内容才能使其在生产中工作,而且我在 *** 上找到的所有内容似乎都只能在本地工作。我还应该澄清一下,我在应用程序中的所有 css 文件(就像在应用程序加载后加载的组件样式一样)工作正常,它只是我在 index.html 中包含的一个 css 文件,专门用于加载微调器这需要在 Angular 应用加载之前可用。

我的文件结构(简化):

.
+-- config/
+-- src/
|   +-- app/
|   +-- assets/
|      +-- icons/
|          +-- loading-spinner.svg
|      +-- stylesheets/
|          +--- loading-spinner.css
|    +-- vendor/
|    +-- index.html
|    +-- main.ts
|    +-- polyfills.ts
|    +-- tsconfig.json
+-- package.json
+-- server.js

我的 index.html

<!DOCTYPE html>
<html>
  <head>
    <base href="/">
    <title>Stylist Suite 2.0</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="src/assets/stylesheets/loading-spinner.css">
  </head>
  <body>
    <ss-app>
      <div class="loading-spinner ss-loading"></div>
    </ss-app>
  </body>
</html>

loading-spinner.css 文件:

/* --- Loading Spinner - Needed Before App Loads ---*/

.loading-spinner 
  width: 42px;
  height: 44px;

  background: url("../icons/loading-spinner.svg") no-repeat;
  margin: 0 auto;
  animation: spin 2.5s linear infinite;

  -webkit-animation: spin 2.5s linear infinite;
  -webkit-transform: translateZ(0);
  -ms-transform: translateZ(0);
  transform: translateZ(0);


@keyframes spin 
  0%  transform: rotate(0deg); 
  100%  transform: rotate(360deg); 


.loading-spinner .ss-loading 
  position: fixed;
  top:50%;
  left:50%;
  margin-left:-21px;
  margin-top: -22px;
  align-self: center;
  justify-self: center;

我的 webpack.common.js(位于 config/ 下)

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');

module.exports = 
  entry: 
    'polyfills': './src/polyfills.ts',
    'vendor': './src/vendor/vendor.ts',
    'app': './src/main.ts'
  ,

  resolve: 
    extensions: ['.ts', '.js']
  ,

  devtool: 'source-map',

  module: 
    rules: [
      
        test: /\.html$/,
        loader: 'html-loader'
      ,
      
        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
        loader: 'file-loader?name=assets/[name].[hash].[ext]'
      ,
      
        test: /\.css$/,
        exclude: helpers.root('src', 'app'),
        loader: ExtractTextPlugin.extract( loader: 'style-loader', use: 'css-loader?sourceMap' )
      ,
      
         test: /\.css$/,
         include: helpers.root('src', 'app'),
         loader: 'raw-loader'
      ,
      
        test: /\.css$/,
        exclude: helpers.root('src', 'assets'),
        loader: ExtractTextPlugin.extract( loader: 'style-loader', use: 'css-loader?sourceMap' )
      ,
      
        test: /\.css$/,
        include: helpers.root('src', 'assets'),
        loader: 'raw-loader'
      ,
      
        test: /\.scss$/,
        exclude: /node_modules/,
        loaders: ['to-string-loader', 'style-loader', 'css-loader', 'resolve-url-loader', 'sass-loader?sourceMap']
      ,
      
        test: /\.ts$/,
        loaders: [
          
            loader: 'awesome-typescript-loader',
            options:  configFileName: helpers.root('src', 'tsconfig.json') 
          , 'angular2-template-loader'
        ]
      
    ]
  ,

  plugins: [
    // Workaround for angular/angular#11580
    new webpack.ContextReplacementPlugin(
      // The (\\|\/) piece accounts for path separators in *nix and Windows
      /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
          helpers.root('./src'), // location of your src
       // a map of your routes
    ),

    new webpack.optimize.CommonsChunkPlugin(
      name: ['app', 'vendor', 'polyfills']
    ),

    new HtmlWebpackPlugin(
      template: 'src/index.html'
    )
  ]
;

我的 webpack.dev.js(位于 config/ 下)

var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');

module.exports = webpackMerge(commonConfig, 
  devtool: 'cheap-module-eval-source-map',

  output: 
    path: helpers.root('dist'),
    publicPath: 'http://localhost:4200/',
    filename: '[name].js',
    chunkFilename: '[id].chunk.js'
  ,

  plugins: [
    new ExtractTextPlugin('[name].css'),
    new webpack.DefinePlugin(
      'process.env.API_APPLICATION_ID': JSON.stringify(""),
      'process.env.REDIRECT_URL': JSON.stringify("http://localhost:4200/login"),
      'process.env.API_BASE_URL': JSON.stringify("http://localhost:3000"),
      'process.env.SITE_URL': JSON.stringify("http://localhost:3000")
    )
  ],

  devServer: 
    historyApiFallback: true,
    stats: 'minimal'
  
);

我的 webpack.prod.js(位于 config/ 下)

var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');

const ENV = process.env.NODE_ENV = process.env.ENV = 'production';

module.exports = webpackMerge(commonConfig, 
  devtool: 'source-map',

  output: 
    path: helpers.root('dist'),
    publicPath: '/',
    filename: '[name].[hash].js',
    chunkFilename: '[id].[hash].chunk.js'
  ,

  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new webpack.optimize.UglifyJsPlugin( // https://github.com/angular/angular/issues/10618
      mangle: 
        keep_fnames: true
      
    ),
    new ExtractTextPlugin('[name].[hash].css'),
    new webpack.DefinePlugin(
      'process.env.API_APPLICATION_ID': JSON.stringify(process.env.API_APPLICATION_ID),
      'process.env.REDIRECT_URL': JSON.stringify(process.env.REDIRECT_URL),
      'process.env.API_BASE_URL': JSON.stringify(process.env.API_BASE_URL),
      'process.env.SITE_URL': JSON.stringify(process.env.REDIRECT_URL)
    ),
    new webpack.LoaderOptionsPlugin(
      htmlLoader: 
        minimize: false // workaround for ng2
      
    )
  ]
);

再次,一旦应用程序加载完毕,我的所有资产(其他样式表、图标文件夹下的图像)都可以正常加载,因为它们正在由我的 webpack.common.js 中的各种 css 和文件加载器正确处理。我假设问题与我的 webpack.prod.js 文件有关,因为所有这些都在本地完美运行,但我尝试的一切似乎都无法解决问题,loading-spinner.css 文件根本没有被加载。任何建议将不胜感激。

【问题讨论】:

嗨,谢谢@peeskillet 我更新了它,现在它包含了我的 webpack.common.js、webpack.dev.js 和 webpack.prod.js。 【参考方案1】:

从您的配置看来,您只是逐字逐句遵循了Angular docs on webpack。在这些文档中,他们使用了全局样式表src/assets/css/styles.css。他们将其导入AppComponent

import '../assets/css/styles.css';

@Component(
)
export class AppComponent  

这将导致ExtractTextPluginimportrequire 的(在本例中为css)文件中提取文本,将其放入styles.css 文件中,然后将该文件添加为&lt;link&gt;。输出文件名与实际文件名无关。 styles.css 恰好是为 ExtractTextPlugin 配置的名称。

如果您检查dist 文件夹并检查index.html,您会看到此styles.[hash].css 文件被添加为&lt;link&gt;

话虽如此,您应该对加载程序 css 执行相同的操作。只需将其导入AppComponent。这将导致加载器 css content 进入styles.[hash].css。如果您想让加载器 css 与其他全局样式分开,请再想一想,因为这并不重要。无论如何,在所有样式表都加载完之前,浏览器不会开始渲染。

它适用于开发服务器的原因是因为开发服务器的工作方式略有不同。它被配置为为服务器添加一个公共路径,以便它可以从某个位置提供文件。

在这种情况下,可以从服务器访问资产。但是当您为生产而构建时,这些资产不会被转移。它们需要在某个地方导入,因为这是 webpack 对大多数端口的工作方式。通过导入,webpack 知道它是一个应该为分发而构建的模块。

您也不需要像您所做的那样手动将加载程序 css 添加到 index.html。只需导入它,它就可以用于开发和生产。此外,您不需要在任何地方导入 svg,因为 webpack 已经在加载器 css url 中检测到它,并将其传输到生产构建中

【讨论】:

是的,当我第一次设置它时,我使用上面包含在应用程序组件中的 styles.css 进行了测试,但是每当我尝试启动服务器时,都会出现以下错误:ERROR in ./src/assets/stylesheets/loading-spinner.css Module build failed: Unknown word (1:1) &gt; 1 | module.exports = 后跟样式.css 文件。 loading-spinner.css 现在也发生了同样的事情,这就是为什么我一直在寻找一种外部方式来包含 css 文件的原因。还有其他方法可以将它包含在 app.component.ts 之外吗? 另外,我在@peeskillet 上方的评论中没有足够的空间,但我非常感谢您对开发和生产服务器如何在这里工作的解释。我们的应用从 6 月份开始就一直在使用 systemjs,现在我们刚刚切换到 webpack,所以它对我来说有点新。 嗯。不确定那个错误。我刚测试过。我有一个项目,它几乎是我提到的文档的逐字记录,只是添加了一个随机的 css 加载器文件。它奏效了。 您搜索过任何相关问题吗?比如github.com/webpack-contrib/css-loader/issues/295或github.com/webpack-contrib/css-loader/issues/352或github.com/webpack-contrib/css-loader/issues/362。搜索“模块构建失败:未知单词 (1:1) > 1 | module.exports”还有更多内容 我刚回去发现我有两个额外的 css 加载程序专门处理资产文件夹。前一天我在尝试调试一个不相关的 sass 加载问题时添加了它们,早在我尝试添加我们的加载微调器之前,因为当时文件中没有 css,我没有意识到它们是一个问题或直到现在都与这个问题有关。这一定是导致导入 styles.css 文件以及现在正在加载的 loading-spinner.css 文件时出错的原因。我刚刚删除了它,现在它似乎已修复。非常感谢您的帮助!

以上是关于如何在 Angular + Webpack + Heroku 应用程序中加载应用程序之前包含 CSS的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 webpack 和 angular2 为生产部署代码

如何在 Angular + Webpack + Heroku 应用程序中加载应用程序之前包含 CSS

如何在 angular 2 webpack starter 中使用猫鼬类型

如何在 angular2-webpack 中导入“stompjs/lib/stomp.min”

如何在 Angular 2 应用程序上使用 webpack 进行代码拆分?

Angular2 webpack:如何导入引导 css