【问题标题】:Lazy load application theme styles with Angular CLI使用 Angular CLI 延迟加载应用程序主题样式
【发布时间】:2018-02-19 12:17:12
【问题描述】:

我正在尝试切换 <link />href 以用于主题化,并且 SCSS 主题位于我的 monorepo 的包文件夹中,这些文件夹在 node_modules 中符号链接。我需要能够编译和引用这些。

我遇到了以下固定问题:angular/angular-cli#3401 并一直在尝试实现类似的东西:

"styles": [
    "styles.scss",
    {
        "input": "../node_modules/@org/themes/dark.scss",
        "output": "dark",
        "lazy": true
    }
],

我的理解(可能不正确)是这会将dark.scss 文件编译为dist/dark.bundle.css,并且我可以通过http://localhost:4200/dist/dark.bundle.css 加载它,但它没有按预期工作。我是误解了什么还是完全错误地这样做了?

如何从node_modules 编译一个 SCSS 文件,然后我可以在应用程序中延迟加载?我可以尝试另一种/更好的方法吗?

补充说明:

  • 使用 Angular 版本4.2.4
  • 使用 Angular CLI 版本1.3.0
  • documentation for this approach
  • 我在 monorepo 工作,所以node_modules/@org/themes 是一个符号链接
  • 我已尝试使用ng serve --preserve-symlinks 选项以防出现上述问题。没有区别

我研究了Angular Material docs website approaches this problem 的方式,似乎他们有一个自定义构建脚本,可以在提供应用程序之前将 SCSS 文件编译为 assets 目录中的 CSS 文件。我认为上面的固定问题消除了对这一步的需要,但也许不是。这是唯一的方法吗?

已解决

感谢@Kuncevic。我错过了--extract-css 标志。

工作配置:

"styles": [
    "styles.scss",
    {
        "input": "../node_modules/@org/themes/src/dark.scss",
        "output": "themes/dark",
        "lazy": true
    }
],

使用以下服务脚本,我可以通过http://localhost:4200/themes/dark.bundle.css 访问它:

ng serve --extract-css --preserve-symlinks

【问题讨论】:

  • 我错过了添加 Angular 自动附加的“.bundle.css”扩展名
  • 您的解决方案对我帮助很大...我不知道“懒惰”标志。
  • “lazy”关键字在最新版本中已替换为“inject”。

标签: angular sass angular-cli


【解决方案1】:

通过设置"lazy": true 意味着它不会出现在index.html 中,但是没有机制会为您延迟加载该捆绑包,请检查comment

lazy 选项实际上不会延迟加载任何内容。它只是防止 它不会在应用程序启动时执行。

我同意"lazy": true 一开始有点混乱。

如果您运行ng build,您实际上可以看到构建中输出的内容并分析由 cli 生成的所有文件。

当你这样做时:

{
    "input": "../node_modules/@org/themes/dark.scss",
    "output": "dark",
    "lazy": true
}

您应该可以直接在http://localhost:4200/dark.bundle.js 访问您的文件,但它不会出现在index.html 中,因为您设置了"lazy": true

如果您想获得dark.bundle.css 捆绑包而不是 dark.bundle.js 在开发模式下你可以使用--extract-css 标志。

cli 在开发模式下生成样式到js bundle 的原因是因为这种方式要快得多。当你像 ng buld --prod 这样进行 prod build 时,默认情况下它会输出到 .css

【讨论】:

  • 我从我的问题中删除了关于lazy: true 的项目符号,因为这似乎使事情变得混乱。我了解它是如何工作的,但是在使用它时,我似乎无法直接懒惰地访问我生成的 CSS 文件。这就是我要解决的问题。
  • @jjenzz 你可以试试localhost:4200/dark.bundle.js,以确保它在那里运行ng build 并检查dist 文件夹。
  • 生成了一个dark.bundle.js 文件,但这不是我需要的。问题是我希望能够访问dark.bundle.css 文件。
  • 只是想补充一点,CSS 惰性样式仍然会在 prod 构建中获得哈希(如 lazy-style.d41d8cd98f00b204e980.bundle.css),这会使延迟加载它们变得困难,因为您事先不知道哈希。为防止这种情况,请在 prod 构建上使用 --output-hashing=media 标志。现在你会得到lazy-style.bundle.css
  • @FilipeSilva 啊当然。嗯,我们真的不想从所有内容中删除哈希,只是主题。我想这就是 Angular Material 拥有自己的构建脚本的原因。
【解决方案2】:

对于任何想在 .angular-cli.json 中使用全局 css scipts 的人,我编写了以下脚本(例如 patch-ng-cli.js)

const fs = require('fs');

const stylesFileToPatch = "node_modules/@angular/cli/models/webpack-configs/styles.js";

