webpack4搭建现代Hybird-h5工程

1、前言

这篇文章会分享一下我是如何针对混合开发(Hybird h5端)搭建构建环境css

内容涉及到以下几点:html

(1)混合开发h5端页面特色以及需求

(2)如何利用webpack4进行多入口的代码分割以达到较优的缓存利用率

(3)如何针对首屏渲染进行优化

(4)使用webpack4如何同时输出ES next(现代)与es5(向后兼容)的包

(5)客户端打包h5资源到包内策略

代码仓库webpack-esnext-cli,你们也能够边看代码边理解,固然能本身动手搭建一次是最好的前端

2、混合开发h5端页面特色以及需求

混合开发的h5端页面有什么特色?vue

入口繁杂、部分页面仅文案、须要较快的首屏渲染速度、部分页面的使用率不高,依赖网络的速度快慢node

(1)入口繁杂

入口繁杂其实意味着你的前端工程搭建必须是以多入口为起点搭建的,如webpack你能够配置entry,自行写一个脚本在构建时获取每个页面的js入口,而多入口意味着你必须考虑页面之间共享的模块应该如何抽取以达到一个较优的模块利用率,这点咱们在文章下一节详细讲。webpack

(2)部分页面仅文案,且利用率不高

其实有作过混合开发你天然会发现有一些页面是只有文案,没有任何js交互的,这类的页面,相似用户协议、常见问题之类的文案类页面,使用率不高,对首屏又有要求的咱们彻底能够将html写在html文件中,不须要用vue这类的框架去写虚拟DOM依赖框架去渲染,这样能够节省掉请求脚本以及框架运行渲染的时间,保证文字能够快速出如今页面当中。git

(3)较快的首屏渲染以及网络的快慢

由于咱们的h5页面是比较依赖手机网络的,它不像客户端资源都是大包到本地的,如今虽然4g普及了,可是用户不少时候网络其实并很差,那在弱网的状况下对页面的打开速度就有了必定的挑战了,从工程角度上考虑,咱们可不能够像客户端那样把h5须要的资源也打包进本地呢?答案固然是能够的,后面我会陆续讲到。es6

3、如何利用webpack4进行代码分割

根据上面第二点咱们提到的页面特色和需求,咱们的多页面共享的模块应该是下面这样的:github

一、每一个入口基本都须要用到的包应该长期缓存(hash值不变)

二、共享的chunk可自由分割

三、共享的chunk可在页面配置引用

那咱们先来看看在webpack4中咱们能够经过什么手段保证如上三点web

例如在vue的工程当中,咱们的每一个入口基本都依赖于vue.js,而在咱们打出的包中vue所占的资源大小比重也是比较大的,而这部分就是咱们须要长期缓存的。关于这个点我在个人另外一篇文章中讲过。这里咱们在webpack4中会单独将vue打包成vendor做为项目的基础包供页面引入(固然你也能够将其他的模块打包进来)

...

optimization: {
    splitChunks: {
        ...
        
        'vendor': {
            test: /node_modules\/vue/,
            name: 'vendor',
            chunks: 'all',
            enforce: true,
            priority: 2
          },
          
        ...
    }
},
plugins: [
    // 稳定moduleId,避免引入了一个新模块后,致使模块ID变动使得vender和common的hash变化后缓存失效
    new webpack.HashedModuleIdsPlugin(),
]

...
复制代码

单独将vue打包进vendor后,咱们就保证了一个基础模块是稳定的,但咱们还须要一些灵活性,好比咱们有一些复杂的页面可能会作成单页面,这时候咱们就须要引入vue-router、vuex这类的工具。

你可能会说为何不把这些包打包进vendor中?由于混合开发中页面的入口是很是繁杂的,若是用户打开一个普通的页面,仅依赖vue就能够了,可是由于你抽取的时候把vue-router也打包进去了,致使用户下载了一个它可能用不到的又比较大的文件,同时这样也会影响到其余页面的渲染速度,由于js包太大了,致使下载时间过长,而分开打包能起到一个增量下载的做用。

这时候咱们在splitChunks中增长一项spa-vendor配置:

optimization: {
    splitChunks: {
        ...
        
        // 项目基础包
        'vendor': {
            test: /node_modules\/vue/,
            name: 'vendor',
            chunks: 'all',
            enforce: true,
            priority: 2
        },
        // 单页面须要引入vue-router, vuex,这里单独分割出来
        'spa-vendor': {
            test: /node_modules\/vue-router/g,
            name: 'spa-vendor',
            chunks: 'all',
            enforce: true, 
            priority: 10
        },
          
        ...
    }
},
复制代码

好了,到这里,咱们已经把项目一些比较大的,不常变动的包独立分割出来而且作到持久缓存了,那剩余的大小不那么大的包咱们就可让webpack根据大小和引用率去自动打包了,这里咱们加一个commons包的配置

