看懂前端脚手架你需要这篇WEBPACK

本文转载自网络。转载编辑过程中,可能有遗漏或错误,请以原文为准。

原文作者:二口南洋

原文链接: https://gold.xitu.io/post/586ddb8ab123db005d0b65cb

Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。
Webpack 官网
Webpack 中文指南

分割WEBPACK配置文件的多种方法

将你的配置信息写到多个分散的文件中去,然后在执行webpack的时候利用--config参数指定要加载的配置文件,配置文件利用module imports导出。你可以在webpack/react-starter看到是使用这种发方法的。

//配置文件
|-- webpack-dev-server.config.js
|-- webpack-hot-dev-server.config.js
|-- webpack-production.config.js
|-- webpack.config.js
//npm 命令
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev-server": "webpack-dev-server --config webpack-dev-server.config.js --progress --colors --port 2992 --inline",
    "hot-dev-server": "webpack-dev-server --config webpack-hot-dev-server.config.js --hot --progress --colors --port 2992 --inline",
    "build": "webpack --config webpack-production.config.js --progress --profile --colors"
  },

调用第三方的webpack工具,使用其集成的api,方便进行webpack配置。HenrikJoreteg/hjs-webpack 这个repo就是这么做的。

var getConfig = require('hjs-webpack')
module.exports = getConfig({
  // entry point for the app
  in: 'src/app.js',

  // Name or full path of output directory
  // commonly named `www` or `public`. This
  // is where your fully static site should
  // end up for simple deployment.
  out: 'public',

  // This will destroy and re-create your
  // `out` folder before building so you always
  // get a fresh folder. Usually you want this
  // but since it's destructive we make it
  // false by default
  clearBeforeBuild: true
})

三 Scalable webpack configurations

ones that can be reused and combined with other partial configurations

在单个配置文件中维护配置,但是区分好条件分支。调用不同的npm命令时候设置不同的环境变量,然后在分支中匹配,返回我们需要的配置文件。
这样做的好处可以在一个文件中管理不同npm操作的逻辑,并且可以共用相同的配置。webpack-merge这个模块可以起到合并配置的作用。

const parts = require('./webpack-config/parts');
switch(process.env.npm_lifecycle_event) {
  case 'build':
    config = merge(common,
      parts.clean(PATHS.build),
      parts.setupSourceMapForBuild(),
      parts.setupCSS(PATHS.app),
      parts.extractBundle({
        name: 'vendor',
        entries: ['react', 'vue', 'vuex']
      }),
      parts.setFreeVariable('process.env.NODE_ENV', 'production'),
      parts.minify()
      );
    break;
  default:
    config = merge(common,
      parts.setupSourceMapForDev(),
      parts.devServer(),
      parts.setupCSS(PATHS.app));
}
// minify example
exports.minify = function () {
  return {
    plugins: [
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false,
          drop_console: true
        },
        comments: false,
        beautify: false
      })
    ]
  }
}

开发环境下的自动刷新

webpack-dev-server

webpack-dev-server在webpack的watch基础上开启服务器。
webpack-dev-server是运行在内存中的开发服务器,支持高级webpack特性hot module replacement。这对于react vue这种组件化开发是很方便的。
使用webpack-dev-server命令开启服务器,配合HMR及可以实现代码更改浏览器局部刷新的能力。

hot module replacement

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.

当应用在运行期间hmr机制能够修改、添加、或者移除相应的模块,而不使整个页面刷新。

hmr机制适用于单页应用。
要实现hmr机制,需要配合webpack-dev-server服务器,这个服务器本身就实现了监察watch文件改动的能力,再开启HMR选项,就添加了watch模块变化的能力。这是HMR机制能生效的基础。

从webpack编译器角度

每次修改一个模块的时候,webpack会生成两部分,一个是manifest.json,另一部分是关于这次模块更新编译完成的chunksmanifest.json中存着的是chunk更改前后的hash值。
从编译器webpack的角度来讲提供了hmr的原材料。供后续使用。

从模块的角度

模块发生变化时,webpack会生成之前讲过的两部分基础文件,但是何时将变化后的模块应用到app中去?这里就需要在应用代码中编写handler去接受到模块变化信息。但是不能在所有模块中编写handler吧?这里就用到了消息冒泡机制。