const regex = /extraPlugins\.push\(.*\}\)\)\;/;
const patchContent = `
        // PATCHED CONTENT START
        const globalStyles = utils_1.extraEntryParser(appConfig.styles, appRoot, 'styles');
        extraPlugins.push(new ExtractTextPlugin({ filename: getPath => {
            const generatedFileName = getPath(\`[name]\${hashFormat.extract}.bundle.css\`);
            const name = generatedFileName.split(".")[0];
            const globalAppStylesConfigEntry = globalStyles.find(path => path.output === name);
            if (globalAppStylesConfigEntry && globalAppStylesConfigEntry.lazy){
                console.log(\`\${name} will not be hashed due to lazy loading\`);
                return \`\${name}.bundle.css\`
            }
            console.log(generatedFileName);
            return generatedFileName;
        }}));
        // PATCHED CONTENT END
`;


fs.readFile(stylesFileToPatch, (err, data) => {
    if (err) { throw err; }

    const text = data.toString();

    const isAlreadyPatched = !!text.match("PATCHED CONTENT");

    if (isAlreadyPatched) return console.warn("-- already patched --", stylesFileToPatch);

    console.log('-- Patching ng-cli: ', stylesFileToPatch);
    const patchedContent = text.replace(regex, patchContent);

    const file = fs.openSync(stylesFileToPatch, 'r+');
    fs.writeFile(file, patchedContent, () => console.log("-- Patching -- OK"));
    fs.close(file);
});

然后通过 package.json 中的 npm 脚本在 npm install 之后运行此脚本

"postinstall": "node ./patch-ng-cli.js",

【讨论】:

    【解决方案3】:

    由于我无法评论已接受的答案,因此我将单独提供一个重要说明。如果需要,请将其移至此处并从此处删除。因此,接受的答案基于单独的 CSS 文件。从 Angular 6 开始,您的 can't 既不使用 package.json 中的 package.json 标志 ng serve,也不使用 extractCss: true 中的 extractCss: true 标志 serve 配置部分。不过,您可以使用this 方法使其工作。然后,您可以使用this 方法和APP_INITIALIZER 上的promise 来加载您的惰性样式。

    【讨论】:

      【解决方案4】:

      我知道这是一篇旧文章,但我还没有找到一个完整的示例,只是在 Angular 中为 CSS 文件js 文件 实现延迟加载的片段。这是 Angular 7.1.4 版本和 font-awesome 4.7bootstrap 4

      延迟加载的解决方案
      1. 安装 bootstrap 和 font-awesome
          npm install --save font-awesome bootstrap
      
      1. 编辑您的 angular.json 文件,以确保编译器将在 dist 文件夹中生成单独的文件

           "styles": [
              "src/styles.css",
              {
                "input": "./node_modules/font-awesome/css/font-awesome.css",
                "lazy": true,
                "bundleName": "font-awesome"
              },
              {
                "input": "./node_modules/bootstrap/dist/css/bootstrap.min.css",
                "lazy": true,
                "bundleName": "bootstrap"
              }
            ], 
        

        参数说明:

      "input" : "第一步中的命令将下载 bootstrap 和 font-awesome 的位置"

      "lazy" : "true 的值将确保编译器不会嵌入 bootstrap.min.css 和 font-awesome.css 文件转换成编译后的文件,发送到浏览器"

      "bundleName" : "是您将在 dist 文件夹中找到的名称"

      1. 根据this 的回答,您必须在<base href="/"> 下的header 标记内将以下script 添加到index.html
                <script>
                   window.onload = function () {
                    function loadScript(scriptUrl) {
                      const script = document.createElement('script');
                      script.src = scriptUrl;
                      document.body.appendChild(script);
                    }
      
                    loadScript('font-awesome.js');
                    loadScript('bootstrap.js');
                  }
      
               </script>
      

      注意: window.onload = function () 用于确保页面已加载(这是因为我们进入 dist 的文件位于 .js格式;这是 Angular 编译的快捷方式)

      1. 运行此命令,编译应用程序
          ng build --extract-css=false --prod
      

      注意: 在构建时我们使用 --extract-css=false 来生成 .js 文件

      测试

      • 进入浏览器并将其更改为 dev(F12 或右键单击并检查元素)
      • 选择网络选项卡
      • 按 Ctrl+Shift+R 硬重新加载

      您必须能够看到 bootstrap.js 和 font-awesome.js 像照片中一样分别加载,并且您还可以看到没有样式的页面 => 这是样式的时刻DOM 加载好后就加载好了

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-17
        • 2018-10-04
        • 1970-01-01
        • 2018-11-20
        • 2013-12-31
        相关资源
        最近更新 更多