optimization: {
    splitChunks: {
        ...
        
        // 项目基础包
        'vendor': {
            test: /node_modules\/vue/,
            name: 'vendor',
            chunks: 'all',
            enforce: true,
            priority: 2
        },
        // 单页面须要引入vue-router, vuex,这里单独分割出来
        'spa-vendor': {
            test: /node_modules\/vue-router/g,
            name: 'spa-vendor',
            chunks: 'all',
            enforce: true, 
            priority: 10
        },
        // 剩余chunk自动分割
        'commons': {
            name: 'commons',
            minChunks: 5, // 引用次数大于5则打包进commons
            minSize: 3000, // chunk大小大于这个值才容许打包进commons
            chunks: 'all',
            enforce: true,
            priority: 1
        }
        ...
    }
},
复制代码

你们看到这里会看到splitChunk中每一个chunk的priority(优先级)是不同的,commons的优先级是最低的,由于要等到spa-vendor和vendor抽取完成后才会到commons抽取

完成后,打出的包是下面这样的

vendor(60k):

spa-vendor(23k,仍是比较大的):

commons:

自由分割和长期缓存咱们已经作到了,那剩下的就是chunk在页面中自由引入了。在我写的webpack-esnext-cli中,我是用了nunjucks模板引擎去作页面的资源引入的,利用webpack4和webpack-manifest-plugin插件在打包后输出的资源表,对资源进行页面的自由配置

好比,须要引入spa-vendor的单页,咱们会引入manifest、vendor、spa-vendor、commons包、页面入口js(业务文件)默认引入

<!DOCTYPE html>
<html lang="en" bgc-f7f7f7>
<head>
  ...
</head>
<body>
  <div id="app"></div>
  <!-- 以注释的方式添加模板语法,addAssets方法能够注入对应模块组(按顺序) -->
  <!-- {{ 'js' | addAssets(['manifest', 'vendor', 'spa-vendor', 'commons']) }} -->
</body>
</html>
复制代码

若是仅仅是普通的只须要基于vue的,咱们会引入manifest、vendor、commons(这里的addAssets方法传入空数组默认引入这几个chunk),这样咱们在页面打开时,就不须要加载spa-vendor了,达到一个模块冗余的做用

<!DOCTYPE html>
<html lang="en" bgc-f7f7f7>
<head>
  ...
</head>
<body>
  <div id="app"></div>
  <!-- 以注释的方式添加模板语法,addAssets方法能够注入对应模块组(按顺序) -->
  <!-- {{ 'js' | addAssets([]) }} -->
</body>
</html>
复制代码

固然你能够更细致的区分你的chunk应该如何去分割,这里只是演示一下。

4、使用webpack4输出ES next语法的包

PHILIP WALTON这篇文章讲解了输出es next的原理,那时候看得我是激情澎湃,因此就本身花了时间去实现了一套

这里就讲一下思路吧,代码实现你们能够自行去看仓库代码

在打包的时候,咱们须要输出两套包

原理其实就是经过改变broswerList让babel编译出不一样语法的包

modern(es6):

legacy(es5):

构建es6语法的包后咱们须要输出es5的入口文件,为了不输出的资源表重叠的状况须要给es5的入口从新命名

其中a.js为咱们的es6构建入口,脚本建立的a-legacy.js为咱们的es5包构建入口,内容以下:

// a-legacy.js

import './a.js'
复制代码

打包时,咱们根据js入口生成对应的html文件后,根据每次打包生成的资源表选择资源进行插入

资源表:

如上图,资源表对应了本来的路径与输出后的资源路径,在输出html时根据路径去作资源匹配就能够了

输出后的html以下(modern包的manifest文件内联了):

其中支持type=module语法的浏览器就会自动加载es6语法的包,不支持则加载es5的向后兼容包

这么作其实效益是很是大的,下面引用两张PHILIP WALTON文章的图

可见输出的es6的包无论在size仍是解析速度都是优于es5语法的包的,对于移动端加速效果仍是很是大的

5、加速首屏与将h5资源打包进客户端

加速首屏要作什么?就是让内容尽快的出现啊。

在以前咱们已经经过模块分割和输出es next包让咱们的资源利用率和大小,还有代码解析的速度都获得了提高了,接下来咱们应该要考虑一下如何利用客户端的能力进行优化了。

加速首屏,无非就是加快webview的启动速度,和减小包的下载时间嘛,由于首屏的速度很大一部分缘由是由于资源下载致使页面阻塞。

设想一下,若是webview能够拦截咱们的资源请求,那咱们是否是就能够把咱们页面的js与css等静态资源一块儿打包到客户端中,在客户端开启webview后,经过拦截url,对本地资源进行url的匹配,命中则读取本地文件,文件过时则再次从服务器上拉取,甚至可让服务端作一个推送服务更新资源文件,能作到这样h5页面在客户端基本能达到秒开了,也能减轻服务器的压力,在弱网的状况下优化很是明显。

固然首屏你还能够经过构建预渲染一部分html到html文件中,个人另外一篇文章中有讲--如何在webpack中作预渲染下降首屏空白时间,这里就再也不说了

6、总结

Hybird h5的工程搭建,其实更可能是根据需求去作的,使用webpack只是一种方式,更重要的我以为是对资源加载、缓存的理解和运用。不说了,不说了,该时候搬砖了。有什么疑问或者建议欢迎提出。

相关文章
相关标签/搜索