利用 webpack 处理开发与线上环境静态资源切换问题

前言

webpack,做为一个处理模块加载、资源依赖管理、构建化的工具,已经逐渐成为了前端工程化领域的新贵。其创造性的把每一个静态资源归为一个 module(模块)并能被其强大的 loader 所加载的这种方式,成功的开辟了前端工程界的另外一大生态。基于其官网文档的完善度较高,这篇文章就不对 webpack 的主要内容作过多的介绍,而是回归到本文的主题,即经过介绍几款 webpack 相关的插件,来解决一个常见的工程问题:如何作到静态资源路径能够在不一样的环境下自动切换。javascript

问题说明

这究竟是个怎样的问题?设想一下,在使用 webpack 打包编译以后,它会生成一个 js 文件,随后咱们须要在 html 或者模板文件里指定这个文件的路径确保其被正确的引入,css

<script type="javascript" src="app.js"></script>

对于开发环境下的单入口文件(稍后会介绍 webpack 打包到多个入口的解决方案 ),这个标签内的引入文件路径彻底能够写死,并且在 webpack-dev-server 热替换机制的帮助下,咱们也无须经过对打包生成的文件添加 hash 值来处理因浏览器缓存的缘故引发的引用不到最新资源。html

但在产品模式下,咱们很是有必要在 webpack 的 output 属性里的 filename 里配置一个 chunkhash 来变向的为静态资源注入版本号,以下,前端

output: {
 filename: [name].[chunkhash].js,
}

以便上线以后页面能够引入版本更新后的代码。chunkhash 是一个基于文件内容,经过摘要算法(如md5)生成的一个被称之为文件指纹的序号,即只有当文件内容发生改变的时候,这个值才会相应更改。vue

经过给静态资源注入 hash 值来做为版本号的好处主要有两个:java

  1. 实现 long term caching 策略。当发布新版本时,咱们只须要更新更改了的资源。这比起将新版资源存放在例如/v1.3/xx.js这种带版本号的路径或文件夹下的部署方式会显得更科学一点:减小手动配置版本号的额外操做、已经缓存过且缓存还没有过时的浏览器只需请求更新过的资源,确保未变动过的资源能够依旧从缓存内读取。node

  2. 实现非覆盖式发布策略。张云龙老师的原文中提到的这种平滑的版本升级方式更加完美的解决了静态资源部署至CDN出现的问题。react

这个时候咱们再来看下线上的 script 引入,webpack

<script type="javascript" src="http://xxx.cdn.com/app.82076244596568c8c929.js
"></script>

Fine, 也许你会说我能够手动 copy/paste 这个版本号当你须要从开发切到产品环境,额额,单个入口文件这么处理虽是能够,但想象下当有多个入口文件的时候。。。(感受个人左手大拇指肌腱炎又要犯了。。),这么经典的问题webpack早已准备好了它的解决方案。git

从 webpack 的编译数据里获取开发与生产的资源路径对应关系

这一部分的工做能够说是解决这个问题的一个核心环节,即咱们须要经过 webpack 来生成相似以下一张对应关系图:

{
    'app.js': 'http://xxx.cdn.com/app.82076244596568c8c929.js'
}

像在 webpack 的 plugin 属性里配置以下,咱们就能够经过返回 webpack 的编译数据里获取到带有 chunkhash 的文件信息:

// webpack.config.js
module.exports = {
  ...
  plugins: [
    function() {
      this.plugin("done", function(stats) {
        require("fs").writeFileSync(
          path.join(__dirname, "..", "stats.json"),
          JSON.stringify(stats.toJson()));
      });
    }
  ]
}

stats.json require 到项目中,经过读取 publicPathassetsByChunkName 属性,能够获得开发与线上环境资源路径的对应关系。

webpack 官方也推荐了几个有一样效果,我我的以为更好用的插件:assets-webpack-plugin 或者 webpack-manifest-plugin 来生成出一个 JSON 对应关系文件。

切换资源路径

接下来的工做基本上就是如何利用这个对应关系来切换对应环境下的路径。这个还要取决于你的页面是否会涉及到服务端的渲染。

服务端渲染资源路径

以 node 做为服务端语言,handlebars(或者ejs)为模板语言为例,咱们经过编写模板语言的 helper 来读取由 assets-webpack-plugin 生成的 stats.json,在不一样的环境下实现路径切换:

stats.json -- webpack 跑开发配置

{
    "app": {
        "js": "app.js"
    }
}

stats.json -- webpack 跑生产配置

{
    "app": {
        "js": "http://xxx.cdn.com/app.82076244596568c8c929.js"
    }
}

example.handlebars

<script type="text/javascript" src="{{app.js}}"></script>

后台经过 require stats.json 数据并传入到模板便可实现根据环境动态渲染资源路径。

若是你的后台是使用 Rails 来搭建的话,那么这篇文章更详细的介绍了处理这种状况下处理资源切换的问题

前端渲染页面模板

若是你的项目不依赖任何后端渲染,那么 html-webpack-plugin 这款插件能够为你动态生成一个带有 css、js 等资源路径的 html 文件。

html-webpack-plugin 具体的用法能够点击这里,其中 inject 这个属性可让你将 script 标签插入到 dom 的指定位置。为了可以更大权限的将 webpack 编译过的资源能够插入到 html 文件的任意位置,咱们能够在 HtmlWebpackPlugin 里指定的 template 文件里写入以下代码:

<% for (var css in htmlWebpackPlugin.files.css) { %>
  <link href="<%= htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
  <% } %>
<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
<script src="<%= htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
<% } %>

就能够一样实现静态资源的切换,因此对于前端渲染模板的这种状况,咱们无须再生成一个 json 文件,对于使用诸如 react、vue 这种框架,仅使用这个插件也是极好的。

htmlWebpackPlugin 具体还有哪些属性能够配置,能够参考下这个 default template 查看完整例子

相关文章
相关标签/搜索