webpack 从入门到放弃(一)

什么是 webpack,为何要使用 webpack

什么是 webpack

官网给出的概念是:javascript

本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序须要的每一个模块,而后将全部这些模块打包成一个或多个 bundle。 根据官网最直观最出名的那个图咱们能够知道,webpack 能够打包/脚本/图片/样式/表 从图中咱们能够看出左边有依赖关系的模块(MODULES WITH DEPENDENCIES)经过 webpack 打包成了各类静态资源(STATIC ASSETS)css

为何要使用 webpack

经过上面的概念,你是否是已经大概知道了 webpack 是干什么的,那么问题来了,为何要使用 webpack 呢? 这就说来话长了,那就长话短说,emmmmhtml

为何使用 webpack,这应该是和前端的发展有关系的,我认为,webpack 是前端发展到必定阶段的必然产物(貌似是一句废话)。 由于计算机网络的飞速发展,致使 web 前端也在迅猛发展。最初的实践方案已经不能知足咱们的需求,因而,愈来愈多的新技术新思想新框架孕育而生,好比:前端

前端模块化

随着前端项目的复杂度愈来愈高,相互以前的依赖愈来愈多,以及为了更好的复用代码,前端也须要用模块化的思想来组织代码。java

首先咱们要明白模块化解决了前端的哪些痛点:node

  • 命名冲突
  • 文件依赖(js 加载顺序)
  • 代码复用

咱们这里说的模块和 Java 的 package 的概念是相似的。逻辑上相关的代码放在一个包中,每个包都是相互独立的,不用担忧命名冲突的问题,若是其余人想要用这部分功能,直接 import 导入包就好react

因此前端代码模块化的实现,会帮咱们解决命名冲突和代码复用的问题,那么文件依赖要怎么处理呢?这就用到了咱们的 webpack,稍后再作介绍。webpack

因此有了模块,咱们就能够方便的复用他人的代码,那么问题来了,无规矩不成方圆,咱们在使用他人代码的时候确定是要遵循某种规范,因此就出现了 CommonJS、AMD 和 终极模块化方案 —— ES6 模块,这些都是前端模块化的规范。web

咱们来简单了解一下:正则表达式

CommonJS

node.js 采用的就是 CommonJS 规范,使用 require 的方法同步加载依赖,一个文件就是一个模块,导入导出格式以下:

// 导入
const moduleA = require('./moduleA');

// 导出
module.exports = moduleA.someFunc;
复制代码

缺点是加载的模块是同步的,只有加载完才能执行后面的操做。由于 node.js 的模块文件通常存在于本地硬盘,因此通常不会出现这个问题,可是在浏览器环境该规范就不那么适用了。

AMD

缘由如上,由于 CommonJS 不适用于浏览器环境,因此出现了 AMD 规范。该规范异步加载依赖,能够再声明的时候指定须要加载的依赖,而且须要当作参数传入,对于依赖的模块提早执行,依赖前置。

写法以下:

define("module", ["dep1", "dep2"], function(d1, d2) {
  return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });
复制代码

ES6 模块化

ES6 直接在语言层面上实现了模块化。咱们如今用的最多的就是 ES6 模块化的实践方式。

写法以下:

// 导入
import { readFile } from 'fs';
import React from 'react';
// 导出
export function hello() {};
export default {
  // ...
};
复制代码

样式模块化

如今愈来愈多人也开始使用模块化的思想写样式。好比如今大部分的 CSS 预编译器都支持 @import 的写法。将一些公用样式放在一个文件中,在其余文件中导入。


三大框架的出现,使咱们不须要像传统的 JQ 同样操做 DOM,将精力集中在对数据的处理上。

以及 ES6/7/8 和 TS 的使用越来普及,无疑使咱们的开发效率提升了不少。

可是出现的问题是:这些新兴的技术并非在全部的浏览器上都适用,都须要将源代码转化为能够直接在浏览器上运行的代码

因此,webpack 就解决了这个问题。

Gulp/Grunt、Rollup 和 webpack 的比较

Gulp/Grunt

其实,Gulp/Gunt 和 webpack 应该是没有可比性的,可是他们均可以称为前端自动化构建工具(让咱们再也不作机械重复的事情,解放咱们的双手)

