# 1.webpack应用实例
# 1.1.快速上手
初始化项目
mkdir webpack-demo
cd webpack-demo
npm init -y
安装webpack
npm i webpack@4.41.0 webpack-cli@3.3.9 -D
零配置使用webpack,webpack约束源文件目录必须为src, 默认配置文件为 src/index.js
我们新建目录如下:
webpack-demo
├── node_modules
├── src
| ├── index.js
| └── module1.js
├── package.json
├── package-lock.json
index.js
const fn1 = require("./module1")
fn1()
module1.js
function fn1() {
alert(1)
}
module.exports = fn1
运行
npx webpack
注意: npx是npm的一个包运行器,是npm 5.2版本后提供的一个工具,它会自动找到项目下 node_modules/.bin下的相关命令来运行
接下来,我们将运行命令配置到package.json
"scripts": {
"dev": "webpack --mode development"
},
配置完成后,我们就可以使用npm run dev来执行webpack命令了,注意: --mode是webpack的模式,有开发模式(development)和生产模式(production)
# 1.2.自定义配置文件
在前面我们使用的是webpack4的零配置运行的项目,如果我们需要实现更多的功能,我们需要自定义配置,比如:我们可以更改入口文件名、可以使用各种loader、插件等,webpack的配置文件名字通常是固定的,即webpack.config.js,配置都往这个文件里面写,因此,我们需要在项目根目录下创建好这个文件
// 引入nodejs的path模块来处理路径
const path = require("path")
module.exports = {
// 模式配置
mode: "development",
// 入口文件 通常使用绝对路径
entry: path.resolve(__dirname, "./src/index.js"),
// 出口配置
output: {
filename: "app.js",
path: path.resolve(__dirname, "dist")
}
}
上面已经在配置文件里设置了模式,因此,在package.json中就不需要传入mode这个参数了
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack"
},
# 1.3.环境变量
一个项目从开发到上线面临不同的环境,在开发阶段我们通常称为开发环境,部署到线上的环境我们通常称为生产环境,环境的不同,配置也是不一样的,因此,我们有必要来区分环境:
1.注入环境变量
const path = require('path')
const webpack = require('webpack')
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'src', 'app.js'),
output: {
filename: 'app.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('prod')
})
]
}
注入变量后,我们就可以在各个模块中使用环境变量了,例如,在app.js中去使用
console.log(ENV)
if (ENV === 'dev') {
console.log('发送请求:http://localhost:8080')
}
if (ENV === 'prod') {
console.log('发送请求:http://nodeing.com')
}
2.使用cross-env设置环境变量
npm install --save-dev cross-env
在package.json中使用
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config config/webpack.config.js"
}
# 1.4.拆分配置文件
我们可以将配置拆分到单独的文件中去方便管理,拆分后目录结构如下:
webpack-demo
├── config
| ├── webpack.base.js 公共配置文件
| ├── webpack.dev.js 开发环境配置
| └── webpack.prod.js 生产环境配置
├── node_modules
├── src
| ├── index.js
| └── module1.js
├── package.json
├── package-lock.json
安装webpack-merge插件来合并配置项
npm i webpack-merge@4.2.2 -D
webpack.base.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: path.resolve(__dirname, '../', 'src', 'app.js'),
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../', 'dist')
},
plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('prod')
})
]
}
webpack.dev.js
const base = require('./webpack.base')
const { smart } = require('webpack-merge')
module.exports = smart(base, {
mode: 'development'
})
webpack.prod.js
const base = require('./webpack.base')
const { smart } = require('webpack-merge')
module.exports = smart(base, {
mode: 'production'
})
# 1.5.webpack-dev-server
webpack-dev-server是我们在开发阶段需要用到的一个服务器,它会把代码打包到内存,我们可以通过http的方式访问到打包到内存的代码
安装
npm i webpack-dev-server@3.8.1 -D
修改package.json的启动命令
"dev": "webpack-dev-server --config ./config/webpack.dev.js",
增加相关配置
webpack.dev.js
const base = require('./webpack.base')
const { smart } = require('webpack-merge')
const path = require('path')
module.exports = smart(base, {
mode: 'development',
devServer: {
// 默认访问的根路径,webpack-dev-server会去这个路径下找到index.html文件并返回
contentBase: path.join(__dirname, '../dist'),
// 开启gzip压缩,增加文件返回速度
compress: true,
port: 8000,
// 启动后自动打开浏览器
open: true
}
})
# 1.6.自动创建html文件
在前面webpack-dev-server中,我们设置的启动访问路径是项目下的dist目录,我们需要在这个dist目录下手动去创建一个index.html文件,并且将打包后的js文件手动添加到index.html文件中,这个时候,我们通过浏览器访问才能看到效果,在开发中,能用工具做的事情我们尽量用工具帮我们完成,我们可以使用 html-webpack-plugin 这个插件来帮我们做这件事情,它的作用就是根据模版文件生成一个静态文件,然后自动将打包好的js插入到这个 html文件中
首先,安装这个插件
npm i html-webpack-plugin@3.2.0 -D
webpack.dev.js 增加相关配置
const base = require('./webpack.base')
const { smart } = require('webpack-merge')
const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = smart(base, {
mode: 'development',
devServer: {
// 默认访问的根路径,webpack-dev-server会去这个路径下找到index.html文件并返回
contentBase: path.join(__dirname, '../dist'),
// 开启gzip压缩,增加文件返回速度
compress: true,
port: 8000,
// 启动后自动打开浏览器
open: true
},
plugins: [
new htmlWebpackPlugin({
// 设置标题
title: '螺钉课堂',
// 设置模版路径
template: path.resolve(__dirname, '../public/index.html'),
// 设置打包的文件名
filename: 'index.html',
// 最小输出配置
minify: {
// 折叠空格
collapseWhitespace: true
}
})
]
})
注意,如果要使用配置中的title,模版中需要使用这种方式来获取title值
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
# 1.7.自动清除上次打包的文件
当我们执行打包操作,自动生成一个打包文件,如果前面有已经生成的文件,我们通常会手动去删除前一次已经生成的文件,以避免产生混乱,这个时候可以使用clean-webpack-plugin来帮我们完成这个操作
安装
npm i clean-webpack-plugin@3.0.0 -D
webpack.base.js 增加相关配置
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: path.resolve(__dirname, '../', 'src', 'app.js'),
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../', 'dist')
},
plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('prod')
}),
// 每次打包之前,先清除dist目录下的文件
new CleanWebpackPlugin()
]
}
# 1.8.解析css文件
解析css文件需要两个loader,css-loader和style-loader,css-loader的作用就是解析css的语法,解析完成后传给style-loader, style-loader就会生成style标签,然后把这些css代码插入到html文件中,这样在html就会有样式了
npm i css-loader@3.2.0 style-loader@1.0.0 -D
webpack.base.js 配置文件中增加配置项目
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: path.resolve(__dirname, '../', 'src', 'app.js'),
output: {
filename: 'app.js',
path: path.resolve(__dirname, '../', 'dist')
},
plugins: [
new webpack.DefinePlugin({
ENV: JSON.stringify('prod')
}),
// 每次打包之前,先清除dist目录下的文件
new CleanWebpackPlugin()
],
// 每个文件都称为模块,module就是用来配置解析各种文件模块的,module里面主要涉及到几个问题,
// 1 哪些模块文件需要被转换? 2 用什么loader去转? 3 用什么规则去转?
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
// css文件解析后,需要用后面的1个loader来处理,如果后面有多个loader,需要更改一下importLoaders的数量
importLoaders: 1
}
},
// "css-loader",
'postcss-loader'
]
}
]
}
}
TIP
注意: loader的写法有三种: 1、字符串 2 数组 3、对象, 前面我们使用的就是字符串的形式
实际上,在没有对loader进行参数配置的时候,多个loader 我们更倾向于第二种写法,给loader传一个数组
module: {
rules: [
{
test: /\.css$/,
use: ["css-loader", "style-loader"]
}
]
},
WARNING
注意: css-loader和style-loader是需要注意使用顺序的,如果配置成字符串的形式,css-loader要放在下面,如果配置成数组的形式,css-loader要放在前面,顺序不正确会报错
# 1.9.自动给css添加前缀
在写css的时候,某些属性我们可能会增加浏览器前缀,这个功能我们可以使用postcss-loader来帮我们实现,其中postcss-loader需要一个autoprefix插件来实现自动添加前缀
npm i postcss-loader@3.0.0 autoprefixer@9.6.1 -D
在项目文件夹下,新建一个postcss.config.js来给postcss-loader增加配置
postcss.config.js
module.exports = {
plugins: [require("autoprefixer")]
}
接下来,需要在项目下,新建一个.browserslistrc文件,配置相关浏览器支持情况
// 项目要覆盖95%以上浏览器
cover 95%
最后,在webpack.base.js中配置好css文件的处理loader
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
// css文件解析后,需要用后面的1个loader来处理,如果后面有多个loader,需要更改一下importLoaders的数量
importLoaders: 1
}
},
// "css-loader",
"postcss-loader"
]
},
# 1.10.生产环境单独抽离css文件
前面我们用style-loader把打包的css代码都插入到html的style标签里,如果我们想把css文件单独打包到某个文件中,再通过外联的样式来使用,这种需求,我们需要通过mini-css-extract-plugin插件来实现
npm i mini-css-extract-plugin@0.8.0 -D
增加相关配置:
webpack.prod.js
const dev = require("../build/webpack.dev")
const prod = require("../build/webpack.prod")
const path = require("path")
const merge = require("webpack-merge")
const htmlWebpackPlugin = require("html-webpack-plugin")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = function(env) {
const isDev = env.development
const base = {
entry: path.resolve(__dirname, "../src/index.js"),
output: {
filename: "index.js",
path: path.resolve(__dirname, "../dist")
},
// 每个文件都称为模块,module就是用来配置解析各种文件模块的,module里面主要涉及到几个问题,1 哪些模块文件需要被转换? 2 用什么loader去转? 3 用什么规则去转?
module: {
rules: [
{
test: /\.css$/,
use: [
// 如果是开发环境,使用style-loader 不是开发环境用MiniCssExtractPlugin的loader
isDev ? "style-loader" : MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
// css文件解析后,需要用后面的1个loader来处理,如果后面有多个loader,需要更改一下importLoaders的数量
importLoaders: 1
}
},
// "css-loader",
"postcss-loader"
]
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
},
plugins: [
// 如果是开发环境,就不使用样式抽离的插件
!isDev &&
new MiniCssExtractPlugin({
// 配置一下抽离出来的css文件名
filename: "css/main.css"
}),
new htmlWebpackPlugin({
title: "螺钉课堂",
template: path.resolve(__dirname, "../public/index.html"),
filename: "index.html",
minify: {
collapseWhitespace: isDev ? false : true
}
}),
// 每次打包之前,先清除dist目录下的文件
new CleanWebpackPlugin()
].filter(Boolean)
}
if (isDev) {
return merge(base, dev)
} else {
return merge(base, prod)
}
}
# 1.11.压缩css
默认情况下,webpack只能压缩js而不能压缩css,如果想压缩css,需要单独使用插件来完成
安装插件
npm i optimize-css-assets-webpack-plugin@5.0.3 -D
在生产环境下我们才要压缩css,因此,我们需要将配置放到webpack.prod.js
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
module.exports = {
mode: "production",
// 配置优化项目
optimization: {
// 配置压缩方案
minimizer: [
// 配置css压缩方案,注意,css手动选择压缩方式后,webpack就不会帮助压缩js文件了,也需要手动配置
new OptimizeCSSAssetsPlugin({})
]
}
}
安装了这个插件后,js压缩失效,因此需要单独安装js压缩插件
npm i terser-webpack-plugin@2.1.0 -D
webpack.prod.js配置
const TerserJSPlugin = require("terser-webpack-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
module.exports = {
mode: "production",
// 配置优化项目
optimization: {
// 配置压缩方案
minimizer: [
new TerserJSPlugin({}),
// 配置css压缩方案,注意,css手动选择压缩方式后,webpack就不会帮助压缩js文件了,也需要手动配置
new OptimizeCSSAssetsPlugin({})
]
}
}
# 1.12.预处理器文件处理
# 1.sass文件
sass这种css预处理器是以.scss结尾,需要用node-sass和sass-loader来处理
安装loader
npm i node-sass sass-loader -D
增加相关配置:
webapck.base.js
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"]
}
]
}
WARNING
注意: 如果出现css文件中引入sass文件的情况,只用css-loader是不能解析的,必须加入sass-loader,并且需要在css-loader中声明,使用sass-loader才可以
举个例子: index.css 引入 a.css , a.css引入a.scss,这个时候需要增加配置才行
{
test: /\.css$/,
use: [
"style-loader",
// 需要把原来的字符串换成对象的形式,然后给css-loader配置参数
{
loader: "css-loader",
options: {
// 如果css文件引入其他文件(@import), 使用后面的1个loader来处理
importLoaders: 2
}
},
// "css-loader",
"postcss-loader",
"sass-loader"
]
},
# 2.less文件
# 3.stylus文件
# 1.13.解析图片
解析图片需要用到file-loader,它的作用是将图片拷贝到打包后到输出目录,并且返回一个可以引用的地址
安装file-loader
npm i file-loader@4.2.0 -D
增加图片支持配置
webpack.base.js
{
test: /\.(jpe?g|png|gif|bmp)$/,
use: "file-loader"
}
当图片比较小的时候,我希望把这个图片转成base64格式的字符串,这样的好处是少发http请求,完成这个转base64字符串需求需要用到url-loader, url-loader是在file-loader上做的一层封装, 我们需要将这个loader先安装上
npm i url-loader@2.1.0 -D
修改相关配置
{
test: /\.(jpe?g|png|gif|bmp|svg)$/,
use: {
loader: "url-loader",
options: {
// 8k以下的图片转成base64
limit: 8192,
name: "img/[name].[ext]"
}
}
}
如果要支持字体图标,可以再新建一条匹配规则,使用file-loader来处理即可
相关配置
{
test: /\.(eof|ttf|woff|woff2)$/,
use: "file-loader"
}
# 1.14.解析js文件
ES的一些比较新的语法浏览器是不支持的,因此,我们需要将这些新的语法转成比较通用的语法,babel就是一个很好的转换工具,我们先安装需要的工具
npm i @babel/core@7.6.2 babel-loader@8.0.6 @babel/preset-env@7.6.2 -D
@babel/core是babel的核心,它转换语法的时候会用到 @babel/preset-env这个插件包, @babel/preset-env这些插件包里包含了各种新语法的转换功能,babel这个工具是可以独立运行的,如果想要和webpack结合,还需要安装一个babel-loader, babel-loader的作用就是把ES的一些新语法送到@babel/core,@babel/core再去调用@babel/preset-env插件来完成转换
增加配置
{
test: /\.js$/,
use: "babel-loader"
}
babel-loader 会去调一个babel的配置文件,我们需要在项目根目录下创建.babelrc的配置文件
{
"presets": ["@babel/preset-env"]
}
如果使用了一些@babel/preset-env不能转换的语法,你还可以单独安装对应的转换插件来解决,例如:
class Person {
name = "小红"
}
这种语法需要用 @babel/plugin-proposal-class-properties 这个插件来转换 安装插件
npm i @babel/plugin-proposal-class-properties@7.5.5 -D