webpack
官方文档地址链接 ,本文主要介绍开始webpack
前的准备工作。
-
前言
为什么要引入webpack
随着前端工程越来越复杂,单独创建html
js
css
的方式已经无法保证项目的可维护性,所以我们就需要考虑把不同的业务逻辑拆分成模块,然后分开引入这些模块,每个模块做自己的事情,这样就可以保证项目的可维护性和可扩展性了。假如我们一个复杂的工程需要几千个js
文件,我们不可能引入几千个js
文件,所以我们需要借助工具来管理模块,目前使用率最高的有webpack
、GRUNT
、Gulp
、Browserify
,并且Vue
,React
,Angular
脚手架都开始使用webpack
做底层框架,主要得益于它的Tree Shaking
代码懒加载
代码分割
等特性。
什么是webpack
官方介绍:webpack
是一个现代 JavaScript
应用程序的静态模块打包器(module bundler)
。当 webpack
处理应用程序时,它会递归地构建一个依赖关系图(dependency graph)
,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle
。核心定义为:‘模块打包工具’。那么,在了解webpack
之前,首先要清楚什么是模块,如何使用模块。
什么是modules
在模块化编程中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为模块。
每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。 精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。
以上是官方的介绍,以我自己的理解,程序我可以理解为车轮,我们都知道,车轮有轮胎和轮毂,那么轮胎的生产线生产轮胎这个零件,轮毂的生产线生产轮毂这个零件,各自分工互不打扰,那么轮胎和轮毂的生产线我们可以理解为模块,而产出的轮胎和轮毂我们可以理解为模块的方法,而生产出轮胎和轮毂的过程我们可以理解为导出,而车间人员拿过来组装的过程我们可以理解为导入。
常见的webpack modules
ES Module
、CommonJS
、 CMD
、AMD
、 css/sass/less 文件中的 @import
语句...
相关modules使用方法
-
Es Module
export
默认导出整个模块,或具名导出模块
// 具名导出
export let Hello = 'Echonessy';
export const Word = () => { return ‘Boy’; }
// 默认导出
export default {
Hello,Word
}
import
通过 import 以静态的方式,导入另一个通过 export 导出的模块。
// 具名导出的引用
import { Hello,Word } from './hello-word.js';
// 使用方式
Hello;
Word();
// 默认导出的引用 HelloWord 包含Hello、Word两个对象
import HelloWord from './hello-word.js';
// 使用方式
HelloWord.Hello;
HelloWord.Word();
export导出多个对象,export default只能导出一个对象并且一个文件里只能有一个。
export导出对象需要用{ },export default不需要{ }
export {A,B,C};
export default A;
import()
动态地加载模块。调用 import()
之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中。
import
规范不允许控制模块的名称或其他属性,因为 "chunks
" 只是webpack
中的一个概念。幸运的是,webpack
中可以通过注释接收一些特殊的参数,而无须破坏规定,具体参数介绍请参考官方文档
import(
/* webpackChunkName: "newChunkName" */
/* webpackMode: "lazy" */
'./hello-word.js'
).then(HelloWord =>{
HelloWord.Hello;
HelloWord.Word();
}).catch(e => { })
-
CommonJS
require
以同步的方式检索其他模块的导出。由编译器(compiler)来确保依赖项在最终输出 bundle 中可用。对应的导出方法为exports
、module.exports
const HelloWord = require('HelloWord');
exports
、module.exports
:一个模块被引用的时候最终都会被输出成module.exports,而exports只是module.exports的一个引用
const helloWord = require('../controllers/helloWord');
module.exports = helloWord;
console.log(module)
// 控制台打印如下
Module {
id: 'E:\\ExpServer\\routes\\index.js',
exports: { Hello: [Function] }
...
}
require.resolve
以同步的方式获取模块的 ID。由编译器(compiler)来确保依赖项在最终输出 bundle 中可用。更多关于模块的信息,请点击这里 module.id
。使用require.resolve
函数查询模块文件名时并不会加载该模块。
module.id === require.resolve("./HelloWord.js")
require.cache
多处引用同一个模块,最终只会产生一次模块执行和一次导出。所以,会在运行时(runtime)中会保存一份缓存。删除此缓存,会产生新的模块执行和新的导出。
require.cache[module.id] === module
require.ensure()
是 webpack 特有的,已经被 import()
取代。
-
webpack 特定的方法
require.context()
使用 directory 路径、includeSubdirs 选项和 filter 来指定一系列完整的依赖关系,便于更细粒度的控制模块引入。
当我们需要引入某一目录下多个文件的时候,一个一个的导入似乎会显得非常麻烦,这里我们就可以考虑使用此方法获取该目录下的文件,通过提取注入到一个文件里。
require.context(directory:String, includeSubdirs:Boolean /* 可选的,默认值是 true */, filter:RegExp /* 可选的 */);
require.include
引入一个不需要执行的依赖
,这可以用于优化输出 chunk
中的依赖模块的位置。
require.include(dependency: String)
webpack 环境搭建
准备工作:nodeJs ,这里我们最好安装最新版本的,因为会提升webpack
打包速度,
下载下来之后,傻瓜式安装完成之后打开CMD
输入以下指令
G:\Echonessy>node -v
v10.16.3
G:\Echonessy>npm -v
6.9.0
这样说明node
和npm
安装完毕
接下来我们新建一个文件夹,作为我们webpack
的工程目录webpackDemo
,我们通过npm
进行初始化,让它符合webpack
的使用环境具体指令
G:\Echonessy\webpack>cd webpackDemo
G:\Echonessy\webpack\webpackDemo>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (webpackdemo)
version: (1.0.0)
description: this is a demo
entry point: (index.js)
test command:
git repository:
keywords:
author: Echonessy
license: (ISC)
About to write to G:\Echonessy\webpack\webpackDemo\package.json:
{
"name": "webpackdemo",
"version": "1.0.0",
"description": "this is a demo",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Echonessy",
"license": "ISC"
}
Is this OK? (yes) yes
接下来我们就会发现文件夹里多了一个package.json
文件
初始化完成之后,那么接下来开始安装
webpack
(注,webpack
可能会出现安装失败的情况,这里是由于webpack
源会被国内强掉,这里可以通过手机热点来连接WIFI
安装),我们首先进入webpackDemo
这个目录下
// 项目内安装webpack(建议)
npm install webpack webpack-cli -D
G:\Echonessy\webpack\webpackDemo>npm install webpack webpack-cli -D
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN webpackdemo@1.0.0 No repository field.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9:
wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
+ webpack-cli@3.3.7
+ webpack@4.39.2
added 385 packages from 218 contributors and audited 5286 packages in 441.279s
found 0 vulnerabilities
// 查看版本 npx 想要解决的主要问题,就是调用项目内部安装的模块
G:\Echonessy\webpack\webpackDemo>npx webpack -v
4.39.2
// 全局安装webpack(不建议,因为如果存在多个项目假如各项目webpack版本不同,会有问题)
npm install webpack webpack-cli -g
webpack 配置
webpack
的打包文件为webpack.config.js
,webpack
默认内部有打包配置,如果找不到配置文件会执行内部默认配置。
G:\Echonessy\webpack\webpackDemo>npx webpack index.js
Hash: 53742f9efd12248f7206
Version: webpack 4.39.2
Time: 941ms
Built at: 2019-08-21 2:40:55 PM
Asset Size Chunks Chunk Names
main.js 5.09 KiB 0 [emitted] main
Entrypoint main = main.js
[0] ./index.js 365 bytes {0} [built]
+ 2 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/configuration/mode/
那么如何配置我们自定义的规则?
这里我们新建一个index.js
作为需要打包的文件
新建一个webpack.config.js
配置文件
那么接下来我们需要在配置文件里去配置我们需要打包的规则
打开
webpack.config.js
配置文件
const path = require('path');
module.exports = {
//两种模式 development 代码不会被压缩 production会被压缩
mode: 'development',
//入口
entry: './index.js', // 我们需要打包的文件
//出口
output:{
filename:'outBundle.js',// index.js打包后的文件名字
path: path.resolve(__dirname,'bundle'),//index.js打包后所在的文件夹
}
}
执行打包配置
G:\Echonessy\webpack\webpackDemo>npx webpack
Hash: b82930305775bdc66443
Version: webpack 4.39.2
Time: 650ms
Built at: 2019-08-21 2:50:15 PM
Asset Size Chunks Chunk Names
outBundle.js 930 bytes 0 [emitted] main
Entrypoint main = outBundle.js
[0] ./index.js 49 bytes {0} [built]
这里我们会发现,我们的工程多出了一个文件夹bundle
文件夹,里面有个outBundle.js
那么问题来了,这个配置文件我想自己命名,并且放到配置文件目录里,这个时候打包的时候该怎么办?
这里我把webpack.config.js
重命名为webpackConfig.js
并且放在config
文件夹下面
这里,我们就需要通过命令指定配置文件去打包,我们进入到
config
目录下通过--config
指向webpackConfig.js
,这样就可以打包成功了。
const path = require('path');
module.exports = {
mode: 'development',
//入口
entry: '../index.js', // 我们需要打包的文件
//出口
output:{
filename:'newOutBundle.js',// index.js打包后的文件名字
path: path.resolve(__dirname,'../newBundle'),//index.js打包后所在的文件夹
}
}
G:\Echonessy\webpack\webpackDemo\config>npx webpack --config webpackConfig.js
Hash: b82930305775bdc66443
Version: webpack 4.39.2
Time: 186ms
Built at: 2019-08-21 3:02:21 PM
Asset Size Chunks Chunk Names
newOutBundle.js 930 bytes 0 [emitted] main
Entrypoint main = newOutBundle.js
[0] ../index.js 49 bytes {0} [built]
G:\Echonessy\webpack\webpackDemo\config>
以上打包每次都需要用npx webpack
这种固定语法去打包我们的文件,那么问题来了,这指令我们可不可以自定义?我们都知道,vue有两种我们常用的指令,一个是npm run dev
一个是npm run build
,那么我们是否可以创建我们自己的指令?答案是肯定的
首先,我们打开package.json
会有个scripts
对象 这里我们新建一个指令,取名为setup
,执行的语句为webpack
{
"name": "webpackdemo",
"version": "1.0.0",
"description": "this is a demo",
"main": "index.js",
"scripts": {
"setup": "webpack"
},
"author": "Echonessy",
"license": "ISC",
"devDependencies": {
"webpack": "^4.39.2",
"webpack-cli": "^3.3.7"
}
}
执行后你会发现
npx webpack
==npm run setup
实例1:
我们都知道,浏览器是不能直接运行es6的相关语法的,那么,如果我们要运行,该怎么处理?其实webpack还可以进行代码的简单 '翻译'。
这里我们实现一个简单的需求,就是我们有两个模块,分别对应A
和B
,将A
和B
通过模块的内容通过导入导出方式显示在页面上。
这里我们新建个文件夹src
用来存放我们的源代码
我们新建一个
a.js
export const A = () => {
return "I'm A";
}
我们新建一个b.js
export const B = () => {
return "I'm B";
}
新建一个index.js
作为模块引入文件
import { A } from './a.js'
import { B } from './b.js'
document.getElementById('main').innerHTML = A() + '</br>' + B();
同样的,我们需要创建一个index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="main"></div>
<script src="./index.js"></script>
</body>
</html>
我们直接运行,可以看到,浏览器控制台会报index.js
的语法错误,并且,我们的页面也没有显示任何东西。
那么这里我们创建一个
webpack
配置文件 将我们的index.js
打包出来
const path = require('path');
module.exports = {
mode: 'production',
//入口
entry: './src/index.js', // 我们需要打包的文件
//出口
output:{
filename:'newIndex.js',// index.js打包后的文件名字
path: path.resolve(__dirname,'newDist'),//index.js打包后所在的文件夹
}
}
我们执行在工程目录下执行
npx webpack src/index.js
或者执行我们自定义的指令
G:\Echonessy\webpack\webpackDemo>npm run setup
> webpackdemo@1.0.0 setup G:\Echonessy\webpack\webpackDemo
> webpack
Hash: e4f933d03797360cf137
Version: webpack 4.39.2
Time: 162ms
Built at: 2019-08-21 4:19:06 PM
Asset Size Chunks Chunk Names
newIndex.js 1010 bytes 0 [emitted] main
Entrypoint main = newIndex.js
[0] ./src/index.js + 2 modules 373 bytes {0} [built]
| ./src/index.js 173 bytes [built]
| ./src/a.js 100 bytes [built]
| ./src/b.js 100 bytes [built]
这样我们新的index.js
打包文件就打包完成了,接下来,我们更改index.html
的js
引入路径
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="main"></div>
<script src="../newDist/newIndex.js"></script>
</body>
</html>
我们再次打开浏览器,发现我们想要的效果出来了