webpack
如图A.js、C.js没有相关hmr代码,B.js有相关hmr代码,如果c模块发生了变化,c模块没有hmr,那么就会冒泡到a、b模块。b模块捕捉到了消息,hmr运行时会相应的执行一些操作,而a.js捕捉不到信息,会冒泡到entry.js,而一旦有消息冒泡的入口块,这就代表本次hmr失败了,hmr会降级进行整个页面的reload。

从HMR运行时的角度

HMR运行时是一些相关的操作api,运行时支持两个方法: check、apply
check发起 HTTP 请求去获取更新的 manifest,以及一些更新过后的chunk。

webpack

环境变量的设置

var env = {
    'process.env.NODE_ENV': '"production"'
}
new webpack.DefinePlugin(env)

注意这里单引号间多了个双引号 why?

以及webpack.DefinePlugin插件的原理?

开发的时候会想写很多只在开发环境出现的代码,比如接口mock等,在build命令后这些代码不会存在。
这对框架或者插件、组件的开发是很有帮助的。vue,react等都会这么做。可以在这些框架的dev模式提供很多有用的提示信息。

打包文件分割

为何要进行打包文件分割?

对于一个单页应用项目来说,有分为业务代码和第三方代码,业务代码会频繁改动,而第三方代码一般来讲变动的次数较少,如果每次修改业务代码都需要用户将整个js文件都重新下载一遍,对于加载性能来讲是不可取的,所以一般而言我们会将代码分为业务代码和第三方代码分别进行打包,虽然多了一个请求的文件,增加了一些网络开销,但是相比于浏览器能将文件进行缓存而言,这些开销是微不足道的。

我们在entry中定义了app入口,相应的业务逻辑都封装在这个入口文件里,如果我们想要第三方代码独立出来,就要再增加一个入口,我们习惯使用vendor这个命名。

// app.js
require('vue');
require('vuex');

// webpack.config.js
entry: {
    app: 'app/app.js',
    vendor: ['vue', 'vuex'],
  },

vendor入口的传参是以一个数组的形式传递的,这是一种非常方便的注入多个依赖的方式,并且能把多个依赖一起打包到一个chunk中。而且不用手动的创建真实存在的入口文件。
这相当于:

// vendor.js
require('vue');
require('vuex');

// app.js
require('vue');
require('vuex');

// webpack.config.js
entry: {
    app: 'app/app.js',
    vendor: 'app/vendor.js',
  },

但是这样做只是声明了一个vendor入口而已,对于app这个入口来说,打包完成的文件还是会有vuevuex依赖,而新增的入口vendor打包完成的文件也有了vuevuex两个依赖。模块依赖关系如下图所示。

webpack
这里的A可以代表vue依赖,最后生成的打包文件是两个平行关系的文件,且都包含vue的依赖。
此时需要引入CommonsChunkPlugin插件

This is a pretty complex plugin. It fundamentally allows us to extract all the common modules from different bundles and add them to the common bundle. If a common bundle does not exist, then it creates a new one.

这是个相当复杂的插件,他的基础功能是允许我们从不同的打包文件中抽离出相同的模块,然后将这些模块加到公共打包文件中。如果公共打包文件不存在,则新增一个。同时这个插件也会将运行时(runtime)转移到公共chunk打包文件中

webpack

plugins: [
  new webpack.optimize.CommonsChunkPlugin({
    names: ['vendor', 'manifest']
  })
]

这里的name可以选择已经存在的块,这里就选择了vendor块,因为我们本来就是将vendor块当做管理第三方代码的入口的。

而names传入一个数组,数组里包含两个trunk name,表示CommonsChunkPlugin插件会执行两次这个方法,第一次将公共的第三方代码抽离移到vendor的块中,这个过程之前也讲过会将运行时runtime也转移到vendor块中,第二次执行则是将运行时runtime抽离出来转移到manifest块中。这步操作解决了缓存问题。

这样处理,最后会生成3个打包文件chunkapp.js是业务代码,vendor则是公共的第三方代码,manifest.js则是运行时。

CHUNK TYPE 块的类型大揭秘

webpack1.0官网介绍中的chunk类型读起来及其拗口chunk type, 所以我这里解读一下。

chunk是webpack中最基本的概念之一,且chunk常常会和entry弄混淆。在「打包文件分割部分」我们定义了两个入口entry point -- appvendor,而通过一些配置,webpack会生成最后的一些打包文件,在这个例子中最后生成的文件有``app.js 、 vendor.js 、 manifest.js。这些文件便被称为块chunk。

entry & chunk 可以简单的理解为一个入口、一个出口

