如何使用webpack HASH文件名和dotnet发布?

时间:2017-01-23 13:21:27

标签: javascript c asp.net-mvc webpack .net-core

我在我的后端网站上使用dotnet核心,使用MVC网页(index.cshtml)和angular2作为我的应用程序。

我的问题是,对于每个新版本,用户都会获取旧的javascript文件,因为我的index.cshtml看起来像这样

@{
    Layout = "";
}
<!DOCTYPE html>
<html>
<head>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
    <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
</head>
<!-- 3. Display the application -->
<body>
    <my-app>
        <div class="container text-md-center">
            <div class="mb-1">Loading application, please wait...</div>
            <div>
                <i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>
                <span class="sr-only">Loading...</span>
            </div>
        </div>
    </my-app>

    <script defer type="text/javascript" src="~/dist/webpack.bundle.js"></script>
    @if (ViewBag.Environment != "Production")
    {
        <script defer type="text/javascript" src="~/dist/app-style.bundle.js"></script>
        <script defer type="text/javascript" src="~/dist/vendor-style.bundle.js"></script>
    }

    <script defer type="text/javascript" src="~/dist/polyfills.bundle.js"></script>
    <script defer type="text/javascript" src="~/dist/vendor.bundle.js"></script>
    <script defer type="text/javascript" src="~/dist/builders.bundle.js"></script>
    <script defer type="text/javascript" src="~/dist/app.bundle.js"></script>
</body>
</html>

我也在使用webpack捆绑所有的打字稿,html视图等。

在我的dotnet发布&#34; prepublish&#34;标签,我正在运行webpack来创建生产版本,如下所示

"scripts": {
    "prepublish": [ "npm run build" ],
}

在我的package.json文件中,&#34; npm run build&#34;定义如此。

"scripts": {
    "clean": "rimraf node_modules doc dist && npm cache clean",
    "clean-install": "npm run clean && npm install",
    "clean-start": "npm run clean-install && npm start",
    "watch": "webpack --watch --progress --profile",
    "debug": "rimraf dist && webpack --progress --profile --bail",
    "build": "rimraf dist && webpack --progress --profile --bail",
    "lint": "tslint --force \"wwwroot/app/**/*.ts\"",
    "docs": "typedoc --options typedoc.json wwwroot/app/app.component.ts",
    "postinstall": "npm run"
},

这一切都很好,但是因为dotnet发布将文件复制到新位置,并且webpack在复制之前运行...如何更新我的index.cshtml文件以包含脚本文件的哈希标记,而不更改实际的index.cshtml文件,因为显然已经签入并且不希望每次发布时都发布新版本(因为它应该更像是模板)

非常感谢任何帮助!

修改 这是我的实际webpack.config.js文件

var path = require('path');
var webpack = require('webpack');
var autoprefixer = require('autoprefixer');
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var ForkCheckerPlugin = require('awesome-typescript-loader').ForkCheckerPlugin;

/**
 * Env
 * Get npm lifecycle event to identify the environment
 */
var ENV = process.env.npm_lifecycle_event;
var isTestWatch = ENV === 'test-watch';
var isTest = ENV === 'test' || isTestWatch;
var isProd = ENV === 'build';

console.log(isProd ? 'Production build...' : 'Debug build...');