可是 Gulp/Gunt 和 webpack 确实干的不是一件事

Gulp 本质是 task runner,Webpack 是 module bundler

我认为 Gulp 正如他的定义同样:基于流的自动化构建工具,定义每个任务,而后自动将一个个任务执行。

而 webpack 是模块化地组织,模块化地依赖,而后模块化地打包。相对来上,场景局限在前端模块化打包上。

推荐知乎 寸志 大佬的回答:gulp 有哪些功能是 webpack 不能替代的?

Rollup

Rollup 是一个和 Webpack 很相似但专一于 ES6 的模块打包工具。 Rollup 的亮点在于能针对 ES6 源码进行 Tree Shaking 以去除那些已被定义但没被使用的代码,以及 Scope Hoisting 以减少输出文件大小提高运行性能。 然而 Rollup 的这些亮点随后就被 Webpack 模仿和实现。 因为 Rollup 的使用和 Webpack 差很少,这里就不详细介绍如何使用了,而是详细说明它们的差异:

Rollup 是在 Webpack 流行后出现的替代品; Rollup 生态链还不完善,体验不如 Webpack; Rollup 功能不如 Webpack 完善,但其配置和使用更加简单; Rollup 不支持 Code Spliting,但好处是打包出来的代码中没有 Webpack 那段模块的加载、执行和缓存的代码。 Rollup 在用于打包 JavaScript 库时比 Webpack 更加有优点,由于其打包出来的代码更小更快。 但功能不够完善,不少场景都找不到现成的解决方案。

安装与使用

建立 package.json 文件

能够手动的建立,也可使用命令自动建立

npm init
复制代码

安装

webpack 能够直接使用 npm 安装,能够安装到全局,也能够安装到项目

//全局安装
npm install -g webpack
//安装到你的项目目录
npm install --save-dev webpack
复制代码

使用

推荐你们阅读这篇文章: 入门 Webpack,看这篇就够了

我就是跟着这篇文章作的

新建文件

首先新建一个文件夹,而后在终端打开该文件夹并执行来初始化你的项目,在初始过程当中,会有一些问题帮助你建立 package.json 文件。

npm init
复制代码

初始化以后咱们还要建立几个文件来存放咱们的项目文件。

建立一个 app 文件夹来存放咱们打包以前的源文件

建立一个 public 文件夹来存放一个入口文件 index.html 和经过 webpack 打包以后浏览器可直接运行的 js 文件

好比咱们在 app 文件夹下建立一个 Greeter.js 文件,里面有一个方法能够再页面显示文字信息 Hi there and greetings!

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};
复制代码

而后咱们建立一个 main.js 来引入 Greeter.js 这个文件,

浏览器的入口 index.html 文件内容以下(其中 bundle.js 是打包以后的文件):

<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>
复制代码

此时的文件路径以下

.
├── app
│   ├── Greeter.js
│   └── main.js
├── package.json
├── public
│   ├── bundle.js
│   └── index.html
复制代码

安装 webpack

初始化项目以后,要安装 webpack

npm install --save-dev webpack
复制代码

在安装完 webpack 以后,能够再 package.json 文件中看到增长了 webpack 的依赖。

配置 webpack

安装完 webpack 以后,咱们就要配置 webpack 了,首先建立配置文件 webpack.config.js 文件内容以下:

module.exports = {
  entry:  __dirname + "/app/main.js",//已屡次说起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  }
}
复制代码

而后咱们在该项目的终端输入

webpack
复制代码

就能够看到以下信息:

Hash: 4e6a6b5eb88a83b29e02
Version: webpack 4.12.0
Time: 551ms
Built at: 2018-06-24 14:53:39
    Asset      Size  Chunks             Chunk Names
bundle.js  6.82 KiB       0  [emitted]  main
[3] ./node_modules/css-loader!./app/main.css 190 bytes {0} [built]
[4] ./app/main.css 1.04 KiB {0} [built]
[5] ./app/Greeter.js 143 bytes {0} [built]
[6] ./app/main.js 119 bytes {0} [built]
    + 3 hidden modules

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
复制代码

看到这样的信息,那么恭喜你,你的第一个 webpack 项目就完成了!

