5

webpack :现代 JavaScript 应用程序的静态模块打包器

前言:本文描述一个小白学习webpack4从零开始的完整过程全纪录,可以说完全以一个初学者的口吻来写这篇文章的,与此同时这篇文章还会长期更新,不足之处还望大佬雅正!

大白话讲webpack

对于小白来说,看到一些专业的术语就脑壳疼
什么是webpack?
简单来讲,就是把我们写好的代码打包。
怎么个打包法,压缩代码,相同资源抽取,处理一下浏览器不识别的代码(如ES6+、TS、SASS等)。
一句话,用不到就扔掉,能压缩就压缩,能兼容就兼容

初探webpack

①、安装npm

npm是世界上最大的软件注册表,使用npm我们可以快速安装各种大小型软件和库
下载:在node.js中都自带了npm,所以我们下载node.js即可
node.js官网: https://nodejs.org/en/
下载完后,我们可以在dos命令行下可以看到node.js的版本和npm的版本 node -v npm -v
图片描述

②、建好目录架构

①、新建练习文件目录 ex1
①、在ex1目录下新建源文件目录 src
②、在src目录下新建入口文件 index.js
形成的目录结构如下:
图片描述

③、初始化package.json

package.json文件定义了这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证等元数据)。
在ex1目录下使用命令

npm init -y

init指令会询问一系列的问题,并将你的配置写成一个package.json文件。
如果使用了-f|--force|-y|--yes这些参数,那么会生成一个默认的package.json文件。
这个init过程很快,可以看到在ex1目录下多了个package.json文件
图片描述

④、安装webpack

提示:webpack4.0以上版本的webpack的脚手架webpack-cli已经被分离出来,需要另外安装。
安装webpack分为全局安装本地安装(局部安装)

全局安装(不推荐) npm install webpack webpack-cli -g
(-g就是全局安装)
本地安装 npm install webpack webpack-cli -D
(-D 就是安装到开发环境)

安装成功后如图所示:
图片描述

再来看下我们的文件目录发生了什么变化:
图片描述
node_modules文件夹主要是用于放用包管理工具下载安装了的包,这个文件的内容有很多
package-lock.json锁定了包的版本,确保能够避免包版本不同产生的问题。

⑤、使用npx进行打包

npx 会帮你执行依赖包里的二进制文件。
npx会自动找到node_modules/bin 的webpack.cmd去执行,免去了script的配置

在src的目录下新建一个名为 demo1.js的文件

demo1.js 中写入如下内容

module.exports = '这是common.js的语法,在node端运行,把我用webpack打包之后,就可以在浏览器上运行了'

index.js中写入如下内容

let d1 = require('./demo1.js');
console.log(d1);  

使用命令进行打包

npx webpack

结果如图:
图片描述

打包后我们可以发现在ex1目录下多了个 dist 文件夹
图片描述
这个dist 文件夹中有个 main.js文件,是打包后的js文件

为了查看结果,我们可以在dist目录下新建一个 index.html 文件
在index.html使用script标签引入main.js
<script src="./main.js"></script>
在控制台里面就可以看到这么一句话:'这是common.js的语法,在node端运行,把我用webpack打包之后,就可以在浏览器上运行了'

细心的你可能会发现上面打包的结果有一个 黄色的警告

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/concep...

黄色警告意为webpack没有指定是开发模式development 还是 生产模式production
那我们就可以在命令后面加上 --mode development
图片描述
看,是不是没有了黄色警告提示。

npx的缺点:虽然使用npx可以快速的帮我们打包,但是不够灵活,比如打包后文件夹名字都是固定 dist,因此我们需要定义webpack打包的配置文件,这样才可以随心所欲的打包

webpack配置

①、新建配置文件

我们现在ex1的目录下新建一个名为 webpack.config.js 文件
图片描述

由于webpack是基于node,所以要遵循common.js规范

②、入口(entry)

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
entry 属性的单个入口语法

module.exports = {
  entry: './src/index.js'
};

③、出口(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。
默认值为 ./dist。

在 webpack 中配置 output 属性的最低要求是,将它的值设置为一个对象,包括以下两点:

  • filename 用于输出文件的文件名。
  • 目标输出目录 path 的绝对路径。

④、入口和出口的整合

webpack.config.js

// 基于node,遵循common.js规范
const path = require('path') // 引入node.js Path 模块
module.exports = {
    entry: './src/index.js', // 入口
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, './build')  
    }
}

