Skip to content

Latest commit

 

History

History
executable file
·
498 lines (414 loc) · 14.1 KB

README_ZH.md

File metadata and controls

executable file
·
498 lines (414 loc) · 14.1 KB

html-res-webpack-plugin

NPM Version Travis Deps

为什么我要重构

最近随着我对 webpack 了解的深入,发现 webpack 主要是基于 chunks。因此基于 chunks 去写插件会更贴近 webpack 的理念。

基本概念:chunksassets

在 webpack 里,基本的要素就是chunksentry配置项中的值其实就是 chunks。例如下面的entry配置中的indexdetail都是chunks的名字。大多数情况下,chunk 都是一个 js 文件。但如果你在 js 文件里引入样式或者其它文件,那么一个 js 的 chunk 除了包括 js 文件以外,还会包括你引入的那些文件。

entry: {
  index: xxx;
  detail: xxx;
}

那么assets呢?Assets是那些将要被 webpack 输出的文件。它们可以是任何类型的文件,比如样式、图片或者 html 文件。

V3 重大更新

从 V3 版本开始, 我们在使用 defaulthtml mode 的时候,资源必须加上后缀名才可以正常注入或匹配。否则,它会报错。

依赖 html-loader

v1.1.3以后,webpack 配置中需要包含html-loader的配置,例如:

rules: [
  // some other loaders
  {
    test: /\.html$/,
    loader: "html-loader"
  }
];

如何开始

src/index.html --> dist/index.html

<!DOCTYPE html>
<html lang="en" id="html">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-dns-prefetch-control" content="on" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" />
    <title>html-res-webpack-example</title>
</head>
<body>
    <div class="preview-wrapper"></div>
</body>
</html>

src/page/preview/main.js --> dist/js/preview/preview.js

require("./index.scss");

var init = function() {
  // some code here
};

src/page/preview/index.scss --> dist/css/preview/preview.css

html, body {
    margin: 0;
}
// some code here

webpack.config.js

    var config = {
        hash: "-[hash:6]",
        chunkhash: "-[chunkhash:6]",
        contenthash: "-[contenthash:6]"
    };
    var webpackconfig = {
        entry: {
            'preivew/preview': [path.join(config.path.src, "/page/preview/main.js")],
        },

        /**
         *  webpack options below
         */
        .
        .
        .
        output: {
            publicPath: (config.env === 'prod') ? config.cdn : config.defaultPath,
            path: path.join(config.path.dist),
            filename: "js/[name]" + config.chunkhash + ".js"
        },

        .
        .
        .

        plugins: [
            // some other plugins
            new ExtractTextPlugin("./css/[name]" + config.contenthash + ".css"),
            new HtmlResWebpackPlugin({
                filename: "index.html",
                template: "src/index.html",
                chunks:{
                    'index.js': {
                        attr: 'async="true"',  // attributes for js file in index chunk
                        inline: true,                   // inline or not for index chunk
                    },
                    'index.css': {
                        attr: "offline",  // attributes for css file in index chunk
                    }
                },
            });
        ]
    };

package.json

"scripts": {
    "dev": "webpack --progress --colors --watch",
    "publish-mac": "export NODE_ENV=prod&&webpack -p --progress --colors",
    "publish-windows": "SET NODE_ENV=prod&&webpack -p --progress --colors"
 },

有一个需要提及的是,hash 与 chunkhash 的不同点在于,hash 对于所有资源都是一样的,而每个入口文件却只有一个自己的 chunkhash(如果你使用 extract-text-webpack-plugin 插件,在入口 js 文件里被引用的样式文件,则会共享相同的 chunkhash。但请使用contenthash,这样能保证样式文件的 hash 会随着自身内容的改变而改变)。

另一个值得提及的,是chunks的顺序。那些被注入的资源的顺序,就是基于html-res-webpack-pluginchunks配置的顺序。

注入外部资源

有时候你可能需要使用外部的公共资源。如果是这样,请仿照以下的配置写法:

chunks:{
    'qreport': {
        external: true,                                 // 告诉插件不要带上publicPath
        res:  "//s.url.cn/pub/js/qreport.js?_bid=2231", // 资源路径
        attr: {
            js: 'async="true"',
        }
    }
}

多 html 页面

有时候,一个项目会有多于一个 html 文件。这种情况下,请对每个 html 文件添加对应的一个 HtmlResWebacpk 插件。