// Webpack Config
module.exports = function makeWebpackConfig() {

    /**
     * Config
     * Reference: http://webpack.github.io/docs/configuration.html
     * This is the object where all configuration gets set
     */
    var config = {};

    /**
     * Devtool
     * Reference: http://webpack.github.io/docs/configuration.html#devtool
     * Type of sourcemap to use per build type
     */
    if (isProd) {
        config.devtool = 'source-map';
    }
    else if (isTest) {
        config.devtool = 'inline-source-map';
    }
    else {
        config.devtool = 'source-map';
    }

    /**
     * Entry
     * Reference: http://webpack.github.io/docs/configuration.html#entry
     */
    config.entry = isTest ? {} : {
        'polyfills':       './wwwroot/polyfills.ts',
        'vendor':          './wwwroot/vendor.ts',
        'builders':        './wwwroot/builders.ts',
        'app':             './wwwroot/app.ts',
        'vendor-style':    './wwwroot/style/vendor-style.ts',
        'app-style':       './wwwroot/style/app-style.ts'
    };

    /**
     * Output
     * Reference: http://webpack.github.io/docs/configuration.html#output
     */
    config.output = isTest ? {} : {
        path: './wwwroot/dist',
        publicPath: './dist/',
        filename: '[name].bundle.js',
        sourceMapFilename: '[name].bundle.js.map',
        chunkFilename: '[id].chunk.js'
    };

    /**
     * Resolve
     * Reference: http://webpack.github.io/docs/configuration.html#resolve
     */
    config.resolve = {
        // only discover files that have those extensions
        extensions: ['.ts', '.js']
    };

    var atlOptions = '';
    if (isTest && !isTestWatch) {
        // awesome-typescript-loader needs to output inlineSourceMap for code coverage to work with source maps.
        atlOptions = 'inlineSourceMap=true&sourceMap=false';
    }

    /**
     * Loaders
     * Reference: http://webpack.github.io/docs/configuration.html#module-loaders
     * List: http://webpack.github.io/docs/list-of-loaders.html
     * This handles most of the magic responsible for converting modules
     */
    config.module = {
        rules: [
            // .ts files for TypeScript
            {
                test: /\.ts$/,
                loader: 'awesome-typescript-loader?' + atlOptions,
                exclude: [isTest ? /\.(e2e)\.ts$/ : /\.(spec|e2e)\.ts$/, /node_modules\/(?!(ng2-.+))/]
            },
            {
                test: /\.html$/,
                loader: 'html-loader'
            },
            {
                test: /\.css$/,
                loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: ['css-loader', 'postcss-loader'] })
            },
            {
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: ['css-loader', 'postcss-loader', 'sass-loader'] })
            },
            {
                test: /\.(jpe?g|png|gif|svg)$/i,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            hash: 'sha512',
                            digest: 'hex',
                            name: '[hash].[ext]'
                        }
                    },
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            bypassOnDebug: true,
                            optimizationLevel: 7,
                            interlaced: false
                        }
                    }
                ]
            },
            {
                test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file-loader'
            },
            {
                test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            prefix: 'font/',
                            limit: 5000,
                            publicPath: '../dist/'
                        }
                    }
                ]
            },
            {
                test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 10000,
                            mimetype: 'application/octet-stream',
                            publicPath: '../dist/'
                        }
                    }
                ]
            },
            {
                test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 10000,
                            mimetype: 'image/svg+xml'
                        }
                    }
                ]
            }
        ]
    };

    if (!isTest || !isTestWatch) {
        // tslint support
        config.module.rules.push({
            test: /\.ts$/,
            enforce: 'pre',
            loader: 'tslint-loader'
        });
    }

    /**
     * Plugins
     * Reference: http://webpack.github.io/docs/configuration.html#plugins
     * List: http://webpack.github.io/docs/list-of-plugins.html
     */
    config.plugins = [

        // Define env variables to help with builds
        // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
        new webpack.DefinePlugin({
            // Environment helpers
            'process.env': {
                ENV: JSON.stringify(ENV)
            }
        }),

        new webpack.LoaderOptionsPlugin({
            minimize: true,
            debug: true,
            options: {
                /**
                 * Apply the tslint loader as pre/postLoader
                 * Reference: https://github.com/wbuchwalter/tslint-loader
                 */
                tslint: {
                    emitErrors: false,
                    failOnHint: false
                },

                // htmlLoader
                htmlLoader: {
                    minimize: true,
                    removeAttributeQuotes: false,
                    caseSensitive: true,
                    customAttrSurround: [ [/#/, /(?:)/], [/\*/, /(?:)/], [/\[?\(?/, /(?:)/] ],
                    customAttrAssign: [ /\)?\]?=/ ]
                },

                // postcss
                postcss: [
                    autoprefixer({
                        browsers: ['last 2 version']
                    })
                ]
            }
        })
    ];

    if (!isTest && !isTestWatch) {
        config.plugins.push(
            new ForkCheckerPlugin(),

            // Generate common chunks if necessary
            // Reference: https://webpack.github.io/docs/code-splitting.html
            // Reference: https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin
            new CommonsChunkPlugin({
                name: ['app', 'builders', 'vendor', 'polyfills', 'webpack'],
                minChunks: Infinity
            }),

            // Extract css files
            // Reference: https://github.com/webpack/extract-text-webpack-plugin
            // Disabled when in test mode or not in build mode
            new ExtractTextPlugin({
                filename: '[name].css',
                disable: !isProd
            })
        );
    }

    // Add build specific plugins
    if (isProd) {
        config.plugins.push(
            // Reference: http://webpack.github.io/docs/list-of-plugins.html#noerrorsplugin
            // Only emit files when there are no errors
            new webpack.NoErrorsPlugin(),

            // // Reference: http://webpack.github.io/docs/list-of-plugins.html#dedupeplugin
            // // Dedupe modules in the output
            // new webpack.optimize.DedupePlugin(),

            // Reference: http://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
            // Minify all javascript, switch loaders to minimizing mode
            new webpack.optimize.UglifyJsPlugin({
                sourceMap: true,
                mangle: {
                    keep_fnames: true
                }
            })
        );
    }

    return config;
}();