在官方1.0文档中webpack的chunk类型分为三种:

  • entry chunk 入口块
  • normal chunk 普通块
  • initial chunk 初始块
  • entry chunk 入口块

entry chunk 入口块不能由字面意思理解为由入口文件编译得到的文件,由官网介绍

An entry chunk contains the runtime plus a bunch of modules

可以理解为包含runtime运行时的块可以称为entry chunk,一旦原本存在运行时(runtime)的entry chunk失去了运行时,这个块就会转而变成initial chunk

normal chunk 普通块

A normal chunk contains no runtime. It only contains a bunch of modules.

普通块不包含运行时runtime,只包含一系列模块。但是在应用运行时,普通块可以动态的进行加载。通常会以jsonp的包装方式进行加载。而code splitting主要使用的就是普通块。

initial chunk 初始块

An initial chunk is a normal chunk.

官方对initial chunk的定义非常简单,初始块就是普通块,跟普通块相同的是同样不包含运行时runtime,不同的是初始块是计算在初始加载过程时间内的。在介绍入口块entry chunk的时候也介绍过,一旦入口块失去了运行时,就会变成初始块。这个转变经常由CommonsChunkPlugin插件实现。

例子解释

还是拿「打包文件分割」的代码做例子,

// app.js
require('vue');
require('vuex');

// webpack.config.js
entry: {
    app: 'app/app.js',
    vendor: ['vue', 'vuex'],
  },

没有使用CommonsChunkPlugin插件之前,两个entry分别被打包成两个chunk,而这两个chunk每个都包含了运行时,此时被称为entry chunk入口块。

而一旦使用了CommonsChunkPlugin插件,运行时runtime最终被转移到了manifest.js文件,此时最终打包生成的三个chunkapp.js 、 vendor.js 、 manifest.js,app.js、vendor.js失去了runtime就由入口块变成初始块。

code splitting

前文有讲到将依赖分割开来有助于浏览器缓存,提高用户加载速度,但是当业务复杂度增加,代码量大始终是一个问题。这时候就需要normal chunk普通块的动态加载能力了。

It allows you to split your code into various bundles which you can then load on demand — like when a user navigates to a matching route, or on an event from the user.
code splitting 允许我们将代码分割到可以按需加载的不同的打包文件中,当用户导航到对应的路由上时,或者是用户触发一个事件时,异步加载相应的代码。

我们需要在业务逻辑中手动添加一些分割点,标明此处事件逻辑之后进行代码块的异步加载。

// test
window.addEventListener('click', function () {
  require.ensure(['vue', 'vuex'], function (require) {

  })
})

这段代码表明当用户点击时,异步请求一个js文件,这个文件中包含该有vue vuex的依赖。

webpack
打包后会根据手动分割点的信息生成一个打包文件,就是图中第一行0开头的文件。这个文件也就是异步加载的文件。

下面是之前的一个vue项目,采用code splitting将几个路由抽离出来异步加载之后,文件由212kb减少到了137kb,同样样式文件也由58kb减少到了7kb。对于首屏渲染来说,性能是会增加不少的。

webpack
webpack

参考

原文链接

https://gold.xitu.io/post/586ddb8ab123db005d0b65cb

时间: 2024-08-17 16:59:32

看懂前端脚手架你需要这篇WEBPACK的相关文章

先让自己看懂 才能让用户看懂

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 不管是发表在自己网站上的文章,还是发布到各大论坛的软文,都要先过自己这一关,自己先把写好的文章看一遍,自己能够看懂再去发布,这样的文章才能让用户看懂.一篇文章不一定要表达多么深刻的含义,但一定要让用户知道你要表达的意思,再好的东西,被你写的乱七八糟,也没有人愿意去看. 在很多软文中,这一点被表现的淋漓尽致,一些文章的标题非常有诱惑力,让人忍不

安防人 这篇文章你能看懂多少?

安防人 这篇文章你能看懂多少? 在互联网技术高速发展的今天,因网络的开放性.隐蔽性.跨地域性等特性,许多安全问题亟待解决,比如在过去的2015年,全球便发生了多起网络安全事件,如美国人事管理局OPM数据泄露,规模达2570万,直接导致主管引咎辞职:英宽带运营商TalkTalk被反复攻击,400余万用户隐私数据终泄露:摩根士丹利35万客户信息涉嫌被员工盗取:日本养老金系统遭网络攻击,上百万份个人信息泄露等. 虽然这些数据我们时常耳闻,但安防行业人士仍然会认为网络安全问题距离我们仍很遥远甚至无关,但

