app内H5页面离线技术和性能优化工作总结
前提:需要app native支持html文件存储(仅仅离线化html文件)
技术实现思路:
- 访问H5地址
- app识别是否有 对应版本/离线H5的html文件 (没有就去服务器下载)
- webview打开html
- 是否有js、css等静态资源(第一次没有,要花网络请求时间去请求)
- 静态资源做好本地缓存设置,可以设置http的response-headers缓存
- 渲染页面
一些注意点
- 离线方案中仅仅离线了HTML文件,其他的资源首次以在线形式进行访问,然后充分利用CDN缓存。
- 二次访问,达到离线+缓存资源实现页面快速渲染。
- 更新分成两部分:离线HTML文件更新,由口袋离线应用包通过资源版本号管理,每次发版会触发重新拉取;资源更新,由html中资源路径的变化控制,每次发版,资源路径会变化。
- 多页面部分资源尽量做到共用,做到缓存最大使用
根据业务代码目录结构打包html产物:
modules.js
const glob = require('glob');
const fs = require('fs');
const path = require('path');
const resolveApp = (pathname) => path.resolve(process.cwd(), pathname);
const APP_DIR = resolveApp('src/views/');
function handleEntryConfig(checkPath) {
const chunk = [];
const list = glob.sync(checkPath).map(file => {
const data = path.parse(file);
// 拿到.html存在对应的.js
if (fs.existsSync(`${data.dir}${path.sep}${data.name}.js`)) {
const dir_path = data.dir.replace(APP_DIR, '');
// console.log('dir_path====', dir_path);
const arr = dir_path.split(path.sep).filter(Boolean);
const name = arr.join('_');
let filename = '';
if (data.name === 'index') {
filename = name;
} else {
filename = `${name}_${data.name}`;
}
chunk.push({
filename: `${filename}.html`,
template: file,
chunk: filename,
});
return {
[filename]: `${data.dir}${path.sep}${data.name}.js`,
};
}
});
const entry = list.reduce(function (reduce, item) {
return Object.assign(reduce, item);
}, {});
// console.log('输出页面配置=====', { entry: entry, chunk: chunk, list: list });
return {
chunk,
entry,
listEntry: list,
};
}
module.exports = function (options) {
let chunk = [],
entry = {},
list = [];
const checkPath = `${APP_DIR}/**/*.html`; // 查找目录入口, 默认查找src/views下面所有的html
const modules = require('../modules/')(options);
// sirus离线打包
if (options.pages) {
modules.increase = [options.pages];
}
// dev环境pack为空或增量,increase存在配置
if (['', 'inc'].includes(options.pack) && modules.increase.length > 0) {
modules.increase.forEach(module => {
const path = resolveApp(`${APP_DIR}/${module}`);
const data = handleEntryConfig(`${path}/**/*.html`);
// console.log('data 数据======', data);
data.chunk.forEach(item => {
chunk.push(item);
});
list = list.concat(data.listEntry);
Object.assign(entry, data.entry);
});
} else if (options.pack === 'all' || modules.increase.length === 0) {
// all 或为空则全量
const data = handleEntryConfig(checkPath);
chunk = data.chunk;
entry = data.entry;
list = list.concat(data.listEntry);
}
// console.log('最终输出页面配置=====', { entry: entry, chunk: chunk, listEntry: list });
return {
chunk,
entry,
listEntry: list,
};
};
modules.js
module.exports = {
ignore: [], // 忽略构建的模块
increase: ['xxx/xxxxxx'], // 为空则打全量,否则构建指定的模块
};
webpack.js打包部分引入modules.js中运行出来的产物:
entry赋值给entry使用;
chunk给HtmlWebpackPlugin,打包chunk使用
性能优化部分
- 使用 link标签 对cdn资源域名、接口资源域名 进行提前预建联,字体文件预加载
<link rel="preconnect" href="https://cdn-xxx.xxxx.xxx.xx" />
<link rel="dns-prefetch" href="https://cdn-xxx.xxxx.xxx.xx" />
<link rel="dns-prefetch" href="https://api-xxx.xxxx.xxx.xx" />
<link rel="preload" as="font" type="font/woff2" crossorigin="anonymous" href="https://xxx.xx/woff2" />
- cdn资源、公用js库,不经常更新版本的设置缓存,加载缓存的文件
- 分屏加载组件,非首屏的组件做动态加载,降低了首屏加载的js大小
<!--第二屏渲染组件-->
<component ref="dynamicComponent" :is="comName" :prdCode="prdCode" :templateId="templateId"></component>
mounted() {
this.$nextTick(() => {
this.lazyLoadUnFirstBundle();
});
}
lazyLoadUnFirstBundle() {
this.comName = () =>
import(/* webpackChunkName: "deposit_structured_detai_second_screen" */ './detail_part2.vue').then(
m => m.default,
);
},
- 首屏数据做LocalStorage的缓存,注意5M内存满了没法继续存时清空旧的
- 首屏渲染之后,再使用loadjs加载三方组件,比如分享、电梯、广告弹窗等js
- 函数、变量的引入使用按需引入
- 编码时考虑tree shaking
- 按需加载polyfill.js替代babel编译
- 优化FCP前的接口请求,修改接口的调用时机,非必要的接口后置,可合并的接口合并
标签:
性能优化
本文转载自: https://blog.csdn.net/qq_39302236/article/details/140174473
版权归原作者 Chngoshawk 所有, 如有侵权,请联系我们删除。
版权归原作者 Chngoshawk 所有, 如有侵权,请联系我们删除。