1. 什么是webpack
webpack可以看做是模块打包机:它做的事情是,分析你的项目结构,
找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。
构建就是把源代码转换成发布到线上的可执行 JavaScrip、CSS、HTML 代码
代码转换:LESS、SCSS 编译成 CSS 等。
文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。
自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。
- webpack核心概念
Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
Plugin:扩展插件,在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情。
Output:输出结果,在 Webpack 经过一系列处理并得出最终想要的代码后输出结果。
- 初始化package.json
npm init -y
默认约定webpack 的 4.x 版本中默认约定
打包的 入口文件为 src -> index.js
打包的 输出文件为 dist -> main.js
修改 修改打包的入口与出口,可以在 webpack.config.js 中新增加如下配置信息
const path = require('path') // 导入 node.js 中专门操作路径的模块 path
module.exports = {
entry: path.join(_dirname,'./src/index.js'),//打包入口文件的路径
output: {
path: path.join(__dirname,'./dist'), // 输出文件的存放路径
filename: 'bundle.js' //输出文件的名称
}
}
配置自动打包
npm install webpack-dev-server -D
-D 指安装依赖到开发环境,上线环境使用 -S
- 修改 package.json 文件中 scripts 对象的 dev 命令,如下配置:
// package.json 中的配置
"scripts": {
"dev": "webpack-dev-server --profile --progress --colors" // script 节点下的脚本,可以通过 npm run 执行
},
//--display-modules --profile --progress --colors --display-error-details
//color 输出结果带彩色,比如:会用红色显示耗时较长的步骤
//profile 输出性能数据,可以看到每一步的耗时
//progress 输出当前编译的进度,以百分比的形式呈现
//display-modules 默认情况下 node_modules 下的模块会被隐藏,加上这个参数可以显示这些被隐藏的模块
//display-error-details 输出详细的错误信息
将 src -> index.html 中,scripts 脚本的引用路径 ,修改为根目录下的 buldle.js
注意:
webpack-dev-server 会启动一个实时打包的 http 服务器
webpack-dev-server 打包生成的输出文件,默认放到了项目根目录中,而且是虚拟的,看不到的
// --open 打包完成后自动打开浏览器页面 --host 配置 IP地址 --port 配置端口
"dev": "webpack-dev-server --profile --progress --colors --open --port 8888"
配置预览页面
- 在终端中运行如下命令,安装生成预览页面的插件
npm install html-webpack-plugin -D
- 修改 webpack.config.js 文件头部区域,添加如下配置信息:
// 导入生成预览页面的插件,得到一个构造函数
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebpackPlugin({//创建插件的实例对象
template: './src/index.html',// 指定要用到的模版文件
filename: 'index.html'//指定生成的文件的名称,该文件存在于内存中,在目录中不显示
})
- 修改 webpack.config.js 文件中向外暴露的配置对象 ,新增如下配置节点:
module.exports = {
plugins: [ htmlPlugin ] //plugin 数组是 webpack 打 包期间会用到的一些插件列表
}
2. 配置webpack
2.1 配置文件webpack.config.js
//entry:配置入口文件的地址
//output:配置出口文件的地址
//module:配置模块,主要用来配置不同文件的加载器
//plugins:配置插件
//devServer:配置开发服务器
// 基于node的 遵循commonjs规范的
let path = require('path');
module.exports = {
// 入口
entry: './src/index.js',
// 出口
output: {
filename: 'build.js', // 这个路径必须是绝对路径
path: path.resolve('./dist')
},
// 开发服务器
devServer: {
//contentBase 配置开发服务运行时的文件根目录
contentBase: path.resolve(__dirname, 'dist'),
//host:开发服务器监听的主机地址
host: 'localhost',
//port:开发服务器监听的端口
port: 8080,
//compress 开发服务器是否启动gzip等压缩
compress: true,// 服务器压缩
open: true,// 自动打开浏览器
// hot:true//热更新
},
module: {}, // 模块配置
plugins: [], // 插件的配置
mode: 'development', // 可以更改模式
resolve: {}, // 配置解析}//
//在webpack中如何配置开发服务器 webpack-dev-server
}
//开启本地服务 npm run dev
"scripts": {
"build": "webpack --mode development --profile --progress ",
"dev": "webpack-dev-server --open --mode development --profile --progress "
}
2.2 入口文件的类型
单入口+单出口
entry: './src/index.js',
2.4 多入口数组形式+单出口
entry:['./src/index.js','./src/a.js']
2.4 多入口+多出口
有时候我们的页面可以不止一个HTML页面,会有多个页面,所以就需要多入口
npm i html-webpack-plugin -D
let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 入口
entry: {
index: './src/index.js',
main: './src/main.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js',
publicPath: '/'
},
// 插件的配置
plugins: [
new HtmlWebpackPlugin({
minify: {
removeAttributeQuotes: true, //删除属性双引号
collapseWhitespace: true //压缩空白
},
hash: true,
template: './src/index.html',
chunks: ['index'],
title: 'index',
filename: 'index.html'
}),
new HtmlWebpackPlugin({
minify: {
removeAttributeQuotes: true, //删除属性双引号
collapseWhitespace: true //压缩空白
},
hash: true,
template: './src/index.html',
chunks: ['main'],
title: 'main',
filename: 'main.html',
})
]
}
2.5 我们可以使用插件来生成html文件,这样就避免了html每次去手动引入js;使用插件来删除上次打包的结果
使用CleanWebpackPlugin可以每次构建前清空输出目录.
当你多次打包的时候,你会发现由于使用hash来命名输出的文件每次的文件名称都不一样,导致文件越来越多.
npm i -D html-webpack-plugin clean-webpack-plugin
//修改webpack.common.js文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
...
plugins:[
new HtmlWebpackPlugin({template:'./src/index.html'}),
new CleanWebpackPlugin()
]
...
}
2.6 每次修改完文件都要重新打包才能看到效果,我们可以使用webpack-dev-server来搭建一个本地服务器来实时更新。
npm i -D webpack-merge
//webpack.dev.js
const merge = require('webpack-merge')
const common = require('./webpack.common')
module.exports = merge(common, {
mode: 'development',
devServer: {
contentBase: './index.html',
hot: true,
port: 3000
}
})
//修改package.json的dev
"scripts": {
"dev": "webpack-dev-server --open --config ./config/webpack.dev.js",
...
},
2.7 安装babel
npm i -D @babel/preset-env babel-loader @babel/core
//webpack.common.js
module.exports = {
module: {
rules: [
{
test: /\.(js)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
},
]
}
}
2.8 处理css
npm i -D style-loader css-loader
//webpack.common.js
...
module:{
rules:[
...
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
//index.css
...
.red {
color: red;
}
可以看到样式被插进了head中。当项目变大时样式直接插入head中的方式并不好,我们需要将样式分离
webpack4的版本中建议使用mini-css-extract-plugin插件(以前是ExtractTextWebpackPlugin
插件)
npm i -D mini-css-extract-plugin
//webpack.common.js
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
rules:[
...
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// you can specify a publicPath here
// by default it uses publicPath in webpackOptions.output
publicPath: '../',
hmr: process.env.NODE_ENV === 'development',
},
},
'css-loader',
],
}
]
plugins:[
...
new MiniCssExtractPlugin({
filename: 'static/css/[name].css', //打包到static的css目录下
ignoreOrder: false, // Enable to remove warnings about conflicting order
})
]
开发环境不存在这个,在生产环境中,打包的文件上传服务器,被用户使用后,会被缓存在浏览器时;当需要修改代码时,又重新打包上传,而用户的浏览器发现加载的文件名字一样,就不会重新加载,还是会从缓存中加载,就造成了修改的代码不能更新使用;
解决方法:每次打包的文件,都会有自己的hash值,只要文件修改,hash值才会变化,不修改,无论打包几次,值都不会发送改变;
在webpack.prod.js生成配置中输出时使用contenthash
,这样修改的文件名就会改变,而用户的浏览器发现不一样的文件,就会重新加载,相同名文件则不会加载,这有提高了性能
一个滚动插件https://www.npmjs.com/package/smooth-scroll
Shimming:垫片,解决某些问题
** webpack的核心就是模块与模块之间分开,**
** 但是当有些公共模块或库需要在多个模块中调用,**
** 或者比如有一些第三方库使用了JQ的$,但是不是用import引用的,当你调用时,不能识别,**
** 就可以在webpack的配置中引入webpack的插件ProvidePlugin**
以JQ为例,当模块使用 from 'jquery'
通过懒加载的方式动态引入文件
const router = new Router({
routes: [{
path: '/home',
name: 'Home',
component: () =>
import('./views/home/Home.vue')
}]
})
解决方案
安装插件babel-plugin-dynamic-import-webpack
npm install babel-plugin-dynamic-import-webpack --save-dev
在配置文件的module的rules下进行插件的配置,如下
{
test: /\.js/,
use: [{
loader: 'babel-loader',
options: {//如果有这个设置则不用再添加.babelrc文件进行配置
"babelrc": false,// 不采用.babelrc的配置
"plugins": [
"dynamic-import-webpack"
]
}
}]
}