1、开发一个前端统计SDK
const PV_URL_SET = new Set()
class MyStatistic {
constructor(productId) {
this.productId = productId
this.initPerformance()//性能统计
this.initError()//错误监控
}
// 发送统计数据
send(url, params = {}) {
params.productId = this.productId
const paramArr = []
for (var key in params) {
const val = params[key]
paramArr.push(`${key}=${val}`)
}
const newUrl = `${url}?${paramArr.join('&')}`//url?a=10&b=20
// 用<img> 发送:1.可跨域2.兼容性非常好
const img = document.createElement('img')
img.src = newUrl//get
}
// 初始化性能统计
initPerformance() {
// send
const url = 'yyy'
this.send(url, performance.timing)//给最原始的结果,原始数据
}
// 初始化错误监控
initError() {
// send window.onerror
window.addEventListener('error', event => {
const { error, lineno, colno } = event
this.error(error,{lineno,colno})
})
// Promise 未 catch报错
window.addEventListener('unhandledrejection',event=>{
this.error(new Error(event.reason),{type:'unhandledrejection'})
})
}
pv() {
const href = location.href
if (PV_URL_SET.get(href)) return//不重复发送PV
// 特殊的event
this.event('pv')
PV_URL_SET.set(href)//路由切换 hash webAPI
}
event(key, val) {
// 自定义事件统计
const url = 'xxx'//server API
this.send(url, { key, val })
}
error(err, info = {}) {
const url = 'zzz'
const {message,stack} = err
this.send(url, { message,stack,...info })
}
}
const s = new MyStatistic('a1')
s.pv()//SPA
try {
} catch (err) {
s.error('key', err.stack)
}
2、sourcemap的作用
- js上线时要压缩、混淆
- 线上的js报错信息,将无法识别行、列
- sourcemap
- eval-JS在eval(…)中,不生成 sourcemap
- source-map-生成单独的 map 文件,并在 JS最后指定
- eval-source-map,JS在eval(…)中,sourcemap 内嵌
- inline-source-map-sourcemap 内嵌到JS中
- cheap-source-map-sourcemap 中只有行信息,没有列
3、何时用SPA,何时用MPA?
- SPA-Single-page Application 单页面应用
- MPA-Mutil-page Application 多页面应用
- 默认情况下,Vue React 都是SPA
SPA
- 功能较多,一个页面展示不完
- 以操作为主,非展示为主
- 适合一个综合Web应用
- 大型后台管理系统,如阿里云的console
- 知识库、语雀、石墨文档
- 比较复杂的WebApp,如外卖H5
MPA
- 功能较少,一个页面展示的完
- 以展示为主,操作较少
- 适合功能孤立的页面
- 分享页,如腾讯文档分享出去
- 新闻详情页,如新闻App的详情页,微信公众号发布的页面
4、何时使用SSR,何时不用?
SSR-Server-side render服务端渲染
SSR优势
- 服务端直出html
- 性能好
- 对SEO优化
SSR劣势
- 前后端同构,开发成本高(学习、调试、运维等)
是否需要SSR
管理后端不需要
SSR的应用场景
C端,以阅读为主的单页面,如新闻页,运营宜传广告页面,官网等。1.需要快:2.需要 SEO
5、设计 用户-角色-权限
RBAC- Role-based access control(基于角色的访问控制)
功能模块
- 用户管理:增删改查,绑定角色
- 角色管理:增删改查,绑定权限
- 权限管理:增删改查
6、如何做技术选型
- 前端框架(Vue React Nuxt.js或者nodejs框架)
- 语言(Javascript或TypeScript)
- 其他(构建工具、CI/CD等)
技术选型的依据
- 社区是否成熟
- 公司是否已有经验积累
- 团队成员的学习成本
- 管理成本(如用TS遍地是any怎么办?)
- 运维成本
7、设计实现一个H5图片懒加载SDK
- 定义< img src=loading.png data.src=xxx.png />
- 页面滚动,图片露出来,将data.src赋值给src
- 滚动要节流
获取图片定位
- 可以根据元素位置elem.getBoundingClientRect(),获取图片位置
- 根据图片top和window.innerHeight作对比
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.item-container{
border-top: 1px solid #ccc;
margin-bottom: 30px;
}
.item-container img{
width: 100%;
border: 1px solid #eee;
border-radius: 10px;
overflow: hidden;
}
</style>
</head>
<body>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<div class="item-container">
<p>新闻标题</p>
<img src="./img/loading.gif" data-src="./img/bg.png" alt="">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script>
function mapImagesAndTryLoad(){
const images = document.querySelectorAll('img[data-src]')
if(images.length===0) return
images.forEach(img=>{
const rect = img.getBoundingClientRect()
if(rect.top<window.innerHeight){
// 露出来
img.src = img.dataset.src
img.removeAttribute('data-src')//移除data-src属性,为了下次执行减少计算成本
}
})
}
window.addEventListener('scroll',_.throttle(()=>{
mapImagesAndTryLoad()
}),100)
mapImagesAndTryLoad()
</script>
</body>
</html>