var config = {
  hash: "-[hash:6]",
  chunkhash: "-[chunkhash:6]",
  contenthash: "-[contenthash:6]"
};

var route = ["index", "detail"];

var webapckConfig = {
  entry: {
    "js/index": "xxx/index",
    "js/detail": "xxx/detail"
  }
};

let pageMapping = {
  detail: {
    "js/detail.js": {
      attr: ""
    },
    "js/detail.css": {
      attr: ""
    }
  },
  index: {
    "js/index.js": {
      attr: ""
    },
    "js/index.css": {
      attr: ""
    }
  }
};

webpackConfig.addPlugins = function(plugin, opt) {
  devConfig.plugins.push(new plugin(opt));
};

route.html.forEach(function(page) {
  webapckConfig.addPlugins(HtmlResWebpackPlugin, {
    filename: page + ".html",
    template: "src/" + page + ".html",
    favicon: "src/favicon.ico",
    chunks: pageMapping[page]
  });
});

Favicon

webpack.config.js

new HtmlResWebpackPlugin({
    filename: "index.html",
    template: "xxx/index.html",
    favicon: "xxx/favicon.ico",
    chunks:[
        'js/index.js',
        'js/index.css',
    ],
}),

输出前修改 Html 内容

new HtmlResWebpackPlugin({
    filename: "index.html",
    template: "src/index.html",
    templateContent: function(tpl) {
        // 你可以在这里修改html内容tpl
        // 你也可以在这里使用this.options[用户传入的插件options]
        // 和this.webpackOptions[webpack配置]
        // 打开插件的index.js可以查看都有哪些option提供使用
        return tpl;
    }
}),

支持在 html 文件中配置资源 Support Writing Assets in html files

在 0.0.7 版本及之前,我们支持在 html 文件中配置资源,这是一种对开发者来说比较直观的方式。这个功能在 1.0 版本去掉,但在 1.1 版本回归了。但使用方法会稍微有点不同。

例如,若 entry 配置中如下:

entry: {
    'index': xxx,
    'detail': xxx,
    'libs/react': xxx,
}

那么 html 中的资源可以这样写,也就是将 entry 的 key 值,放在资源路径中,以便插件方便进行替换。

<script src="libs/react"></script>
<link rel="stylesheet" href="index">
<script src="index"></script>

如果你想给资源添加属性,请在srcrel之前添加。

<script src="libs/react.js"></script>
<link rel="stylesheet" href="index.css">
<script src="index.js"></script>

而 favico 则直接配置:

<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
<link rel="icon" type="image/x-icon" href="favicon.ico">

If you have no idea about the chunk name, you can try running webpack, the plugin will print chunk names available for usage. 如果你不知道 chunka 的名字,你可以尝试运行 webpack,插件会打印出可用的 chunk 的名字

=====html-res-webapck-plugin=====
chunk1: commons.js
chunk2: js/index.js
chunk3: js/index.css
chunk4: js/list.js
chunk5: js/list.css
chunk6: js/detail.js
chunk7: js/detail.css
chunk8: libs/react

如果你想内联一些不需要经过 webpack 打包的资源,你既可以通过 copy-webpack-plugin-hash (下一部份提及),也可以通过插件达成。例如,如果项目的目录结构如下:

-- src
  |-- index.html
  |-- libs
  |    |-- react.js
  |-- css
  |    |-- index.css

那么,你可以将在 index.html 中的资源匹配写成这样:

<script asycn defer src="./libs/react.js?__inline"></script>
<link asycn defer rel="stylesheet" href="./css/index.css?__inline">

如果你想某些资源只在生产环境里显示,你需要设置 envproduction,然后 html 的资源匹配写成这样:

<script asycn defer src="./libs/react.js?__production"></script>
<link asycn defer rel="stylesheet" href="./css/index.css?__production">

如果只想在开发环境里出现某些资源,也如发炮制:

<script asycn defer src="./libs/react.js?__development"></script>
<link asycn defer rel="stylesheet" href="./css/index.css?__development">

copy-webpack-plugin-hash插件搭配使用

copy-webpack-plugin-hash 是一个帮助直接复制文件的 webpack 插件。 我添加了一个namePattern的选项目,这样能够让复制的文件也带上 hash(一旦主要的 repo 接受了我的 request,我可能会删掉这个临时的 repo)

如果你使用copy-webpack-plugin-hash,你也可以轻松使用html-res-webpack-plugin。例如,你想复制/xxx/libs文件夹到libs/。若你的文件夹包含reactreact-dom,你可以添加 chunks libs/react/libs/react-domhtml-res-webpack-plugin中。

