重要提示
本教程是笔者通过亲身实践,总结梳理出来的,可以保证代码是没有任何问题的。墙裂建议大家学习的时候能跟着手敲一遍,学完一定对大家的webpack有很大提升!本文是基础篇,会带你学习webapck的核心基础内容。
如果您已经掌握webapck5,不妨看看笔者的# 手把手教你学习webapck插件plugin的开发
webpack简介
不想看概念的这里可以直接略过!
Webpack 是什么?
Webpack 是一种前端资源构建工具,一个静态模块打包器。
- 前端资源构建工具:主要理解一下这个前端资源是哪些资源。这些前端资源就是浏览器不认识的 web 资源, 比如 sass、less、ts,包括 js 里的高级语法。这些资源要能够在浏览器中正常工作,必须一一经过编译处理。而 webpack 就是可以集成这些编译工具的一个总的构建工具。
- 静态模块打包器:静态模块就是 web 开发过程中的各种资源文件,webpack 根据引用关系,构建一个依赖关系图,然后利用这个关系图将所有静态模块打包成一个或多个 bundle 输出。
为什么我们需要 Webpack
回答这个问题,可以和还没有 Webpack、没有构建工具时对比一下,就能明显地感觉出来了。这里就来列举一下不使用构建工具时的痛点。
- web 开发时调用后端接口跨域,需要其他工具代理或者其他方式规避。
- 改动代码后要手动刷新浏览器,如果做了缓存还需要清缓存刷新。
- 因为 js 和 css 的兼容性问题,很多新语法学习了却不能使用,无论是开发效率和个人成长都受影响。
- 打包问题。需要使用额外的平台如 jekins 打包,自己编写打包脚本,对各个环节如压缩图片,打包 js、打包 css 都要一一处理。 ......
而这些问题,Webpack 都提供了解决方案,你只需要做一些简单的配置就可以上手使用了。当然,Webpack 做的还不止这些,下面就来一一介绍。
webpack初体验
webpack的安装
全局安装
mkdir webpack //创建文件夹
npm init -y //初始化仓库
cnpm i webpack@5.45.1 -g
cnpm i webpack-cli@4.7.2 -g
如果你使用 webpack v4+ 版本,并且想要在命令行中调用 webpack,你还需要安装webpack-cli webpack-cli用于在命令行中运行webpack,cli即命令行接口(Command Line Interface)
局部安装(仅安装在项目里)
不同电脑的webpack可能版本不同,使用全局命令打包可能是不安全的,因此,我们可以在项目局部进行webpack安装
cnpm i webpack@5.45.1 -D
cnpm i webpack-cli@4.7.2 -D
打包一个文件试试
按照如图所示完善文件夹
html文件中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>webpack练习</title>
</head>
<body>
<script src="./src/index.js"></script>
</body>
</html>
index.js中
import {sum, square} from './js/untils.js'
console.log(sum(10 ,20));
console.log(square(10));
在utils.js中写入代码,模拟真实开发
//定义一个求和函数
const sum = (m ,n) => {
return m + n
}
//定义一个求积函数
const square = (m) => {
return m * m
}
//导出相关函数
export { sum, square}
使用live-server打开index.html(live-server是可以运行前端静态文件的一个服务器)(可以在vscode中直接安装这个插件)
发现控制台报错,这是因为浏览器不识别ES6模块化语法,需要在index.html的script标签中加入type="module"
<script src="./src/index.js" type="module"></script>
真实项目中,可能使用的规范不止一个,因此,我们在js文件夹下在创建一个api.js的文件,并使用commo.js的模块规范
const getinfo = () => {
return {
name:'gcshi',
age:18
}
}
module.exports = getinfo
并在index.js中进行导入
此时控制台报错了,因为浏览器并不识别require
index.js:2 Uncaught ReferenceError: require is not defined
此时,我们就可以通过使用webpack将两种规范进行统一,我们在编译器的的控制台输入webpack
将文件打包
webpack
会发现,文件里多了个dist目录,里面有一个main.js的一个文件。
这个文件,就是webpack打包出来的文件。webpack打包时,会自动寻找src目录下的index.js文件,然后输出dist文件。(观察图中打包出来的文件,可以发现,webpack并没有将ES6的语法转换为ES5,不过,这不是重点)
此时,我们在inde.html中引入该文件
<script src="./dist/main.js"></script>
会发现,控制台正常输出了结果
就这样,我们使用webopack将两种不同的规范代码进行了打包,输入成了浏览器可以识别的文件!
配置文件webpack.config.js
上面示例中,webapck打包是按照src/index.js作为入口文件,dist作为输出文件,这一默认配置是可以更改的。
比如,我们也可以在package.json中配置一下打包的入口和出口
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"build": "webpack --output-path ./build --entry ./hh/index.js"
},
现在,你运行npm run build
,入口目录就变成了hh/index.js
当然,这是比较麻烦的,我们直接在根目录创建一个webpack.config.js文件夹,就可以进行各种想要的配置了!
默认配置
初学期间,默认配置我们可以不做任何改变!
const path = require("path");
module.exports = {
//入口
entry:"./src/js/index.js",
//出口
output:{
path:path.resolve(__dirname,"build"),
filename:"./js/build.js"
},
//打包模式
//development,production,none
mode:'development'
}
注意:入口文件可以写相对路径,但是出口文件只能写绝对路径,因此需要使用node.js的path模块。
配置文件名是可以更改的
默认的配置文件名称为webpack.config.js,创建在根目录即可。该配置项默认导出一个对象。
默认的配置文件名也可以自定义,在package.json的scripts里添加相应命令即可。
"build": "webpack --config webpack.config.js",
其中 webpack.config.js可以替换为自己想要的名称
loader的基础概念与使用(含postcss)
基本功能
loader 用于对模块的源代码进行转换。
loader 可以使你在 import 或 "load(加载)" 模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的得力方式。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件!
语法
//Node.js path 模块提供了一些用于处理文件路径的小工具,我们可以通过以下方式引入该模块:
const path = require('path');
module.exports = {
.......
module:{
rules:[
//规则一
{
test:'', //匹配的文件类型
use:'', //使用的loader类型,use是一个数组或字符串
options:'', //配置项,可省略
},
{//规则二},
]
}
}
css-loader
传统的样式引入方法有行内样式、内部样式表、外部样式表。这几种方法都是基于html文件进行的,如图,下面这种引入方式一定是不能被识别的。
import './index.css'
function setTitle() {
let h = document.createElement('h1')
h.innerHTML = 'gcshi'
h.className = 'gcshi'
return h
}
document.body.appendChild(setTitle())
使用css-loder,我们就可以实现js文件中引入css文件。
首先,需要安装css-loader
npm install --save-dev css-loader
然后在webpack、config、js中进行配置
module:{
rules:[
{
test:/.css$/,//一般就是一个正则表达式,用于匹配要处理的文件类型,
use:[
{
loader:'css-loader',
}
]
},
]
}
简写形式
{
test:/.css$/,
loader:'css-loader',
},
{
test:/.css$/,
use:['css-loader'],
},
{
test:/.css$/,
use:'css-loader',
},
但此时,打包出的来文件依旧没有样式,因为css-loader只有让js文件具备引入CSS的功能,我们还需要将导入的这些样式使用style-loader进行解析。
style-loader
style-loader 是通过一个JS脚本创建一个style标签,把 CSS 插入到 DOM 中,与css-loader
结合使用。
首先,需要安装style-loader
npm install --save-dev style-loader
然后,在webpack.config.js中进行配置
module:{
rules:[
{
test:/.css$/,
use:['style-loader','css-loader'],
},
]
}
注意:use中的依赖关系是从右至左左,从下至上执行的,因此,必须把css-loader放在style-loader后面!!
OK,此时打开打包后的文件,可以看见 我们创建的文件已经可以被识别了
less-loader
webpack 将 Less 编译为 CSS 的 loader。
安装
$ npm install less less-loader --save-dev
配置
module:{
rules:[
{
test:/.css$/,
use:['style-loader','css-loader'],
},
{
test: /.less$/i,
use: [
// 将less转换为css
'style-loader',
'css-loader',
'less-loader',
],
},
]
}
browserslistsc
前端开发中需要兼容浏览器平台,browserslistsc用于配置此项。有些版本的webpack在安装时,在nodemodelus中会带一个默认的browserslistsc文件夹,携带相关配置。
postcss
对于不同浏览器,css的写法可能是不一样的。
兼容性测试网站:autoprefixer.github.io/
简介
postcss是一个用 JavaScript 工具和插件转换 CSS 代码的工具,postcss自身没有什么功能,只是一个平台,可以下载各种插件,从而实现一些功能!
- Autoprefixer ****自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀。
- PostCSS Preset Env ****帮你将最新的 CSS 语法转换成大多数浏览器都能理解的语法,
- CSS 模块 能让你你永远不用担心命名太大众化而造成冲突,只要用最有意义的名字就行了。
示例
index.js
import './postcss.css'
function setTitle() {
let h = document.createElement('h1')
h.innerHTML = 'gcshi'
h.className = 'gcshi'
return h
}
document.body.appendChild(setTitle())
打包后的打开调试工具查看样式
可以看到,webpack打包时,样式除了引入、解析是没有做任何其他处理的。现在,我们使用postcss对样式进行统一的前缀补全。
安装
为了使用本 loader,你需要安装 postcss-loader 和 postcss
npm install --save-dev postcss-loader postcss
安装前缀补全插件Autoprefixer
npm i autoprefixer -D
配置
在webpack.config.js中配置
module:{
rules:[
{
test:/.css$/,
use:[
'style-loader',
'css-loader',
{
loader:'postcss-loader',
//配置项
options:{
//postcss-loader的配置项
postcssOptions:{
//postcss-loade需要使用的插件
plugins:[require('autoprefixer')]
}
}
}
],
},
]
}
plugins:[require('autoprefixer')] 可简写为 plugins:['autoprefixer']
重新打包可以发现前缀自动补全了,真香!
简写方式: 在根目录创建postcss.config.js文件配置
module.exports = {
plugins: [
['autoprefixer'],
],
};
module:{
rules:[
{
test:/.css$/,
use:[
'style-loader',
'css-loader',
'postcss-loader',
],
},
]
}
注:PostCSS Preset Env 实际预设了很多好用的css插件,完全可以替代autoprefixer使用。
plugin
接下来,笔者会为大家介绍webapck一些重要的plugin(这些plugin大多都已经集成在脚手架中了,实际开发中,我们无需配置)
基本功能
插件 是 webpack 的 支柱 功能。Webpack 自身也是构建于你在 webpack 配置中用到的 相同的插件系统 之上!
插件目的在于解决 loader 无法实现的其他事。Webpack 提供很多开箱即用的 插件。
语法
const HtmlWebpackPlugin = require('html-webpack-plugin');//引入第三方插件
const webpack = require('webpack'); // 访问内置的插件
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {rules: [.....],},
plugins: [
//实例化对象
new webpack.ProgressPlugin(),
//实例化对象并传入数据
new HtmlWebpackPlugin({ template: './src/index.html' }),
],
};
clean-webpack-plugin
github:github.com/johnagan/cl…
生产环境编译文件的时候,先把 build或dist (就是放生产环境用的文件) 目录里的文件先清除干净,再生成新的。
安装
npm install --save-dev clean-webpack-plugin
配置
const { CleanWebpackPlugin } = require('clean-webpack-plugin');//引入
module.exports = {
entry: '',
output: {},
module: {rules: [.....],},
plugins: [
new CleanWebpackPlugin(),//使用
],
};
html-webpack-plugin
github:www.npmjs.com/package/htm…
webpack进行打包时,自动在打包文件中创建index.html文件,并引入打包出来的js入口文件
安装
Webpack 5
npm i --save-dev html-webpack-plugin
yarn add --dev html-webpack-plugin
Webpack 4
npm i --save-dev html-webpack-plugin@4
yarn add --dev html-webpack-plugin@4
配置
const HtmlWebpackPlugin = require('html-webpack-plugin') //引入插件
module.exports = {
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin() //使用插件
]
}
默认index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<script defer src="index_bundle.js"></script>
</head>
<body>
</body>
</html>
常用参数
Name | Type | Default | Description | |
---|---|---|---|---|
title | {String} | Webpack App | html的标题 | |
filename | {String | Function} | 'index.html' | 打包后的文件名 |
template | {String} | `` | 自定义的html模板 |
title属性
先看插件的默认渲染模板
可见,渲染出来的title值是可以在webpack中预设的,其默认值是Webpack App
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin({title:"gcshi"}) //自定义title属性
]
}
template属性
默认的index.html模板显然是不满足我们的要求的,我们看看vue项目中的模板
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
那如何使用vue的index.html模板呢?
很简单,首先,在我们的webpack项目中新建一个public文件夹,把vue的index.html放进去
然后,在webpack.config.js中进行配置
plugins: [
new CleanWebpackPlugin(),//使用
new HtmlWebpackPlugin({title:"gcshi",template:'./public/index.html'})
],
ok,这样就大工告成了,但直接打包会报错
ERROR in Template execution failed: ReferenceError: BASE_URL is not defined
因此,我们还需要处理一下index.html中 BASE_URL 报错得问题
DefinePlugin
在index.html中,我们可以用htmlWebpackPlugin.options.title的方式自定义数据,那么如果自定义其他数据呢?比如,让BASE_URL这个常量可以被识别。
这个时候,我们就需要使用webpack自带的插件DefinePlugin来实现这个功能。
DefinePlugin 允许在 编译时 将你代码中的变量替换为其他值或表达式。这在需要根据开发模式与生产模式进行不同的操作时,非常有用。例如,如果想在开发构建中进行日志记录,而不在生产构建中进行,就可以定义一个全局常量去判断是否记录日志。这就是 DefinePlugin 的发光之处,设置好它,就可以忘掉开发环境和生产环境的构建规则。
使用
const { DefinePlugin } = require('webpack')
module.exports = {
plugins: [
new DefinePlugin({
BASE_URL:"'./'"
})
],
}
注意:请注意,由于本插件会直接替换文本,因此提供的值必须在字符串本身中再包含一个 实际的引号 。通常,可以使用类似 '"production"' 这样的替换引号,或者直接用 JSON.stringify('production')。
babel-loader
Babel 是一个 JavaScript 编译器
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过引入第三方 polyfill 模块,例如 core-js)
- 源码转换(codemods)
- 更多参考资料!(请查看这些 视频 以获得启发)
// Babel 输入: ES2015 箭头函数
[1, 2, 3].map(n => n + 1);
// Babel 输出: ES5 语法实现的同等功能
[1, 2, 3].map(function(n) {
return n + 1;
});
CopyWebpackPlugin
将已存在的单个文件或整个目录复制到生成目录。比如将自定义静态文件夹assets直接复制到生成的目录。
安装
npm install copy-webpack-plugin --save-dev
官网示例
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{ from: "source", to: "dest" },
{ from: "other", to: "public" },
],
options: {
concurrency: 100,
},
}),
],
};
参数说明
姓名 | 类型 | 描述 | |
---|---|---|---|
patterns | {Array<String | Object>} | 为插件指定文件相关复制规则模式 |
options | {Object} | 指定插件的选项 |
姓名 | 类型 | 默认 | 描述 | |
---|---|---|---|---|
from | {String} | undefined | 我们从其中复制文件的全局或路径。 | |
to | {String | Function} | compiler.options.output | 输出路径。默认为webpack配置的输出路径 |
项目配置
//---------------------------------------------------------------------插件
plugins: [
.........
new CopyPlugin({
patterns:[
{from:'public',to:''}
]
})
],
这句配置的意思是:复制public文件夹里面的内容到dist的根目录里。(to的默认目录是webpack设置的输出目录)
打包后,我们发现报错了
仔细观察,可以发现报错原因是dist文件目录下已经有html-webpack-plugin生成的index.html文件了,我们在复制public里面的html时会和它产生冲突。
我们可以创建其他目录试一试
模式patters里面还有这样一个配置
globOptions | {Object} | undefined | 传递给 glob 模式匹配库的选项,包括选项ignore。 |
---|
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({title:"复制文件",template:'./public/index.html'}),
new DefinePlugin({
BASE_URL:"'./'"
}),
new CopyPlugin({
patterns:[
{from:'public',to:'',globOptions:{ ignore: ['**/index.html']}}
]
})
],
在ingnore中加入'index.html'就配置好了复制时忽略的文件了。
**/ 代表忽略的文件是from配置的文件夹里面对应的文件
但是,这样配置后依旧会报错,因为public里面如果忽略的index.html的复制,此时public里面为空了,复制会出错!因此,我们至少需要给publi文件夹里面创建个文件或者文件夹(里面必须有文件)才行。
如图,创建了一个asses文件夹里面有a.txt文件,然后npm run dev打包,打包结果
总结
至此,我们已经完成了webapck5一些最基本概念的学习,相信你对它也有了更深层次的理解!接下来,可以学习笔者的进阶版文章了!