在项目中,存在微信公众号(即:H5页面)以及安卓和iOS等两款app,在app中调用了H5页面部分业务代码和相关设备的详情页面,但在app中设置WebView缓存的时候,发现切换页面的时候数据没有及时更新,导致app放弃了webview缓存,其结果就是每次从app进入H5都会加载H5相关资源,导致加载缓慢。
WebView的四种缓存机制:
// LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
// LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
// LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
// LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
首先当WebView加载H5页面时,尽可能只加载其所需页面资源,配合webpack配置和vue-router,可以较为方便的实现。
vue-router提供的路由懒加载主要依赖与webpack的代码分割和vue的异步组件,webpack提供了两种方式分离方式:1、es6提供的import()即vue-router推荐的方法,2、是webpack特定的require.ensure。
具体使用如下:
const chunkname1 = r => require.ensure([], () => r(require('@/components/chunkname1.vue')), 'chunkname-1')
其中chunkname-1为传入的chunkName,若想真正将每个chunk独立出来,还需要给每个chunk名不同的名称,若chunkName相同会导致,相同名称的chunk打到同一个js中。
命名在webpack配置的output中设置,即输出,新增一项:chunkFilename: utils.assetsPath(js/[id].[hash].js
)
其中id为webpack的模块id,不方便读,可以替换成name,
即chunkFilename:utils.assetsPath(js/[name].[hash].js
),这的name为chunk name,值为上面的’chunkname-1’
utils.assetsPath(js/[name].[hash].js
)中的hash可以有三种值:
1、hash,工程级别的,即每次修改任何一个文件,所有文件名的hash至都将改变。
2、chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。
3、contenthash是针对文件内容级别的,只有你自己模块的内容变了,那么hash值才改变
对于某些公用的第三方库文件,很少去修改,可以将其打包为静态资源直接放在app本地,app访问时,拦截这部分资源直接使用本地资源,以加快第一次加载。
具体实现思路为:
1、使用Dll plugin提前打包第三方资源,并生成新的dll.js和manifest.json文件,其中dll.js为打包后的第三方资源,json为其依赖文件。可参考:https://blog.csdn.net/cjvalue/article/details/100083323
2、在npm run build构建生产版本代码时,加入DllReferencePlugin插件配置,以排除已经在Dll中打包的第三方资源文件,减小生产的vendor-async.js体积
new webpack.DllReferencePlugin({
context: path.resolve(__dirname, '..'),
manifest: require('../static/dll/vendor-manifest.json')
}),
* 需要将dll.js引入index.html中,可以直接使用script标签或者通过add-asset-html-webpack-plugin插件引入dll.js文件
3、将dll打包应用到开发环境,可以节省开发中构建项目的时间,webpack-bundle-analyzer可以对当前构建进行分析改进。
虽然在WebView中使用自带的缓存导致了数据异常问题,但通过nginx只缓存静态资源文件,还是可以的。通过匹配js和css文件设置相关的过期时间,尽量设置一个比较大的时间。
location ~.*\.(js|css)$
{
root /nginx/dist/;
expires 30d; ##不建议直接设置为max,可以设置为一个月或一年
}
这里需要注意的是,需要配合webpack的output的chunkFilename使用,建议使用
chunkFilename: utils.assetsPath(js/[name].[chunkhash].js)
* 在vue项目中,按路由懒加载方式,修改一个xx.vue文件至少会导致app.chunkhash.js、manifest.chunkhash.js和该文件本身生成的xx.chunkhash.js。
app.chunkhash.js由于引用了该vue的xx.chunkhash.js中的chunkhash改变。
mainfest.chunkhash.js改变是由于mainfest.chunkhash.js中包含了该vue的js生成时的chunkhash,引用名称改变,mainfest.chunkhash.js改变
即在网络空闲的时间加载用户可能访问的模块的资源文件:
prefetch(预取):将来某些导航下可能需要的资源
preload(预加载):当前导航下可能需要资源
可以借助import方法
const LoginModal = import(/* webpackPrefetch: true */ 'LoginModal');
const ChartingLibrary = import(/* webpackPreload: true */ 'ChartingLibrary');
在H5中可能使用较多的图标,以便让页面看起来更美观,使用SVG或者雪碧图等,需要每次修改相关的文件,而使用iconfont只需要在index.html中修改相关的引用地址名称,也不需要考虑图片缓存问题。
可以参考webpack插件uglifyjs-webpack-plugin
注册serviceWorker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}
缓存资源
var cacheName = 'v1';
var assetsToCache = [
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(cacheName).then(function(cache) {
return cache.addAll(assetsToCache);
}).then(function() {
return self.skipWaiting();
})
);
});
拦截请求,从缓存中获取资源
self.addEventListener('fetch', function(event) {
var requestUrl = new URL(event.request.url);
if (requestUrl.origin === location.origin) {
if (requestUrl.pathname === '/') {
event.respondWith(
caches.open(cacheName).then(function(cache) {
return fetch(event.request).then(function(networkResponse) {
cache.put(event.request, networkResponse.clone());
return networkResponse;
}).catch(function() {
return cache.match(event.request);
});
})
);
}
}
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
缓存控制更新
var OFFLINE_PREFIX = 'offline-';
var CACHE_NAME = 'main_v1.0.0';
self.addEventListener('activate', function(event) {
var mainCache = [CACHE_NAME];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if ( mainCache.indexOf(cacheName) === -1 && cacheName.indexOf(OFFLINE_PREFIX) === -1 ) {
// When it doesn't match any condition, delete it.
console.info('SW: deleting ' + cacheName);
return caches.delete(cacheName);
}
})
);
})
);
return self.clients.claim();
});
* serviceWorker主要处理缓存问题,web Worker主要处理计算问题(详见:https://blog.csdn.net/cjvalue/article/details/97921396)