看懂SqlServer查询计划

原文:看懂SqlServer查询计划 对于SQL Server的优化来说,优化查询可能是很常见的事情.由于数据库的优化,本身也是一个涉及面比较的广的话题, 因此本文只谈优化查询时如何看懂SQL Server查询计划.毕竟我对SQL Server的认识有限,如有错误,也恳请您在发现后及时批评指正. 首先,打开[SQL Server Management Studio],输入一个查询语句看看SQL Server是如何显示查询计划的吧. 说明:本文所演示的数据库,是我为一个演示程序专用准备的数据库,

六张图看懂 Amazon Go智能购物,专利文件解密AI 核心技术细节

亚马逊Go推广视频:1分钟,颠覆你对线下实体购物的认知 几天前,就在亚马逊迎来可能是公司史上最大的 Holiday Season时,亚马逊发布了一段介绍旗下新的零售商店 Amazon Go 的视频,消费者可以直接走进商店,拿下货架上的货物,然后离开.整个过程不需要排队,也不用结账. <福布斯>的记者 Ryan Mac 说:"这看起来非常有趣,充满了未来感,但是,至少目前为止,这一切还都只是宣传." 每年从感恩节到圣诞节的长假中,亚马逊都会成为媒体上的最大赢家.2013年,B

一张图看懂阿里云网络产品[一]网络产品概览

 一张图看懂网络产品系列文章,让用户用最少的时间了解网络产品,本文章是第一篇 网络产品概览 系列文章持续更新中,敬请关注 [一]网络产品概览 [二]VPC [三]EIP [四]NAT网关 [五]负载均衡SLB [六]共享带宽 [七]共享流量包 [八]高速通道 [九]VPN网关 [十]全球加速 [十一]云托付 目前阿里云网络产品共有10个,包括专有网络VPC,负载均衡SLB,NAT网关,EIP,共享流量包,共享带宽,高速通道,VPN网关,全球加速.这么多产品有什么关联呢?为了便于大家理解,我们可以

深入浅出看懂AlphaGo Zero - PaperWeekly 第51期

AlphaGo Zero = 启发式搜索 + 强化学习 + 深度神经网络,你中有我,我中有你,互相对抗,不断自我进化.使用深度神经网络的训练作为策略改善,蒙特卡洛搜索树作为策略评价的强化学习算法. 1. 论文正文内容详细解析 先上干货论文:Mastering the Game of Go without Human Knowledge [1],之后会主要以翻译论文为主,在语言上尽量易懂,避免翻译腔. AlphaGo Zero,从本质上来说完全不同于打败樊麾和李世石的版本. 算法上,自对弈强化学习

十分钟看懂图像语义分割技术

大多数人接触 "语义" 都是在和文字相关的领域,或语音识别,期望机器能够识别你发出去的消息或简短的语音,然后给予你适当的反馈和回复.嗯,看到这里你应该已经猜到了,图像领域也是存在 "语义" 的. 今天是 AI 大热年,很多人都关注与机器人的语音交互,可是有没有想过,将来的机器人如果不能通过图像来识别主人,家里的物品.宠物,那该多没意思.说近一些,假如扫地机器人能够机智地绕开你丢在地上的臭袜子而扫走旁边的纸屑,一定能为你的生活解决不少麻烦. 没错,图像语义分割是 AI

看懂UML类图和时序图(转)

这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图所表达的含义和最终的代码对应起来: 有了这些知识,看后面章节的设计模式结构图就没有什么问题了: 本章所有图形使用Enterprise Architect 9.2来画,所有示例详见根目录下的design_patterns.EAP 从一个示例开始 请看以下这个类图,类之间的关系是我们需要关注的: _images/uml_cla

让冰冷的机器看懂这个多彩的世界

从茹毛饮血的蛮荒,到钢筋水泥的城市,人类逐步将自身能力投射到计算机上.无论是计算能力,还是记忆能力,计算机的如今表现都堪称卓越.但仅拥有这些还远远不够,我们期待计算机可以做得更多.一部风靡全球<星际穿越>激起了无数人对探索浩瀚宇宙奥秘的渴望,也让许多人记住了Tars这个聪明可爱.幽默风趣的智能机器人.人工智能主题的好莱坞电影一直广受影迷们的喜爱,人类用无尽的想象力和炫目的特技构筑了一个又一个无比精彩的未来世界,令人如痴如醉.不过,回到现实,计算机科学家们的行动力却看似远远赶不上电影艺术家们的想