1. Webpack 简介及相关知识
Webpack —— JavaScript应用模块打包工具。
1.1 核心概念:
Webpack 有四个核心概念:
-
entry(入口)
单入口——单页面应用
多入口——多页面应用
-
output(输出)
path: 输出路径(必须是绝对路径) path.resolve
filename: 输出文件名
-
loaders
编译文件
-
plugins(插件)
webpack 功能扩展
1.2 相关知识(Node.js & ES6 模块化)
1.2.1 Node.js
Node.js 是 javascript的服务器版本,Webpack 在执行打包压缩时是依赖 Node.js 的。
1.2.2 ES6 模块化
1.2.2.1 export 导出
//分别导出一个或几个变量(函数)
export let a = 1;
export const b = 2;
export function fn(){
console.log('fn')
}
//导出一个对象
let a = 1;
const b = 2;
function fn(){
console.log('fn')
}
export { a, b, fn };
//导出默认成员——仅能导出一个,默认引入的就是这一个
export default let a = 1;
1.2.2.2 import 导入
//引入所有成员(as —— 给引入的值重命名)
import * as name from 'xxx';
//引入 default 成员
import a from 'xxx';
//只引入:引入 css 或 函数
import 'xxx';
//异步引入
let a =import("./util");
2.开始使用 Webpack —— 搭建项目
2.1 新建项目
npm init
npm i webpack webpack-cli --save-dev
初始化项目,并安装 Webpack 依赖,安装 webpack-cli 才能在命令行运行 webpack 命令。
2.2 webpack 配置
创建并配置 webpack.config.js
- 单入口 | 单输出
根目录内新建src
文件夹,新建index.js
文件。
index.js 文件:
console.log('index')
webpack.config.js 文件
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.min.js'
}
}
- 多入口 | 多输出
根目录内新建src
文件夹,新建index.js
及home.js
文件。
index.js 文件:
console.log('index')
home.js 文件:
console.log('home')
webpack.config.js 文件
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
home: './src/home.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].min.js' //name为对应入口文件的文件名
}
}
2.3 node 命令配置
配置 package.json 文件,验证基本配置
- 修改
script
属性:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
- 运行
npm run build
执行webpack
命令,验证输入输出配置。
根目录内会生成dist
文件夹,里面有对应的文件生成。
2.4 文件引入
在 index.html 中引入打包好的 js 文件
方式一:手动引入
新建 index.html,并直接手动引入 js 文件。
Title
打开 index.html,即已成功引入编译好的 index.min.js 及 home.min.js,执行里面的代码。
3.plugins —— 扩展 webpack 功能
前面已经可以使用 webpack ,但开发过程中,每次都要手动引用 js 文件,更改代码后还要重新编译是很麻烦的,所以我们接下来看下 webpack 的 plugins,来便于开发。
3.1 自动生成 html
可通过插件html-webpack-plugin
,由指定的 html
模板生成新的 html
文件,并自动引入所需 js
等内容。
1)执行 npm i html-webpack-plugin --save-dev
命令,安装依赖。
2)新建 index.ejs
文件
<%= htmlWebpackPlugin.options.title %>
3)修改 webpack.config.js
的配置:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
home: './src/home.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].min.js' //name为对应入口文件的文件名
},
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
})
]
}
4)重新运行 npm run build
,生成新的 dist
包,包内会生成一个新的 index.html
文件,并自动引入了 index.min.js
文件。
index.html 文件
New HTML
3.2 删除旧文件
再次打包时需删除旧文件
3.2.1 更改配置
修改 webpack.config.js
,将输出的 index.html
文件名改为其他的,如 indexNew.html
运行
npm run build
,生成的
dist
内文件列表如下:
之前生成的无用文件
index.html
还在,这里可以使用插件
clean-webpack-plugin
来删除
webpack.config.js
中
output.path
目录中的所有文件。
3.2.2 安装依赖
运行 npm install clean-webpack-plugin --save-dev
安装依赖。
3.2.3 验证
修改 webpack.config.js
配置。
再次运行
npm run build
,生成的
dist
内文件列表如下:
可见之前生成的
indexNew.html
文件已被删除。
3.3 自动打开浏览器,支持热更新
3.3.1 安装依赖
npm i open-browser-webpack-plugin webpack-dev-server --save-dev
3.3.2 更改配置
修改 webpack.config.js
配置如下:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
home: './src/home.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].min.js' //name为对应入口文件的文件名
},
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
}),
// 默认情况下,此插件将删除 webpack output.path目录中的所有文件。
new CleanWebpackPlugin(),
new OpenBrowserPlugin({ url: 'http://localhost:8080' })
]
}
3.3.3 更改 node 命令
修改 package.json
,添加 start
,使得运行 npm run start
可直接执行 webpack-dev-server
命令。
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server",
"build": "webpack"
},
3.3.4 测试
执行 npm run start
:
1)浏览器会自动打开 http://localhost:8080
;
2)修改 index.js
文件,保存文件,浏览器会自动刷新,更新内容。
3.4 代码调试定位 —— sourcemap
代码经过编译、打包后,与源代码已有很大差异,不便于调试。
source-map 会解决这个问题,生成 .map 文件,便于错误定位调试。
开启方法:在 webpack.config.js 文件中添加一行配置:devtool: "source-map"
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
home: './src/home.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].min.js' //name为对应入口文件的文件名
},
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
}),
// 默认情况下,此插件将删除 webpack output.path目录中的所有文件。
new CleanWebpackPlugin(),
new OpenBrowserPlugin({ url: 'http://localhost:8080' })
],
//编译前文件调试
devtool: "source-map"
}
如上,即可。可通过打印等方式进行测试。
4. loader
loader ——文件编译。
4.1 ES6 & React 编译 —— babel-loader
Babel 是一个 JavaScript 编译器,Webpack 使用 babel-loader 来编译 ES6。
此处我们要创建一个 React 项目,所以同时也要添加 React 的相关依赖即配置。
4.1.1 安装依赖
yarn add babel babel-core [email protected] babel-preset-env babel-preset-react
注: 现在直接安装 babel-core 和 babel-loader,安装的版本不对应会报错,所以我在安装时指定了 babel-loader 的版本,避免这个问题。
4.1.2 更改配置
修改 webpack.config.js 如下:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
home: './src/home.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].min.js' //name为对应入口文件的文件名
},
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
}),
// 默认情况下,此插件将删除 webpack output.path目录中的所有文件。
new CleanWebpackPlugin(),
new OpenBrowserPlugin({ url: 'http://localhost:8080' })
],
module: {
rules: [
{
test: /\.(js|jsx)$/i,
use: [{
loader: "babel-loader",
options: {
presets: ["env", "react"],
}
}],
exclude: /node_modules/
}
]
}
}
4.1.3 修改文件
修改 index.js 及 home.js 文件为 react 语法
home.js文件
import React, { Component } from 'react';
export default class Home extends Component {
render() {
return (
home page
);
}
}
index.js文件
import React from 'react';
import { render } from 'react-dom';
import Home from './home';
render( , document.getElementById('root'));
此时,页面效果如下:
如上,React 语法被成功编译。
4.2 处理 CSS 文件 —— style-loader & css-loader
4.2.1 修改文件
创建 index.css 文件
.container {
background-color: aquamarine
}
修改 home.js 文件如下:
import React, { Component } from "react";
import './index.css'
export default class Home extends Component {
render() {
return (
home page
);
}
}
npm run start
重新启动项目,项目会报错如下:
4.2.2 安装依赖
npm i style-loader css-loader --save-dev
4.2.3 更改配置
修改 webpack.config.js 配置如下:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = {
entry: {
index: './src/index.js',
home: './src/home.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].min.js' //name为对应入口文件的文件名
},
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
}),
// 默认情况下,此插件将删除 webpack output.path目录中的所有文件。
new CleanWebpackPlugin(),
new OpenBrowserPlugin({ url: 'http://localhost:8080' })
],
module: {
rules: [
{
test: /\.(js|jsx)$/i,
use: [{
loader: "babel-loader",
options: {
presets: ["env", "react"],
}
}],
exclude: /node_modules/
},
{
test: /\.css$/i, // 针对 .css 后缀的文件设置 loader
use: ['style-loader', 'css-loader']
}
]
}
}
添加了针对 .css 文件的 loader,来对 .css 文件进行处理。
注:
- css-loader:读取并编译 js 中引入的 css 文件
- style-loader: html 中创建一个
标签,里面是 css-loader 解析出的 css 样式。
- webpack 配置中,style-loader 必须在 css-loader 之前,顺序不可变,即必须先由 css-loader 处理,再由 style-loader 处理。
4.3 处理 LESS 文件 —— less-loader
4.3.1 更改文件
创建 index.less 文件如下:
.container {
background-color: aquamarine
}
修改 home.js 文件如下:
import React, { Component } from "react";
import './index.less'
export default class Home extends Component {
render() {
return (
home page
);
}
}
4.3.2 安装依赖
npm i less less-loader --save-dev
4.3.3 更改配置
修改 webpack.config.js 配置,module/rules 下添加对于 less 文件的解析:
{
test: /\.less$/, // 针对 .less 后缀的文件设置 loader
use: ['style-loader', 'css-loader', 'less-loader']
}
4.3.4 验证
重新启动项目,less 文件生效,页面效果如下:
4.4 添加浏览器前缀 —— postcss-loader & autoprefixer
autoprefixer 按照浏览器使用量决定需要添加哪些浏览器前缀,postcss-loader 按照 autoprefixer 的结果来添加浏览器前缀。
4.4.1 更改文件
修改 index.less 文件如下:
.container {
background-color: aquamarine;
transform: rotate(0deg);
transition-duration: 1000ms;
}
.container:hover {
background-color: aquamarine;
transform: rotate(360deg)
}
4.4.2 安装依赖
npm i less postcss-loader autoprefixer --save-dev
4.4.3 更改配置
修改 webpack.config.js 配置,给 css、less 文件添加 postcss-loader 的解析,
有两种方式:
1)在 webpack.config.js 的 modele/rules 内指定 postcss-loader 的配置
{
test: /\.css$/, // 针对 .css 后缀的文件设置 loader
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
},
{
test: /\.(less|css)$/, // 针对 .less 后缀的文件设置 loader
use: ['style-loader', 'css-loader', 'less-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
}
2)修改 webpack.config.js,并单独创建 postcss.config.js ,进行配置
修改webpack.config.js 文件,modele/rules 添加如下配置:
{
test: /\.css$/, // 针对 .css 后缀的文件设置 loader
use: ['style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.(less|css)$/, // 针对 .less 后缀的文件设置 loader
use: ['style-loader', 'css-loader', 'less-loader', 'postcss-loader']
}
postcss.config.js 文件
module.exports = {
plugins: [require('autoprefixer')]
}
相对比而言,我更倾向于第一种,以免创建太多的配置文件,造成混乱。
4.4.4 验证
重新启动项目,查看浏览器元素,样式前缀已添加成功。
注: 如有需要,可以自己进行支持的浏览器版本配置,有两种方式:
1)添加 .broserslistrc 文件,进行配置
last 3 version //支持每个版本的最近三个版本
>2% //大于百分之二的用户使用
2)无需创建新文件,直接在 package.json 文件内添加属性进行配置
"browserslist": [
"last 5 version",
" >1%"
]
重新启动,效果如下:
按照设置的规则重新对我们的 css 样式添加了前缀。
4.5 处理图片 —— file-loader & url-loader
4.5.1 引入图片
src 文件夹下创建 images 文件夹,并添加 bg.png 图片
修改 index.less 文件如下:
.container {
width: 100%;
height: 600px;
background: url('./images/bg.png');
background-size: 100%;
background-repeat: no-repeat;
}
重新启动项目,会报错如下:
4.5.2 安装依赖 —— file-loader
如上,需要配置对应的 loader 来解析图片。
执行npm i file-loader --save-dev
,安装依赖。
4.5.3 更改配置 —— file-loader
修改 webpack.config.js 配置, modole/rule 下添加配置如下:
{
test: /\.(png|jpg|jpeg|gif)$/i, // 针对 .png|.jpg|.jpeg | .gif 后缀的图片设置 loader
use: [{
loader: 'file-loader',
options: {
outputPath: 'imgs/', //相对于 output.path 的输出目录
}
}]
}
重启项目,则引入图片成功,效果如下:
file-loader 可以解析项目中的图片引入,根据配置,将图片输出到相应路径,并修改打包后文件中的引用路径,使其指向打包后的文件。
4.5.4 安装依赖 —— url-loader
针对图片引用,url-loader 封装了 file-loader。
增加了 limit 属性,文件大小大于 limit 值,会调用 file-loader 进行处理;
文件大小小于 limit 值,会将文件转为 base64 格式,以减少文件请求次数。
安装 url-loader 依赖
npm i url-loader --save-dev
4.5.5 更改配置 —— url-loader
修改 webpack.config.js 配置, modole/rule 下添加配置如下:
{
test: /\.(png|jpg|jpeg|gif)$/i, // 针对 .png|.jpg|.jpeg | .gif 后缀的图片设置 loader
use: [{
loader: 'url-loader',
options: {
outputPath: 'imgs/', //相对于 output.path 的输出目录
limit: 8*1024 //大于此数值依旧用 file-loader处理,打包到 imgs 里面,小于此数值的转为 base64,以减少文件请求次数
}
}]
}
重启项目,图片引用成功,执行 npm run build
,可发现,大于 8k 的图片会被打包到 dist/imgs 文件夹内,小于 8k 的则不会被打包输出,而是转为 base64 。
4.6 处理文字 —— file-loader & url-loader
4.6.1 引入字体
src 文件夹下一个字体文件——difital-7.ttf,这是一个液晶字体文件,修改 index.less 文件如下:
@font-face{
font-family: "digital";
src: url(./digital-7.ttf);
}
.container {
width: 100%;
height: 600px;
background: url('./images/bgimg.jpg');
background-size: 100%;
background-repeat: no-repeat;
font-family: "digital"
}
保存项目编译后,会报错如下:
4.6.2 更改配置
同图片,需要配置对应的 url-loader 来解文字片,修改 webpack.config.js 配置, modole/rule 下添加配置如下:
{
test: /\.(eot|svg|ttf|woff|woff2)$/i, // 针对 eot|svg|ttf|woff|woff2 后缀的字体设置 loader
use: [{
loader: 'url-loader',
options: {
outputPath: 'fonts/', //相对于 output.path 的输出目录
limit: 4*1024 //大于此数值依旧用 file-loader处理,打包到 fonts 里面,小于此数值的转为 base64,以减少文件请求次数
}
}]
}
重新启动项目,则字体引用成功,效果如下:
通过修改 limit 值,重新打包,可测试 limit 值设置是否有效。
5. 开发/生产模式
5.1 开发/生产模式的区别
- 开发模式需要进行调试,需要自动打开浏览器,而生产模式不需要
- 生产模式需要设置打包文件目录,需要启用压缩,忽略错误。
上述区别需要通过两个设置来实现。
5.1.1 设置 process.env.NODE_ENV
在 webpack.config.js 中通过判断 env 的值,来进行不同的配置
修改 package.json 来设置 process.env.NODE_ENV:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --env.development",
"build": "webpack --env.production",
"eslint": "eslint --init"
},
根目录下新建 config 文件夹,并在下面新建 webpack.production.config 和 webpack.development.config 文件。
webpack.config.js 文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = function(env, argv) {
console.log('env', env)
env = env || { development: true }
return {
entry: {
index: './src/index.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/i,
use: [{
loader: "babel-loader",
options: {
presets: ["env", "react"],
}
}],
exclude: /node_modules/
},
{
test: /\.css$/i, // 针对 .css 后缀的文件设置 loader
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
},
{
test: /\.(less|css)$/i, // 针对 .less 后缀的文件设置 loader
use: ['style-loader', 'css-loader', 'less-loader', {
loader: 'postcss-loader',
options: {
plugins: [require('autoprefixer')]
}
}]
},
{
test: /\.(png|jpg|jpeg|gif)$/i, // 针对 .png|.jpg|.jpeg | .gif 后缀的图片设置 loader
use: [{
loader: 'url-loader',
options: {
outputPath: 'imgs/', //相对于 output.path 的输出目录
limit: 8*1024 //大于此数值依旧用 file-loader处理,打包到 imgs 里面,小于此数值的转为 base64,以减少文件请求次数
}
}]
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/i, // 针对 eot|svg|ttf|woff|woff2 后缀的字体设置 loader
use: [{
loader: 'url-loader',
options: {
outputPath: 'fonts/', //相对于 output.path 的输出目录
limit: 4*1024 //大于此数值依旧用 file-loader处理,打包到 fonts 里面,小于此数值的转为 base64,以减少文件请求次数
}
}]
}
]
},
...env.production ? require('./config/webpack.production.config') : require('./config/webpack.development.config')
}
}
5.1.2 设置 webpack 的 mode ,来决定 webpack 的优化级别。
mode 有三种模式:
- none: 不优化
- development: 输出调试信息
- production: 最高优化,启用压缩,忽略错误
webpack.production.config 文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin')
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = {
mode: 'production',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.min.js' //name为对应入口文件的文件名
},
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title html模板时不生效
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
}),
// 默认情况下,此插件将删除 webpack output.path目录中的所有文件。
new CleanWebpackPlugin()
]
}
webpack.development.config 文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OpenBrowserPlugin = require('open-browser-webpack-plugin') //自动打开浏览器
const path = require('path')
module.exports = {
mode: 'development',
output: {
filename: 'main.js' //name为对应入口文件的文件名
},
//编译前文件调试
devtool: "source-map",
plugins: [
new HtmlWebpackPlugin({
// 打包输出HTML
title: 'New HTML', //打包后生成 html 的 title
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true // 压缩内联 css
},
filename: 'index.html', // 生成后的文件名
template: 'index.ejs' // 根据此模版生成 HTML 文件
}),
new OpenBrowserPlugin({ url: 'http://localhost:8080' })
],
}