工程中的相关优化
渲染相关
渲染流程
- 生成 dom-tree,
- 生成 css-tree
- 合成 css-dom-tree,也就是 render-tree
- 计算布局(layout):根据生成的 render-tree,进行回流,以计算每个节点的几何信息(位置,大小,字体样式)。
- 绘制(painting):根据渲染树和回流得到的几何信息,得到每个节点的绝对像素
- 展示(display):将像素发送给图形处理器(GPU),展示到页面上
页面渲染技术架构
服务端渲染
- 后端同步渲染、同构直出页面
客户端渲染
- js 渲染:静态化、前后端分离、单页应用
- web app:ang, vue, react
lazy-load实现
<image src='default.png' :data-src='xxx.png' class='lazy-load-img'>
function throttle(fn, time) {
let isRun = false
return function() {
if(isRun) return
isRun = true
setTimeout(() => {
fn.call(this, arguments)
isRun = false
}, time);
}
}
window.scroll = throttle(watchScroll, 200);
function watchScroll() {
var bodyScrollHeight = document.body.scrollTop;
var windowHeight = window.innerHeight;
var images = document.querySelectorAll("lazy-load-img")
for (let i = 0; i < images.length; i++) {
var imgHeight = images[i].offsetTop;
if (imgHeight < windowHeight + bodyScrollHeight) {
images[i].src = images[i].getAttribute('data-src');
images[i].className = images[i].className.replace('lazy-load-img','')
}
}
}
预加载
实现方式
- html 标签
- image对 象
- 使用 preload,prefetch,preconnect
<link rel="preload" href="xxx.css">
<link rel="prefetch" href="scr/image.png">
<link rel="dns-prefetch" href="scr/image.png">
<link rel="preconnect" href="https://my.com" crossorigin>
webview
ios UIWebview
不足
- 内存泄露
- 极高内存峰值
- Touch Delay(300ms延迟)
- JavaScript 的运行性能还有通信限制
ios wkWebview
优势
- 苹果在 wwdc 2014 上推出的新一代 webview 组件
- 内存开销变低
- 在性能,稳定性,占有内存方面有很大的提升
- 高达 60fps 的刷新率
- 支持右滑返回
- 更多的 html 属性
监控
h5质量即时检测
页面错误
- js 报错
- 接口报错
- 线上环境检测
- 页面白屏
页面性能
- 页面加载时间检测
- 前端 html,css, js 压缩检测
- js,css 个数检测
- 服务器 gzip 检测
- 服务器缓存设置检测
页面安全
- http 和 https 检测
- xss 检测
上线后h5性能和错误监控
页面性能监控
- js 错误监控
- API 接口监控
- 日志详情
- 用户轨迹
统计报表
- 大盘走势
- 地域
- 运营商
- 浏览器
页面管理
性能服务
报警服务
打包优化手段
TreeShaking
TreeShaking
会在静态解析过程中将代码中的冗余代码去除。在 production
环境中默认开启。该功能借鉴自 rollup
ScopeHoisting
ScopeHoisting
主要用于处理构建过程中由于模块处理导致出现的大量匿名闭包函数,在 ES6-
中,通常会通过使用匿名闭包函数来构造模块。
ScopeHoisting
的原理是通过分析代码中的模块执行顺序,替换变量名称来达到减少闭包产生的效果。闭包会形成大量的块级作用域,增加内存损耗。
SplitChunk
对于大的应用来讲,将所有代码都放到一个文件中不够显示,需要对代码进行分割和懒加载(用到的时候才加载)。
懒加载 js 脚本的方式
- commonJS: require.ensure
- ES6:动态 import(目前原生未支持,需要babel转换)
@babel/plugin-syntax-dynamic-import
Terser
压缩工具,uglify 在遇到 es6 时会报错,terser 可以进行处理。
Eslint
Eslint 代码检查工具
eslint 的使用
安装 husky,增加 npm script, 通过 lint-staged 增量检查修改方式
"scripts": {
"precommit": "lint-staged"
},
"lint-staged": {
"linters": {
"*.{js,css}": ["eslint --fix", "git add"]
}
}
webpack 打包基础库
webpack 打包基础库,满足以下条件
- 需要打包压缩版和非压缩版
- 支持
AMD/CJS/ESM
模块引入
webpack.config.js
服务端渲染
渲染:HTML+CSS+JS+Data->渲染后的 HTML
服务端:
- 所有模板等资源都储存在服务端
- 内网机器拉取数据快
- 一个HTML返回数据
| |客户端渲染| 服务端渲染| |--|--|--| |请求|多个请求|1个请求| |加载过程|HTML&数据串行加载|1个请求返回HTML&数据| |渲染|前端渲染|服务端渲染|
服务端渲染的核心是减少请求,减少白屏时间,对SEO更加友好
实现思路
服务端:使用库的
renderToString
方法将组件渲染成字符串,服务端返回对应的模版。客户端:打包出针对服务端的组件
构建包配置
通用性
- 业务开发者无需关注构建配置
- 统一团队构建脚本
可维护性
- 构建配置合理拆分
- README 文档
质量
- 冒烟测试,单元测试,测试覆盖率
- 持续集成
webpack优化
webpack 的优化分两块
- 速度优化
- 体积优化
性能分析
- 速度分析
分析整个打包总耗时,每个插件和 loader 的耗时情况
const speedMeasureWebpackPlugin = require("speed-measure-webpack-plugin")
const smp = new speedMeasureWebpackPlugin()
const webpackConfig = smp.warp({
plugins: [
xxx
]
})
- webpack-bundle-analyzer
依赖的第三方模块文件大小,业务里面的组件代码大小
const wba = require("webpack-bundle-analyzer")
module.exports = {
plugins: [
new wba()
]
}
速度优化
- HappyPack 解析资源
每次 webpack 解析一个模块,HappyPack 会将它及它的依赖分配给 worker 线程中
- 使用 thread-loader 解析资源
每次 webpack 解析一个模块,thread-loader 会将它及它的依赖分配给 worker 线程中
- 并行压缩
方法一:使用 parallel-uglify-plugin 插件 方法二: uglifyjs-webpack-plugin 开启 parallel 参数 方法三: terser-webpack-plugin 开启 parallel 参数
分包:设置 Externals
- externals
将 vue 等基础包通过 cdn 引入,不打入 bundle 中。 使用 html-webpack-externals-plugin
预编译资源模块
将 vue, vuex、vue-router 基础包和业务基础包打包成一个文件
使用 DLLPlugin 进行分包,DllReferencePlugin 对 manifest.json 引用
使用 DllReferencePlugin 引用 manifest.json
缓存
目的:提升二次构建速度
- babel-loader 开启缓存
- terser-webpack-plugin 开启缓存
- 使用 cache-loader 或者 hard-source-webpack-plugin
缩小构建目标
目的:尽可能的少构建模块,比如 babel-loader 不解析 node_modules
减少文件搜索范围
优化 resolve.modules 配置(减少模块搜索层级)
优化 resolve.mainFields 配置
优化 resolve.extensions 配置
合理使用 alias
体积优化
- 删除掉无用的 CSS
PurifyCSS: 遍历代码,识别已经用到的 CSS class uncss: HTML 需要通过 jsdom 加载,所有的样式通过PostCSS解析,通过document.querySelector 来识别在 html 文件里面不存在的选择器
使用 purgecss-webpack-plugin 和 mini-css-extract-plugin 配合使用。
- 动态 Polyfill
Polyfill Service 原理: 识别 User Agent,下发不同的 Polyfill
loader 和 plugin
loader loader 是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中
- 处理一个文件可以使用多个 loader,loader 的执行顺序是和本身的顺序是相反的,即最后一个 loader 最先执行,第一个 loader 最后执行。
- 第一个执行的 loader 接收源文件内容作为参数,其他loader接收前一个执行的 loader 的返回值作为参数。最后执行的 loader 会返回此模块的 JavaScript 源码
loader 接收一个参数,并且 return 一个内容就 ok 了。
- plugin
在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。