打开 public 文件夹下面的 index.html,你就能够再浏览器上看到以下的效果。

Loader

loader 用于对模块的源代码进行转换。loader 可使你在 import 或"加载"模块时预处理文件。所以,loader 相似于其余构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 能够将文件从不一样的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至容许你直接在 JavaScript 模块中 import CSS文件!

由于 webpack 自己只能处理 JavaScript,若是要处理其余类型的文件,就须要使用 loader 进行转换,loader 自己就是一个函数,接受源文件为参数,返回转换的结果。

举个 🌰 —— css-loader

例如,咱们想在刚刚的页面增长样式,使文字居中显示,那么我在 app 文件夹下面新建一个 main.css 文件。在 webpack 中,全部的文件都是模块,因此要使用这个 css 文件,就必需要先引入。

引入 css 文件

因此我就在 app 文件夹下面的 main.js 中引入该 css 文件

const greeter = require('./Greeter.js');
require ('./main.css')
document.querySelector("#root").appendChild(greeter());
复制代码

从新打包

而后我从新打包一遍,蓝后,发现竟然报错了!

Hash: 179c18498fac6de89a96
Version: webpack 4.12.0
Time: 533ms
Built at: 2018-06-24 15:00:24
 1 asset
[0] ./app/Greeter.js 143 bytes {0} [built]
[1] ./app/main.js 119 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/

ERROR in ./app/main.js
Module not found: Error: Can't resolve 'style-loader' in '/Users/cherry/Workspace/webpack-demo' @ ./app/main.js 2:0-22 复制代码

根据报错信息,咱们很明显能发现是提示咱们项目缺乏 style-loader,这是由于 webpack 原生只支持解析 js 文件,要支持非 js 类型的文件,就须要使用 loader

安装 loader

因此咱们要安装 style-loadercss-loader

npm i -D style-loader css-loader
复制代码

修改配置文件

而后修改 webpack 的配置文件 webpack.config.js

