Webpack系列-第一篇基本杂记

系列文章

Webpack系列-第一篇基础杂记
Webpack系列-第二篇插件机制杂记
Webpack系列-第三篇流程杂记

媒介

公司的前端项目基础都是用Webpack来做工程化的,而Webpack虽然只是一个东西,但内部涉及到异常多的学问,之前一向靠CV来处理题目,之知其然不知其所以然,愿望此次能整顿一下相干的学问点。

简介

这是webpack官方的首页图

《Webpack系列-第一篇基本杂记》

本质上,webpack 是一个当代 JavaScript 运用递次的静态模块打包器(module bundler)。当 webpack 处置惩罚运用递次时,它会递归地构建一个依靠关联图(dependency graph),个中包含运用递次须要的每一个模块,然后将一切这些模块打包成一个或多个 bundle。

那末打个比方就是我们搭建一个项目比方搭建一个屋子,我们把所须要的材料(js文件、图片等)交给webpack,末了webpack会帮我们做好统统,并把屋子(即bundle)输出。

webpack中有几个观点须要记着

entry(进口)

进口出发点(entry point)等于webpack经由历程该出发点找到本次项目所直接或间接依靠的资本(模块、库等),并对其举行处置惩罚,末了输出到bundle中。进口文件由用户自定义,可所以一个或很多个,每一个entry末了对应一个bundle。

output(出口)

经由历程设置output属性可以通知webpack将bundle定名并输出到对应的位置。

loader

webpack中心,webpack自身只能辨认js文件,关于非js文件,即须要loader转换为js文件。换句话说,,Loader就是资本转换器。由于在webpack里,一切的资本都是模块,差别资本都终究转化成js去处置惩罚。针对差别情势的资本采纳差别的Loader去编译,这就是Loader的意义。

插件(plugin)

webpack中心,loader处置惩罚非js文件,那末插件可以有更普遍的用途。悉数webpack实在就是种种的插件构成的,插件的局限包含,从打包优化和紧缩,一向到从新定义环境中的变量。插件接口功用极为壮大,可以用来处置惩罚林林总总的使命。

Chunk

被entry所依靠的分外的代码块,一样可以包含一个或很多个文件。chunk也就是一个个的js文件,在异步加载中用途很大。chunk现实上就是webpack打包后的产品,假如你不想末了天生一个包含一切的bundle,那末可以天生一个个chunk,并经由历程按需加载引入。同时它还能经由历程插件提取大众依靠天生大众chunk,防止多个bundle中有多个雷同的依靠代码。

设置

webpack的相干设置语法官方文档比较细致,这里就不赘述了。
指南
设置

实践&优化

url-loader & image-webpack-loader

url-loader 可以在文件大小(单元 byte)低于指定的限定,将文件转换为DataURL,这在现实开辟中异常有效,可以削减请求数,在vue-cli和create-react-app中也都能看到对这个loader的运用。

// "url" loader works just like "file" loader but it also embeds
          // assets smaller than specified size as data URLs to avoid requests.
          {
            test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
            loader: require.resolve('url-loader'),
            options: {
              limit: 10000,
              name: 'static/media/[name].[hash:8].[ext]',
            },
          },

image-webpack-loader 这是一个可以经由历程设置质量参数来紧缩图片的插件,但个人以为在现实开辟中并不会常常运用,图片平常是UI供应,平常来说,他们是不会赞同图片的质量有题目。

资本私有化

以这类体式格局加载资本,你可以以更直观的体式格局将模块和资本组合在一起。无需依靠于含有悉数资本的 /assets 目次,而是将资本与代码组合在一起。比方,相似如许的构造会异常有效

- |- /assets
+ |– /components
+ |  |– /my-component
+ |  |  |– index.jsx
+ |  |  |– index.css
+ |  |  |– icon.svg
+ |  |  |– img.png

固然,这类挑选见仁见智

Tree-Shaking

前端中的tree-shaking就是将一些无关的代码删掉不打包。在Webpack项目中,我们一般会援用很多文件,但现实上我们只援用了个中的某些模块,但却须要引入悉数文件举行打包,会致使我们的打包结果变得很大,经由历程tree-shaking将没有运用的模块摇掉,如许来到达删除无用代码的目标。

Tree-Shaking的道理可以参考这篇文章

归纳起来就是

1.ES6的模块引入是静态剖析的,故而可以在编译时准确推断究竟加载了什么代码。

2.剖析递次流,推断哪些变量未被运用、援用,进而删除此代码

Tree-Shaking不起作用,代码没有被删?

归纳起来就是

