一、H5简介
什么是H5?
H5全名为HTML5,HTML5是HTML的第五个版本,HTML(Hyper Text Mark-up Language)即超文本标记语言,是目前网络上应用最为广泛的语言,也是构成网页文档的主要语言。
HTML5本质上不是什么新的技术,但是由于其在功能特性上有了极大的丰富,在加上各大浏览器性能上的支持,HTML5相对以前的HTML4有着更广泛的应用。
H5新特性
1 语言特性
HTML5赋予网页更好的意义和结构,简单地说就是程序员更方便地跟浏览器沟通。字符编码变得简洁,不区分大小写,同时添加了布尔值,类似checked,selected等,引号可以省略,有可以省略结束符的标签,和完全省略的标签。
2 本地储存特性
基于HTML5开发的网页APP拥有更短的启动时间,更快的联网速度,因可将一些常用、不常更新的内容储存在本地。
3 设备兼容特性
HTML5提供了前所未有的数据与应用接入开放接口,使外部应用可以直接与浏览器内部的数据直接相连。例如视频影音可以与microphones及摄像头相联。
4 连接特性
HTML5拥有更有效的服务器推送技术,ServerSentEvent和WebSockets就是其中的两个特性,这两个特性能够帮助实现服务器将数据“推送”到客户端的功能。更有效的连接工作效率,可以实现基于页面的实时聊天,更快速的网页游戏体验,更优化的在线交流。
5 CSS3特性
如果把网页比喻成舞台,文字图片视频这些比喻成演员,那么CSS3就是化妆师和舞美,它控制着网页所有的元素的视觉和动作效果。相对于旧的CSS版本,HTML5所支持的CSS3中提供了更多的风格和更强的效果,也提供了更高的灵活性和控制型。
二、页面适配问题
viewport
传统桌面web页面布局通常是定宽布局,但是定宽布局的方式对移动端却不适用,原因手机屏幕尺寸大小各异,定宽布局可能在某些手机上出现横向滚动条,导致阅读效果比较差。
为了让手机有更好的网页浏览体验,苹果引入了viewport,为页面提供了一个虚拟的布局窗口,在这个虚拟的布局窗口中渲染页面,然后系统会把渲染好的页面自动缩放到手机屏幕大小。
虽然viewport还没有成为正式的规范,但是现在绝大部分浏览器都支持viewport。
在桌面浏览器中,viewport严格等于浏览器窗口大小,页面渲染时,页面宽度不会超过浏览器的宽度。
移动端屏幕太窄,为了提供更好的页面体验,移动端提供了两种viewport:可视viewport,布局viewport。
可视viewport就是当前屏幕正在展示的区域,也就是移动设备屏幕的宽度,宽高通过window.innerWidth和window.innerHeight获取(存在兼容性问题)。
布局viewport,页面布局实际用到的viewport,通常比可视viewport要宽,宽高通过document.documentElement.clientWidth和document.documentElement.clientHeight获取。
移动端还有一种viewport概念,可以理解为理想viewport,作用就是在理想viewport下,不同移动设备,展示的字体大小接近,并且不需要用户缩放就可以展示全部的页面内容。
理想viewport的宽度默认等于可视viewport的宽度,但是对同一台设备来说,这个理想viewport的宽度是可以改变的,而可视viewport的宽度是不可变的。
如何使用理想viewport来布局页面呢?只需要设置viewport的width等于device-width。
viewport的属性,推荐使用以及支持度较广泛的属性只有6个:width
,height
,initial-scale
,maximum-scale
,minimum-scale
,user-scalable
。
width
设置viewport布局宽度,内核是webkit的浏览器默认值是980px,取值范围在200-10000px,也可以取值为设备宽度device-width(等于横向设备无关像素数量)。
height
设置viewport布局高度,默认值依赖设备长宽比以及宽度值,取值范围在223-10000px,也可以取值为设备高度device-height。
initial-scale
设置初始缩放比例,页面第一次加载时的缩放比例。默认比例依赖于显示密度。在密度低于200 dpi的显示设备上,比例为1.0。在密度介于200及300 dpi之间的显示设备上,比例为1.5。对于具有300 dpi以上密度的显示设备,比例为密度/150 dpi向下取整的结果。取值范围由maximum-scale
属性以及minimum-scale
属性决定。如果设置initial-scale
值为1,width
默认是device-width,height
默认是device-height
initial-scale
设置的缩放大小会改变理想viewport的大小,不会改变可视viewport的大小,也不会改变布局viewport的大小,这是某些适配方案依赖的基本原理,也是解决1px问题的关键。后面分析适配方案时,动态viewport适配方案就依赖这个知识点。
maximum-scale
允许用户缩放到的最大比例,默认值是0.5,范围从0到10.0。
minimum-scale
允许用户缩放到的最小比例,默认值是5.0,范围从0到10.0。
user-scalable
用户是否可以手动缩放,值可以是:yes/true允许用户缩放;no/false不允许用户缩放。
适配方案
方案一
固定高度,宽度自适应。
这种方案是目前使用较多的方案,也是相对较简单的实现方案:
该方法使用了理想视口:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
垂直方向使用固定的值,水平方向使用弹性布局,元素采用定值、百分比、flex布局等。这种方案相对简单,还原度也非常低。
方案二
固定布局视口宽度,使用viewport进行缩放
如:荔枝FM、网易应用
荔枝的代码:
if(/Android (\d+.\d+)/.test(navigator.userAgent)){
var version = parseFloat(RegExp.$1);
if(version>2.3){
var phoneScale = parseInt(window.screen.width)/640;
if(/MZ-M571C/.test(navigator.userAgent)){
document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
}else if(/M571C/.test(navigator.userAgent)&&/LizhiFM/.test(navigator.userAgent)){
document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
}else{
document.write('<meta name="viewport" content="width=640, minimum-scale = '+ phoneScale +', maximum-scale = '+ phoneScale +', target-densitydpi=device-dpi">');
}
}else{
document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">');
}
}else{
document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">');
}
网易应用:
var win = window,
width = 640,
iw = win.innerWidth || width,
ow = win.outerHeight || iw,
sw = win.screen.width || iw,
saw = win.screen.availWidth || iw,
ih = win.innerHeight || width,
oh = win.outerHeight || ih,
ish = win.screen.height || ih,
sah = win.screen.availHeight || ih,
w = Math.min(iw, ow, sw, saw, ih, oh, ish, sah),
ratio = w / width,
dpr = win.devicePixelRatio;
if (ratio = Math.min(ratio, dpr), 1 > ratio) {
var ctt = ",initial-scale=" + ratio + ",maximum-scale=" + ratio,
metas = document.getElementsByTagName("meta");ctt += "";
for (var i = 0, meta; i < metas.length; i++) meta = metas[i], "viewport" == meta.name && (meta.content += ctt)
}
固定布局视口,宽度设置固定的值,总宽度为640px,根据屏幕宽度动态生成viewport。(设计稿应该是640px的)
<meta name="viewport" content="width=640, minimum-scale = 0.5625, maximum-scale = 0.5625, target-densitydpi=device-dpi">
这种方式布局如荔枝FM的网页宽度始终为640px。缩放比例scale为:
var scale = window.screen.width / 640
设计稿为640px时,正好可以1:1以px来写样式。但是1px所对应的物理像素就不一定是1了。
(window.screen.width * dpr) / 640 // 1px对应的物理像素
方案三
根据不同屏幕动态写入font-size,以rem作为宽度单位,固定布局视口。
如网易新闻:
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
以640px设计稿和750px的视觉稿,网易这样处理的:
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 7.5; // 750px设计稿将布局视口分为7.5份
var rem = width / 6.4; // 640px设计稿将布局视口分为6.4份
这样不管是750px设计稿还是640px设计稿,1rem 等于设计稿上的100px。故px转换rem时:
rem = px * 0.01;
在750px设计稿上:
75px 对应 0.75rem, 距离占设计稿的10%;
在ipone6上:
width = document.documentElement.clientWidth = 375px;
rem = 375px / 7.5 = 50px;
0.75rem = 37.5px; (37.5/375=10%;占屏幕10%)
在ipone5上:
width = document.documentElement.clientWidth = 320px;
rem = 320px / 7.5 = 42.667px;
0.75rem = 32px; (32/320=10%;占屏幕10%)
故对于设计稿上任何一个尺寸换成rem后,在任何屏下对应的尺寸占屏幕宽度的百分比相同。故这种布局可以百分比还原设计图。
方案四
以rem作为宽度单位,动态写入viewport和font-size进行缩放。
根据设置的dpr设置font-size。如:
document.documentElement.style.fontSize = 50 * dpr;
// dpr 为设置的设备像素比。(注意不是设备自身的设备像素比,而是认为设置的dpr)
这种情况下,dpr = 1时,1rem = 50px;
dpr = 2时, 1rem = 100px;
当设计以iphone6为标准,出750px的设计稿时,此时dpr=2,故1rem 等于100px,将图上的尺寸转换为rem非常方便,除以100就行。代码如下:
var scale = 1.0;
var dpr = 1;
var isAndroid = window.navigator.appVersion.match(/android/gi);
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = window.devicePixelRatio;
// 此处只简单对ios做了伸缩处理,安卓没有做伸缩处理,统一dpr = 1
if ( isIPhone ) {
scale /= devicePixelRatio;
dpr *= devicePixelRatio;
}
var viewport = document.getElementById('viewport');
var content = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no';
viewport.setAttribute( 'content', content );
document.documentElement.style.fontSize = 50 * dpr + 'px';
document.documentElement.setAttribute('data-dpr', dpr);
对于该方案,
假设肉眼看到的宽度(视觉宽度):visualWidth,令dpr=1时,其1rem对应的宽度为50.
dpr = 1 时, 1rem = 50px, initial-scale=1, 缩放为1。
visualWidth = 50 * 1 = 50;
dpr = 2 时, 1rem = 100px, initial-scale=0.5, 缩放为0.5。
visualWidth = 100 * 0.5 = 50;
dpr = 3 时, 1rem = 150px, initial-scale=0.3333, 缩放为0.3333。
visualWidth = 150 * 0.3333 = 50;
所以该方案,1rem在所有屏幕上对应的肉眼距离相同,故不同屏幕下,总的rem数不同,大屏下总的rem数大于小屏下,如iphone6下,总宽度为7.5rem,iphone5下,总宽度为6.4rem。故此方案不能百分比还原设计稿,故写样式时,对于大块元素应该用百分比,flex等布局,不能直接用rem。
方案五
根据不同屏幕动态写入font-size和viewport,以rem作为宽度单位
将屏幕分为固定的块数10:
var width = document.documentElement.clientWidth; // 屏幕的布局视口宽度
var rem = width / 10; // 将布局视口分为10份
这样在任何屏幕下,总长度都为10rem。1rem对应的值也不固定,与屏幕的布局视口宽度有关。
对于动态生成viewport,他们原理差不多,根据dpr来设置缩放。看看淘宝的:
var devicePixelRatio = window.devicePixelRatio;
var isIPhone = window.navigator.appVersion.match(/iphone/gi);
var dpr,scale;
if (isIPhone) {
if (devicePixelRatio >=3) {
dpr = 3;
} else if (devicePixelRatio >=2) {
dpr = 2;
} else {
dpr = 1;
}
} else {
dpr = 1;
}
scale = 1 / dpr;
淘宝只对iphone做了缩放处理,对于android所有dpr=1,scale=1即没有缩放处理。
此方案与方案三相似,只是做了viewport缩放,能百分比还原设计稿。
适配中要解决的问题
移动端适配最主要的是使在不同屏幕下不用缩放页面就能正常显示整个页面。以上方案都完成了这一需求。其次有几个需求:
1、解决高清屏下1px的问题,其实有很多hack方法,这里只讲了缩放视口。先将布局视口设置为高清屏的物理像素。这样css中1px就是1个物理像素,这样看到的线条才是真正的1px。但是此时视口宽度大于设备的宽度,就会出现滚动条。故对视口进行缩放,使视口宽度缩放到设备宽度。
淘宝团队在处理安卓端的缩放存在很多问题,所以dpr都做1处理,所以安卓端就没有解决1px的问题。
2、在大屏手机中一行看到的段落文字应该比小屏手机的多。
由于淘宝和网易新闻rem都是百分比,故如果用rem一行显示的文字个数应该是相同的。故对于段落文本不能用rem作为单位,应该用px处理,对于不同的dpr下设置不同的字体。
.selector {
color: red;
font-size: 14px;
}
[data-dpr="2"] .selector {
font-size: 28px; // 14 * 2
}
[data-dpr="3"] .selector {
font-size: 42px; // 14 * 3
}
对于方案四,不管什么情况下,1rem对应的视觉上的宽度都是一样的,而对应的大屏、小屏手机其视觉宽度当然不同,故字体设置为rem单位时,也能满足大屏手机一行显示的字体较多这个需求。
五种方案对比
上面四种方案对设计稿还原程度是有差别的。
除了方案一和方案四以外,其他方案都是百分比还原设计稿,大屏下元素的尺寸就大。
方案一还原设计稿程度较低,这里不做说明。
方案二做了百分比适配,部分1px适配,没有字体适配。
方案三做了百分比适配,没有1px适配,有字体大小适配。
方案四没有做百分比适配,布局要用百分比和flex布局,做了1px的适配,并且对于段落文字直接可以用rem做单位,不需要做适配。
方案五做了百分比适配,有1px适配,有字体大小适配。
三、开发工具
微信web开发者工具
1.使用自己的微信号来调试微信网页授权
2.调试、检验页面的 JS-SDK 相关功能与权限,模拟大部分 SDK 的输入和输出
3.使用基于 weinre 的移动调试功能(仅0.5.0-0.7.0版本支持),支持
<a href="http://x5.tencent.com/guide?id=4000 "X5 Blink 介绍"" target="_blank">
X5 Blink内核的远程调试
4.利用集成的 Chrome DevTools 协助开发
内网穿透工具
注意:鉴于很多开发者在临时体验开发时往往没有公网域名或者公网IP,本工具提供了一个公网代理服务,目的是方便开发测试。
本工具当前不保证多个开发者随意设置相同的子域名导致的冲突以及通道稳定性,因此正式应用、正式环境必须是真实的公网IP或者域名,正式应用上线绝对不能使用本工具。
启动内网穿透
下载工具
git clone https://github.com/open-dingtalk/pierced.git
启动工具,执行命令“./ding -config=./ding.cfg -subdomain=域名前缀 端口”
以mac为例:
cd mac_64
chmod 777 ./ding
./ding -config=./ding.cfg -subdomain=canway 8080
以windows为例:
cd windows_64
ding.exe -config=ding.cfg -subdomain=canway 8080
启动完客户端后,你访问http://canway.vaiwan.com/xxxxx
都会映射到http://127.0.0.1:8080/xxxxx
。
UI组件
四、插件
fastclick
简介
移动端项目中,在某些机型某些浏览器下,存在click事件300ms延迟的问题,影响用户满意度。原因是:从点击屏幕上的元素到触发元素的 click 事件,移动浏览器会有大约 300 毫秒的等待时间,因为它想看看你是不是要进行双击(double tap)操作。
vue项目中,可以通过引入fastclick第三方依赖包来解决。
安装
npm install --save fastclick
使用: 在main.js中
import fastclick from 'fastclick'
fastclick.attach(document.body)
better-scroll
简介
better-scroll 是一个移动端滚动的解决方案,它是基于 iscroll 的重写,它和 iscroll 的主要区别在这里。better-scroll 也很强大,不仅可以做普通的滚动列表,还可以做轮播图、picker 等等。
安装
npm install better-scroll -S # install 1.x
npm install better-scroll@next -S # install 2.x,该版本带有所有插件的能力。
起步
<div class="wrapper">
<ul class="content">
<li>...</li>
<li>...</li>
...
</ul>
</div>
wrapper元素上要给定位
.wrapper{
position: absolute;
left: 0;
top: 0;
overflow: hidden;
}
上面的代码中 BetterScroll 是作用在外层 wrapper 容器上的,滚动的部分是 content 元素。这里要注意的是,BetterScroll 只处理容器(wrapper)的第一个子元素(content)的滚动,其它的元素都会被忽略。
wrapper里面不能存在多个同级div
如果这样写:
<div class="classifyTitle" ref="wrapper">
<div class="">
ss
</div>
<ul>
<li v-for="(item,index) in classifyData.products">
<router-link :to="{name:'详情'}">{{item.title}}</router-link>
</li>
</ul>
</div>
那么ul中的元素将不能滚动
scroll组件化
<template>
<div ref="wrapper">
<slot></slot>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
props: {
/**
* 1 滚动的时候会派发scroll事件,会截流。
* 2 滚动的时候实时派发scroll事件,不会截流。
* 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件
*/
probeType: {
type: Number,
default: 1
},
/**
* 点击列表是否派发click事件
*/
click: {
type: Boolean,
default: true
},
/**
* 是否开启横向滚动
*/
scrollX: {
type: Boolean,
default: false
},
/**
* 是否派发滚动事件
*/
listenScroll: {
type: Boolean,
default: false
},
/**
* 列表的数据
*/
data: {
type: Array,
default: null
},
/**
* 是否派发滚动到底部的事件,用于上拉加载
*/
pullup: {
type: Boolean,
default: false
},
/**
* 是否派发顶部下拉的事件,用于下拉刷新
*/
pulldown: {
type: Boolean,
default: false
},
/**
* 是否派发列表滚动开始的事件
*/
beforeScroll: {
type: Boolean,
default: false
},
/**
* 当数据更新后,刷新scroll的延时。
*/
refreshDelay: {
type: Number,
default: 20
}
},
mounted() {
// 保证在DOM渲染完毕后初始化better-scroll
setTimeout(() => {
this._initScroll()
}, 20)
},
methods: {
_initScroll() {
if (!this.$refs.wrapper) {
return
}
// better-scroll的初始化
this.scroll = new BScroll(this.$refs.wrapper, {
probeType: this.probeType,
click: this.click,
scrollX: this.scrollX
})
// 是否派发滚动事件
if (this.listenScroll) {
this.scroll.on('scroll', (pos) => {
this.$emit('scroll', pos)
})
}
// 是否派发滚动到底部事件,用于上拉加载
if (this.pullup) {
this.scroll.on('scrollEnd', () => {
// 滚动到底部
if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
this.$emit('scrollToEnd')
}
})
}
// 是否派发顶部下拉事件,用于下拉刷新
if (this.pulldown) {
this.scroll.on('touchEnd', (pos) => {
// 下拉动作
if (pos.y > 50) {
this.$emit('pulldown')
}
})
}
// 是否派发列表滚动开始的事件
if (this.beforeScroll) {
this.scroll.on('beforeScrollStart', () => {
this.$emit('beforeScroll')
})
}
},
disable() {
// 代理better-scroll的disable方法
this.scroll && this.scroll.disable()
},
enable() {
// 代理better-scroll的enable方法
this.scroll && this.scroll.enable()
},
refresh() {
// 代理better-scroll的refresh方法
this.scroll && this.scroll.refresh()
},
scrollTo() {
// 代理better-scroll的scrollTo方法
this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
},
scrollToElement() {
// 代理better-scroll的scrollToElement方法
this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
}
},
watch: {
// 监听数据的变化,延时refreshDelay时间后调用refresh方法重新计算,保证滚动效果正常
data() {
setTimeout(() => {
this.refresh()
}, this.refreshDelay)
}
}
}
</script>
<style scoped>
.wrapper {
position: absolute;
left: 0;
top: 0;
overflow: hidden;
}
</style>
vue-touch
简介
vue-touch封装了 hammer.js的方法,用于处理移动端滑动事件
安装vue-touch:
npm install vue-touch@next
在main.js中将其注册为全局组件:
var VueTouch = require('vue-touch')
Vue.use(VueTouch, {name: 'v-touch'})
使用实例:
<!-- 左划 默认渲染为div data为参数 -->
<v-touch v-on:swipeleft="onSwipeLeft(data)">Swipe me!</v-touch>
<!-- 点击 渲染为一个a标签 -->
<v-touch tag="a" v-on:tap="onTap">Tap me!</v-touch>
<!--
点击 渲染为p标签
常用的事件有:swiper(滑动事件)、tap(短时间内的点击事件)、press(事件大于tap的按压事件)
-->
<v-touch tag="p" v-on:tap="onTap">Tap me!</v-touch>
Api及相关事件
1、pan,触屏中的拖动事件,在指定的dom区域内,一个手指放下并移动事件
事件类型有:pan, panstart, panmove, panend, pancancel, panleft, panright, panup, pandown; 使用方法为:v-on:panstart="callback";
2、Swipe:滑动事件,在指定的dom区域内,一个手指快速的在触屏上滑动。
事件类型有:swipe, swipeleft, swiperight,
swipeup, swipedown
使用方法为:v-on:swipeleft="callback";
3、Tap :在指定的dom区域内,一个手指轻拍或点击时触发该事件(类似PC端的click)。该事件最大点击时间为250毫秒,如果超过250毫秒则按Press事件进行处理。
事件类型:tap
使用方法:v-on:tap="callback"
4、Press: 在指定的dom区域内触屏版本的点击事件,这个事件相当于PC端的Click事件,该不能包含任何的移动,最小按压时间为500毫秒,常用于我们在手机上用的“复制、粘贴”等功能。该事件分别对以下事件进行监听并处理:
事件类型:press, pressup
使用方法:v-on:press = "callback"
5、Rotate事件:在指定的dom区域内,当两个手指或更多手指成圆型旋转时触发(就像两个手指拧螺丝一样)。该事件分别对以下事件进行监听并处理
事件类型:rotate, rotatestart, rotatemove,
rotateend, rotatecancel,
使用方法:v-on:rotate = "callback"
6、Pinch:在指定的dom区域内,两个手指(默认为两个手指,多指触控需要单独设置)或多个手指相对(越来越近)移动或相向(越来越远)移动时事件。该事件事以分别对以下事件进行监听并处理:
Pinchstart:多点触控开始、Pinchmove:多点触控过程、Pinchend:多点触控结束、Pinchcancel:多点触控取消、Pinchin:多点触控时两手指距离越来越近、Pinchout:多点触控时两手指距离越来越远
注意:vue-touch 使用的是2.0.0版本 需要与vue2.0.0兼容;
应用实例:
通过手势滑动,进行页面的切换,如:
swiperleft: function () {
this.$router.push({'path':'/queuehistory'});
}
vConsole
简介
一个轻量、可拓展、针对手机网页的前端开发者调试面板。
移动端项目,由于在移动端无法打开控制台,所以无法像pc端chrome控制台那样直观查看console信息;不过我们可以使用vConsole插件进行调试。
安装vConsole:
npm install vconsole --save-dev
在main.js中引用并实例化:
import VConsole from 'vconsole';
const vConsole = new VConsole(); // 不使用的时候,可以将这句屏蔽掉;
此时可以使用console.log 原理:改写了console.log,重写了实现,用vConsole代理
五、清理微信浏览器缓存
在微信中打开以下网址,找到清理缓存栏清理即可
debugx5.qq.com
六、IOS页面自动缩放问题
在templates/index.html
中添加
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
七、项目打包
如何把web端和移动端同时在一个项目中,打包成多个页面?
- 新建文件夹src-mobile,将移动端代码全部再src-mobile中实现
- 修改打包配置文件webpack.base.conf.js
- 配置多入口打包,后面单独写篇文章,先不再次赘述了 配置示例
entry: {
app: './src/main.js',
app2: './src-mobile/main.js'
},
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
chunks: ['app']
}),
new HtmlWebpackPlugin({
filename: 'index2.html',
template: 'index2.html',
inject: true,
chunks: ['manifest', 'vendor', 'app2']
}),