module.exports = {
  entry:  __dirname + "/app/main.js",//已屡次说起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}
复制代码

配置文件增长了 module.rules 数组,该数组是一些配置规则,告诉 webpack 符合 test 的文件须要使用 use 后面的 loader 处理。因此该规则就是对全部 .css 结尾的文件使用 style-loadercss-loader 进行处理。

loader 特性

咱们来看一下 loader 有哪些特性:

  • loader 支持链式传递。可以对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  • loader 能够是同步的,也能够是异步的。
  • loader 运行在 Node.js 中,而且可以执行任何可能的操做。
  • loader 接收查询参数。用于对 loader 传递配置。
  • loader 也可以使用 options 对象进行配置。
  • 除了使用 package.json 常见的 main 属性,还能够将普通的 npm 模块导出为 loader,作法是在 package.json 里定义一个 loader 字段。
  • 插件(plugin)能够为 loader 带来更多特性。
  • loader 可以产生额外的任意文件。

使用 webpack 的三种姿式

webpack 中使用 loader 有三种姿式

经过 CLI

命令行中运行

webpack --module-bind jade  --module-bind 'css=style!css'   
复制代码

jade,style,css后面可省略-loader,他们分别对.jade使用jade-loader,对.css使用style-loader和css-loader

经过require

能够直接在源码中指定使用什么 loader 去处理文件。

require('style-loader!css-loader?minimize!./main.css')
复制代码

这样就是对 ./main.css 文件先使用 css-loader 再使用 style-loader 进行转换

使用配置文件 webpack.config.js

最经常使用的方式就是使用本文所使用的配置文件的方式

常见的 loader

常见的 loader

loader name loader des
babel-loader 加载 ES2015+ 代码,而后使用 Babel 转译为 ES5
buble-loader 使用 Bublé 加载 ES2015+ 代码,而且将代码转译为 ES5
cache-loader 在一些性能开销较大的 loader 以前添加此 loader,以将结果缓存到磁盘里。
coffee-loader CoffeeScript 转化为 JS
css-loader css-loader 是将 @importurl() 引入的 css 转换为 import/require() 的方式而后在解析他们
exports-loader 经过添加 exports[...] = ... 语句导出文件中的变量。
expose-loader expose-loader 将模块添加到全局对象上
file-loader file-loader 能够解析项目中的url引入(不只限于css),根据咱们的配置,

将图片拷贝到相应的路径,再根据咱们的配置,修改打包后文件引用路径,使之指向正确的文件。 gzip-loader | 能够加载 gzip 压缩以后的资源 html-loader | 将 html 输出为字符串,也能够根据配置进行压缩 imports-loader | imports-loader 容许使用依赖于特定全局变量的模块,这对于依赖于像 $ 这样的全局变量的第三方模块很是有用 jshint-loader | 为加载的模块使用 jshint json-loader | 因为 webpack >= v2.0.0 默认支持导入 JSON 文件。若是你使用自定义文件扩展名,你可能仍然须要使用此 loader json5-loader | 将 json5 文件解析成 js 对象 less-loader | 将 less 转化为 css null-loader | 返回一个空模块 postcss-loader | 将 postcss 转化为 css raw-loader | 加载文件原始内容(utf-8格式) sass-loader | 将 SASS/SCSS 转换为 css source-map-loader | 从现有源文件(源代码源URL)中提取源映射,方便调试 style-loader | 将 CSS 放在 <style> 标签中注入到 DOM 中 script-loader | 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不须要解析 svg-inline-loader | 将 SVG 做为模块嵌入 url-loader | 将文件加载为 Base64 编码的 URL

row 2 col 1 | row 2 col 2

Plugin

插件(plugin)是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上! 插件目的在于解决 loader 没法实现的其余事

Plugin 是用来扩展 Webpack 功能的,经过在构建流程里注入钩子实现,它给 Webpack 带来了很大的灵活性。

经过plugin(插件)webpack能够实 loader 所不能完成的复杂功能,使用 plugin 丰富的自定义 API 以及生命周期事件,能够控制 webpack 打包流程的每一个环节,实现对 webpack 的自定义功能扩展。

举个 🌰 —— ExtractTextPlugin

webpack4 已经不支持用extract-text-webpack-plugin来优化 css, 须要改为optimize-css-assets-webpack-plugin和mini-css-extract-plugin

在刚刚的例子中,咱们查看打包以后的 index.html 文件能够看到咱们刚刚写的 css 代码放在了 head 中的 style 标签中,这是 style-loader 帮咱们处理的

可是,若是你但愿打包以后 css 在单独的文件中,那么你就须要 ExtractTextPlugin 这个 plugin 了。

安装 ExtractTextPlugin

npm i -D ExtractTextPlugin
复制代码

修改配置文件

咱们须要修改配置文件:

const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
  entry:  __dirname + "/app/main.js",//已屡次说起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js",//打包后输出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        // 转换 .css 须要的 loader
        loaders: ExtractTextPlugin.extract({
          use: ['css-loader'],
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: '[name]-[contenthash:8].css'
    })
  ]
}
复制代码

而后咱们再从新打包,就能够发如今 public 文件夹下面多了一个 main-493a2c3c.css 文件,下面咱们要在 index.html 中自动引入这个 css 文件

html-webpack-plugin

html-webpack-plugin 能够根据你设置的模板,在每次运行后生成对应的模板文件,同时所依赖的 CSS/JS 也都会被引入,若是 CSS/JS 中含有 hash 值,则 html-webpack-plugin 生成的模板文件也会引入正确版本的 CSS/JS 文件。

安装 html-webpack-plugin

安装方式咱们已经很熟悉了

npm i -D html-webpack-plugin
复制代码

修改配置文件

const path = require('path')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');  

