Ionic 2如何更改main.js文件名(webpack.js输出文件名设置)

5

我们有一个Ionic 2应用程序,可以本地部署,也可以在Web上使用。构建时我使用npm run build --prod --release。这只是包装了ionic build

我正在尝试更新我们的构建过程,以便能够替换index.html中默认的main.js文件。

因此,我希望能够将此文件从以下内容更改为:

<script src="build/main.js"></script>

使用(自动生成的哈希值)

<script src="build/main.7b297e8f7d1c2760a1bc.js"></script>

第一步是生成文件。我通过使用webpack的output.filename设置,在每次构建时成功地生成了正确的文件。

module.exports = {
   entry: [process.env.IONIC_APP_ENTRY_POINT, './web.config', './src/ai.min.js'],
   output: {
    path: '{{BUILD}}',
    filename: '[name].[chunkhash].js',

当我构建时,可以看到它正确地生成了源文件,但在完成ionic build后不久,会出现一个错误消息,说找不到build/main.js.那是原始文件名,所以我认为我需要让ionic知道我正在更改main.js文件的名称。错误信息如下:[11:00:32] build prod failed: ENOENT: no such file or directory, open '/Users/work/client/www/build/main.js' [11:00:32] ionic-app-script task: "build" [11:00:32] Error: ENOENT: no such file or directory, open '/Users/work/client/www/build/main.js' 我不确定如何更新ionic build,以便它知道查找动态生成的main.js文件名。
3个回答

4
我在ionic论坛(https://forum.ionicframework.com/t/file-revisions/75028/2)中找到一个更好的解决方案来解决这个问题,aszmyd提供了解决方案,它可以为main.css文件名哈希值。由于我没有oauth.html或kajam.js,所以对脚本进行了小的调整。
这种解决方案的优点是它不会试图拦截ionic构建,而只是操作结果。
毫无疑问,有些人可能对我发布的方式有意见,但它对我非常有用,我希望对别人也有所帮助。如果缺少对所有css和js文件进行完全缓存破坏的Web应用程序,那将难以想象。
运行此命令,只需添加:
node <the-file-name.js>

在您的构建中,在Ionic脚本构建完成后。
#!/usr/bin/env node
'use strict';

var md5File = require('md5-file'),
    fs = require('fs');

/**
 * This script renames files inside platforms/browser/www/ folder and updates their references in html files like index.html
 * The mechanism is for improve caching. So file like `main.js` will be renamed to `main.[FILE-MD5-HASH].js` and its references
 * in html files will be updated.
 */
var buildFolder = 'www/';
var assetsFolder = buildFolder + 'build/';

var jsFiles = [
    'main'
];
var cssFiles = [
    'main'
];
var htmlFilesToUpdate = [
    'index.html'
];
var replacements = [];

jsFiles.forEach(function (file) {
    var hash = md5File.sync(assetsFolder + file + '.js');
    renameFile(file + '.js', file + '.' + hash + '.js');
});

cssFiles.forEach(function (file) {
    var hash = md5File.sync(assetsFolder + file + '.css');
    renameFile(file + '.css', file + '.' + hash + '.css');
});
htmlFilesToUpdate.forEach(function (htmlFile) {
    console.log('Update "' + htmlFile + '" with new file revisions.');
    console.log('Replacements: ' + JSON.stringify(replacements));
    replacements.forEach(function (replacementObject) {
        replaceInFile(buildFolder + htmlFile, replacementObject.from, replacementObject.to);
    });
});

function renameFile(input, output) {
    console.log('Rename "' + input + '" to "' + output + '"');
    fs.rename(assetsFolder + input, assetsFolder + output);
    if (fs.existsSync(assetsFolder + input + '.map')) {
        console.log('Rename "' + input + '.map" to "' + output + '.map"');
        fs.rename(assetsFolder + input + '.map', assetsFolder + output + '.map');
    }
    replacements.push({from: input, to: output});
}

function replaceInFile(file, regex, replacement) {
    var fileContents = fs.readFileSync(file, 'utf-8');
    fs.writeFileSync(file, fileContents.replace(regex, replacement), 'utf8');
}

1
这个解决方案支持ionic v3 + lazy load吗? - ChokYeeFan
@ChokYeeFan 懒加载模块目前是0.js、1.js等。由于Angular内部不知道文件名已更改,所以它无法正常工作。正在寻找解决方案。 - tobika

4

编辑

这是一个更简单的解决方案,当ionic进行重大更新时,它不太可能出现问题。

https://gist.github.com/haydenbr/7df417a8678efc404c820c61b6ffdd24


这是有关Ionic的缓存破解。虽然这是一个hackish的解决方案,但它现在可以使用。问题在于Ionic构建系统有时抽象得有点过头了。去年十月份,有人问是否有一种实现缓存破解的方法。Ionic团队回复说他们可能会在将来考虑它,但自那以后就没有任何活动了。这里是Github问题链接
因此,我将展示webpack配置和package.json的所有更改,然后解释一切。
您的package.json的配置部分应如下所示。
  "config": {
    "ionic_webpack": "./webpack.config.js",
    "ionic_source_map_type": "source-map",
    "ionic_uglifyjs": "./www/uglifyjs.config.json"
  }

对于你的webpack配置,你的入口和输出可以保持不变。确保你已经要求了以下模块,然后你需要添加以下插件:

var path = require('path'),
    fs = require('fs'),
    ManifestPlugin = require('webpack-manifest-plugin'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

...

plugins: [
  new HtmlWebpackPlugin({
    filename: './../index.html',
    inject: 'body',
    template: './src/index.html',
    title: 'My App',
  }),
  new ManifestPlugin(),
  updateFileName
]

其中updateFileName如下所示

function updateFileName() {
  this.plugin("done", function() {
  var manifest = require(process.env.IONIC_BUILD_DIR + '/manifest.json'),
      fileName = process.env.IONIC_OUTPUT_JS_FILE_NAME;

    updateUglifyConfig(fileName, manifest);

    process.env.IONIC_OUTPUT_JS_FILE_NAME = manifest[fileName];
  });
}

function updateUglifyConfig(fileName, manifest) {
  var uglifyConfig = {
    sourceFile: manifest[fileName],
    destFileName: manifest[fileName],
    inSourceMap: manifest[fileName + '.map'],
    outSourceMap: manifest[fileName + '.map'],
    mangle: true,
    compress: true,
    comments: true
  };

  // we're writing this to www because it's specific to the current
  // build and we don't want to commit it
  fs.writeFileSync(
    path.join(__dirname, 'www', 'uglifyjs.config.json'),
    JSON.stringify(uglifyConfig, null, 4)
  );
}

所以这里实际上发生了什么?首先,在package.json中,我们将不得不为ionic构建过程生成一个新的uglify配置。您可以在构建过程中更改文件名,只要将新名称分配给process.env.IONIC_OUTPUT_JS_FILE_NAME,则构建的其余部分将正常工作,除了uglify步骤仍将查找默认名称main.js。我们将看到如何生成它。

现在是我们要添加的三个插件。

第一个插件有些神奇。它真的很可配置。设置方式是获取一个默认的index.html,设置标题,注入javascript输出的<script>标签,然后将其写入您在filename属性中指定的位置。如果您使用从ionic starter应用程序中获取的默认index.html,则只需摆脱<script src="build/main.js"></script>,它将自动将带有哈希的新链接放在那里。此处的文档

下一个插件会输出清单文件,以便我们知道带有哈希的文件名。默认情况下,它会在www/build/中输出。文档在此处
下一个插件会将新的文件名分配给process.env.IONIC_OUTPUT_JS_FILE_NAME,并且为我们生成新的uglify配置。基本上,我们获取输出的清单,将新的uglify配置写入www目录,然后从清单中获取新的文件名进行分配。
所以基本上就是这样。如果您不想在开发中使用缓存破坏,请保留html插件,删除其他两个,并将输出文件名更改回process.env.IONIC_OUTPUT_JS_FILE_NAME。如果这样做,您就无需在src/index.html中引用主js文件。无论你是运行dev还是prod,它都会被放置在其中。有关在不同环境中使用不同webpack设置的更多信息,请查看此处更新:

对于Ionic 3:

  1. 确保在tsconfigcompilerOptions中设置了以下选项:

"module": "es2015", "target": "es5"

  1. npm i cheerio --save-dev
  2. in your webpack config add var cheerio = require('cheerio')
  3. Get rid of the Webpack Manifest plugin.
  4. Change updateFileName to the following:

    function updateFileName() {
      this.plugin("done", function(stats) {
        var buildOutput = stats.toJson()['assetsByChunkName']['main'],
            fileName = process.env.IONIC_OUTPUT_JS_FILE_NAME,
            manifest = {
              [fileName]: buildOutput[0],
              [fileName + '.map']: buildOutput[1]
            };
    
        updateUglifyConfig(fileName, manifest);
    
        process.env.IONIC_OUTPUT_JS_FILE_NAME = manifest[fileName];
        console.log('IONIC_OUTPUT_JS_FILE_NAME', process.env.IONIC_OUTPUT_JS_FILE_NAME);
      });
    }
    
  5. Get rid of the Html Webpack Plugin

  6. In place of the html plugin, put the following function in the plugins array in your webpack config:

    function updateIndexHTML() {
    this.plugin("done", function(stats) {
    var buildOutput = stats.toJson()['assetsByChunkName']['main'],
        outputFileName = buildOutput[0],
        currentIndexHTML = fs.readFileSync(
          path.join(__dirname, 'src', 'index.html'),
          { encoding: 'utf8' }
        ),
        $ = cheerio.load(currentIndexHTML);
    
    $('body').append(`<script src="build/${outputFileName}"></script>`);
    
    fs.writeFileSync(
      path.join(process.env.IONIC_WWW_DIR, 'index.html'),
      $.html()
    );
      });
    }
    

1
我已经将这个放在我们的@Nix项目中,所以你可以在那里查看它 :)。 - Hayden Braxton
嗨,使用您的Ionic v3代码构建正常,但它不会对文件进行哈希。我需要更改什么才能对文件进行哈希?我已经尝试在updateFileName方法中设置process.env.IONIC_OUTPUT_JS_FILE_NAME,但始终使用main.js。 - tito.icreativos
我今晚会仔细查看并确保我已正确更新了所有内容。我想,为此发布完整的webpack配置可能会有所帮助。首先,请确保您的配置中有output: { path: '{{BUILD}}', filename: '[name].[chunkhash].js' } - Hayden Braxton
如果将 output: { path: '{{BUILD}}', filename: '[name].[chunkhash].js' } 放入开发模式中,将无法正常工作。浏览器控制台会显示错误信息 Uncaught reflect-metadata shim is required when using class decorators。但是,如果只是为生产模式构建,则可以正常工作并且还支持懒加载。 - ChokYeeFan
应用程序已经加载后,再次按刷新会导致问题 使用类装饰器时需要未捕获的 reflect-metadata shim。有什么想法吗? - ChokYeeFan

0

这个解决方案与Ionic 2.x和3.x完美兼容。

#!/usr/bin/env node

var fs = require('fs'),
    path = require('path'),
    cheerio = require('cheerio'),
    revHash = require('rev-hash');

/**
 *
 * @param string fileName
 * @returns string
 */
function hashFile(file) {

    // Get file name
    var fileName = file.replace(/\.[^/.]+$/, "");
    // Get file extension
    var re = /(?:\.([^.]+))?$/;
    var fileExtension = re.exec(file)[1];

    var filePath = path.join(buildDir, file);
    var fileHash = revHash(fs.readFileSync(filePath));
    var fileNewName = `${fileName}.${fileHash}.${fileExtension}`;
    var fileNewPath = path.join(buildDir, fileNewName);
    var fileNewRelativePath = path.join('build', fileNewName);
    //Rename file
    console.log("cache-busting.js:hashFile:Renaming " + filePath + " to " + fileNewPath);
    fs.renameSync(filePath, fileNewPath);

    return fileNewRelativePath;
}


var rootDir = path.resolve(__dirname);
var wwwRootDir = path.resolve(rootDir, 'www');
var buildDir = path.join(wwwRootDir, 'build');
var indexPath = path.join(wwwRootDir, 'index.html');
$ = cheerio.load(fs.readFileSync(indexPath, 'utf-8'));

$('head link[href="build/main.css"]').attr('href', hashFile('main.css'));
$('body script[src="build/main.js"]').attr('src', hashFile('main.js'));
$('body script[src="build/polyfills.js"]').attr('src', hashFile('polyfills.js'));
$('body script[src="build/vendor.js"]').attr('src', hashFile('vendor.js'));

fs.writeFileSync(indexPath, $.html());

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