如何在Ionic中对图像和其他静态资源进行指纹识别以实现缓存破坏?

20
我已经扩展了Ionic v3中的默认Web Pack配置,以强制进行缓存破坏。
我能够为生成的JavaScript构件指纹,但我无法为assets文件夹下的图像和JSON文件指纹。我从“捆绑文件和缓存破坏”获得了帮助。
一个webpack config.js的摘录。
module.exports = {
  // ...
  output: {
    filename: '[name].[chunkhash].js',
    chunkFilename: '[name].[chunkhash].js',
  },
  plugins: [
    new WebpackChunkHash({algorithm: 'md5'}) // 'md5' is default value
  ]
}

上述是指纹识别JavaScript包的方法,它运作得很好。我想在assets文件夹内添加哈希/指纹图像和JSON文件。我用相同的方法处理图片,但没有成功。
我扩展了webpack config.js并添加了一个新规则来处理图片。默认情况下,webpack直接将图像和资产复制到输出文件夹中。
请复制Config.js文件。
module.exports = {
  copyAssets: {
    src: ['{{SRC}}/assets/**/*'],
    dest: '{{WWW}}/assets'
  },
  copyIndexContent: {
    src: ['{{SRC}}/index.html', '{{SRC}}/manifest.json', '{{SRC}}/service-worker.js'],
    dest: '{{WWW}}'
  },
  copyFonts: {
    src: ['{{ROOT}}/node_modules/ionicons/dist/fonts/**/*', '{{ROOT}}/node_modules/ionic-angular/fonts/**/*'],
    dest: '{{WWW}}/assets/fonts'
  },

这里的图片和其他资源是直接复制的。 我在扩展的webpack.config.js中添加了一个新规则,但构建过程忽略了它。我该如何解决这个问题?
webpack config.js摘录:
 {
        test: /\.(png|jpg|gif)$/,
        loader: 'file-loader',
        options: {

            name:'[name].[hash].[ext]',//adding hash for cache busting
            outputPath:'assets/imgs',
            publicPath:'assets/imgs'


        },

整个Webpack.config.js文件
/*
 * The webpack config exports an object that has a valid webpack configuration
 * For each environment name. By default, there are two Ionic environments:
 * "dev" and "prod". As such, the webpack.config.js exports a dictionary object
 * with "keys" for "dev" and "prod", where the value is a valid webpack configuration
 * For details on configuring webpack, see their documentation here
 * https://webpack.js.org/configuration/
 */

var path = require('path');
var webpack = require('webpack');
var ionicWebpackFactory = require(process.env.IONIC_WEBPACK_FACTORY);

var ModuleConcatPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin');
var PurifyPlugin = require('@angular-devkit/build-optimizer').PurifyPlugin;

var optimizedProdLoaders = [
  {
    test: /\.json$/,
    loader: 'json-loader'
  },
  {
    test: /\.js$/,
    loader: [
      {
        loader: process.env.IONIC_CACHE_LOADER
      },

      {
        loader: '@angular-devkit/build-optimizer/webpack-loader',
        options: {
          sourceMap: true
        }
      },
    ]
  },
  {
    test: /\.ts$/,
    loader: [
      {
        loader: process.env.IONIC_CACHE_LOADER
      },

      {
        loader: '@angular-devkit/build-optimizer/webpack-loader',
        options: {
          sourceMap: true
        }
      },
      {
        test: /\.(png|jpg|gif)$/,
        loader: 'file-loader',
        options: {

            name:'[name].[hash].[ext]',
            outputPath:'assets/imgs',
            publicPath:'assets/imgs'
        },
      },


      {
        loader: process.env.IONIC_WEBPACK_LOADER
      }
    ]
  }
];

function getProdLoaders() {
  if (process.env.IONIC_OPTIMIZE_JS === 'true') {
    return optimizedProdLoaders;
  }
  return devConfig.module.loaders;
}

var devConfig = {
  entry: process.env.IONIC_APP_ENTRY_POINT,
  output: {
    path: '{{BUILD}}',
    publicPath: 'build/',
    filename: '[name].js',
    devtoolModuleFilenameTemplate: ionicWebpackFactory.getSourceMapperFunction(),
  },
  devtool: process.env.IONIC_SOURCE_MAP_TYPE,

  resolve: {
    extensions: ['.ts', '.js', '.json'],
    modules: [path.resolve('node_modules')]
  },

  module: {
    loaders: [
      {
        test: /\.json$/,
        loader: 'json-loader'
      },
      {
        test: /\.ts$/,
        loader: process.env.IONIC_WEBPACK_LOADER
      },
      {
      test: /\.(jpg|png)$/,
         use: {
         loader: "file-loader",
         options: {
         name: "[name].[hash].[ext]",
         outputPath:'assets/imgs',
         publicPath:'assets/imgs'

    },
  }},
    ]
  },

  plugins: [
    ionicWebpackFactory.getIonicEnvironmentPlugin(),
    ionicWebpackFactory.getCommonChunksPlugin()
  ],

  // Some libraries import Node.js modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  }
};

var prodConfig = {
  entry: process.env.IONIC_APP_ENTRY_POINT,
  output: {
    path: '{{BUILD}}',
    publicPath: 'build/',
    filename: '[name].js',
    devtoolModuleFilenameTemplate: ionicWebpackFactory.getSourceMapperFunction(),
  },
  devtool: process.env.IONIC_SOURCE_MAP_TYPE,

  resolve: {
    extensions: ['.ts', '.js', '.json'],
    modules: [path.resolve('node_modules')]
  },

  module: {
    loaders: getProdLoaders()
  },

  plugins: [
    ionicWebpackFactory.getIonicEnvironmentPlugin(),
    ionicWebpackFactory.getCommonChunksPlugin(),
    new ModuleConcatPlugin(),
    new PurifyPlugin()
  ],

  // Some libraries import Node.js modules but don't use them in the browser.
  // Tell Webpack to provide empty mocks for them so importing them works.
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  }
};


module.exports = {
  dev: devConfig,
  prod: prodConfig
}

你在生产模式下运行了构建吗? - Mike Doe
@emix 是的,据我所知,Angular为我们提供了“output-hashing”,但Ionic没有,这就是为什么我转向使用webpack配置进行哈希处理的原因。 - Vikas
这个怎么样 [hash] -> loader:'url-loader?limit=1024&name=images/[name][hash].[ext]' - Vadim Hulevich
你看过这个 Stack Overflow 的其他问题吗?https://dev59.com/WqDia4cB1Zd3GeqPDGUE - Derek Nguyen
@DerekNguyen 是的,我做了,但没有帮助。 - Vikas
4个回答

2
使用 Webpack 4,您不需要任何额外的插件或 loader。
它将为您提供命名选项 [contenthash]。
此外,看起来您将此块嵌套在 test: .ts 块下。
{
    test: /\.(png|jpg|gif)$/,
    loader: 'file-loader',
    options: {
        name:'[name].[hash].[ext]', // Adding hash for cache busting
        outputPath:'assets/imgs',
        publicPath:'assets/imgs'
    }
}

最终,您可以这样做:
    // Copy static assets over with file-loader
    {
        test: /\.(ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader', options: {name: '[name].[contenthash].[ext]'},
    },
    {
        test: /\.(woff|woff2|eot|ttf|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader', options: {name: 'fonts/[name].[contenthash].[ext]'},
    },
    {
        test: /\.(jpg|gif|png|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
        loader: 'file-loader', options: {name: 'images/[name].[contenthash].[ext]'},
    }
]

使用 [chunkhash] 而不是 content 应该仍然有效,如果您没有使用 webpack4,请这样做,否则请参阅 this issue 以获取更多信息和解释。
如需更多帮助,请阅读来自 Google 的 长期缓存性能指南 和最新的 Webpack 缓存文档。

如果我选择使用webpack 4,我必须重新设计整个项目,但在这个阶段这是不可能的,我需要通过扩展ionic webpack配置找到解决方法。 1 paulsouche / angular6-ionic4-webpack4-starter 这个存储库具有与您提到的相同的配置。虽然这可以工作,但客户希望使用最少量的更改。很遗憾我不能使用这个解决方案。非常感谢 :) - Vikas
我该如何用JSON文件实现同样的功能?当我使用 import json from './file.json' 时,我希望获得JSON文件名的哈希字符串,而不是JS对象。这是否可能? - tonix
通常情况下,当底层内容发生变化时,您希望通过更改实际文件名来进行“缓存清除”...您可以自己对文件进行哈希处理,并在名称中添加“哈希”。 - adamrights

1

通过CopyPlugin复制的文件将不会传递给加载器,因此即使您为图像设置了正确的带哈希名称的加载器设置,也无法正常工作。

但是您可以查看https://github.com/webpack-contrib/copy-webpack-plugin#template

CopyPlugin提供了一种指定输出名称的方法,该名称可以设置为哈希值:

module.exports = {
  plugins: [
    new CopyPlugin([
      {
        from: 'src/',
        to: 'dest/[name].[hash].[ext]',
        toType: 'template',
      },
    ]),
  ],
};

1
最终,我使用了gulp对静态资源进行指纹识别。
  1. 取消Angular输出哈希并构建应用程序。ng build --prod --aot --output-hashing none
  2. 在构建后执行一个gulp脚本,该脚本将为所有资产打上指纹并更新引用。
npm i gulp gulp-rev gulp-rev-delete-original gulp-rev-collector

gulpfile.js

const gulp = require('gulp');
const rev = require('gulp-rev');
const revdel = require('gulp-rev-delete-original');
const collect = require('gulp-rev-collector');

// finger priniting static assets
gulp.task('revision:fingerprint', () => {
  return gulp
    .src([
      'dist/welcome/**/*.css',
      'dist/welcome/**/*.js',
      'dist/welcome/**/*.{jpg,png,jpeg,gif,svg,json,xml,ico,eot,ttf,woff,woff2}'
    ])
    .pipe(rev())
    .pipe(revdel())
    .pipe(gulp.dest('dist/welcome'))
    .pipe(rev.manifest({ path: 'manifest-hash.json' }))
    .pipe(gulp.dest('dist'));
});

gulp.task('revision:update-fingerprinted-references', () => {
  return gulp
    .src(['dist/manifest-hash.json', 'dist/**/*.{html,json,css,js}'])
    .pipe(collect())
    .pipe(gulp.dest('dist'));
});
gulp.task(
  'revision',
  gulp.series(
    'revision:fingerprint',
    'revision:update-fingerprinted-references'));


在 package.json 中新增一个脚本 "gulp-revision": "gulp revision" 在构建后执行 npm run gulp-revisionSolving Browser Cache Hell With Gulp-Rev 可帮助解决浏览器缓存问题。

0

使用webpack-assets-manifest,您可以生成一个资产名称到指纹名称的映射,如下所示:

{
  "images/logo.svg": "images/logo-b111da4f34cefce092b965ebc1078ee3.svg"
}

使用此清单,您可以重命名目标文件夹中的资产,并在项目中使用“正确”的哈希包含的src或href。
修复不是特定于框架的。

让我试一试。 - Vikas
这并没有解决我的问题,HTML模板和CSS没有被更新,我不得不使用自定义脚本来完成它,但我不想这样做,我希望Webpack能够对资产进行指纹处理,并相应地更新相关文件的路径。谢谢你的帮助,但是同样的效果可以通过哈希实现 :) - Vikas
你必须使用加载器(https://survivejs.com/webpack/loading/images/)来利用Webpack哈希,或者你需要使用外部哈希。在捆绑之前使用清单并更新链接是正确的方法。 如果您不为某些资产使用加载器,则无法对这些资产使用Webpack哈希! 如果您不想手动获取清单并在捆绑之前以编程方式更新资产哈希,请支付自由职业者。 - Oleg
你能详细解释一下使用此清单,您可以在目标文件夹中重命名资产,并在项目中使用“正确的”,包含哈希的src或href。吗? - Pants

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接