module.exports = {
  entry:  __dirname + "/app/main.js",//已屡次说起的惟一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js",//打包后输出文件的文件名
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        loaders: ExtractTextPlugin.extract({
          use: ['css-loader'],
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin({
      filename: '[name]-[contenthash:8].css'
    }),
    new HtmlWebpackPlugin(),
  ]
}
复制代码

蓝后咱们删除以前咱们在 punlic 文件夹下面 index.html 的内容,在打包一次,就会生成一个 html 模板

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  <link href="main-493a2c3c.css" rel="stylesheet"></head>
  <body>
  <script type="text/javascript" src="bundle.js"></script></body>
</html>
复制代码

new HtmlWebpackPlugin 的时候,咱们能够进行一系列的配置

new HtmlWebpackPlugin({  
      // 生成的HTML模板的title,若是模板中有设置title的名字,则会忽略这里的设置
      title: "This is the webpack config", 
      
      // 生成的模板文件的名字
      filename: "/index.html", 
      
      // 模板来源文件 
      template: "index.html", 
      
      // 引入模块的注入位置;取值有true/false/body/head 
      // true 默认值,script标签位于html文件的 body 底部
      // body script标签位于html文件的 body 底部
      // head script标签位于html文件的 head中
      // false 不插入生成的js文件,这个几乎不会用到的
      inject: "body", 
      
      // 指定页面图标; 
      favicon: "", 
      
      // 是html-webpack-plugin中集成的 html-minifier ,生成模板文件压缩配置 
      minify: {  
          caseSensitive: false, //是否大小写敏感 
          collapseBooleanAttributes: true, // 省略布尔属性的值 
          collapseWhitespace: true //删除空格 
      }, 
      
      // 是否生成hash添加在引入文件地址的末尾,相似于咱们经常使用的时间戳,避免缓存带来的麻烦
      hash: true,
      
      // 是否须要缓存,若是填写true,则文件只有在改变时才会从新生成
      cache: true,  
      
      // 是否将错误信息写在页面里,默认true,出现错误信息则会包裹在一个pre标签内添加到页面上
      
      showErrors: true,  
      
      // 引入的模块,这里指定的是entry中设置多个js时,在这里指定引入的js,若是不设置则默认所有引入
      chunks: "",  
      
      // 引入模块的排序方式
      // 默认四个选项: none auto dependency {function}
      // 'dependency' 文件的依赖关系
      // 'auto' 默认值,插件的内置的排序方式
      // 'none' 无序
      // {function} 自定义
      chunksSortMode: "auto",  
      
      // 排除的模块
      excludeChunks: "",  
      
      // 生成的模板文档中标签是否自动关闭,针对xhtml的语法,会要求标签都关闭,默认false
      xhtml: false  
    
    }),
复制代码

常见的 plugin

摘自:http://www.css88.com/doc/webpack/plugins/

Name Description
AggressiveSplittingPlugin 将原来的 chunk 分红更小的 chunk
BabiliWebpackPlugin 基于 Babel 的裁剪工具:Babili
BannerPlugin 在每一个生成的 chunk 顶部添加 banner
CommonsChunkPlugin 提取 chunks 之间共享的通用模块
ComponentWebpackPlugin 经过 webpack 使用组件
CompressionWebpackPlugin 预先准备的资源压缩版本,使用 Content-Encoding 提供访问服务
ContextReplacementPlugin 重写 require 表达式的推断上下文
DefinePlugin 容许在编译时(compile time)配置的全局常量
DllPlugin 为了极大减小构建时间,进行分离打包
EnvironmentPlugin DefinePlugin 中 process.env 键的简写方式。
ExtractTextWebpackPlugin 从 bundle 中提取文本(CSS)到单独的文件
HotModuleReplacementPlugin 启用模块热替换(Enable Hot Module Replacement - HMR)
HtmlWebpackPlugin 简单建立 HTML 文件,用于服务器访问
I18nWebpackPlugin 为 bundle 增长国际化支持
IgnorePlugin 从 bundle 中排除某些模块
LimitChunkCountPlugin 设置 chunk 的最小/最大限制,以微调和控制 chunk
LoaderOptionsPlugin 用于从 webpack 1 迁移到 webpack 2
MinChunkSizePlugin 确保 chunk 大小超过指定限制
NoEmitOnErrorsPlugin 在输出阶段时,遇到编译错误跳过
NormalModuleReplacementPlugin 替换与正则表达式匹配的资源
NpmInstallWebpackPlugin 在开发时自动安装缺乏的依赖
ProvidePlugin 没必要经过 import/require 使用模块
SourceMapDevToolPlugin 对 source map 进行更细粒度的控制
UglifyjsWebpackPlugin 能够控制项目中 UglifyJS 的版本
ZopfliWebpackPlugin 经过 node-zopfli 将资源预先压缩的版本

参考文章:

相关文章
相关标签/搜索