安装webpack
1. 先在npm init初始化项目,生成package.json
webpack4中除了正常安装webpack之外,还需要单独安装webpack-cli
npm i webpack webpack-cli -D
★ i -D 是install --save-dev 的简写,指安装模块并保存到 本地的package.json 的 devDependencies中
2. 在项目下创建一个webpack.config.js文件来配置webpack
module.exports = {
entry: '', // 入口文件
output: {}, // 出口文件
module: {}, // 处理对应模块
plugins: [], // 对应的插件
devServer: {}, // 开发服务器配置
mode: 'development' // 模式配置
}
以上就是webpack的基本配置模块
★ 启动devServer需要安装webpack-dev-server
npm i webpack-dev-server -D
单入口文件的配置
下面我们重新开始配置 webpack,(下面是webpack.config.js文件)
const path = require('path');
module.exports = {
entry: './src/index.js', // 入口文件
output: {
filename: 'bundle.js', // 打包后的文件名称
path: path.resolve('dist') // 打包后的目录,必须是绝对路径
}
}
现在可以说是实现了最简单的webpack配置,接下来需要在生成的package.json文件内添加打包命令执行的语句,然后创建src目录以及index.js文件
package.json的语句添加如下
目录结构如下
执行npm run build命令后目录结构如下
其中dist就是我们打包后的文件,这也是生产环境下,上线需要的文件
而npm run dev运行的是我们开发环境下打包的文件,当然由于devServer帮我们把文件放到内存中了,所以并不会输出打包后的dist文件夹
多入口文件的配置
多个入口可以有两种实现方式进行打包
一种所有文件都打包到一起的,可以写一个数组,实现多个文件打包
另一种就是每一个文件都单独打包成一个文件的
下面就来看看这两种方式的写法
let path = require('path');
module.exports = {
// 1.写成数组的方式就可以打出多入口文件,不过这里打包后的文件都合成了一个
// entry: ['./src/index.js', './src/login.js'],
// 2.真正实现多入口和多出口需要写成对象的方式
entry: {
index: './src/index.js',
login: './src/login.js'
},
output: {
// 1. filename: 'bundle.js',
// 2. [name]就可以将出口文件名和入口文件名一一对应
filename: '[name].js', // 打包后会生成index.js和login.js文件
path: path.resolve('dist')
}
}
配置Html模板
文件都打包好了,我们在dist目录下创建一个html文件,然后去引用打包后的js,这其实并不合理,实际开发中也不会这样去做
我们需要的是,实现html的打包功能,并且可以通过一个模板实现,打包出引用好路径的html来
这时就需要用到一个常用的插件html-webpack-plugin了,用之前我们来安装一下它
npm i html-webpack-plugin -D
因为是个插件,所以需要在config.js里引用一下
const path = require('path');
// 插件都是一个类,所以我们命名的时候尽量用大写开头
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
// 添加hash可以防止文件缓存,每次都会生成4位的hash串
filename: 'bundle.[hash:4].js',
path: path.resolve('dist')
},
plugins: [
// 通过new一下这个类来使用插件
new HtmlWebpackPlugin({
// 用哪个html作为模板
// 在src目录下创建一个index.html页面当做模板来用
template: './src/index.html',
hash: true, // 会在打包好的bundle.js后面加上hash串
})
]
}
通过上面的配置后,我们再npm run build打包看一下现在是个什么样子了
多页面开发,怎么配置多页面
如果开发的时候不只一个页面,我们需要配置多页面,那么需要怎么来搞呢?不用担心,html-webpack-plugin插件自有办法
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 多页面开发,怎么配置多页面
entry: {
index: './src/index.js',
login: './src/login.js'
},
// 出口文件
output: {
filename: '[name].js',
path: path.resolve('dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
chunks: ['index'] // 对应关系,index.js对应的是index.html
}),
new HtmlWebpackPlugin({
template: './src/login.html',
filename: 'login.html',
chunks: ['login'] // 对应关系,login.js对应的是login.html
})
]
}
继续使用npm run build打包看效果
webpack对css的解析需要用到loader,所以我们需要先提前安装好,后续好方便使用
引用CSS文件
可以在src/index.js里引入css文件
需要下载一些解析css样式的loader
npm i style-loader css-loader -D
引入less文件的话,也需要安装对应的loader
npm i less less-loader -D
下面我们来看一下如何配置css文件的解析
index.js
import './css/style.css'; // 引入css
import './less/style.less'; // 引入less
console.log('这里是打包文件入口-index.js');
webpack.config.js
module.exports = {
entry: {
index: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve('dist')
},
module: {
rules: [
{
test: /\.css$/, // 解析css
use: ['style-loader', 'css-loader'] // 从右向左解析
/*
也可以这样写,这种方式方便写一些配置参数
use: [
{loader: 'style-loader'},
{loader: 'css-loader'}
]
*/
}
]
}
}
此时打包后的css文件是以行内样式style的标签写进打包后的html页面中
如何才能直接用link的方式引入进去呢?
这时候需要把css拆分出来
extract-text-webpack-plugin插件就是将打包到js里的css文件进行一个拆分
拆分CSS
@next表示可以支持webpack4版本的插件
npm i extract-text-webpack-plugin@next -D
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 拆分css样式的插件
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filaneme: 'bundle.js',
path: path.resolve('dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract({
// 将css用link的方式引入就不再需要style-loader了
use: 'css-loader'
})
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
// 拆分后会把css文件放到dist目录下的css/style.css
new ExtractTextWebpackPlugin('css/style.css')
]
}
此时拆分完css后,打包的html页面就以link的方式去引入css了
处理图片
处理图片时也需要加载loader
npm i file-loader url-loader -D
如果是在css文件里引入的如背景图,则需要指定一下图片的相对路径
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextWebpackPlugin.extract({
use: 'css-loader',
publicPath: '../'
})
},
{
test: /\.(jpe?g|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 8192, // 小于8k的图片自动转成base64格式,并且不会存在实体图片
outputPath: 'images/' // 图片打包后存放的目录
}
}
]
}
]
}
}
在css中指定了publicPath路径这样就可以根据相对路径引用到图片资源了
页面img引用图片
页面中经常会用到img标签,img引用的图片地址也需要一个loader来帮我们处理好
npm i html-withimg-loader -D
module.exports = {
module: {
rules: [
{
test: /\.(htm|html)$/,
use: 'html-withimg-loader'
}
]
}
}
这样打包后的html文件下img标签就可以正常引用图片路径了
引用字体图片和svg图片
字体图标和svg图片都是通过file-loader来解析
module.exports = {
module: {
rules: [
{
test: /\.(eot|ttf|woff|svg)$/,
use: 'file-loader'
}
]
}
}
添加CSS3前缀
通过postcss中的autoprefixer可以实现将CSS3中,一些需要兼容写法的属性添加响应的前缀
安装loader
npm i postcss-loader autoprefixer -D
安装后,我们还需要写一个config的配置文件,在项目根目录下创建一个postcss.config.js文件,配置如下:
module.exports = {
plugins: [require('autoprefixer')] // 引用该插件即可了
}
然后在webpack里配置postcss-loader
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
}
}
转义ES6
在实际开发中,我们去使用ES6及之后的api去写代码,来提高我们写代码的速度,不过由于低版本浏览器不兼容问题,所以必须要转换成兼容的代码,其中
Babel会将ES6的代码转成ES5的代码
首先安装babel
npm i babel-core babel-loader babel-preset-env babel-preset-stage-0 -D
由于要兼容的代码包含ES6及之后的版本,所以我们可以创建一个.babelrc的文件来配置一下对这些版本的支持
// .babelrc
{
"presets": ["env", "stage-0"] // 从右向左解析
}
然后在webpack里配置一下babel-loader就可以做到代码转成ES5了
module.exports = {
module: {
rules: [
{
test:/\.js$/,
use: 'bable-loader',
include: /src/, // 只转化src目录下的js
exclude: /node_modules/ // 排除掉node_modules,优化打包速度
}
]
}
}
在我们每次npm run build的时候都得手动将dist目录下的文件都清空,然后再把打好包的文件放进去这样会很麻烦,而这里提供一个clean-webpack-plugin插件,实现了打包前自动清空dist目录
npm i clean-webpack-plugin -D
let CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins: [
// 打包前先清空
new CleanWebpackPlugin('dist')
]
}
启动静态服务器
执行npm run dev命令后,会启动静态服务器,我们访问localhost:3000端口就可以看到开发的页面内容了
如果devServer里open设为true后,会自动打开浏览器
module.exports = {
devServer: {
contentBase: './dist',
host: 'localhost', // 默认是localhost
port: 3000, // 端口
open: true, // 自动打开浏览器
hot: true // 开启热更新
}
}
npm run dev命令下,打包的文件存在于内存中,并不会产生在dist目录
热更新和自动刷新的区别
如果hot为true,就代表开启了热更新
但是这并没那么简单,因为热更新还需要配置一个webpack自带的插件并且还要在js文件里检查是否有module.hot
下面就让我们直接看下代码是如何实现的
webpack.config.js
let webpack = require('webpack');
module.exports = {
plugins: [
// 热替换,热替换不是刷新
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist',
hot: true,
port: 3000
}
}
此时还没完,虽然配置了插件和开启了热更新,但实际上并不会生效,还需要
index.js
let a = 'hello world';
document.body.innerHTML = a;
console.log('这是webpack打包的入口文件');
// 还需要在主要的js文件里写入下面这段代码
if (module.hot) {
// 实现热更新
module.hot.accept();
}
在index.js中的内容,如果将变量a的值进行修改保存后,会在不刷新页面的情况下直接修改掉,这样就实现了热更新
热更新的好处在于其中某一个组件被修改的时候就会针对这个组件进行热更新
resolve解析
在webpack的配置中,resolve我们常用来配置别名和省略后缀名
module.exports = {
resolve: {
// 别名
alias: {
$: './src/jquery.js'
},
// 省略后缀
extensions: ['.js', '.json', '.css']
},
}
这个配置在webpack中比较简单,我们也就不再叙述了,下面来看点干货
提取公共代码
在webpack4之前,提取公共代码都是通过CommonsChunkPlugin插件来办到的。4以后,内置了一个一模一样的功能,而且起了一个好听的名字叫“优化”
1.如何提取公共代码
// 假设a.js和b.js都同时引入了jquery.js和一个写好的utils.js
a.js和b.js
import $ from 'jquery';
import {sum} from 'utils';
这两个js中的公共部分的代码就是jquery和utils里的代码
针对第三方插件和写好的公共文件
module.exports = {
entry: {
a: './src/a.js',
b: './src/b.js'
},
output: {
filename: '[name].js',
path: path.resolve('dust')
},
提取公共代码
optimization: {
splitChunks: {
cacheGroups: {
vendor: { // 抽离第三方插件
test: /node_modules/, // 指定是node_modules下的第三方包
chunks: 'initial',
name: 'vendor', // 打包后的文件名,任意命名
// 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
priority: 10
},
utils: { // 抽离自己写的公共代码,utils这个名字可以随意起
chunks: 'initial',
name: 'utils', // 任意命名
minSize: 0 // 只要超出0字节就生成一个新包
}
}
}
},
plugins: [
new HtmlWebpackPlugin({
filename: 'a.html',
template: './src/index.html', // 以index.html为模板
chunks: ['vendor', 'a']
}),
new HtmlWebpackPlugin({
filename: 'b.html',
template: './src/index.html', // 以index.html为模板
chunks: ['vendor', 'b']
})
]
}
通过以上配置,可以把引入到a.js和b.js中的这部分公共代码提取出来