由于path要求为绝对路径,所以我们使用了 path.join 拼接路径这个node的语法,

node不能在浏览器端运行,所以我们可以利用 vscode 编辑器 的 Code Runner 插件来查看当前路径的结果
在webpack.config.js保留如下代码:

const path = require('path') // 引入node.js Path 模块
console.log(path.join(__dirname, './build'));

结果如图:
图片描述

在webpack.config.js配置好出入口后,那我们可以尝试着打包了
打包结果如图:
图片描述
在ex1的目录下多了个 build文件以及其目录下的bundle.js文件
那么说明了我们的配置是成功的!
图片描述

⑤、npm脚本

可以参考阮一峰老师的博客:http://www.ruanyifeng.com/blo...
npm 允许在package.json文件里面,使用scripts字段定义脚本命令。
npm 脚本。它的优点很多

  • 项目的相关脚本,可以集中在一个地方。
  • 不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。用户不需要知道怎么测试你的项目,只要运行npm run test即可。 可以利用
  • npm 提供的很多辅助功能。

在我们原有的package.json中,我们可以发现有一个这样"scripts"字段

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
}

因此我们可以尝试使用 npm test 命令看下会有什么结果
图片描述
结果执行了echo语句

我们可以利用npm脚本来替代webpack语句,我们修改一下test字段对应为webpack命令

"scripts": {
    "test": "webpack"
}

接着我们尝试使用 npm test 命令来运行一下
图片描述
可以发现我们已经打包成功了。

那么有的朋友就会问了,一定要test吗?
好,我们随便来个命令,就 abc
在test字段下,我们加多一个abc字段,如下面代码所示:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "abc": "webpack"
}

执行 npm abc 命令看看结果怎样
图片描述

哎,奇怪了呀,这是什么情况,有一点很明确的是,并没有执行webpack打包执行命令。
不急不急,我们先来看下提示先:
注意到这句话:where <command> is one of:
也就是说我们的命令应该下面那些,也就是说只能使用 npm <command>,这个command应该是提示其中之一,我们在提示中可以发现有 test 命令
图片描述

也就是说只要满足了上面字段的其中之一,就可以使用 npm <command>
但是这些命令是npm自带的,并不是所有都可以用来执行script脚本的。
除此之外,还有一个start命令也是可以直接执行的,如图

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack"
}

图片描述

那么问题来了,我们怎么知道哪些命令可以直接执行script,要一个个尝试岂不是很麻烦吗?
此时我们可以使用npm run <command> 命令来直接执行脚本
使用npm run script执行脚本的时候都会创建一个shell,然后在shell中执行指定的脚本。
所以这次我们不用再为使用什么script字段而烦恼了。
好,我们再来试一下abc字段

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "abc": "webpack"
}

图片描述
哈哈,终于成功了!

⑥、模式设置

在之前我有讲到过,黄色警告是由于没有设置模式而出现,那时候提的方法是每次都要在执行打包的命令的时候加上这么一句 --mode development/production
那这样是不是很麻烦,所以我们可以在配置文件这样定义

mode: 'development'

那么,此时我们的webpack.config.js的文件内容如下:

// 基于node,遵循common.js规范
const path = require('path') // 引入node.js Path 模块
module.exports = {
    entry: './src/index.js', // 入口
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, './build')
    },
    mode: 'development'  // 模式
}

此时,我们再打包试一下,结果如图:
图片描述
我们可清楚地看到黄色警告已经去掉了

webpack入门

①、关系依赖

在我们的package.json文件里,可以看到这么一个字段 "devDependencies"
图片描述

除此之外还有一个与之相关的字段叫 “dependencies ”

*devDependencies 与 dependencies 的区别

devDependencies 里面的模块只用于开发环境(development)
dependencies 里面的模块是需要发布到生产环境(production) 。

比如我们有一个项目要用到jQuery,在线上运行的时候如果没有jQuery包的依赖运行就会报错,这时候就应该把这个依赖写入dependencies
而我们使用的一些构建工具如webpack这些只是在开发中使用的包,上线以后就和这些包没关系了,所以将这种依赖写入 devDependencies

在我们使npm安装模块的时候,都会使用一个命令

npm install(可以简写为 npm i)