由于Babel的转译,使得援用包的代码有了副作用,而副作用会致使Tree-Shaking失效。

Webpack 4 默许启用了 Tree Shaking。对副作用举行了消弭,以下是我在4.19.1的试验

index.js
import { cube } from './math.js'

console.log(cube(5))

math.js

// 不打包square
export class square {
  constructor() {
    console.log('square')
  }
}


export class cube {
  constructor(x) {
    return x * x * x
  }
}
// babel编译后 同不打包
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.cube = cube;

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var square = exports.square = function square() {
  _classCallCheck(this, square);

  console.log('square');
};

function cube(x) {
  console.log('cube');
  return x * x * x;
}
// 不打包
export function square(x) {
  console.log('square')
  return x.a
}

export function cube (x) {
  return x * x * x
}
// wow 被打包
export function square() {
  console.log('square')
  return x.a
}

square({a: 1})

export function cube () {
  return x * x * x
}

sourcemap

简单说,Source map就是一个信息文件,内里储存着位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。

有了它,失足的时刻,除错东西将直接显现原始代码,而不是转换后的代码。这无疑给开辟者带来了很大轻易。

webpack中的devtool设置项可以设置sourcemap,可以参考官方文档然则,devtool的很多选项都讲的不是很清晰,这里引荐该文章,讲的比较细致

要注重,防止在生产中运用 inline- 和 eval-,由于它们可以增添 bundle 大小,并下降团体机能。

模块热替代

热替代这一块如今大多数都是用的webpack-dev-middleware插件合营服务器运用的,而官方供应的watch形式反而比较罕用,固然,webpack-dev-middleware的底层监听watch mode,至于为何不直接运用watch形式,则是webpack-dev-middleware疾速编译,走内存;只依靠webpack的watch mode来监听文件变动,自动打包,每次变动,都将新文件打包到当地,就会很慢。

DefinePlugin

webpack.DefinePlugin 定义环境变量process.env,这在现实开辟中比较经常使用,参考create-react-app中的代码以下:

// Makes some environment variables available to the JS code, for example:
    // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
    new webpack.DefinePlugin(env.stringified),

不过,要注重不能在config中运用,由于

process.env.NODE_ENV === 'production' ? '[name].[hash].bundle.js' : '[name].bundle.js'

NODE_ENV is set in the compiled code, not in the webpack.config.js file. You should not use enviroment variables in your configuration. Pass options via
--env.option abc and export a function from the webpack.config.js.

大抵意义就是NODE_ENV是设置在compiled内里,而不是config文件里。

ExtractTextWebpackPlugin

ExtractTextWebpackPlugin,将css抽取成零丁文件,可以经由历程这类体式格局合营后端对CSS文件举行缓存。

SplitChunksPlugin

webpack4的代码支解插件。
webpack4中支撑了零设置的特征,同时对块打包也做了优化,CommonsChunkPlugin已被移除了,如今是运用optimization.splitChunks替代。

SplitChunksPlugin的设置有几个须要比较关注一下
chunks: async | initial | all

  • async: 默许值, 将按需援用的模块打包
  • initial: 离开优化打包异步和非异步模块
  • all: all会把异步和非异步同时举行优化打包。也就是说moduleA在indexA中异步引入,indexB中同步引入,initial下moduleA会出如今两个打包块中,而all只会涌现一个。

cacheGroups
运用cacheGroups可以自定义设置打包块。

更多细致内容参考该文章

动态引入

则是运用动态引入的文件打包成另一个包,并懒加载它。其与SplitChunksPlugin的cacheGroups区分:

  • Bundle splitting:现实上就是建立多个更小的文件,并行加载,以取得更好的缓存结果;重要的作用就是使浏览器并行下载,进步下载速率。而且运用浏览器缓存,只要代码被修正,文件名中的哈希值转变了才会去再次加载。
  • Code splitting:只加载用户最须要的部份,其他的代码都顺从懒加载的战略;重要的作用就是加速页面加载速率,不加载不必要加载的东西。

参考代码:

+ import _ from 'lodash';
+
+ function component() {
    var element = document.createElement('div');
+   var button = document.createElement('button');
+   var br = document.createElement('br');

+   button.innerHTML = 'Click me and look at the console!';
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+   element.appendChild(br);
+   element.appendChild(button);
+
+   // Note that because a network request is involved, some indication
+   // of loading would need to be shown in a production-level site/app.
+   button.onclick = e => import(/* webpackChunkName: "print" */ './print').then(module => {
+     var print = module.default;
+
+     print();
+   });

    return element;
  }

+ document.body.appendChild(component());

