{"action":"created","issue":{"url":"https://api.github.com/repos/mipengine/mip2/issues/69","repository_url":"https://api.github.com/repos/mipengine/mip2","labels_url":"https://api.github.com/repos/mipengine/mip2/issues/69/labels{/name}","comments_url":"https://api.github.com/repos/mipengine/mip2/issues/69/comments","events_url":"https://api.github.com/repos/mipengine/mip2/issues/69/events","html_url":"https://github.com/mipengine/mip2/issues/69","id":342984130,"node_id":"MDU6SXNzdWUzNDI5ODQxMzA=","number":69,"title":"前端预渲染方案","user":{"login":"huanghuiquan","id":1880596,"node_id":"MDQ6VXNlcjE4ODA1OTY=","avatar_url":"https://avatars0.githubusercontent.com/u/1880596?v=4","gravatar_id":"","url":"https://api.github.com/users/huanghuiquan","html_url":"https://github.com/huanghuiquan","followers_url":"https://api.github.com/users/huanghuiquan/followers","following_url":"https://api.github.com/users/huanghuiquan/following{/other_user}","gists_url":"https://api.github.com/users/huanghuiquan/gists{/gist_id}","starred_url":"https://api.github.com/users/huanghuiquan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/huanghuiquan/subscriptions","organizations_url":"https://api.github.com/users/huanghuiquan/orgs","repos_url":"https://api.github.com/users/huanghuiquan/repos","events_url":"https://api.github.com/users/huanghuiquan/events{/privacy}","received_events_url":"https://api.github.com/users/huanghuiquan/received_events","type":"User","site_admin":false},"labels":[{"id":988946949,"node_id":"MDU6TGFiZWw5ODg5NDY5NDk=","url":"https://api.github.com/repos/mipengine/mip2/labels/discussion","name":"discussion","color":"8ee884","default":false},{"id":990137740,"node_id":"MDU6TGFiZWw5OTAxMzc3NDA=","url":"https://api.github.com/repos/mipengine/mip2/labels/feature%20request","name":"feature request","color":"eaba96","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":13,"created_at":"2018-07-20T06:06:43Z","updated_at":"2018-09-20T07:00:56Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"# MIP 前端预渲染机制&方案\r\n\r\n## 概述\r\n\r\nMIP 预渲染即在结果页浏览期间，对部分 MIP 页面在后台提前创建 iframe 并进行文档预取和预解析渲染，在点击的同时将 iframe 进行展示并完成后续渲染加载。\r\n\r\n## 预渲染触发机制\r\n\r\n1. 结果页 JS 模块判断页面中有需要预加载的页面\r\n2. 在触发 load 事件后尝试进行预渲染\r\n3. 在用户点击预渲染好的结果时直接带着预渲染好的 iframe 页面滑入，动画完成后，继续渲染组件内容\r\n\r\n## 预渲染机制\r\n\r\n- 落地页预渲染环境获取：\r\n在结果页创建 iframe 时传入 hash `#prerender=1`， 通过 hash 通知 mip 页面处于预渲染环境\r\n\r\n- 组件生命周期 block：\r\nMIP 核心初始化时获取`#prerender=1`, 初始化`MIP.env.prerender=true`，并监听`hashchange`事件，mip 页面内部组件通过判断运行时环境`MIP.env.prerender=true`选择阻塞 build 生命周期执行，并等待`MIP.env.prerender=false` 时才继续执行\r\n\r\n- 激活 mip 组件生命周期\r\n结果页在 iframe 动画结束后通过删除  hash `#prerender=1` 通知mip页面进入激活状态，触发 `MIP.env.prerender=false`，并继续执行 build 生命周期\r\n\r\n> 注意：1. 在 connectedCallback 阻塞 build 回调； 2. attributeChangedCallback 也需要阻塞和激活；\r\n\r\n- 显示声明不阻塞组件生命周期\r\n开发者可以通过组件标签属性控制组件是否需要在预渲染期间阻塞 build 生命周期的执行，例如 mip-img 不需要阻塞图片加载，可以这么显示声明：\r\n\r\n```\r\n<mip-img src=\"xxx\" prerender></mip-img>\r\n```\r\n\r\n## 预渲染策略\r\n\r\n### 预渲染时机选择\r\n\r\n由于页面预渲染需要较重的运算、网络等资源，可能会导致结果页面造成短暂卡顿\r\n\r\n### 预渲染内容\r\n\r\n- 资源加载： 主文档 HTML、JS 资源、CSS 资源\r\n- 渲染内容：\r\n\t- HTML 解析，内置标签 DOM 完全渲染\r\n\t- JS 加载和执行\r\n\t- CSS 样式应用\r\n- 不会渲染的内容：\r\n\t- 不会执行组件 build 周期内的内容，包括生成组件内部结构\r\n\t- 由于 Vue 实例在build 周期内创建，所以预渲染也不会创建 Vue 实例\r\n\r\n## 实现\r\n\r\n### mipSerivce\r\n\r\n预渲染事实上提前创建了整个 view 与 scope，并执行了部分 iframe-shell 逻辑，使得其在固有的 DOM 上创建了对应节点以保证 iframe 不会被移动。\r\n\r\n在部分 iOS 手百上，隐藏的 iframe 渲染可能在某些极端情况下导致手百卡死（似乎要求嵌套 iframe 内部调用了端能力，具体需要再测试），此处可能需要做出策略避让。\r\n\r\n### mip 核心代码\r\n\r\nmip 核心通过 hash 标记执行环境，通过注册 hashchange 事件回调阻止生命周期执行，并通过结果页激活iframe，并修改 hash 触发i frame 内 mip 标记环境变更为正常渲染模式，激活事件，继续执行 build 生命周期。\r\n\r\n改造点：\r\n1. mip 核心代码\r\n2. 检查和确认内置组件影响渲染的代码再build周期之内\r\n3. 规范业务组件的生命周期使用，确保预渲染下不执行的逻辑在 build 回调周期内，比如 ajax 请求\r\n\r\n\r\n## 预渲染效果预期\r\n\r\n1. 对站点而言，由于页面、脚本、样式本身都由 mip cache 提供，原理上对站长没有任何感知。\r\n2. 只对文档 HTML 上直接使用的标签有效，对于组件内结构包含的 mip 组件标签无效（因为内部结构还没有被创建）\r\n3. 预估MIP组件内部结构渲染的速度在 50ms 以内,下图是 build 开始到结束的打点时间（不包含图片和ajax之类的异步请求时间）\r\n![图片](http://agroup-bos.su.bcebos.com/27a38eb3d639ef5f1d67c96afe47a6a2b096be12)\r\n\r\n## 性能与统计\r\n\r\n### 统计打点\r\n\r\n1. mip 性能统计增加 buildStart 打点 和 buildEnd 打点\r\n2. 若预渲染策略命中，前端性能指标统一记为 1ms 上报；若预渲染失败则忽略维持正常统计\r\n\r\n> 如何确认 mip 页面首屏渲染完成？\r\n\r\n## 其它问题\r\n\r\n1. 预渲染需要网络加载大量资源，并且需要执行JS代码，需要考虑预渲染对结果页的影响，是否会导致结果页卡顿\r\n"},"comment":{"url":"https://api.github.com/repos/mipengine/mip2/issues/comments/423063880","html_url":"https://github.com/mipengine/mip2/issues/69#issuecomment-423063880","issue_url":"https://api.github.com/repos/mipengine/mip2/issues/69","id":423063880,"node_id":"MDEyOklzc3VlQ29tbWVudDQyMzA2Mzg4MA==","user":{"login":"huanghuiquan","id":1880596,"node_id":"MDQ6VXNlcjE4ODA1OTY=","avatar_url":"https://avatars0.githubusercontent.com/u/1880596?v=4","gravatar_id":"","url":"https://api.github.com/users/huanghuiquan","html_url":"https://github.com/huanghuiquan","followers_url":"https://api.github.com/users/huanghuiquan/followers","following_url":"https://api.github.com/users/huanghuiquan/following{/other_user}","gists_url":"https://api.github.com/users/huanghuiquan/gists{/gist_id}","starred_url":"https://api.github.com/users/huanghuiquan/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/huanghuiquan/subscriptions","organizations_url":"https://api.github.com/users/huanghuiquan/orgs","repos_url":"https://api.github.com/users/huanghuiquan/repos","events_url":"https://api.github.com/users/huanghuiquan/events{/privacy}","received_events_url":"https://api.github.com/users/huanghuiquan/received_events","type":"User","site_admin":false},"created_at":"2018-09-20T07:00:56Z","updated_at":"2018-09-20T07:00:56Z","author_association":"CONTRIBUTOR","body":"update: 根据 https://github.com/mipengine/mip2/issues/215 MIP 核心和 SF 通讯消息整理，统一事件名命名规范"}}
{"actor":{"display_login":"huanghuiquan"}}