官方定义:webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。
所谓打包工具就是把开发环境的代码转化成运行环境代码。一般来说,开发环境的代码是为了更好的阅读和开发,而运行环境的代码则是为了能够更快地执行。因此开发环境和运行环境的代码形式也不相同。比如开发环境的代码,要通过混淆压缩后才能放在线上运行,这样代码体积更小,而且对代码执行也不会有任何影响;另外在使用第三方框架(Vue、React)或ES6等新语法来开发时,可以提升代码阅读和可维护性以及开发效率,但浏览器又不能直接识别这些文件,这时候就要一个构建工具来打包转换,转换成浏览器可以认识的文件。
打包构建工具主要作用:
1、编译语法
浏览器是不能直接识别ES6、TypeScript、Vue、React等语法与文件,包括CSS预处理器Less、Sass、Stylus,为了能够被浏览器兼容运行,就需要通过构建工具打包成浏览器能够识别的文件(如原生Js、Html、CSS),比如使用Babel编译ES6语法转成ES5语法,让浏览器能够识别。
Babel是基于webpack构建工具上编译的
2、代码压缩
将JS、CSS代码混淆压缩,让代码体积更小,加载更快。
webpack类似Android中的Gradle插件工具,配置、编译、混淆、压缩优化、模块集成打包,这些都是在开发环境需要做的动作,同样地webpack也是只有开发环境使用,在生成环境是不用webpack。
webpack5 运行于 Node.js v10.13.0+ 的版本
首先要安装Node.js,参考 https://note.youdao.com/s/KLOurbW6
新建一个项目(空文件夹),cmd进入项目目录下,执行
npm init -y
执行命令会生成package.json文件,内容如下:
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
安装webpack有两种方式,局部安装与全局安装。
局部安装是指在当前项目下安装指定版本webpack,又称为本地安装。
npm install --save-dev webpack
# 或指定版本
npm install --save-dev webpack@
执行后会在项目根目录下一个node_modules
文件夹,同时在配置文件package.json
中的devDependencies
会显示webpack的版本号,如下:
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.75.0"
}
}
npm安装注意项:
在没有指定安装版本情况下,默认是安装最新的版本,在我安装时,5.75.0是最新版本。
上面安装命令是npm install --save-dev webpack
,不加--save-dev
也是可以安装,两者的区别是,--save-dev
会将插件包依赖到devDependencies
下,而npm install webpack
会将插件包依赖到dependencies
下。如下:
{
"name": "webpack-demo",
"version": "1.0.0",
....
"dependencies": {
"webpack": "^5.75.0"
}
}
dependencies
是生成环境的依赖,devDependencies
是开发环境的依赖。
是否使用
--save-dev
取决于你的应用场景。假设你仅使用 webpack 进行构建操作,那么建议你在安装时使用--save-dev
选项,因为可能你不需要在生产环境上使用 webpack。如果需要应用于生产环境,请忽略--save-dev
选项。
全局环境安装webpack,就是所有的项目都是使用同一个版本。
npm install --global webpack
官方是不推荐使用全局安装,不利用项目的维护。
如果是webpack4+版本,就需要安装webpack-cli
,在webpack5+版本可以忽略。
执行:
npm install --save-dev webpack-cli
同样地在devDependencies
下可以看到版本号:
"devDependencies": {
"webpack": "^5.75.0",
"webpack-cli": "^4.10.0"
}
入口起点:指示webpack应该使用哪个模块,用来作为构建其内部依赖关系的开始。
在webpack.config.js
配置entry
属性来指定入口:
module.exports = {
// entry : 入口文件,默认是`./src/index.js`
entry: './path/to/my/entry/file.js',
};
https://webpack.docschina.org/concepts/entry-points
既然有入口,那肯定有出口,webpack也是有出口
output
属性,webpack默认输出位置是在./dist
文件夹。
出口output:告诉webpack打包编译后文件(Bundle)的输出位置,以及这些文件的命名方式。
在webpack.config.js
中配置output
属性:
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
// path: 文件输出到的路径,默认是dist目录下
// __dirname表示项目根目录
path: path.resolve(__dirname, 'dist'),
// filename:输出文件名称,默认是./dist/main.js
filename: 'my-first-webpack.bundle.js',
},
};
更多请参考:https://webpack.docschina.org/concepts/output
默认情况下webpack只识别 JavaScript 和 JSON 文件,如果要让webpack能够处理其他类型文件,并转成有效文件,让程序识别使用。
在webpack.config.js
中配置loader
的相关功能,是通过rules
属性来配置loader
的具体功能。
注意:在module对象中没有loader这个名字的属性,它是一个功能描述词,真正配置是通过
rules
属性来配置,另外rules
属性是一个数组类型,元素是一个对象,这个对象元素主要有两个属性:
test
属性:识别出哪些文件会被转换。use
属性,定义出在进行转换时,应该使用哪个loader
const path = require('path');
module.exports = {
module: {
// test:识别.txt文件
// use: 使用raw-loader来识别文件
rules: [{ test: /\.txt$/, use: 'raw-loader' }],
},
};
https://webpack.docschina.org/concepts/loaders
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。总之插件目的在于解决 loader 无法实现的其他事。
在webpack.config.js
中配置plugins
属性,是一个数组类型。
// 1、导入所需文件
// html-webpack-plugin 为应用程序生成一个 HTML 文件,并自动将生成的所有 bundle 注入到此文件中。必须
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 用于访问内置插件。必须
const webpack = require('webpack');
module.exports = {
// 2、配置插件,plugins添加插件
// 数组的元素是一个HtmlWebpackPlugin对象
plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};
https://webpack.docschina.org/concepts/plugins
模式(mode)是指运行环境,是开发还是生产环境,通过mode
属性配置,提供了三个值 development
, production
或 none
,默认值是production
。
module.exports = {
mode: 'development',
};
在不同模式环境下,会做对应的优化,比如生产环境,会进行代码压缩,去空行、混淆等操作。
https://webpack.docschina.org/configuration/mode
首先在项目根目录创建index.html
和src文件夹
、webpack.config.js
,接着在src目录
下创建index.js
文件,结构如下:
index.js
文件内容如下,index.html
内容先放着,在后面会手动引入webpack打包生成的入口js文件。
document.write("hello webpack")
const sum = (x, y) => x + y
sum(1,2)
function foo(name,age){
console.log(`name: ${name} age: ${age}`)
}
在**webpack.config.js
**设置webpack配置,重点
module.exports = {
// 运行模式,开发模式
mode: 'development',
// 入口,以index.js为打包入口文件
entry: "./src/index.js",
// 出口,默认是输出到./dist目录下
output: {
// 输出的文件名,默认是main.js
filename: "index.js"
}
}
接着运行webpack命令,编译打包,打包需注意事项:
如果是全局安装webpack,直接执行
webapck
命令就可以运行打包编译,
如果你是本地安装的,运行的是项目内的webpack版本,要用npx webpack
命令来打包编译
这里以本地安装webpack为例,也是官网推荐的方式,执行npx webpack
,如下
在执行npx webpack
打包后,会在项目根目录下生成一个dist文件夹
,在dist
下的index.js
就是打包生成的,对应的是./src/index.js
的代码。
最后,在index.html
中引入./dist/index.js
,如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack-demotitle>
head>
<body>
<script src="./dist/index.js">script>
body>
html>
在浏览器运行index.html
,可以看到这样的界面:
上面是运行mode模式是development
开发模式,接着将mode模式改成production
生产模式,看看打包生成./dist/index.js
文件有何区别。
production
模式打包后文件会更小,但打包时间会更长,说明在production
模式打包有压缩优化代码,而这些工作在打包期间需要消耗一些时间。
这是webpack最简单的使用例子。
在上面的例子,会发现在打包输出的dist目录并没有index.html,说明没有将html文件打包进去,接下来使用html plugin插件来打包html文件。
首先安装html插件 html-webpack-plugin
可以在npm官网搜索html-webpack-plugin的使用,任何一个插件都可以在npm官网查找文档
npm i --save-dev html-webpack-plugin
安装完html-webpack-plugin
插件后,然后在webpack.config.js
配置插件
// 引入插件包
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// 运行模式
mode: 'development',
// ....
// 添加插件
plugins: [new HtmlWebpackPlugin()]
}
执行npx webpack
打包命令,会在dist
目录默认生成一个index.html文件,可以运行这个html文件,如下
doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack Apptitle>
<meta name="viewport" content="width=device-width,initial-scale=1">
<script defer="defer" src="index.js">script>
head>
<body>body>
html>
在实际项目开发中会定义一个html模板,然后在打包过程中会依照这个html模板来打包工作,要用到template
属性设置目标文件,如下:
由于webpack.config.js文件就是JavaScript代码,可以查看HtmlWebpackPlugin这个插件源码,看看有哪些属性,
template
只是里面其中的一个属性。
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
// 将项目根目录的index.html设置成模板文件
plugins: [new HtmlWebpackPlugin({template: "./index.html"})]
}
然后我们将项目根目录的index.html(模板文件)修改一些内容,如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack-demotitle>
head>
<body>
<div>欢迎使用webpackdiv>
body>
html>
最后执行打包命令npx webpack
,看看打包后dist
目录下的index.html
文件,如下
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack-demotitle>
<script defer src="index.js">script>
head>
<body>
<div>欢迎使用webpackdiv>
body>
html>
打包生成的html文件,除了和模板文件内容一样外,还自动引入了配置下的index.js。这个就是html打包的基本流程。
如果想将某些文件不参与编译打包,只是想原封不动拷贝到dist
目录下,可以使用copy-webpack-plugin
插件。
在项目根目录新建一个static
文件夹,然后创建一个constants.js
文件,内容如下:
const key = "hello"
const total = 100
接着,安装插件npm install copy-webpack-plugin --save-dev
,然后在webapck.config.js
文件引入配置插件,如下:
const CopyPlugin = require('copy-webpack-plugin')
module.exports = {
mode: 'development',
plugins: [
/*配置文件拷贝来源与输出地 ,可以配置多个*/
new CopyPlugin({patterns: [{from: './static',to:'static'}]})
]
}
最后,执行打包命令npx webpack
,会在dist下生成一个static文件夹,内容与根目录是一样的
我们对比下dist
下的constants.js
和index.js
文件内容的区别
// constants.js
const key = "hello"
const total = 100
// index.js
eval("\r\n\r\nconst sum = (x, y) => x + y\r\nsum(1,2)\r\n\r\nfunction foo(name,age){\r\n console.log(`name: ${name} age: ${age}`)\r\n}\r\ndocument.write(\"hello webpack\")\r\n\r\n\n\n//# sourceURL=webpack://webpack-demo/./src/index.js?");
constants.js
中的代码是原封不动,没有任何修改,而参与打包的index.js
文件有修改过。