注重当挪用 ES6 模块的 import() 要领(引入模块)时,必需指向模块的 .default 值,由于它才是 promise 被处置惩罚后返回的现实的 module 对象。

缓存runtimeChunk

由于webpack会把运转时期码放到末了的一个bundle中, 所以纵然我们修正了其他文件的代码,末了的一个bundle的hash也会转变,runtimeChunk是把运转时期码零丁提取出来的设置。如许就有利于我们和后端合营缓存文件。

设置项

  • single: 一切进口同享一个天生的runtimeChunk
  • true/mutiple: 每一个进口天生一个零丁的runtimeChunk

模块标识符

有时刻我们只是增加了个文件print.js, 并在index引入

import Print from './print'

打包的时刻,希冀只要runtime和main两个bundle的hash发作转变,然则一般一切bundle都发作了变化,由于每一个 module.id 会基于默许的剖析递次(resolve order)举行增量。也就是说,当剖析递次发作变化,ID 也会随之转变。
可以运用两个插件来处理这个题目。第一个插件是 NamedModulesPlugin,将运用模块的途径,而不是数字标识符。虽然此插件有助于在开辟历程当中输出结果的可读性,然则执行时间会长一些。第二个挑选是运用 HashedModuleIdsPlugin。

参考文章

ProvidePlugin

经由历程ProvidePlugin处置惩罚全局变量
其他更细粒度的处置惩罚

polyfills的处置惩罚

起首相识一下polyfills, 虽然在webpack中可以运用es6es7等的API,但并不代表编译器支撑这些API,所以一般我们会用polyfills来自定义一个API。
那末在webpack中,平常是运用babel-polyfill VS babel-runtime VS babel-preset-env等来支撑这些API,而这三种怎样挑选也是一个题目。
在真正进入主题之前,我们先看一个preset-env的设置项,同时也是package.json中的一个设置项browserslist

{
  "browserslist": [
    "last 1 version",
    "> 1%",
    "maintained node versions",
    "not dead"
  ]
}

依据这个设置,preset-env或许postcss等会依据你的参数支撑差别的polyfills,详细的参数设置参考该文章
别的引荐一个网站,可以看种种浏览器的运用状况。

  • babel-polyfill 只须要引入一次,但会重写一些原生的已支撑的要领,而且体积很大。
  • transform-runtime 是运用 plugin 自动辨认并替代代码中的新特征,你不须要再引入,只须要装好 babel-runtime 和 配好 plugin 就可以了。优点是按需替代,检测到你须要哪一个,就引入哪一个 polyfill,值得注重的是,instance 上新增加的一些要领,babel-plugin-transform-runtime 是没有做处置惩罚的,比方 数组的 includes, filter, fill 等
  • babel-preset-env 依据当前的运转环境,自动肯定你须要的 plugins 和 polyfills。经由历程各个 es规范 feature 在差别浏览器以及 node 版本的支撑状况,再去保护一个 feature 跟 plugins 之间的映照关联,终究肯定须要的 plugins。

参考文章

后编译

一样平常我们援用的Npm包都是编译好的,如许带来的轻易的同时也暴露了一些题目。

代码冗余:平常来说,这些 NPM 包也是基于 ES2015+ 开辟的,每一个包都须要经由 babel 编译宣布后才被主运用运用,而这个编译历程往往会附加很多“编译代码”;每一个包都邑有一些雷同的编译代码,这就形成大批代码的冗余,而且这部份冗余代码是不能经由历程 Tree Shaking 等手艺去撤除的。

非必要的依靠:斟酌到组件库的场景,一般我们为了轻易一股脑引入了一切组件;但现实状况下关于一个运用而言能够只是用到了部份组件,此时假如悉数引入,也会形成代码冗余。

所以我们本身的公司组件可以采纳后编译的情势,即宣布的是未经编译的npm包,在项目构建时才编译,我们公司采纳的也是这类做法,由于我们的包都在一个目次下,所以不必斟酌递归编译的题目。

更多的细致请直接参考该文章

设置环境变量

这个比较简单,直接看代码或许官方文档即可

webpack --env.NODE_ENV=local --env.production --progress

其他插件

插件总结归类

总结

Webpack自身并不难于明白,难的是林林总总的设置和四周生态带来的庞杂,然则也是这类庞杂给我们带来了极高的便利性,明白这些有助于在搭建项目更好的优化。后面会继承写出两篇总结,分别是webpack的内部道理流程和webpack的插件开辟道理。

    原文作者:菜的黑人牙膏
    原文地址: https://segmentfault.com/a/1190000018224679
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