3 个答案:

答案 0 :(得分:2)

答案 1 :(得分:0)

警告:我自己没有使用这种方法。我尝试了,但多个匹配的文件问题很痛苦。留下答案,以防它可以帮助处于不同情况的人。

另一种方法是:

  • 使用[name]配置webpack。[chunkhash] .js和[name]。[chunkhash] .css,使正则表达式不太可能发生错误
  • 编写代码以补充更灵活的Bundle.Include,例如支持{hash}以及{version}和*,可能使用正则表达式
  • 如果有多个与正则表达式匹配的文件,请确保使用最新的文件!
  • 在BundleConfig.cs
  • 中将文件设置为捆绑包
  • 在.cshtml页面上,使用Scripts.Render和Styles.Render为每个包提取正确的底层文件

以下是一些示例代码,尽管有哈希名称,但仍可找到匹配的物理文件。我将把它集成到BundleConfig.cs作为读者练习,因为它可能会有所不同,具体取决于你还有什么。此外,它需要更改才能获得最新的文件。

    private static string ReplaceHash(string pathWithHash)
    {
        var i = pathWithHash.LastIndexOf('/');
        var virtualPath = pathWithHash.Substring(0, i);
        var physicalPath = HostingEnvironment.MapPath(virtualPath);
        var fileName = pathWithHash.Substring(i + 1);

        if (!Directory.Exists(physicalPath))
        {
            throw new FfcException(string.Format("Bundle path '{0}' not found", pathWithHash));
        }
        var re = new Regex(fileName
            .Replace(".", @"\.")
            .Replace("{hash}", @"([0-9a-fA-F]+)")
            .Replace("{version}", @"(\d+(?:\.\d+){1,3})")
            .Replace("*", @".*")
            , RegexOptions.IgnoreCase
        );

        fileName = fileName
            .Replace("{hash}", "*")
            .Replace("{version}", "*");

        var matchingFiles = Directory.EnumerateFiles(physicalPath, fileName).Where(file => re.IsMatch(file)).ToList();
        if (matchingFiles.Count == 0)
        {
            throw new FfcException(string.Format("Bundle resource '{0}' not found", pathWithHash));
        }
        if (matchingFiles.Count > 1)
        {
            // TODO: need to pick the most recently created matching file
            throw new FfcException(string.Format("Ambiguous Bundle resource '{0}' requested", pathWithHash));
        }
        var matchingPhysicalFile = matchingFiles[0];
        var matchingVirtualFile = matchingPhysicalFile.Replace(physicalPath + "\\", virtualPath + "/");
        return matchingVirtualFile;
    }

答案 2 :(得分:-1)

这可以在您的webpack配置

中配置
   output: {
    path: path.resolve(__dirname, './your-output-dir'),
    publicPath: '/your-output-dir/',
    filename: '[name][hash].js'
},

因此它将是

/dist/appasdadfasd.js //something like that

您将不会遇到缓存问题。

   <script defer type="text/javascript" src="~/dist/app.bundle.js"></script>

您的app.bundle.js将指向此文件/dist/appasdadfasd.js