与此对应的还有这两个重要的对应参数:
--save (可以简写为-S)
--save-dev (可以简写为-D)

①、npm install m

  • 将m模块安装到node_modules目录中
  • 不会修改package.json
  • 运行npm install命令时,不会安装m模块

②、npm install m –save:

  • 把m模块安装到node_modules目录中
  • 会在package.json的 dependencies 属性下添加m模块
  • 运行npm install命令时,自动安装m模块到node_modules目录中

②、npm install m –save-dev:

  • 把m模块安装到node_modules目录中
  • 会在package.json的 devDependencies 属性下添加m模块
  • 运行npm install命令时,会自动安装到node_modules目录中

②、本地服务器(devServer)

webpack-dev-server 能够用于快速开发应用程序
里面包含了很多丰富的功能,其中最为重要的当然是能够监听本地代码的修改,利用热加载自动刷新浏览器,这样大大地方便了我们进行代码的调试

第一步:安装webpack-dev-server
在前面,我有讲到关系依赖这方面的知识,关于 devDependencies 与 dependencies 的区别,相信大家都能看懂。很明显 webpack-dev-server 只是用来本地调试的,上线之后就用不上了,那么我们可以很明确将webpack-dev-server 放到devDependencies中

执行如下命令:

npm install webpack-dev-server -D

安装好后,我们再来看一下package.json文件的变化
图片描述

第二步:配置webpack-dev-server

配置项目 描述 注意
contentBase 告诉服务器从哪个目录中提供内容 推荐使用绝对路径。
compress 一切服务都启用 gzip 压缩 compress: true
host 指定使用一个 host。默认是 localhost 可以外部服务器访问
https 默认情况下,dev-server 通过 HTTP 提供服务 https: true
open 启用 open 后,dev server 会打开浏览器 默认浏览器,可以指定其他
port 指定要监听请求的端口号 默认8080

其实webpack-dve-server的配置项目远远不止这些,这里只是列举了一部分

我们先在package.json中添加script脚本

"dev": "webpack-dev-server"

①、contentBase解析

再回来看下我们的webpack.json.js文件,webpack-dve-server配置的字段为devServer,我们先不配置先,看看会怎么样?
此时webpack.json.js的内容

// 基于node,遵循common.js规范
const path = require('path') // 引入node.js Path 模块
module.exports = {
    entry: './src/index.js', // 入口
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, './build')  
    },
    mode: 'development'  // 模式
}

执行命令:

npm run dev

看下结果:
图片描述

现在可以清楚的看到,我们的项目已经运行在 http://localhost:8080
好的,那么我们在浏览器输入这个url看看这个页面会显示什么

图片描述

如图所示:
这里只显示了我们整个文件的目录

默认情况下,contentBase将使用当前工作目录作为提供内容的目录
为了证明这点,我们在ex1的目录下新建一个index.html
现在的目录结构如下:
图片描述

现在在根目录下的index.html写入如下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    这是根目录的index.html
</body>
</html>

好,现在在重新执行命令 npm run dev
然后在浏览器打开http://localhost:8080 页面
我们就可以发现结果不同了
图片描述

那么这就可以证明了
默认情况下,contentBase将使用当前工作目录作为提供内容的目录

好,现在我们要把内容目录设在build文件夹下
我们现在build目录下新建一个index.html文件
图片描述

写入如下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    这是build文件下的index.html
</body>
</html>

这时候回到我们的配置文件(webpack.config.js)
写入如下内容:

devServer: {
    contentBase: './build'
}

现在webpack.config.js的内容如下:

// 基于node,遵循common.js规范
const path = require('path') // 引入node.js Path 模块
module.exports = {
    entry: './src/index.js', // 入口
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, './build')  
    },
    mode: 'development',  // 模式
    devServer: {
        contentBase: './build'
    }
}

好,现在万事俱备,只欠东风。现在我们再重新执行 npm run dev 命令
同样的,在浏览器可以看到结果
图片描述

②、open
启用 open 后,devServer 会打开浏览器。
在devServer设置如下:

open: true

注意,这里的true是布尔值,不是字符串
在启用命令 npm run dev后,devServer 会打开默认浏览器
大家用window一般都是IE浏览器作为默认浏览器
那么此时如果我们想使用谷歌浏览器来启动应该怎么设置呢?

此时,我们可以在package.json对script脚本进行设置,

