解决公众号SPA的微信缓存问题

进行公众号开发的同学肯定遇到过这样的问题,新上线的功能,重新请求时,页面没有更新。
本人使用VUE开发的单页面应用,就是如此,问题虽小却棘手。
如何解决微信缓存问题,又不用手动更改版本号,实现自动化生成发布文件呢?

问题描述

项目背景:

基于vue-cli生成的webpack-sample项目
项目目录(已去除跟本次问题无关的)
1
2
3
4
5
6
7
8
9
10
project_name
├── dist #编译好的资源目录
│   ├── main.bundle.js
│   ├── common.bundle.js
│   ├── chunks #模块化的js文件
│   │   │── 1-xx.js
│   │  ...
├── index.html #项目入口文件
├── webpack.config.js #webpack配置
...

在使用webpack打包代码时,会有个入口文件,以及一些模块化的chunks文件;
新更新的功能,build代码,更新服务器后,需手动修改入口文件的版本号

index.html

1
2
3
4
<!--...-->
<div id="app"></div>
<script type="text/javascript" src="/dist/common.build.js?v=1.3.35"></script>
<script type="text/javascript" src="/dist/main.build.js?v=1.3.35"></script>

按预期?v=xxx更改后,浏览器会重新请求最新文件,But在微信浏览器中,并非全部如此。
经不完全测试,iphone系列以及部分安卓机可以按预期没有问题,但还有为数不少的安卓手机(比如三星、小米)上必须手动在微信网页右上角入口里刷新页面才行,这对用户来说肯定是不好的体验,不明所以的就会觉得程序有bug。

解决方法

既然更改静态资源版本号不好使,那暴力点儿,直接更改文件名呢?
(之前受制于vue-li集成的webpack-sample项目,入口文件在项目跟目录下,无法自动更改,故每次发版后手动更改index.html里的文件版本号,有一点刀耕火种。)

如此,便解决以下几个问题就万事大吉了

1.如何生成新名字的js文件
2.新的js文件如何被自动引入到项目入口文件里
3.如何不影响本地集成的开发环境
  • 针对第一点,在webpack的配置里修改export的filename即可

    修改webpack配置,在原来的名字后面加了时间戳,每次build时都会发生变化

    1
    2
    3
    4
    5
    6
    7
    //...
    output: {
    path: path.resolve(__dirname, './dist'),
    filename: `[name]-${Date.parse(new Date())}.build.js`, //在原来的名字后面加了时间戳,每次build时都会发生变化
    chunkFilename: 'chunts/[name]-[chunkhash:8].js'
    },
    //...
  • 针对第二点,可以通过html-webpack-plugin这个官方插件解决
    修改webpack配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var HTMLWebpackPlugin = require('html-webpack-plugin');
    //...
    module.exports = {
    //...
    plugins: [
    new HTMLWebpackPlugin({
    filename: 'index.html', //自动在dist目录下生成入口文件的名称(不能直接生成到项目根目录下)
    template: './index_template.html', //以根目录下的`index_template.html`文件为模板
    }),
    //...
    ]
    }
  • 针对第三点,下面稍微解释下为什么会有这个问题

    本地npm run dev开发时,webpack-dev-server默认是以项目根目录下的index.html为项目入口文件,除了手动更改外,请求的静态文件是不变的;
    而之前的两点中重中之重是变化的文件名称,这样就找不到入口js文件了。

    So,解决方案来了,同一个设置,两个环境不能兼容,那就分开设置吧^_^

    webpack.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    if (process.env.NODE_ENV === 'development') {
    module.exports.output.publicPath = '/dist/';
    module.exports.output.filename = '[name].build.js';
    }
    if (process.env.NODE_ENV === 'production') {
    module.exports.output.filename = `[name]-${Date.parse(new Date())}.build.js`;
    module.exports.plugins = (module.exports.plugins || []).concat([
    new HTMLWebpackPlugin({
    filename: 'index.html',
    template: './index_template.html',
    }),
    //...
    ])
    }

    到此,问题全部解决,结果很赞、很完美。

    需要注意

    1.`html-webpack-plugin`自动生成的入口文件只能是在设置的公共资源目录下
    2.打包后的代码的入口文件由原来的 `project_name/index.html` 变成了 `project_name/dist/index.html`,
      一定要记得更改服务器nginx的项目目录配置
    

修改之后的项目目录

1
2
3
4
5
6
7
8
9
10
11
12
project_name
├── dist #编译好的资源目录
│   ├── index.html #线上打包入口文件
│   ├── main-1531500891000.bundle.js
│   ├── common-1531500891000.bundle.js
│   ├── chunks #模块化的js文件
│   │   │── 1-xx.js
│   │  ...
├── index.html #本地开发入口文件
├── index_template.html #项目入口模板
├── webpack.config.js #webpack配置
...

总结

解决微信SPA强缓存的问题,可以通过修改入口文件的js文件名称来彻底解决。除此之外,就是考虑工程化的问题,如何以更少的人工代价来实现了。

此方案也有个弊端,就是每次更新版本后,用户请求网页时需要重新加载入口js文件,会耗些流量。鉴于此,可以尽量避免小功能多次更新,版本周期过短。

  • 话外音

微信为什么会有这么强的缓存机制,或许有机会研读了微信内置qq浏览器后才会有答案。
感谢我们能够站在牛人的肩膀上,不用从头来实现底层的东西,只要简单几行来配置就可以了。

此次解决完问题后,有种蜕变的感觉,出现问题后,看待问题的角度也有些变化,很有成就感。
之前疲于业务逻辑,没有时间好好的沉淀下来,以后要多些时间来自我总结。

参考: https://webpack.js.org/plugins/html-webpack-plugin/