mand-mobile是否ssr支持updssr

1、vue init nuxt/starter ssr-blog
2、cd ssr-blog
3、npm install
4、npm run dev
原文链接:
Nuxt.js实现SSR的应用
搜索引擎优化(Search Engine Optimization),它是指通过站内优化,如:网站结构调整、网站内容建设、网站代码优化以及站外优化等方法,来进行搜索引擎优化。
2017 年,最热开源静态网站生成器 TOP 20 揭晓!
近年来,作为传统动态网站基础架构的替代方案,现代静态网站生成器日渐盛行。在 StaticGen 上有一个关于静态站点生成器的开源目录,2017 年该网站追踪了超过一百个生成器,并见证了这些生成器在这一...
基于 Vue 的轻量级静态网站生成器 VuePress
Vue.js 的创始人尤雨溪大大在 twitter 上发布了一个全新的基于 Vue 的静态网站生成器-,这对于广大 Vue 爱好者来说无疑是一个好消息!
什么是VuePress
VuePress由...
Nuxt.js小试牛刀
转自:https://zhuanlan.zhihu.com/p/
为什么要用Nuxt?
我们需要搜索引擎更多地抓取到我们的内容,更详细地认识到我们的网页结构,而不...
一起学下nuxt.js(上)
Nuxt.js通用 vue.jsSSRSSR:服务器端 VUE渲染成HTML返回浏览器SEO:VUE SPA(单页)新闻搜索引擎比较SPA加载较快因为新闻博客都需要搜索引擎抓取内容,大量的流量来自于服...
Vue.js 后端渲染开源库 Nuxt.js
Nuxt.js 详细介绍Nuxt.js 是一个通过 Vue 用于服务端渲染的简单框架,灵感来自 Next.js。目前尚处于开发阶段,1.0 版本即将发布1 分钟视频演示Nuxt 基于 ES2015,这...
Nuxtjs vue-resource element 安装及使用
新建一个目录
新建一个Nuxt.js的目录,右击此目录使用visaul studio code 打开
安装ESLint、Vetur插件
安装vs code扩展ESLint,此插件用于编辑器的js ...
没有更多推荐了,&figure&&img src=&https://pic1.zhimg.com/v2-543d5aee_b.jpg& data-rawwidth=&1920& data-rawheight=&640& class=&origin_image zh-lightbox-thumb& width=&1920& data-original=&https://pic1.zhimg.com/v2-543d5aee_r.jpg&&&/figure&&p&&i&Mand Mobile&/i& 是由滴滴出行战略事业群前端团队基于Vuejs 2.0开发的移动端组件库。它遵循统一的视觉设计规范,由包括基础、表单、操作反馈和业务在内的四类组件组成。Mand Mobile致力于提升金融相关产品的用户体验,提高设计和研发效率,让复杂的场景变得简单。&/p&&h2&项目背景&/h2&&p&金融类产品种类繁多,功能相对来说较复杂,设计及开发成本较高。从各种表单的填写,验证码/密码输入,到图表展示,再到数字键盘和收银台。这些功能往往使用频率较高,对于视觉一致性和兼容性都有着更高的要求。&/p&&p&在这个背景下,我们尝试在项目的设计和开发过程中积累了部分高频使用的组件,逐渐梳理出统一的视觉和开发规范,以期望能够帮助团队快速地迭代出产品。经过一年时间的积累,组件库已应用于四大业务板块共10余款产品中,并在业务的考验中不断成熟。&/p&&h2&扫码体验&/h2&&p&&a href=&https://link.zhihu.com/?target=https%3A//didi.github.io/mand-mobile/examples/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&didi.github.io/mand-mob&/span&&span class=&invisible&&ile/examples/&/span&&span class=&ellipsis&&&/span&&/a& (二维码自动识别)&/p&&h2&项目特点&/h2&&p&&b&丰富的组件&/b& &/p&&p&Mand Mobile提供了30+的实用组件,能够满足移动端页面开发中的大部分需求。其中的业务类组件还针对金融领域,提取了包括图表、数字键盘等,从而更好地满足相关产品的开发需要。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-259fc53fa443d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2620& data-rawheight=&1730& class=&origin_image zh-lightbox-thumb& width=&2620& data-original=&https://pic1.zhimg.com/v2-259fc53fa443d_r.jpg&&&/figure&&p&&br&&/p&&p&&b&统一的视觉规范&/b& &/p&&p&视觉设计既要兼顾可用性,又要具备信息传达的直观度和界面展现的美观度。为了达成这一目标,&i&Mand Mobile&/i& 的视觉设计规范划分为功能型组件规范和非功能性视觉规范两部分。功能型组件包括但不限于浮层,提示,弹窗,表单等,强调平台化的统一复用和对接研发的高效率实现。非功能性视觉规范会定义主辅颜色体系,场景按钮等。&/p&&p&&i&Mand Mobile&/i& 的视觉规范由滴滴战略事业群设计师设计并维护,保证了应用的项目内部、项目之间都能保持高度的视觉一致性。简洁大气的设计风格,在保证项目整体的美观与格调的同时,也让其有能力适应更广泛的应用场景。&/p&&p&&b&详细的文档和示例&/b& &/p&&p&我们为每个组件编写了详细的说明文档,从组件的引入方法,到属性Props,事件Events,公共方法Methods等都有详细的介绍。为了更直观的介绍组件使用方法以及效果,我们针对每个组件都提供了多个可以即时操作的demo,从而让用户能更直观地了解组件的各项功能。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-bd7c8fea8f0f48e2a4a826_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2420& data-rawheight=&2238& class=&origin_image zh-lightbox-thumb& width=&2420& data-original=&https://pic4.zhimg.com/v2-bd7c8fea8f0f48e2a4a826_r.jpg&&&/figure&&figure&&img src=&https://pic4.zhimg.com/v2-4bd6cbc7af27a860cde6_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2384& data-rawheight=&1042& class=&origin_image zh-lightbox-thumb& width=&2384& data-original=&https://pic4.zhimg.com/v2-4bd6cbc7af27a860cde6_r.jpg&&&/figure&&p&&b&严格控制的Bundle体积&/b& &/p&&p&在保证功能完善、强大的同时,&i&Mand Mobile&/i& 还在Bundle体积上表现出色。Mand Mobile可导出es,umd两种格式包,其中es打包大小仅为139kb(gzip 34kb),umd格式最终大小仅为135kb(gzip 33kb),从而为使用者在控制项目体积上提供有力的帮助。除此之外,Mand Mobile还支持tree shaking以及支持按需加载,对于仅需使用部分组件的项目来说,这能进一步减小项目的体积。具体的配置方法可参考快速上手中的引用部分。&/p&&p&&b&灵活转换的样式主题&/b& &/p&&p&尽管&i&Mand Mobile&/i& 的设计风格简约优雅,足以适应大部分项目的视觉要求,但您仍可为其定制专属的样式主题。Mand Mobile内部样式基于Stylus开发,可通过全局和组件的样式变量对主题样式进行调整。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-b6ec793eab36b51e528e4c471741bffa_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1500& data-rawheight=&600& class=&origin_image zh-lightbox-thumb& width=&1500& data-original=&https://pic1.zhimg.com/v2-b6ec793eab36b51e528e4c471741bffa_r.jpg&&&/figure&&h2&项目反馈&/h2&&p&&i&Mand Mobile&/i& 刚刚起步不久,目前仍存在一些不够完善的地方。在继续完善现有组件的同时,我们还会继续积累更多的实用组件,另外也会尝试将视觉和逻辑抽离,从而来满足更多更广泛的使用需求。&/p&&p&我们衷心地期望 &i&Mand Mobile&/i& 的出现能够对您的工作有所帮助。与此同时,我们也期望得到您的支持、反馈和参与,为让金融场景开发更简单的目标而共同努力。您遇到的任何问题,可随时在&a href=&https://link.zhihu.com/?target=https%3A//github.com/didi/mand-mobile/issues& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&GitHub&/a&提交。&/p&&h2&相关链接&/h2&&ul&&li&Home: &a href=&https://link.zhihu.com/?target=https%3A//didi.github.io/mand-mobile& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&didi.github.io/mand-mob&/span&&span class=&invisible&&ile&/span&&span class=&ellipsis&&&/span&&/a&&/li&&li&Github: &a href=&https://link.zhihu.com/?target=https%3A//github.com/didi/mand-mobile& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/didi/mand-mo&/span&&span class=&invisible&&bile&/span&&span class=&ellipsis&&&/span&&/a&&/li&&/ul&
Mand Mobile 是由滴滴出行战略事业群前端团队基于Vuejs 2.0开发的移动端组件库。它遵循统一的视觉设计规范,由包括基础、表单、操作反馈和业务在内的四类组件组成。Mand Mobile致力于提升金融相关产品的用户体验,提高设计和研发效率,让复杂的场景变得简单…
&p&&/p&&a href=&https://www.zhihu.com/question/& data-draft-node=&block& data-draft-type=&link-card& class=&internal&&为什么很多明知js的OOP是假的,还不厌其烦地实现,而不使用OLOO风格?&/a&&p&今天看到有人问「为什么明知 JS 的 class 是假的,程序员还不厌其烦地实现 class,而不使用原型」。&/p&&p&作为 JS 原型的坚定支持者,我不得不喷一下「JS 里的 class 比 prototype 好」这个观点。&/p&&p&&br&&/p&&p&目录&/p&&ol&&li&class 的问题&/li&&ol&&li&class 功能残缺&/li&&li&class 使你无法理解 JS 的本质&/li&&li&class 不能带来额外的好处,如类型安全&/li&&/ol&&li&prototype 的问题&/li&&ol&&li&ES 6 之前大家不想了解 prototype &/li&&li&ES 6 之前 prototype 操作不方便&/li&&/ol&&li&去学 prototype&/li&&/ol&&p&&br&&/p&&h2&逼格&/h2&&p&我先来提高一下 prototype 的逼格:&/p&&blockquote&「所有」理解 prototype 的 JS 程序员都能轻松理解 JS 里的 class;&br&反过来,理解 class 的 JS 程序员却「有很多」都理解不了 prototype。&/blockquote&&p&&br&&/p&&p&这就是为什么早年间很大 JS 大神不得不用 prototype 去模拟一个蹩脚的 klass 给新人用。&/p&&p&我没有把 klass 拼错,因为 class 是保留字,所以大神只能用 klass 来「代替」class。&/p&&p&虽然我认为原型是 JS 的基础知识,但是根据实际情况,原型居然变成了 JS 的高级知识,很多新人直接在入门的时候放弃学习原型,而去学 class。&/p&&p&所以,prototype 绝对能让你有智商优越感 :)&/p&&p&&br&&/p&&p&好了,开始正经讨论问题。&/p&&p&&br&&/p&&h2&JS 里 class 的问题&/h2&&p&1 第一个问题,功能缺失。&/p&&p&我是一个实用主义者,如果 class 比 prototype 好用,我绝对会总是使用 class。&/p&&p&但是目前情况是:&/p&&ol&&li&JS 里 class 的功能还不如 prototype&/li&&li&JS 里 class 的功能比 Java 里的 class 更是差了十万八千里&/li&&/ol&&p&1.1 JS 里 class 的功能还不如 prototype&/p&&p&以 let frank = new Human() 为例。&/p&&p&如果你要给 Human 加一个 x 属性,怎么加?&/p&&p&对于使用原型的人来说,Human 不就是一个函数对象吗,直接加就是了,何必多此一问:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Human.x = 'xxx'
&/code&&/pre&&/div&&p&完&/p&&p&对于使用 class 的人来说,经典 OO 理论会使他倾向与把 Human 看做一个 class,所以应该使用这样的语法&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class Human{
static x = 1; // 可惜目前会报语法错误
&/code&&/pre&&/div&&p&然而,这个语法,还没有纳入 ES 标准,所以目前在 JS 里用 class 连个静态字段都定义不了,真可惜。&/p&&p&&br&&/p&&p&有些人可能会说,那我混用 class 和原型呗:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class Human{
Human.x = 1
&/code&&/pre&&/div&&p&如果这样写的话 Human 还是类的话,那我就不说什么了。你问问 Java 程序员能不能理解这样不伦不类的 class。&/p&&p&总之,目前你不可能把 JS 的 class 当成真正的类,你还是得把 Human 看成一个函数对象才行。&/p&&p&&br&&/p&&p&有些人可能还会说,我等 class 语法升级不就行了。&/p&&p&你都能升级语法了,那我怎么还能说得过你呢,对吧,只要我说 class 哪里有问题你都可以用升级解决。所以我只讨论目前的 ES 语法。&/p&&p&目前使用 class 前你还是需要完全理解 prototype。&/p&&p&&br&&/p&&p&1.2 JS 里 class 的功能比 Java 里的 class 更是差了十万八千里&/p&&p&请用 JS 的 class 写出一个抽象类。&/p&&p&对不起,目前 class 做不到,等升级语法吧。&/p&&p&JS 的 class 如何实现 private、public 和 protected。&/p&&p&对不起,目前 class 做不到,等升级语法吧。&/p&&p&&br&&/p&&p&可千万别说你可以用其他方法模拟,模拟出个阉割版有意思吗?&/p&&p&&br&&/p&&p&有些人可能会说,class 实现不了我认,你 prototype 不也实现不了吗?大家都实现不了,凭什么说我有问题。&/p&&p&这就是一个自相矛盾的地方了,prototype 为什么要去模拟 class 的特性?&/p&&p&在 prototype 体系里,根本就没有类,更遑论抽象类和 private 关键字了。&/p&&p&没有类(class)!&/p&&p&&br&&/p&&p&怎么有些人老想着用 JS 模拟 class 呢?&/p&&p&不解决这个问题,用 JS 就会总觉得别扭。&/p&&p&&br&&/p&&blockquote&想一想:为什么你非要在一个原型语言里,使用 class 思维来思考问题。&/blockquote&&p&因为你先入为主地觉得 class 比 prototype 好啊……&/p&&p&&br&&/p&&p&如果你还没意识自己的问题,恐怕我是真的扳不过来了。&/p&&p&&br&&/p&&p&再说一遍:不要把 JS 当做经典面向对象语言(比如 Java)使用。&/p&&p&如果你只学 JS 里的 class 不学 prototype,恐怕最终的后果就是既没有学会经典面向对象,又没有学会原型。&/p&&p&方言称之为半吊子,普通话称之为半壶水。&/p&&p&&br&&/p&&p&所以 JS 的 class 到底是个啥?&/p&&blockquote&class 是 prototype 的语法糖而已。&/blockquote&&p&岂有教人只学习语法糖,不学习真正特性的道理?&/p&&p&&br&&/p&&p&2 第二个问题&/p&&p&如果你说我们不能对 JS 这门动态语言要求太多,class 能部分实现 prototype 的功能,够用就行了,没必要再去学习 prototype 了。&/p&&p&那我依然不同意,因为 class 还有一个更严重的问题:&/p&&blockquote&「只会 class」将使你无法系统理解 JS。&/blockquote&&p&依然以最简单的代码为例&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&class Human{
console.log('hi')
&/code&&/pre&&/div&&p&面向对象的解释:Human 类有一个成员方法 sayHi。&/p&&p&只会 class 的同学现在请回答一个问题&/p&&blockquote&Human 的类型是什么?&/blockquote&&p&答案是 'object'(对象),同时 Human 也是一个函数。class 是一个函数,你不觉得奇怪吗?&/p&&p&再问一个问题,console.dir(Human) 你会看到它有以下属性:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-56efdf066ddfc5b372902c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&564& data-rawheight=&348& class=&origin_image zh-lightbox-thumb& width=&564& data-original=&https://pic1.zhimg.com/v2-56efdf066ddfc5b372902c_r.jpg&&&/figure&&p&&br&&/p&&blockquote&请问 Human 为什么有 prototype 属性?&br&为什么 Human.prototype 有一个 sayHi 属性?&/blockquote&&p&&br&&/p&&p&看吧,你始终绕不开原型…… JS 的 class 总会把你引向原型。&/p&&p&我实在是无法理解只会 class 的 JS 开发者要如何理解 JS:&/p&&ol&&li&你需要无视每个对象的 __&i&proto&/i&__ 属性&/li&&li&你需要无视每个类的 prototype 属性&/li&&/ol&&p&&br&&/p&&p&class 的这个问题才是我反对只学 class 的最关键原因。&/p&&p&无论 JS 的 class 语法如何升级,不管是 ES 6 还是 ES 7、ES 8、ES 9、ES 10、ES 11,都绕不开 prototype。永远都绕不开。因为这是 JS 里对象的本质。&/p&&p&如果绕不开 prototype,你还有什么理由不学 prototype?还有什么理由不好好地、深入地学习 prototype?&/p&&p&&br&&/p&&p&有些人可能会说,那我先学会 prototype,但是不用 prototype 只用 class 行不行呢?&/p&&p&当然行。&/p&&p&但你必须先学会 prototype 呀同学,然后才能理解 class 呀!&/p&&p&所以本文标题是「你可以不会 class,但是一定要学会 prototype」。&/p&&p&&br&&/p&&p&推荐学习路径:&/p&&ol&&li&完全理解 JS 的 prototype&/li&&li&熟练使用 prototype&/li&&li&学会 class&/li&&li&熟练使用 class&/li&&li&想用 class 的时候用 class,想用 prototype 的时候用 prototype&/li&&/ol&&p&你硬要当半吊子只学 class 的话,我也没意见。&/p&&p&&br&&/p&&p&3 第三个问题:并不能做到类型安全&/p&&p&class 的第三个问题是,class 带来的好处并不多,除了作为糖能让你少写一些代码外,并没有多少额外的好处。用语法糖我不反对,但是如果只用语法糖不学语法糖背后的原理,那我就反对。&/p&&p&&br&&/p&&p&有观点认为 class 能使 JS 更加「类型安全」。&/p&&p&持这种观点的人可能对 JS 有误解,一门「没有编译阶段」的「动态」「弱类型」语言怎么可能类型安全啊……JS 任何对象随时都可以被改得面目全非,用了 class 也无济于事。&/p&&p&想要类型安全去用 TypeScript 吧,class 并不能拯救 JS。&/p&&p&而且类型安全属于另一个问题,可以另开帖子讨论。&/p&&p&另外不要以为 TypeScript 里面的 class 跟 JS 里的 class 一样,虽然 TypeScript 是 JS 的超集,但是有编译阶段的 class 和没编译阶段的 class 就是质的区别。&/p&&p&这就是编译的威力。不是 class 的威力。&/p&&p&&br&&/p&&p&&br&&/p&&h2&prototype 的问题&/h2&&p&class 这么多问题,难道 prototype 就是完美的吗?&/p&&p&如果 prototype 那么好,这么会有这么多人喜欢 class 呢?&/p&&p&&br&&/p&&p&我先来解释为什么 JS 里 class 的拥趸如此多。&/p&&p&因为最早写 JS 的人,都是从 Java 和 PHP 转行的。&/p&&p&尤其是 Java,Java 里的 class 很好用,所以这批人非常想直接在 JS 里面使用 class。&/p&&p&然而一写才发现 JS 的 class 居然只是一个保留字。&/p&&p&于是他们实在受不了不能写 class 的日子,硬生生用 prototype 模拟出了一个 klass。&/p&&p&嗯,klass 就是一个阉割版的 class。&/p&&p&&br&&/p&&p&为何这群人放着 prototype 不用,非要用 klass 呢?&/p&&p&&br&&/p&&p&因为 JS 这门语言很烂,他们不想去理解 JS。&/p&&p&由于他们不想去理解 JS,所以也不会去理解 JS 的 prototye 了。&/p&&p&&br&&/p&&p&你可能突然发现怎么方方你来了个大反转,刚刚你不是一直在说 JS 好吗?&/p&&p&&br&&/p&&p&不,没有反转,是你理解错了,我刚刚说得是 JS 的原型比 JS 的 class 好,没说 JS 语言(ES 6 之前)本身有多好。&/p&&p&JS 语言本身(在 ES 6 之前)还是很烂的,为此道格拉斯还专门写了 JS the good parts 这本书来把 JS 为数不多的优点给列出来。&/p&&p&JS 多烂我以后再讨论。但是 JS 的原型绝对是好东西。&/p&&p&正如 JS 之父所说:&/p&&blockquote&它的优秀之处并非原创,它的原创之处并不优秀。——JS 之父引用别人的话描述 JS&/blockquote&&p&实际上 JS 原创之处全都很垃圾。这个原型就不是 JS 原创的,而且原型很优秀。&/p&&p&&br&&/p&&p&这些程序员不想理解 JS,更不想在 JS 的一堆垃圾特性里面挑出 prototype 来学习,所以宁愿用 klass,因为他们熟悉 class。&/p&&p&&br&&/p&&p&所以 prototype 的第一个问题是,它隐藏在 JS 的一堆烂特性中,无法被人发现。&/p&&p&&br&&/p&&p&那么第二个问题是什么?&/p&&p&第二个问题是在 ES 6 之前,人们无法很方便地对原型进行操作。&/p&&p&ES 6 之前,你要改变一个对象的原型要怎么做?&/p&&p&你可能会以为这样就可以改变原型:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&frank.__proto__ = anotherPrototype
&/code&&/pre&&/div&&p&但是这个代码很可能报错,因为 __&i&proto&/i&__ 到目前为止,都是不推荐使用的特性。&/p&&p&ES 6 出了 Object.create 和 Object.setPrototypeOf 才让开发者能安心操作原型。&/p&&p&&br&&/p&&p&那么正确的代码是什么呢?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var emptyFn = function(){}
emptyFn.prototype = anotherPrototype
frank = new emptyFn()
&/code&&/pre&&/div&&p&以上三行代码是 JS 程序不懈努力才发现的实现 frank.__&i&proto&/i&__ = anotherPrototype 的方法。&/p&&p&&br&&/p&&p&现在估计你能理解了「为什么在 ES 6 之前,JS 程序员不喜欢 prototype」,因为理解难度实在有点大啊:&/p&&ol&&li&首先你要耐心学会 JS 的各种烂语法,然后走到 prototype 面前&/li&&li&为了兼容性,你需要用特别奇怪的代码来操作 prototype&/li&&/ol&&p&&br&&/p&&p&但是 ES 6 来了之后,这两个问题都解决了呀:&/p&&ol&&li&JS 的各种烂语法都有对应的修正,如 var 被 let 代替&/li&&li&操作 prototype 很简单了,用 Object.create 就好&/li&&/ol&&p&&br&&/p&&p&所以,现在,你应该去学 prototype 了。&/p&&p&学会了 prototype,再去学 class 就跟吃糖一样简单。&/p&&p&&br&&/p&&p&学习过程中推荐看看「方三篇」:&/p&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& class=&internal&&方应杭:this 的值到底是什么?一次说清楚&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& class=&internal&&方应杭:JS 的 new 到底是干什么的?&/a&&a href=&https://zhuanlan.zhihu.com/p/& data-draft-node=&block& data-draft-type=&link-card& class=&internal&&方应杭:「每日一题」什么是 JS 原型链?&/a&&p&以及我的一个回答:(想快速理解 prototype 可以看这个回答)&/p&&a href=&https://www.zhihu.com/question//answer/& data-draft-node=&block& data-draft-type=&link-card& data-image=&https://pic4.zhimg.com/80/v2-e6be3f2b63e1de4e50ddf_180x120.jpg& data-image-width=&1642& data-image-height=&1150& class=&internal&&JS 中 __proto__ 和 prototype 存在的意义是什么?&/a&&p&完。&/p&
今天看到有人问「为什么明知 JS 的 class 是假的,程序员还不厌其烦地实现 class,而不使用原型」。作为 JS 原型的坚定支持者,我不得不喷一下「JS 里的 class 比 prototype 好」这个观…
&p&(文章有更新,添加了SVG方案的讨论以及transform的方法是可以的)&/p&&p&&b&什么是像素&/b&?&/p&&p&像素是屏幕显示最小的单位,在一个1080p的屏幕上,它的像素数量是1920 * 1080,即横边有1920个像素,而竖边为1080个。一个像素就是一个单位色块,是由rgba四个通道混合而成。对于一个1200万像素的相机镜头来说,它有1200万个感光单元,它能输出的最大图片分辨率大约为3000 * 4000。&/p&&p&那么像素本身有大小吗,&b&一个像素有多大&/b&?&/p&&p&有的,如果一个像素越小,那么在同样大小的屏幕上,需要的像素点就越多,像素就越密集,如果一英寸有435个像素,那么它的dpi/ppi就达到了435。Macbook Pro 15寸的分辨率为2880 x 1800,15寸是指屏幕的对角线为15寸(具体为15.4),根据长宽比换算一下得到横边为13寸,所以ppi为2880 / 13 = 220 ppi. 像素越密集即ppi(pixel per inch)越高,那么屏幕看起来就越细腻越高清。&/p&&p&在Mac/Windows上可以设置屏幕显示的分辨率,Mac默认为设备分辨率的一半,它的dpr = 2,即长和宽用2个像素表示1个像素,所以2880个物理像素点实际上只表示1440个逻辑像素:&/p&&p&&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-636abf0dc33d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1168& data-rawheight=&570& class=&origin_image zh-lightbox-thumb& width=&1168& data-original=&https://pic2.zhimg.com/v2-636abf0dc33d_r.jpg&&&/figure&&p&&br&&/p&&p&那么我们的问题来了,怎么在高清屏上画一条0.5px的边呢?0.5px相当于高清屏物理像素的1px。这样的目的是在高清屏上看起来会更细一点,效果会更好一点,例如更细的分隔线。&/p&&p&大漠在《&a href=&http://link.zhihu.com/?target=https%3A//www.w3cplus.com/css/fix-1px-for-retina.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&再谈Retina下1px的解决方案&/a&》已经讨论过这个问题,这里我们再理一理思路。&/p&&p&理论上px的最小单位是1,但是会有几个特例,高清屏的显示就是一个特例。高清屏确实可以画0.5px,对比效果如下:&/p&&p&&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-5d202ba852cfd696337ba_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&628& data-rawheight=&226& class=&origin_image zh-lightbox-thumb& width=&628& data-original=&https://pic3.zhimg.com/v2-5d202ba852cfd696337ba_r.jpg&&&/figure&&p&&br&&/p&&p&如果我们&b&直接设置0.5px&/b&,在不同的浏览器会有不同的表现,使用如下代码:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&cp&&&!DOCType html&&/span&
&span class=&p&&&&/span&&span class=&nt&&html&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&head&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&meta&/span& &span class=&na&&charset&/span&&span class=&o&&=&/span&&span class=&s&&&utf-8&&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&nc&&.hr&/span& &span class=&p&&{&/span&
&span class=&nb&&width&/span&&span class=&o&&:&/span& &span class=&m&&300px&/span&&span class=&p&&;&/span&
&span class=&nb&&background-color&/span&&span class=&o&&:&/span& &span class=&m&&#000&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&nc&&.hr.half-px&/span& &span class=&p&&{&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&0.5px&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&nc&&.hr.one-px&/span& &span class=&p&&{&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&1px&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&&/&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&head&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&body&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&0.5px&span class=&p&&&/&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&div&/span& &span class=&na&&class&/span&&span class=&o&&=&/span&&span class=&s&&&hr half-px&&/span&&span class=&p&&&&/&/span&&span class=&nt&&div&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&1px&span class=&p&&&/&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&div&/span& &span class=&na&&class&/span&&span class=&o&&=&/span&&span class=&s&&&hr one-px&&/span&&span class=&p&&&&/&/span&&span class=&nt&&div&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&body&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&html&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&在PC上的不同浏览器上测试测试结果如下所示:&/p&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-714c175fc30f15cfb4974_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2000& data-rawheight=&300& class=&origin_image zh-lightbox-thumb& width=&2000& data-original=&https://pic1.zhimg.com/v2-714c175fc30f15cfb4974_r.jpg&&&/figure&&p&&br&&/p&&p&其中Chrome把0.5px四舍五入变成了1px,而firefox/safari能够画出半个像素的边,并且Chrome会把小于0.5px的当成0,而Firefox会把不小于0.55px当成1px,Safari是把不小于0.75px当成1px,进一步在手机上观察IOS的Chrome会画出0.5px的边,而安卓(5.0)原生浏览器是不行的。所以直接设置0.5px不同浏览器的差异比较大,并且我们看到不同系统的不同浏览器对小数点的px有不同的处理。所以如果我们把单位设置成小数的px包括宽高等,其实不太可靠,因为不同浏览器表现不一样。&/p&&p&第二种能想到的方法是缩放,能否设置1px,然后&b&scale 0.5&/b&呢,我们可以尝试一下,如下代码所示:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&nc&&.hr.scale-half&/span& &span class=&p&&{&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&1px&/span&&span class=&p&&;&/span&
&span class=&n&&transform&/span&&span class=&o&&:&/span& &span class=&n&&scaleY&/span&&span class=&p&&(&/span&&span class=&m&&0&/span&&span class=&o&&.&/span&&span class=&m&&5&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&&/&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&1px + scaleY(0.5)&span class=&p&&&/&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&div&/span& &span class=&na&&class&/span&&span class=&o&&=&/span&&span class=&s&&&hr scale-half&&/span&&span class=&p&&&&/&/span&&span class=&nt&&div&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&效果如下图所示:&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-fdb0fc74b3_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2000& data-rawheight=&400& class=&origin_image zh-lightbox-thumb& width=&2000& data-original=&https://pic4.zhimg.com/v2-fdb0fc74b3_r.jpg&&&/figure&&p&&br&&/p&&p&我们发现Chrome/Safari都变虚了,只有Firefox比较完美看起来是实的而且还很细,效果和直接设置0.5px一样。所以通过transform: scale会导致Chrome变虚了,而粗细几乎没有变化。但是如果加上transform-origin: 50% 100%:&/p&&div class=&highlight&&&pre&&code class=&language-css&&&span&&/span&&span class=&nc&&.hr.scale-half&/span& &span class=&p&&{&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&1px&/span&&span class=&p&&;&/span&
&span class=&n&&transform&/span&&span class=&o&&:&/span& &span class=&n&&scaleY&/span&&span class=&p&&(&/span&&span class=&m&&0&/span&&span class=&o&&.&/span&&span class=&m&&5&/span&&span class=&p&&);&/span&
&span class=&n&&transform&/span&&span class=&o&&-&/span&&span class=&n&&origin&/span&&span class=&o&&:&/span& &span class=&m&&50%&/span& &span class=&m&&100%&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&大家就都变实了,很完美,Chrome的效果如下:&/p&&p&&br&&/p&&figure&&img src=&https://pic2.zhimg.com/v2-273b18827daef_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&634& data-rawheight=&316& class=&origin_image zh-lightbox-thumb& width=&634& data-original=&https://pic2.zhimg.com/v2-273b18827daef_r.jpg&&&/figure&&p&&br&&/p&&p&除了这个方法之外,我们还想到做移动端的时候还使用了rem做缩放,但实际上rem的缩放最后还是会转化成px,所以和直接使用0.5px的方案是一样的。&/p&&p&&br&&/p&&p&还有什么办法呢?还可以用&b&线性渐变linear-gradient&/b&,如下代码所示:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&nc&&.hr.gradient&/span& &span class=&p&&{&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&1px&/span&&span class=&p&&;&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&n&&linear&/span&&span class=&o&&-&/span&&span class=&n&&gradient&/span&&span class=&p&&(&/span&&span class=&m&&0&/span&&span class=&n&&deg&/span&&span class=&o&&,&/span& &span class=&m&&#fff&/span&&span class=&o&&,&/span& &span class=&m&&#000&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&&/&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&linear-gradient(0deg, #fff, #000)&span class=&p&&&/&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&div&/span& &span class=&na&&class&/span&&span class=&o&&=&/span&&span class=&s&&&hr gradient&&/span&&span class=&p&&&&/&/span&&span class=&nt&&div&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&linear-gradient(0deg, #fff, #000)的意思是:渐变的角度从下往上,从白色#fff渐变到黑色#000,而且是线性的,在高清屏上,1px的逻辑像素代表的物理(设备)像素有2px,由于是线性渐变,所以第1个px只能是#fff,而剩下的那个像素只能是#000,这样就达到了画一半的目的。逻辑分析很完美,实际的效果又怎么样呢,如下图所示:&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-48c8e78fad1a749bebdd42f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2000& data-rawheight=&500& class=&origin_image zh-lightbox-thumb& width=&2000& data-original=&https://pic4.zhimg.com/v2-48c8e78fad1a749bebdd42f_r.jpg&&&/figure&&p&&br&&/p&&p&我们发现这种方法在各个流览器上面都不完美,效果都是虚的,和完美的0.5px还是有差距。这个效果和scale 0.5的差不多,都是通过虚化线,让人觉得变细了。&/p&&p&还有另外一种方法,&b&使用boxshadow&/b&,如下代码所示:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&nc&&.hr.boxshadow&/span& &span class=&p&&{&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&1px&/span&&span class=&p&&;&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&nb&&none&/span&&span class=&p&&;&/span&
&span class=&n&&box&/span&&span class=&o&&-&/span&&span class=&n&&shadow&/span&&span class=&o&&:&/span& &span class=&m&&0&/span& &span class=&m&&0.5px&/span& &span class=&m&&0&/span& &span class=&m&&#000&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&&/&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&box-shadow: 0 0.5px 0 #000&span class=&p&&&/&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&div&/span& &span class=&na&&class&/span&&span class=&o&&=&/span&&span class=&s&&&hr boxshadow&&/span&&span class=&p&&&&/&/span&&span class=&nt&&div&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&设置box-shadow的第二个参数为0.5px,表示阴影垂直方向的偏移为0.5px,效果如下:&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-a7f30b7483_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2000& data-rawheight=&550& class=&origin_image zh-lightbox-thumb& width=&2000& data-original=&https://pic4.zhimg.com/v2-a7f30b7483_r.jpg&&&/figure&&p&&br&&/p&&p&这个方法在Chrome和Firefox都非常完美,但是Safari不支持小于1px的boxshadow,所以完全没显示出来了。&/p&&p&还可以&b&使用SVG&/b&,利用SVG的描边等属性的1px还是物理像素的1px,而不是高清屏的1px。如下代码所示:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&nc&&.hr.svg&/span& &span class=&p&&{&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&nb&&none&/span&&span class=&p&&;&/span&
&span class=&nb&&height&/span&&span class=&o&&:&/span& &span class=&m&&1px&/span&&span class=&p&&;&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&sx&&url(&data:image/svg+utf-8,&svg xmlns='http://www.w3.org/2000/svg' width='100%' height='1px'&&line x1='0' y1='0' x2='100%' y2='0' stroke='#000'&&/line&&/svg&&)&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&&/&/span&&span class=&nt&&style&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&svg&span class=&p&&&/&/span&&span class=&nt&&p&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&div&/span& &span class=&na&&class&/span&&span class=&o&&=&/span&&span class=&s&&&hr svg&&/span&&span class=&p&&&&/&/span&&span class=&nt&&div&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&设置background为一个svg文件,这个svg单独拷出来是这样的:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&svg&/span& &span class=&na&&xmlns&/span&&span class=&o&&=&/span&&span class=&s&&'http://www.w3.org/2000/svg'&/span& &span class=&na&&width&/span&&span class=&o&&=&/span&&span class=&s&&'100%'&/span& &span class=&na&&height&/span&&span class=&o&&=&/span&&span class=&s&&'1px'&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&line&/span& &span class=&na&&x1&/span&&span class=&o&&=&/span&&span class=&s&&'0'&/span& &span class=&na&&y1&/span&&span class=&o&&=&/span&&span class=&s&&'0'&/span& &span class=&na&&x2&/span&&span class=&o&&=&/span&&span class=&s&&'100%'&/span& &span class=&na&&y2&/span&&span class=&o&&=&/span&&span class=&s&&'0'&/span& &span class=&na&&stroke&/span&&span class=&o&&=&/span&&span class=&s&&'#000'&/span&&span class=&p&&&&/&/span&&span class=&nt&&line&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&svg&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&使用svg的line元素画线,stroke表示描边颜色,默认描边宽度stroke-width=&1&,由于svg的描边等属性的1px是物理像素的1px,相当于高清屏的0.5px,另外还可以使用svg的rect等元素进行绘制。在Chrome和Safari的效果如下:&/p&&p&&br&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-4aca87faf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&626& data-rawheight=&610& class=&origin_image zh-lightbox-thumb& width=&626& data-original=&https://pic4.zhimg.com/v2-4aca87faf_r.jpg&&&/figure&&p&&br&&/p&&p&这个方案也是很完美,但是在firefox挂了,究其原因是因为firefox的background-image如果是svg的话只支持命名的颜色,如&black&、&red&等,如果把上面代码的svg里面的#000改成black的话就可以显示出来,但是这样就很不灵活了。否则只能把svg转成base64的形式,我们把svg的内容转成base64(可以找一些在线的工具),对比如下:&/p&&div class=&highlight&&&pre&&code class=&language-css&&&span&&/span&&span class=&nc&&.hr.svg&/span& &span class=&p&&{&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&sx&&url(&data:image/svg+utf-8,&svg xmlns='http://www.w3.org/2000/svg' width='100%' height='1px'&&line x1='0' y1='0' x2='100%' y2='0' stroke='#000'&&/line&&/svg&&)&/span&&span class=&p&&;&/span&
&span class=&nb&&background&/span&&span class=&o&&:&/span& &span class=&sx&&url(&data:image/svg+base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMDAlJyBoZWlnaHQ9JzFweCc+PGxpbmUgeDE9JzAnIHkxPScwJyB4Mj0nMTAwJScgeTI9JzAnIHN0cm9rZT0nIzAwMCc+PC9saW5lPjwvc3ZnPg==&)&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&这样在firefox也能完美展示了。&/p&&p&其实0.5px的需求在移动端应该会更常见,比较一下以上五种方法在IOS和安卓的表现,如下图所示:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-30cbb07ed7be3c740f6f9cb_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1778& data-rawheight=&923& class=&origin_image zh-lightbox-thumb& width=&1778& data-original=&https://pic4.zhimg.com/v2-30cbb07ed7be3c740f6f9cb_r.jpg&&&/figure&&p&&br&&/p&&p&IOS下的Safari和Chrome表现一致,都是以直接设置0.5px的效果最好,而安卓浏览器则是以box-shadow的效果最好(试了5和7),而svg/transform的方案在IOS和安卓的设备上都能完美支持。读者可以打开&a href=&http://link.zhihu.com/?target=https%3A//fed.renren.com/html/half-px.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这个网页&/a&,看一下在你的设备是哪种效果最好。&/p&&p&结合以上,我们初步得到以下结论:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-a8aa225bed227f081ed56fd302aa5e84_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1304& data-rawheight=&642& class=&origin_image zh-lightbox-thumb& width=&1304& data-original=&https://pic1.zhimg.com/v2-a8aa225bed227f081ed56fd302aa5e84_r.jpg&&&/figure&&p&&br&&/p&&p&&br&&/p&&p&使用SVG相对于box-shadow等方法,还有一个好处是可以借助svg的元素画出任意图形,如四边形,圆角等。&/p&&p&&br&&/p&&p&最后还有一个万能的方法,那就是通过控制viewport,在移端开发里面一般会把viewport的scale设置成1:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&meta&/span& &span class=&na&&name&/span&&span class=&o&&=&/span&&span class=&s&&&viewport&&/span& &span class=&na&&content&/span&&span class=&o&&=&/span&&span class=&s&&&width=device-width,initial-sacle=1&&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&其中width=device-width表示将viewport视窗的宽度调整为设备的宽度,这个宽度通常是指物理上宽度。默认的缩放比例为1,如iphone 6竖屏的宽度为750px,它的dpr=2,用2px表示1px,这样设置之后viewport的宽度就变成375px。这时候0.5px的边就使用我们上面讨论的方法。&/p&&p&但是你可以把scale改成0.5:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&p&&&&/span&&span class=&nt&&meta&/span& &span class=&na&&name&/span&&span class=&o&&=&/span&&span class=&s&&&viewport&&/span& &span class=&na&&content&/span&&span class=&o&&=&/span&&span class=&s&&&width=device-width,initial-sacle=0.5&&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&这样的话,viewport的宽度就是原本的750px,所以1个px还是1px,正常画就行,但这样也意味着UI需要按2倍图的出,整体面面的单位都会放大一倍。&/p&&p&在iPone X和一些安卓手机等dpr = 3的设备上,需要设置scale为0.333333,这个时候就是3倍地画了。&/p&&p&&br&&/p&&p&综上讨论了像素和viewport的一些概念,并介绍和比较了在高清屏上画0.5px的几种方法——可以通过直接设置宽高border为0.5px、设置box-shadow的垂直方向的偏移量为0.5px、借助线性渐变linear-gradient、使用transform: scaleY(0.5)的方法,使用SVG的方法。最后发现transfrom scale/svg的方法兼容性和效果都是最好的,svg可以支持复杂的图形,所以&b&在viewport是1的情况下,可以使用transform/SVG画0.5px,而如果viewport的缩放比例不是1的话,那么直接画1px即可&/b&。&/p&&p&&br&&/p&&p&上一篇:&br&《&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&从Chrome源码看HTTP/2&/a&》&/p&&p&&/p&
(文章有更新,添加了SVG方案的讨论以及transform的方法是可以的)什么是像素?像素是屏幕显示最小的单位,在一个1080p的屏幕上,它的像素数量是1920 * 1080,即横边有1920个像素,而竖边为1080个。一个像素就是一个单位色块,是由rgba四个通道混合而成。对…
&figure&&img src=&https://pic1.zhimg.com/v2-45f71562f4cee40eb2d968_b.jpg& data-rawwidth=&1080& data-rawheight=&496& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic1.zhimg.com/v2-45f71562f4cee40eb2d968_r.jpg&&&/figure&&p&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-d456bd43d894a37e4b77d9e65d2f187e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&115& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic1.zhimg.com/v2-d456bd43d894a37e4b77d9e65d2f187e_r.jpg&&&/figure&&p&&b&背景&/b&&/p&&p&&br&&/p&&p&随着美团点评金融业务的高速发展,前端研发数量从2015 年的 1 个人,扩张到了现在横跨北上两地 8 个事业部的将近 150 人。业务新,团队新,前端领域框架技术又层出不穷,各个业务的研发团队在技术选择上没有明确的指导意见,致使业务与业务之间的技术差异越来越大,在技术工具研发上无法共建,在资源调度上成本也很高。 &/p&&p&2017年下半年,金融平台发起了技术栈统一行动,行动分为后端、iOS、Android及前端等四个方向,在前端方向我作为组织者和参与者与金融平台8 个事业部的前端技术代表进行讨论。 &br&通过对各方意见进行归纳整理,结合各团队的情况,金融平台对于技术栈的选型达成了共识。本文将介绍美团点评金融平台前端的技术选型以及背后的思考。先从一些基本原则讲起。&/p&&p&&b&招构建技术体系的基本原则&/b&&/p&&b&&figure&&img src=&https://pic1.zhimg.com/v2-cd7bd83fedb0_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&918& data-rawheight=&468& class=&origin_image zh-lightbox-thumb& width=&918& data-original=&https://pic1.zhimg.com/v2-cd7bd83fedb0_r.jpg&&&/figure&&/b&&h2&&b&业务出发&/b&&/h2&&ol&&li&选型要针对业务形态特点,注重业务场景匹配度&/li&&li&具有一定业务前瞻性(中期或中短期以避免过度设计,短期、中期、长期与迭代速度强相关)&/li&&/ol&&p&金融业务的移动端项目占比超过 70%,尤其是 Hybrid 项目,团队在整个移动端生态的设计上投入了大量的精力,例如 Vue 的选择、EH 的设计、组件库 Vix 的设计等。&/p&&p&同时由于业务的金融属性,对于可用性的要求非常高。在可用性保障上我们还会有一些侧重,例如 TypeScript 的使用,自动化流程测试框架 Freekite 的使用等。&/p&&h2&&b&团队出发&/b&&/h2&&ol&&li&考虑团队规模,成员技术特点和偏好&/li&&li&考虑现有项目和技术迁移成本&/li&&/ol&&p&金融大多数团队都处于初创时期,因此团队历史包袱相对较少,接受新鲜事物的能力强,但快速搭建团队中也会对技术栈有上手成本的要求。在整个技术体系的搭建当中,我们会优先考虑那些新的、上手成本低的技术。&/p&&h2&&b&以简驭繁&/b&&/h2&&p&我们主张使用简单的技术手段解决复杂的问题,而不是用复杂的技术手段解决简单的问题,例如&br& Hybrid 体验问题的解决,常规的有 RN、Weex 等方案,在业界有丰富的实践,但我们也会设计实现更简单的解决方案 &br&EH,让问题的解决变得更聚焦于问题的本质。在首屏渲染速度优化方案上的选择也是一样,业界有很好的 SSR 技术,但我们也会实践研发&b&构建时预渲染&/b&技术,让 TTFB(首字节时间)更快,让系统流量负载更高,同时减少关键环节,让整个系统可用性更强。&/p&&h2&&b&标准化&/b&&/h2&&p&标准化指的就是尽可能让上下游衔接形成标准,并在标准下构建效率和质量工具。&/p&&p&例如在组件库 Vix 的研发上,我们与 UED 形成一致的、强同步的标准,从而大大减少界面搭建的时间消耗。后面会详细介绍。&/p&&h2&&b&自动化&/b&&/h2&&p&用技术去连接技术,用技术去简化步骤,解决某个工具到使用者的“最后一公里”问题。&/p&&p&例如我们使用的自动化流程测试工具 &b&Freekite&/b&,不用一行代码即可以完成复杂的分支逻辑自动化测试与持续集成。我们使用的联调平台 &b&Portm&/b& 可以将接口设计和前端 Mock 、后端单测、接口文档有机的结合起来,将前后端的研发进度解耦,从而大大提升研发效率。&/p&&h2&&b&现有复用&/b&&/h2&&p&顾名思义就是选型上尽量使用公司已有的系统和工具,从而更好的与团队、业务结合。&/p&&p&例如全平台监控工具 &b&CAT&/b&,业务埋点工具&b&灵犀&/b&等等。&/p&&p&下面来看看我们技术体系的细节。&/p&&p&&b&金融平台Web前端技术体系&/b&&/p&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-45f71562f4cee40eb2d968_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&496& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic1.zhimg.com/v2-45f71562f4cee40eb2d968_r.jpg&&&/figure&&p&我们将从开发阶段开始介绍,从视图层、语言层、协作层,再到质量保障工具、体验优化工具、安全技术等。接下来过渡到编译部署阶段,讲一讲编译部署和上线工具。然后是线上监控和埋点工具。最后介绍一些各个团队正在探索和实践当中的技术。&/p&&h2&&b&视图&组件框架&/b&&/h2&&p&在移动端使用 Vue 生态,在桌面版上我们使用 React 生态 或者 Vue 生态。&/p&&p&Vue 的使用主要考虑以下几点:&/p&&ul&&li&体积小,复杂度低&/li&&li&业务上移动端项目占比 70% 以上,Vue 的体积小,网络性能角度相比 React 更适合移动端&/li&&li&移动端一般巨型项目很少,从代码结构上来讲,使用 Vue 实现更符合我们的场景复杂度,React 更适合大型相对更复杂的 SPA&/li&&li&上手成本和迁移成本低&/li&&li&Vue 的学习和上手成本相对更低,团队成员对于 Vue 的认可度和热情也比较高&/li&&li&组件内双向绑定、数据依赖收集&/li&&li&组件内支持双向绑定,更方便的去进行组件内的数据响应与交互&/li&&li&独有的数据依赖收集模式使其默认的数据响应和渲染效率都要比 React 高一些&/li&&/ul&&p&React 的使用主要考虑以下原因:&/p&&ul&&li&有一部分现有后台项目采用 React 技术栈,迭代和维护较少,老的项目如果没有足够的迁移价值则不额外投入资源&/li&&li&保留很小的一部分 React 技术生态也可以一定程度上保持一些技术多样性&/li&&/ul&&h2&&b&组件库&/b&&/h2&&p&组件库是前端领域一个重要的技术单元,为效率、质量、体验服务。&/p&&p&效率是为了能够抽象业务研发中业务组件的共同点去避免重复劳动;质量是如果一个组件经过了测试和质量迭代,那么正确的使用不应该出现质量问题;体验方面组件库可以去统一交互的体验,让组件的表现更一致。&/p&&p&上述三点中,组件库贡献最大的是效率。&/p&&p&谈到组件库如何对效率做贡献,首先想到的是什么样的组件库才能够尽可能的提升我们的研发效率,我认为这里我们需要注意的一点是&b&“控制变量”&/b&,因为变化产生了额外的工作量和时间成本,如果这个产品和上个产品完全一样,我们直接复制一份就好了,没必要开发。在我们的前端业务研发当中,变量是什么?是交互和视觉设计,每个产品之间有不同,也有相同。我们控制变量就应该去控制设计。因此我们与金融&br& UED(设计部门) 沟通制定了一个视觉组件&b&标准&/b&,共同创建了视觉组件库:Vix。&/p&&p&Vix 是一个移动端组件库,其特点是完全遵守与金融 UED 制定的视觉组件标准并保持同步,在 UED 侧有完善的新组件设计提审及审核流程,在业务前端研发侧有强同步的约束。&/p&&p&Vix 的结构分为基础组件、复杂组件和业务组件三层,基础组件例如输入框、按钮等;复杂组件包括组合搜索、日期选择等;业务组件例如支付密码输入框、账单、账单详情等。&/p&&p&再上升一层则是一些包含后端服务的前后端组件,我们称为“微服务”,是一种更高层次的业务服务抽象,在更高的维度优化效率和服务体验一致性,例如支付密码验证服务,找回支付密码服务等。Vix 包含的是前三层,其结构如下图:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-d0cf5cbcf05d3b91e69e8_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&882& data-rawheight=&564& class=&origin_image zh-lightbox-thumb& width=&882& data-original=&https://pic4.zhimg.com/v2-d0cf5cbcf05d3b91e69e8_r.jpg&&&/figure&&p&&br&&/p&&p&在以往的实践过程当中,C端业务使用开源组件库会和设计有很大差异,需要做大量的改造工作才能使用,然而可能还要为各种各样改造过程中所产生的问题负责,同时开源组件库的业务不相关性限制了业务产品的设计或实现。在Vix 中,由于标准统一,我们的研发效率大大提升,同时质量也更加可控。&/p&&p&大多数移动端产品研发过程中至少 40% 以上的精力是在做界面的绘制。有了 Vix 后我们达到了:&/p&&ul&&li&效率大大提升:在界面绘制上相比没有组件库至少能够减少 90% 的工作量&/li&&li&直接组装无需改动:一个新产品没有新组件出现的情况下,我们甚至可以使用交互稿直接开发而不需要等待视觉稿,因为视觉稿即使画出来也是使用视觉组件库去实现的样子,极大的减少了项目研发的时间成本&/li&&li&标准更新仅需升级版本:当视觉标准更新的时候例如列表页两边的边距减小了,各个业务线的产品只需要重新发布一下就能够展示成最新的标准,极大的减少了标准更新时所需的时间成本&/li&&/ul&&figure&&img src=&https://pic4.zhimg.com/v2-540acb574_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&706& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic4.zhimg.com/v2-540acb574_r.jpg&&&/figure&&p&PC 端面向用户和商户的大多都是较为独立的产品,标准化的意义并不大,前端在 PC 端的研发精力主要投入在内部系统上,在内部系统前端研发上有四个特点:&/p&&ul&&li&无产品,要求高:几乎没有产品经理跟进,以完成功能需求为主,但功能流程一定要完善,最好能支持手机端使用&/li&&li&无设计:几乎没有设计跟进,面对内部用户设计收益不高&/li&&li&无测试:几乎没有测试跟进,收益不高,功能验证通过即可&/li&&li&要快:大多数是配合用户端产品的管理系统迭代,也可能是新系统的搭建,对研发速度都有要求,往往这方面的估时较短&/li&&/ul&&p&因此在内部系统的研发上有四点要求:&/p&&ul&&li&组件设计合理,组件数量大而全,最好支持移动端使用&/li&&li&组件库本身要有不错的设计,用户量虽少,但活跃度超高,界面体验需要保障&/li&&li&组件库本身的质量要高,要从工具层面保障质量减少出错&/li&&li&组件库要能够快速拼装出功能&/li&&/ul&&p&PC 端组件库由于设计没有要求,不存在来自设计的“变量”,所以选择很多。&/p&&p&React Cells 也是美团点评内部的一个组件库,金融在使用 React 生态的后台系统研发中使用 React Cells 作为组件库,其具有如下几个特点可以满足我们的需求:&/p&&ul&&li&无状态化的组件设计&/li&&li&主题可定制&/li&&li&跨平台(PC、Mobile)&/li&&li&搭积木式的使用方式&/li&&li&内部组件库专人快速支持&/li&&/ul&&p&在 Vue 生态实现的 PC 端内部系统中,我们使用 Element-UI 作为组件库,组件数量很多,质量也很高,在 Vue 生态中是排名靠前的开源组件库,这里不多赘述。&/p&&h2&&b&语言&/b&&/h2&&p&针对ES6,本文不再进行过多阐述。对于 &b&TypeScript&/b& 的使用是从2015年底开始,当时我们的移动端 Web 版收银台要做质量和可用性保障(详情参考之前的文章&u&&a href=&https://link.zhihu.com/?target=https%3A//tech.meituan.com/%2520checkout_counter_front_end.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&《前端可用性保障实践》&/a&&/u&),在&br& JS 层面我们遇到的最多的运行时问题就是 something is &br&undefined,也就是空指针问题。另外就是由于银行卡支付过程的业务逻辑非常复杂,代码层面可控性差,扩展性也很差。这时候想到的就是使用强类型语言来管理我们的项目,强类型语言可以帮助我们做两个事情:&/p&&ul&&li&在开发期间或编译期间进行强类型检查&/li&&li&使用类型系统让代码可控性、扩展性更强,协作更方便&/li&&/ul&&p&当时我们面临两个选择,一个是微软的 TypeScript ,一个是 Facebook 的 Flow。选择 TypeScript 是因为以下几点:&/p&&ul&&li&RoadMap 清晰,方向以贴合 ECMAScript 为核心,在其之上构建类型系统,传言 ES8 也会增加类型系统&/li&&li&TypeScript 是 JavaScript 的超集,其作用只在开发阶段发挥,其生成的代码不包含任何类型代码,但由类型系统保障&/li&&li&IDE 支持极好,除了自家的 VSCode 集成度超高,用户增长飞速,TypeScript 还支持市面上几乎所有主流 IDE&/li&&li&社区庞大,周边工具丰富&/li&&li&当时已经有几个大型的开源项目在使用,例如 Angular 和 Express&/li&&li&研发团队活力和积极性都很高,很多开源生态均快速推进集成&/li&&/ul&&p&而不选择 Flow 的原因主要包括以下几点:&/p&&ul&&li&当时 Flow 还是以注释为主,单文件非强制型编码,导致其类型检查系统无法发挥最大效用,也无法全面保障质量。&b&后来 Flow 也改成了 TypeScript 类似的方式,但个人认为为时已晚&/b&&/li&&li&集成度不高,IDE 支持落后&/li&&li&当时社区很小,除了 Facebook 自家的项目在使用,大型的开源项目用户很少&/li&&/ul&&p&TypeScript 包括 类型守护、联合类型、类型推导、严格非空检查等功能。&/p&&p&举个例子如图所示:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-f4a165b99e59ec932a21ad7f7658ce2e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&886& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic4.zhimg.com/v2-f4a165b99e59ec932a21ad7f7658ce2e_r.jpg&&&/figure&&p&参数 s 是可能为空的,在 TypeScript 里,如果不做非空检查就会报错,做了非空检查通过 TypeScript 的类型推导就能够通过。&/p&&p&通过使用 TypeScript 我们可以找出前端项目中 99% 的引用问题,由于我们的整个前端框架全部支持 TypeScript,有效的避免了空指针这种运行时低级错误的存在。&/p&&p&在 TypeScript 的使用上金融支付也是公司第一个在线上使用 TypeScript 的业务线,2015年底我们还制定了 TypeScript 代码规范。&/p&&h2&&b&协作解耦&/b&&/h2&&p&在日常开发当中,前后端联调经常遇到一些环境问题或者接口设计的问题,导致前后端当中一方等待另外一方,这种情况在效率上影响非常大。&b&协作解耦指的就是让前后端的研发工作不互相依赖,从而优化研发效率&/b&。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-553fe185d7c727f3bba895f41f0d8bf2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&157& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic4.zhimg.com/v2-553fe185d7c727f3bba895f41f0d8bf2_r.jpg&&&/figure&&p&上图表示的就是协作耦合所造成的效率问题,字母 A 代表项目 A,在前后端研发过程中,前端可能因为后端问题而无法继续开发,反之亦然。&/p&&p&2015年的时候我还在技术工程部,那个时候组内同学一起想到了一个方法去解决这个问题。最初的想法就是&b&“我们能不能通过接口设计一方面生成提供给前端研发使用的假数据,另一方面生成后端的单测。”&/b&&/p&&p&这个想法最终落地就是 &b&Vane&/b& 这个工具,现在叫 &b&Portm&/b&。&/p&&p&它可以在一个项目的接口设计时切入,前后端使用这个平台进行接口设计,同时写入各种逻辑 Case 的输入输出,它可以直接生成三个东西:&/p&&ul&&li&标准化的接口文档&/li&&li&提供给前端使用的标准化假数据&/li&&li&提供给后端使用的单测&/li&&/ul&&p&在项目研发过程中,前端面向假数据开发不必担心遇到后端环境问题;后端面向单测开发不必担心自己跑通了前端跑不通。当双方都能跑通的时候进行集成联调,这个时候前后端集成度会非常高,先完成的一方可以直接进入下一个项目,从部门角度来讲,大大优化了产品迭代研发的效率。&/p&&p&下图表示的是优化后的效果,可以看到前后端已经无需互相等待了。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-5bb0a382bd6b16d21e6dabc9c4351e7c_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&168& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic3.zhimg.com/v2-5bb0a382bd6b16d21e6dabc9c4351e7c_r.jpg&&&/figure&&h2&&b&自动化测试&/b&&/h2&&p&针对自动化测试,美团点评开发了一款工具叫 &b&Freekite&/b& ,它的作用是从用户使用角度验证界面业务流程的正确性,解决了为实现模拟用户点击而带来的诸多问题及 Case 管理的复杂度问题。&/p&&p&Web自动化流程测试除了可以验证 Case 的正确性以外,最重要的功能就是要有一个异常强大的 Case 管理模块。业界目前并没有理想的工具能够支撑我们的场景。“&b&Freekite&/b&”&br& 在 Case 验证功能的基础上,有一个强大的可视化 Case 管理模块,支持复杂的 Case 细分。除了界面操作的细分外,可以全量 Mock &br&或部分 Mock 后端的数据响应,根据响应拆分出不同的 Case 分支。除此之外,还包含智能自动化断言功能,断言基本不需要人工参与。&/p&&p&Case&br& 录完以后遇到界面改版的情况不好处理,Freekite 还支持单独节点的重新录制,也就完美的解决了 Case &br&的维护问题,大幅度减少工作量优化效率。紧接着我们会在项目中增加 Freekite &br&的持续集成,在项目的每一个阶段进行流程上的自动化回归验证,业务逻辑覆盖率的问题就基本解决了。下图为 Freekite 可视化 Case &br&管理的一个应用示例。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-bffd5a6b2fcd75fe951600bebc0c7f98_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1068& data-rawheight=&754& class=&origin_image zh-lightbox-thumb& width=&1068& data-original=&https://pic1.zhimg.com/v2-bffd5a6b2fcd75fe951600bebc0c7f98_r.jpg&&&/figure&&h2&&b&Hybird 体验技术&/b&&/h2&&p&&b&不同的角度对用户体验有不同的分拆方法&/b&,从前端角度讲,我把用户体验分为以下两个方向:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-72bfa83bad82c4e3aebf_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&896& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&896& data-original=&https://pic4.zhimg.com/v2-72bfa83bad82c4e3aebf_r.jpg&&&/figure&&p&前端主要在“&b&交互体验&/b&” 中的&b&功能体验&/b&和&b&界面体验&/b&上寻求优化。&/p&&p&&b&Titans&/b& 是美团点评解决 Hybrid 功能体验的一个集团范围的解决方案,它为 Hybrid 模式的产品封装 Native 的能力供 Web 调用,其能力包括几个大的方向:&/p&&ul&&li&基础API:版本判断、配置与环境判断、获取权限、订阅与广播等&/li&&li&用户信息:获取用户设备信息、风控信息、网络信息、登录及推出登录等&/li&&li&地理位置:获取经纬度、城市信息、定位城市信息等&/li&&li&基础业务功能:打开一个新的 WebView、关闭当前 WebView 打开一个新的 WebView 、关闭 WebView 等&/li&&li&分享:弹出分享、分享设置、分享渠道等&/li&&li&本地存储:存储信息到 Native ,读取信息等&/li&&li&多媒体:选择图片、预览、上传图片、扫描二维码等&/li&&li&系统提示:发送短信、获取联系人、震动、锁屏等&/li&&/ul&&p&业务可以在 Titans 的基础上构建丰富的 Hybrid 应用,既能享受无需发版即可更新迭代的优势,又可以使用 Native 的大多数功能。&/p&&p&在解决了功能体验后,接下来我们再说界面体验的问题。&/p&&p&谈到界面体验我们不得不重新讲起 Hybrid,个人认为&b&在解决功能体验的前提下&/b& Hybrid 存在以下主要的优势和劣势:&/p&&ul&&li&优势&/li&&li&迭代速度快,随时发版&/li&&li&资源节省,减少重复开发(Android & iOS)&/li&&li&跨平台,可浏览器运行&/li&&li&劣势&/li&&li&加载速度慢、白屏&/li&&li&界面体验差,交互不一致&/li&&/ul&&p&针对 Hybrid 的劣势,行业内现有的解决方案有很多,典型的有 Facebook 的 React Native 和阿里的 Weex,除去其它因素,只讲技术本身,它们有几个共同点:&/p&&ul&&li&JS/CSS 编码或类 JS/CSS 编码&/li&&li&Virtual DOM&/li&&li&JavaScriptCore / jsc.so 解析&/li&&li&Native 呈现&/li&&/ul&&p&由此可见行业内解决此类问题的关键套路就是使用 Native 来呈现。&/p&&p&那么回到问题本身,为什么 Native 不存在此类问题而网页存在,经过研究我们发现有以下两个主要区别:&/p&&ol&&li&Apple、Google&br& 这类大厂在界面体验上有深厚的研究,他们把界面体验所需要注意的那些点做成了开发模式的约束,放到了开发过程中,使用 IDE &br&和框架等工具去限制和引导,从而帮助开发者把界面体验做好。Web 是一种开放标准,它更为灵活,对界面体验没有严苛的限制,由开发者自由发挥&/li&&li&资源存放在本地和在远端的加载速度区别&/li&&/ol&&p&关于第一个区别大家可能存在一些困惑,这里我们举个例子,下图就是 Native 为什么没有白屏的根本原因:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-4bf3222b36fee6a38b7bd2475fec98f5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&421& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic4.zhimg.com/v2-4bf3222b36fee6a38b7bd2475fec98f5_r.jpg&&&/figure&&p&&br&&/p&&p&如图所示,Native&br& 从视图 A 跳转到视图 B,当用户点击 A 中的按钮触发跳转到 B 的动作时,B 的代码会开始执行,只有当 B 已经加载完成后,系统才会让 A跳转到 B,在 iOS 中的生命周期是 viewWillAppear,在此之前 viewDidLoad 已经执行完毕,Android 也是相似的生命周期。再加上 Native APP 的资源是本地化的,Native APP 有更多的运算资源和系统级别优化,它可以把这个加载过程时间缩短到接近瞬间。而把界面绘制和加载代码写到 viewWillAppear 之前是这些厂商指导我们去这样做的,并且提供了相应的系统级别支持。这时候我们思考一个问题,如果 Native 代码将界面绘制的代码写到 viewDidAppear 中会发生什么?答案是也会出现白屏。&/p&&p&由此可见,并不是纯 Native 一定体验好,如果你不按照厂商的指导要求做,体验一样不好。&/p&&p&当我们想清楚原因后我们就开始做了一个界面体验技术,名字叫 &b&Enhanced Hybrid (增强混合),简称 EH&/b&。&/p&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-8cf77fa718115dafe1e1efef_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&777& data-rawheight=&446& class=&origin_image zh-lightbox-thumb& width=&777& data-original=&https://pic1.zhimg.com/v2-8cf77fa718115dafe1e1efef_r.jpg&&&/figure&&p&&br&&/p&&p&EH 的核心是“&b&解决 Hybrid 与 Native 体验差异的技术瓶颈&/b&”。它包括两个部分,第一个部分是一个 Native SDK,有目前我们积累的所有解决体验差异技术瓶颈的功能,第二个部分是界面体验指南,也就是如何让我们的 Web 页面变的界面体验更好。&/p&&p&举个例子,在刚刚的白屏例子中,我们可以看到一个重要的信息,A跳转到 B 的时候,当 A 中点击执行跳转动作时第二个界面就已经开始执行了,在 B 执行完渲染部分之前系统不会执行 A 到 B 的实际界面跳转动作。这个操作在 Web 中是不可行的,我们无法在 Web 中让 B 在跳转前执行完渲染部分的代码。&/p&&p&那么无白屏的前提条件是什么?&/p&&p&&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-82f10dba57d4f_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&384& data-rawheight=&456& class=&content_image& width=&384&&&/figure&&p&&br&&/p&&p&无白屏的前提条件是渲染完成,或者至少渲染到一个用户跳转过来有东西,不会给人突兀的感觉。&/p&&p&我们思考这个里面的技术瓶颈是什么?&/p&&ol&&li&无法在跳转到 B 之前执行 B 的加载和渲染&/li&&li&无法获取 B 的渲染完成进度&/li&&/ol&&p&当我们想清楚这个技术瓶颈以后动手解决了这两个问题。首先,B&br&&br&的渲染完成并不是一个绝对的状态,而是由研发自己知道自己控制的,研发可以在到达这个状态的时候把状态主动通知出去。第二我们费了一些周折,在两个平台中可以通过一些技术去控制&br& A 等待一个通知,再让 B 展示出来,最终结合起来的方案如下图所示:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f6fdbf5de0fc5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&978& data-rawheight=&942& class=&origin_image zh-lightbox-thumb& width=&978& data-original=&https://pic3.zhimg.com/v2-f6fdbf5de0fc5_r.jpg&&&/figure&&p&&br&&/p&&p&单独使用此技术会遇到在 A 等待时间长的问题,再辅以&b&“离线化技术”&/b&便可以完美解决。(离线化技术会在后面详细讲)&/p&&p&EH 目前所有的功能包含:&/p&&ul&&li&Open:打开无白屏 WebView&/li&&li&TransPage:SPA 使用 Native 导航,让 SPA 的视图切换在不做任何特殊开发的情况下,具有和 Native 一样的交互表现,例如 iOS 中的左滑后退&/li&&li&TabsEntry:让 App 底部 Tab 可以动态配置,Hybrid Tab 表现效果可以和 Native 一样&/li&&li&Modal WebView:让 Hybrid 应用可以在当前页打开一个弹出式的 WebView ,从而在短暂操作后可以回到原来的流程当中&/li&&li&Config:让 Hybrid 界面高度可定制化,例如分开的上下 Bounce 设置,ScrollView 的设置,导航的设置等&/li&&li&ActionSheet:弹出一个 Native 的 ActionSheet 从而使其蒙层可以盖住导航&/li&&/ul&&p&目前还有更多黑科技功能在逐渐增加中,上述技术当中前三个已经成功申请专利。&/p&&p&很多人会存有疑问,为什么我们不使用 React Native 或者 Weex,而是自己做一个体验技术?&/p&&p&使用此类技术存在这么几个问题:&/p&&ul&&li&平台化而非插件化:使用此类技术后,你的整体前端业务代码就要全部构建在这个平台之上,如果平台出现问题或者架构更新,转型成本是完全重写一套业务代码。而采用插件化方案,加了体验会更好,没有也可以降级,这样转型的成本会少很多&/li&&li&技术栈捆绑:每一个技术都有捆绑的一个生态,在用 RN 的时候你必须使用 React ,在用 Weex 的时候必修使用 Vue,转型成本同样高,且限制了业务选型&/li&&li&解决问题被动:当系统更新或技术本身出现质量问题的时候,业务的研发团队几乎没有能力去解决,只能等待技术官方研发团队或开源社区去解决,这会使我们的业务很被动&/li&&/ul&&p&EH 本身不捆绑任何技术,即使你不使用任何的框架也可以完整使用 EH 的功能。&/p&&p&体验 EH 功能可以在应用商店中下载 “&b&美团钱包&/b&”,在首页中点击手机充值、生活缴费或“优惠” Tab 中的内容。&/p&&h2&&b&SSR / 构建时预渲染技术&/b&&/h2&&p&&u&&a href=&https://link.zhihu.com/?target=https%3A//ssr.vuejs.org/zh/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SSR(Server Side Rendering)&/a&&/u& 这里就不多赘述了,大家都了解。构建时预渲染技术是我们特殊研发的一个技术。它的特点是从首帧速度优化角度来讲,理论上比 SSR 更快更稳定。&/p&&p&构建时预渲染技术主要实现方式是:在编译完成后,启动一个&br& Web Server 来运行整个网站,再开启多个无头浏览器(Headless Chrome,PhantomJS &br&等无头浏览器技术)去请求所有项目的路由,当被请求的网页渲染到第一个有意义的渲染时(FMP &u&&a href=&https://link.zhihu.com/?target=https%3A//developers.google.com/web/updates/2017/06/user-centric-performance-metrics& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&参考 Google 的衡量体系&/a&&/u&)主动抛出一个事件,该事件由无头浏览器截获,无头浏览器截获后将此时的页面 HTML 内容保存下来生成一个 HTML。最终发布这个 HTML。此 HTML 中包含 FMP 所需要的所有 CSS 及 DOM 结构。&/p&&p&事实上 SSR 和构建时预渲染技术都是为首帧速度优化服务的,首帧速度优化的核心有两点:&/p&&ol&&li&TTFB(time to first byte) 首字节时间&/li&&li&在首个请求的响应当中返回首帧绘制所需要的 CSS 及 DOM 结构。&/li&&/ol&&p&为什么说构建时预渲染会比 SSR 快呢?&/p&&p&SSR&br& 目前前端领域主流实现方式是使用 Node 作为中间层,负责数据的获取和界面的拼装,其 Node &br&层可能后面对接着一个或多个数据来源(业务系统),它的响应速度受限于最慢的那个数据来源。而构建时预渲染劣势是不包含数据,但优势是其首帧事件完全不依赖任何数据来源,从&br& Nginx 层直接返回,响应速度更快,同时流量负载更高。&/p&&p&为什么说构建时预渲染会比 SSR 更稳定?&/p&&p&SSR 在 Nginx 层后面还需要一层 Node(典型架构)做支撑 ,而构建时预渲染从 Nginx 层直接返回,其关键链路上少了一环需要保障稳定性的服务,所以稳定性更强。&/p&&p&金融服务平台在 SSR 和构建时预渲染上都有很多项目在运行,在 SSR 的优化上也有丰富的经验去保障速度和稳定性,在选型上的考量主要是首帧对数据的依赖程度。&/p&&h2&&b&离线化技术&/b&&/h2&&p&离线化技术可以将网页的网络加载时间变为 0,在离线化的选型上美团点评内部有很多选择,我们也在不同的方向进行尝试。其中我们的选择包括:&/p&&ul&&li&标准技术:&/li&&li&Application Cache:实现上各个平台各个浏览器有一些差异,即使把“无法更新的坑”踩过还是会有很多“无法离线”的坑,PASS&/li&&li&Service Workers:Service Workers 是团队一直跟进的技术,目前在测试已经接近正式发布,只是在 iOS 上还无法大范围使用,长期看好,暂时 PASS&/li&&li&借助 Native 能力的自有技术:&/li&&li&美团平台技术团队的类 Service Workers 的被动离线化技术&/li&&li&美团旅行技术团队的离线包技术&/li&&/ul&&p&留下来的只剩下两个自有技术,这两个技术的最大区别是,是否解决了首次加载问题?离线化方案的首次加载问题是一个很难的技术领域,我认为其最核心的问题是&b&何时加载&/b&,提前加载会不会用户在很长一段时间内都不会用到导致浪费流量?使用包含首次加载优化的离线化技术的项目多了会不会造成加载拥塞?是不是需要分析用户行为数据去更精准的进行离线包的提前加载?这当中存在太多不确定性,不过我相信我们的技术团队一定能够想出优美的解决方案去解决这个问题。&/p&&p&另外基于 Native 能力的离线化技术还存在一些来自平台的限制,如 iOS 的 WKWebView 不支持请求拦截,而请求拦截是离线化的关键技术,这个原因导致在 WKWebView 上无法实现离线化。&/p&&p&WKWebView 的优势是:运行和渲染速度更快,与 Safari 内核一致 Apple 重点迭代跟进问题;劣势是:启动速度慢,无法拦截请求进而使用自有的离线化技术。&/p&&p&权衡离线化所带来的巨大优势和 WKWebView 的速度优势,我们选择继续使用 UIWebView。(曾经在 iOS 11 发布前业界一度认为 Apple 会在 iOS 11 中支持 WKWebView 的请求拦截)&/p&&h2&&b&字符级增量更新方案&/b&&/h2&&p&字符级增量更新方案是一个前端领域研究了很久的课题,智能支付团队近期在这一领域有了突破性进展,这个技术方案可以通过字符级增量更新减少文件传输大小,节省流量、提高页面成功率和加载速度。其中增量计算能力由美团平台的静态资源托管方案 &b&Build Service&/b& 支持。主要应用在扫码付项目上。&/p&&p&扫码付项目是美团金融智能支付团队面向 C 端消费者推出的一款 H5 融合支付类的产品,消费者在商家消费之后,可使用多种 App 进行扫码支付,同时可对商家进行评价,支持美团、大众点评、微信、支付宝、美团钱包等多种 App,目前业务日均 PV 千万级。&/p&&p&字符级增量更新方案的详细介绍,请参考之前的文章&u&&a href=&https://link.zhihu.com/?target=https%3A//tech.meituan.com/qrcodepayment-static-optimize.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&美团金融扫码付静态资源加载优化实践&/a&&/u&。&/p&&h2&&b&监控系统&/b&&/h2&&p&美团点评内部前端监控系统包括:&/p&&ul&&li&Sentry:异常监控&/li&&li&Performance:性能监控&/li&&li&CAT:网络监控&/li&&/ul&&p&在技术栈统一前,我们团队这三个监控工具在同时使用,然而监控系统上前端和后端不同的是前端对代码尺寸有要求,接入的监控系统多会对项目的加载速度有影响。综合多方面因素,我们在本次技术栈统一中选择了CAT来作为我们主要的监控系统。主要是它包含前两者的功能。&/p&&p&CAT(详情可以参考&u&&a href=&https://link.zhihu.com/?target=https%3A//tech.meituan.com/CAT_in_Depth_Java_Application_Monitoring.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&《深度剖析开源分布式监控CAT》&/a&&/u&&br&&br&一文)是一个美团点评的全端基础监控组件,在后端为各业务线提供全面的监控服务和决策支持,提供系统的性能指标、健康状况、基础告警等功能。在前端覆盖美团点评所有APP,提供近实时的多维数据分析、立体式监控、告警等功能。提供了近实时的多维数据分析,立体式监控功能。&/p&&p&CAT很大的优势是它是一个实时系统,从数据生成到服务端处理结束是秒级别,秒级定义是 48 分钟 40 秒时基本上能看到 48 分钟 38 秒的数据,整体报表的统计粒度是分钟级;第二个优势,数据是接近全量统计,目前大约5%的高QPS 项目是采样统计。&/p&&h2&&b&协议&/b&&/h2&&p&目前我们使用的协议均为 HTTP/2,支付是金融最早使用 HTTP/2 的部门,由于支付业务的特殊性,在一开始我们就是使用的 HTTPS ,进而很早就使用上了 SPDY。&/p&&p&在15年&br& HTTP/2 标准化的时候我们直接更新集群使用上了 HTTP/2,在 SPDY 和 HTTP/2 &br&这种具有多路复用功能的协议上我们的前端架构全部做的都是按需加载的方式,大大减小了由“减少请求数” 所带来的流量冗余。最大化利用了 HTTP &br&本身的缓存机制,通过减小客户端大小的方式大大提升了网络加载性能。&/p&&h2&&b&安全方面&/b&&/h2&&p&安全方面在前端我们使用:&/p&&ul&&li&HSTS: 防 SSLStrip 攻击的标准解决方案&/li&&li&CSP: 防跨站脚本攻击的标准解决方案&/li&&/ul&&p&同时在核心接口上我们有一个自研的网页请求签名方案,来在一定程度上保障请求是从我们的客户端中正常发出的。&/p&&p&&b&总结&/b&&/p&&p&以上是对金融平台前端技术体系的介绍和个人的一些思考,最后说一下采用此技术体系所达到的一些效果。&/p&&h2&&b&效率&/b&&/h2&&ul&&li&由于 Vix 和设计部门统一标准,在界面构建过程中可以减少至少 &b&80%&/b& 的时间,而这部分恰巧占整体研发时间的 &b&60%&/b& 以上&/li&&li&联调部分我们有 Portm 进行协作解耦,可以减少联调时间一半以上,一般一个项目联调部分占整体研发时间的 20% 左右&/li&&li&另外我们还有非常强大的脚手架 &u&&a href=&http

我要回帖

更多关于 支持ssr的路由器 的文章

 

随机推荐