h5混合开发加载优化问题

1、问题描述

在项目中,存在微信公众号(即: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,都使用缓存中的数据。

2、优化方向

1、H5页面按需加载

首先当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

其中id为webpack的模块id,不方便读,可以替换成name,

即chunkFilename:utils.assetsPath(js/[name].[hash].js),这的name为chunk name,值为上面的’chunkname-1’

关于hash

utils.assetsPath(js/[name].[hash].js)中的hash可以有三种值:

1、hash,工程级别的,即每次修改任何一个文件,所有文件名的hash至都将改变。

2、chunkhash根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。

3、contenthash是针对文件内容级别的,只有你自己模块的内容变了,那么hash值才改变

2、关于WebView加载拦截

对于某些公用的第三方库文件,很少去修改,可以将其打包为静态资源直接放在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可以对当前构建进行分析改进。

3、关于nginx缓存问题

虽然在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改变

3、其他方面优化

1、预取/预加载模块(prefetch/preload)

即在网络空闲的时间加载用户可能访问的模块的资源文件:

prefetch(预取):将来某些导航下可能需要的资源

preload(预加载):当前导航下可能需要资源

可以借助import方法

const LoginModal = import(/* webpackPrefetch: true */ 'LoginModal');
const ChartingLibrary = import(/* webpackPreload: true */ 'ChartingLibrary');

2、iconfont

在H5中可能使用较多的图标,以便让页面看起来更美观,使用SVG或者雪碧图等,需要每次修改相关的文件,而使用iconfont只需要在index.html中修改相关的引用地址名称,也不需要考虑图片缓存问题。

3、直接引用第三发CDN资源

4、代码压缩合并

可以参考webpack插件uglifyjs-webpack-plugin

5、serviceWorker

注册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)

你可能感兴趣的:(vue,webapck)