// 打开默认浏览器
"dev": "webpack-dev-server --open"
// 打开谷歌浏览器
"dev": "webpack-dev-server --open chrome"
// 打开火狐浏览器
"dev": "webpack-dev-server --open firefox"

*提示:如果在script设置了--open,那么可以不用在配置文件设置open字段了

③、source map

由于使用webpack打包后,原来的代码经过压缩,去空格,bable的编译之后,已经变成我们不认识的代码了,为了方便我们调试调式代码,定位bug,source map就出现了

在webpack.config.js中,devtool选项用于控制是否生成,以及如何生成 source map。

图片描述

loader入门

css loader安装

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。
webpack本身只能处理javascript文件,而对于css文件需要loader来处理
对于css处理,我们常用的有这两种loader:css-loaderstyle-loader
使用命令安装这两个loader

npm i -D style-loader css-loader

接下来在src文件下新建名为 style 的文件夹,在style 下新建名为 index.css 文件
为了让效果更加直观,往 index.css 文件写入如下内容

body{background-color: yellowgreen;}

接着在我们 src目录下的 index.js 文件中使用ES6的模块语法 import 将index.css文件导入进来

import './style/index.css'

我们先不配置loader,看看浏览器会提示什么
执行 npm run dev 命令,看看会发生什么,如图所示:
图片描述

提示模块解析失败,需要loader进行处理

loader配置

有三种使用 loader 的方式:

  • 配置(推荐):在 webpack.config.js 文件中指定 loader。
  • 内联:在每个 import 语句中显式指定 loader。
  • CLI:在 shell 命令中指定它们。

我们选择在webpack.config.js中配置loader
基本写法如下:

module: {
    rules: [
        {
            test: /\.css$/,     // 利用正则表达式匹配文件名后缀为css的文件
            use: ['style-loader', 'css-loader'] // 一组loader的执行顺序默认是从右向左
        }
    ]
}

接着,我们再重新执行 npm run dev命令,页面如图所示:
图片描述

说明我们css文件有效果了,再来看下网页元素
效果如图,可以看到是以内联样式插入到html

图片描述

插件(plugins)入门

插件目的在于解决 loader 无法实现的其他事。

html-webpack-plugin安装

这个插件用来简化创建服务于 webpack bundle 的 HTML 文件,由于通常在打包中包含一些含有hash值的名字,而这些名字在每次编译的时候都发生变化的时候对我们开发人员很麻烦,利用这个插件,我们可以利用这个插件捆绑式这些文件方便我们的开发。

执行安装命令

npm i -D html-webpack-plugin

使用html-webpack-plugin

在webpack.config.js 配置如下:

// 基于node,遵循common.js规范
const path = require('path') // 引入node.js Path 模块
const HtmlWebpackPlugin = require('html-webpack-plugin')  // 引入HtmlWebpackPlugin

module.exports = {
    entry: './src/index.js', // 入口
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, './build')
    },
    mode: 'development',  // 模式
    devServer: {
        contentBase: './build'
    },
    module: {
        rules: [
            {
                test: /\.css$/,     // 利用正则表达式匹配文件名后缀为css的文件
                use: ['style-loader', 'css-loader'] // 一组loader的执行顺序默认是从右向左
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin()
    ]
}

在这里,我们这个插件做了两步工作:

const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
    new HtmlWebpackPlugin()
]

接下来,我们需要在src目录下新建模板文件,名为 template.html,我们打包后的文件都已这个html文件为模板
在模板html写入如下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>模板文件</title>
</head>
<body>
    使用模板文件
</body>
</html>

这里我们只是简单的使用两个参数

选项 描述 注意
filename 就是html文件的文件名,默认是index.html
template 生成的文件所依赖文件模板的路径

plugins的配置如下

plugins: [
    new HtmlWebpackPlugin({
        filename: 'index.html',
        template: './src/template.html',
    })
]

好的,现在所有都配置好了,我们来测试一下
执行命令 npm run dev

图片描述

可以发现,运行项目时使用的是模板文件
现在,再来测试一下打包文件
我们把原来的build文件删除(当然可以不删,因为会覆盖)

执行命令 npm run abc
此时,我们可以发现 build 目录下 index.html是这个样子的
图片描述

直接就帮我们用script标签插入了bundle.js

总结:入门不易,掌握更难,纸上得来终觉浅,绝知此事要躬行!


magic_xiang
435 声望14 粉丝