// copy-webpack-plugin-hash@5.x 和 6.x
plugins: [
  new CopyWebpackPlugin(
    [
      {
        from: "/xxx/libs/",
        to: "libs/"
      }
    ],
    {
      namePattern: "[name]-[contenthash:6].js"
    }
  ),
  new HtmlResWebpackPlugin({
    filename: "index.html",
    template: config.path.src + "/resource-copy-plugin-1/index.html",
    chunks: [
      "libs/react.js",
      "libs/react-dom.js",
      "js/index.js",
      "js/index.css"
    ]
  })
];

// copy-webpack-plugin-hash@5.x 和 6.x
plugins: [
  new CopyWebpackPlugin([
    {
      from: "/xxx/libs/",
      to: "libs/[name]-[hash:6].[ext]"
    }
  ]),
  new HtmlResWebpackPlugin({
    filename: "index.html",
    template: config.path.src + "/resource-copy-plugin-1/index.html",
    chunks: [
      "libs/react.js",
      "libs/react-dom.js",
      "js/index.js",
      "js/index.css"
    ]
  })
];

如果你倾向于在 html 文件中写资源,那也可以!每一个被插件拷贝的文件都将会被看作一个 chunk, 如 libs/react, libs/react-dom, js/index。你可以用这些 chunk 名字给 html-res-webpack-plugin 用于在 html 文件中匹配资源,那你便可以轻松进行资源内联或者 md5 了!

Options

  • mode:
    • is optional
    • default (在chunks配置中配置资源) | html (在 html 文中配置资源)
  • env
    • is optional
    • production (生产环境) | development (开发环境, 支持不内联元素)
    • 默认值 production
  • filename:
    • is required
    • 生成的 html 文件名
  • template:
    • is required
    • html 模板来源
  • entryLog:
    • is optional
    • [Boolean]
    • 默认值 true, 如果你使用 html mode, 你可以设置此值为true, 便可以看到chunkName以及其如何在html文件中占位的使用办法
  • removeUnMatchedAssets:
    • is optional
    • 不再推荐使用
    • [Boolean]
    • 默认值 false, 这是一个正在测试的配置,用于去掉那些找不到资源的匹配。
  • logLevel:
    • is optional
    • [Integer]
    • 默认值 0, 0 => info(绿色), 1 => alert(黄色)
  • chunks:
    • is required 如果 modedefault, is not required 如果 modehtml
    • [Array|Object]
    • 注入的 chunks
    • examples:

[Array]

    entry: {
        'index': xxx,
        'detail': xxx,
        'libs/react': xxx,
    }

    plugins: [
        new HtmlResWebpackPlugin({
            /** other config */
            chunks: [
                'index.js',
                'index.css',
                'detail.js',
                'detail.css',
                'libs/react.js'
            ]
        })
    ]

[Object]

    plugins: [
        new HtmlResWebpackPlugin({
            /** other config */
            chunks: {
                'qreport.js': {
                    external: true              // 告诉插件不要带上publicPath
                    res: "xxx"                  // 资源路径
                },
                'index.js': {
                    attr: {
                        js: 'async="true"',    // index chunk 中 js 文件注入的html标签属性
                        css: "offline",
                    },
                },
                'index.css': {
                    attr: "offline",    // index chunk 中 css 文件注入的html标签属性
                },
                'detail.js': {
                    inline: true,                 // detail chunk 中 js 文件是否内联
                },
                'detail.css': {
                    inline: true,                 // detail chunk 中 css 文件是否内联
                },
                'libs/react.js': nulls
            }
        })
    ]
  • htmlMinify:
    • is optional
    • 请查看 html-minifier to see detail options. 如果设为 false | null, html 文件则不会被压缩
  • favicon:
    • is optional
    • favicon 路径, 如: "src/favicon.ico"
  • templateContent:
    • is optional
    • 这里可提供给开发者在输出前修改 html 内容。 this.options 和 this.webpackOptions 在这里也可以被使用
  • cssPublicPath:
    • is optional
    • css 的具体路径,默认使用 webpack 的配置output.publicPath

写在最后

为了保证稳定性和可靠性,我添加了测试用例。我早已开始在我自己的开发项目中使用。

如果你对 README 还是不理解,可以查看一下specWebpack目录下的例子。那也是测试用例存放的地方。