为什么我们需要 Webpack
回答这个问题,可以和还没有 Webpack、没有构建工具时对比一下,就能明显地感觉出来了。这里就来列举一下不使用构建工具时的痛点。
web 开发时调用后端接口跨域,需要其他工具代理或者其他方式规避。 改动代码后要手动刷新浏览器,如果做了缓存还需要清缓存刷新。 因为 js 和 css 的兼容性问题,很多新语法学习了却不能使用,无论是开发效率和个人成长都受影响。 打包问题。需要使用额外的平台如 jekins 打包,自己编写打包脚本,对各个环节如压缩图片,打包 js、打包 css 都要一一处理。 ......
定义
webpack是一种前端资源构建工具,一个静态模块打包器
五个核心概念+devSever自动化
entry
入口指示webPack以哪个文件为入口起点开始打包,分析构建内部依赖图
output
输出指示webpack打包后的资源bundles输出到哪里去,以及如何命名
Loader
让webpack能够处理那些非js文件如:sass img 等 【webpack本身只能识别js】
Plugins【插件】
从打包优化和压缩,一直到重新定义环境中的变量等
Mode【模式】
devSever开发服务器【自动化】
- 开发服务器devSever:用来自动化(自动打开浏览器,自动刷新浏览器,自动编译)
- 特点:自会在内存中打包编译,不会有任何输出
- 下载命令 : npm i webpack-dev-server -D
- 启动devSever命令:npx webpack-dev-server
注意:webpac5.0起要加 target:'web'才能自动刷新
webpack loader区别
-
webpack loader是用来加载文件的,webpack plugin是用来扩展功能的。
-
loader主要是用来加载一个个文件的,比如它可以加载js文件并把js文件转译成低版本浏览器可以支持的js文件;也可以用来加载css文件,可以把css文件变成页面上的style标签;还可以加载图片文件,可以对文件进行优化。
-
plugin是用来加强webpack功能的,比如HTML webpack plugin是用来生成一个html文件的;再比如mini css extract plugin是用来抽取css代码并把它变成一个css文件的。
demo案例
迭代1:打包静态资源js和css
1:全局安装【webpack和webpack-cli】 npm i webpack webpack-cli -g【安装一次即可,可以忽略】
2: 项目文件下创建项目配置项 npm init
3:本地安装 npm i webpack webpack-cli -D 【-D把项目开发依赖下载到本地】
4:下载css-loader 和style-loader npm i css-loader style-loader -D
5:执行打包 webpack
代码目录结构 :
迭代2:sass 文件打包
6: 下载包 npm i sass-loader sass -D
7: 添加loader配置
迭代3:打包html
- loader : 1下载 2:使用【配置loader】
- plugins : 1 下载 2:引入 3:使用
8:下载打包 html 包 命令: npm i html-webpack-plugin -D
9: 添加配置 plugin
10:打包运行
迭代4 :打包图片资源
11:把图片引入css和引入html中
12:配置plugins
命令:npm i url-loader file-loader -D 和 npm i html-loader -D
loader 下面的limit
迭代4-1: 打包其他资源【如字体文件,暂时忽略】
13 备好字体图标
14 配置plugins用到exclude[正则]
// 打包其他资源(除了html/js/css 资源以外的资源)
{
exclude:/\.(css|html|js)$/,
loader:'file-loader',
options:{
name:'[hash:10].[ext]'
}
}
迭代5:devSever 用来自动化【自动编译,自动打开浏览器,自动刷新浏览器】看商品devServer开发服务器
迭代1~5的webpack.config.js基本配置 代码如下
/*
* @Descripttion:
* @version:
* @Author: xiewutao
* @Date: 2021-05-10 14:04:54
* @LastEditors: sueRimn
* @LastEditTime: 2021-05-12 11:34:24
* webpack.config.js webpack配置文件
*
* 作用:webpack干哪些活(当你运行webpack指令时,会加载里面的配置)
* 所有构建工具都是基于nodejs 平台运行的 模块化,默认是才有common.js 如:module.exports
*
* loader : 1下载 2:使用【配置loader】
* plugins : 1 下载 2:引入 3:使用
*
*
* // *执行的命令总结
* npm init
* npm i webpack webpack-cli -D
* npm i style-loader sass -D
* npm i sass-loader sass -D
* npm i url-loader -D
* npm i file-loader -D
* npm i html-loader -D
* npm i html-webpack-plugin -D
* npm i webpack-dev-server -D
* npx webpack-dev-server 启动
* webpack 运行
*
* npm uninstall webpack-li
* npm i webpack-cli@3.3.12 -D
*
*/
// resolve 是用来拼接绝对路径的方法
const {resolve} =require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
module.exports={
entry:'./src/index.js',
output:{
filename:'built.js',
// __dirname node.js变量,代表当前文件的目录绝对路径【根目录】
path:resolve(__dirname,'build')
},
// loader 的配置
module:{
// 详细配置
rules:[
{
test:/\.css$/, // 匹配哪些文件
use:[ // use 使用哪些loader处理【注意:uses 这个数组中,执行的顺序是:是倒叙的,即是从右到左,从下到上 执行css-loaderd 再到style-loader】 // 命令下载两个包 npm i css-loader style-loader -D
'style-loader', // 创建style标签,将js中的样式资源插入进行,添加到header中生效
'css-loader' // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
]
},
{
test:/\.scss$/,
use:[
'style-loader',
'css-loader',
'sass-loader', // 将scss文件编译css文件 ,需要下载 scss-laoder 和scss两个包 // npm i sass-loader sass -D
]
},
// 处理css里面的引入图片资源,,但是处理不了html里图片,所以由下面的test:/\.html$/ 处理
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader', // 下载包命令:npm i url-loader file-loader -D 这里的use和loader区别:use是数组多个,loader只有一个
options:{
// 图片大小小于8kb,就会转base64处理
// 优点:减少请求的数量【减轻服务器的请求压力】
//缺点:图片的体积会更大(文件请求速度更慢)
limit:8*1024,
// 为了解决html img中src [object Module],所以引入下面的esModule
// 问题:因为url-loader默认是使用es6模块化解析,而html-loader引入图片是commonjs
// 出现问题:html img中src [object Module]
// 解决:关闭url-loader的es6模块化 ,使用comonjs解析
esModule:false,
// 给图片重新命名【html中图片】
// [hash:10] 取hash的前10位
// .[ext]取文件原来的扩展名
name:'[hash:10].[ext]',
outputPath: "images/", //输出图片放置的位置和src 图片目录一致
publicPath: './images', //html的img标签src所指向图片的位置,与outputPath一致
}
},
// 处理html里面的引入图片资源
{
test:/\.html$/,
loader:'html-loader' // 处理html文件的图片,(负责引入img,从而被url-loader进行处理) 命令: npm i html-loader -D
},
// 打包其他资源(除了html/js/css 资源以外的资源)
// {
// exclude:/\.(css|html|js)$/,
// loader:'file-loader',
// options:{
// name:'[hash:10].[ext]'
// }
// }
]
},
plugins:[
// 命令: npm i html-webpack-plugin -D
// 功能:默认创建一个空的html,自动引入打包输出的所有资源(js/css) 所以 在src/index不用引入js文件,打包后 build/index.html plugins插件自动有引入
new HtmlWebpackPlugin(
{
template:'./src/index.html'//复制 ./src/index.html ,自动引入打包输出的所有资源(js/css)
}
)
], // 插件
// 开发服务器devSever:用来自动化(自动打开浏览器,自动刷新浏览器,自动编译)
// 特点:自会在内存中打包编译,不会有任何输出
// 下载命令 : npm i webpack-dev-server -D
// 启动devSever命令:npx webpack-dev-server
devServer:{
// 项目构建后路径
contentBase:resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 端口号
port:3000,
// 自动打开浏览器
open:true
},
mode:'development' //模式:development 开发模式 production生产模式
}
迭代6:提取css文件
15:提取css插件命令:npm i mini-css-extract-plugin -D
16:plugin 的配置
迭代7:css兼容的处理 如:flex-box的前缀
17: 插件的命令:npm i postcss-loader postcss-preset-env -D
18 配置:代码如19
迭代8 压缩css
命令 npm i optimize-css-assets-webpack-plugin -D
20配置
迭代 9 :eslint js语法检查和自动修复
21 命令:插件 npm i eslint-loader eslint-config-airbnb-base eslint-plugin-import -D
21 : 使用配置 loader
22:规则 eslint-disable-next-line 的意思
迭代10:js兼容性处理【es6处理】
命令:
js兼容性处理: babel-loader @babel/core @babel/preset-env
1:基本js兼容性处理----》 命令:npm i @babel/present-env -D
问题:只能转基本语法如:const let 箭头函数等 ,但是promise不能转换
2:全部js兼容性处理----》命令 npm i @babel/polyfill -D
问题:我只要解决部分的兼容性问题,但是全部引如所有的兼容性代码,体积太大了
3:需要按需加载 -----》 命令 npm i core-js -D
配置
迭代 11 js压缩和html压缩
js压缩
把开发环境改成生产环境即可
直接把 mode:production
html压缩
new HtmlWebpackPlugin(
{
template:'./src/index.html',//复制 ./src/index.html ,自动引入打包输出的所有资源(js/css)
minify:{ //压缩HTML文件
collapseWhitespace:true, //删除空白符与换行符
removeComments:true, //移除HTML中的注释
}
}
),
迭代12 @的作用
--------webpack-总结生产环境基本配置-------------
/*
* @Descripttion:
* @version:
* @Author: xiewutao
* @Date: 2021-05-12 09:46:28
* @LastEditors: sueRimn
* @LastEditTime: 2021-07-02 16:26:58
// *执行的命令
* npm init
* npm i webpack webpack-cli -D
* npm i style-loader sass -D
* npm i sass-loader sass -D
* npm i url-loader -D
* npm i file-loader -D
* npm i html-loader -D
* npm i html-webpack-plugin -D
* npm i webpack-dev-server -D
* npx webpack-dev-server 启动
* webpack 运行
*
* npm uninstall webpack-li
* npm i webpack-cli@3.3.12 -D
*
*
*/
const {resolve} =require('path');
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin=require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin=require('html-webpack-plugin');
process.env.NODE_ENV ='production'; // 修改开发环境
const commonLoaderCss=[
MiniCssExtractPlugin.loader,//这个loader取代style-loader 。 作用提取jsz中的css成单独文件
'css-loader',
{
loader:'postcss-loader',
options:{
postcssOptions:{
ident:"postcss",
plugins:[
require("postcss-preset-env")()
]
}
}
},
]
module.exports={
entry:'./src/js/index.js',
output:{
filename:'js/built.js',
// __dirname node.js变量,代表当前文件的目录绝对路径【根目录】
path:resolve(__dirname,'build')
},
module:{
// 配置
rules:[
{
test:/\.css$/,
use:[
...commonLoaderCss
]
},
{
test:/\.scss$/,
use:[
...commonLoaderCss,
'sass-loader',
]
},
// css图片资源打包
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[hash:10].[ext]',
esModule:false,
outputPath:"../../images"
}
},
{
test:/\.js$/,
exclude:/node_modules/,//排除node_moudles
loader:'babel-loader',
options:{
//预设:指示babel做怎么样的兼容性处理
presets:[[
'@babel/preset-env',
{
//按需加载
useBuiltIns:'usage',
// 指定core-js版本
corejs:{
version:3
},
// 指定兼容性做到 那个版本
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'17'
}
}
]]
}
},
// html中图片资源
{
test:/\.html$/,
loader:'html-loader',
},
]
},
plugins:[
new MiniCssExtractPlugin({
filename:'css/built.css' // 提取的css文件 目录和文件
}),
// 压缩css
new OptimizeCssAssetsWebpackPlugin({
}),
new HtmlWebpackPlugin(
{
template:'./src/index.html',//复制 ./src/index.html ,自动引入打包输出的所有资源(js/css)
minify:{ //压缩HTML文件
collapseWhitespace:true, //删除空白符与换行符
removeComments:true, //移除HTML中的注释
}
}
),
],
mode:"production",
target:'web',//webpack5.0 使用webpack-dev-server时
devServer:{
contentBase:resolve(__dirname,'build'),
compress:true,
port:3000,
open:true,
}
}
----------------第二部分开始 start----------------------
webpack性能优化
开发环境的性能优化
A: HMR(hot module replacement) : 热模块替换 、 模块热替换
作用:一个模块发生变化,只会重新打包这个模块(而不是打包所有模块) 提高构建的速度
HMR代码示例:
```
mode:"production",
target:'web',//webpack5.0 使用webpack-dev-server时
devServer:{
contentBase:resolve(__dirname,'build'),
compress:true,
port:3000,
open:true,
hot:true ,// 开启HMR功能 热模块更新提高构建速度,刚修改必须重启才生效
}
```
1:样式文件:可以用HMR功能,因为style-loader内部实现了
2:js 文件: 默认不能使用HMR功能 : 除非优化非入口js文件,
解决,在到改动的代码上判断
```
if(module.hot){
module.hot.accept('非入口文件的js路径',function(){
重新调用
})
}
```
3: html文件:默认不能使用HMR功能,同时会导致问题:html文件不能热更新了
解决: 修改entry入口,将html文件引入 entry:['./src/js/index.js',''./src/index.html'],
拓展:由于html 只有一个没有必要做热更新了
B: source-map (构建后,代码出错,踪源码错误位置)
作用:一种提供源代码到构建后代码映射技术(也就是构建后,代码出错,通过映射可以追踪源码错误位置)
代码示例:devtool
mode:"production",
target:'web',//webpack5.0 使用webpack-dev-server时
devServer:{
contentBase:resolve(__dirname,'build'),
compress:true,
port:3000,
open:true,
hot:true ,// 开启HMR功能 热模块更新提高构建速度,刚修改必须重启才生效
},
devtool:'source-map' // 开发工具
source-map的参数分别是:
[inline -|hidden-|eval-][nosources-][cheap-[module-]]source-map
内联和外部的区别:1外部生成文件,内联没有 2:内联的构建速度快
0: source-map:外部
1:错误代码准确信息和源代码的错误位置
1: inline-source-map:内联
1: 只生成一个内联source-map
2:错误代码准确信息和源代码的错误位置
2: hidden-source-map:外部
1:错误代码的原因,但是没有错误的位置
2:不能追踪源代码错误,只能提示到构建后的代码错误位置
3:eval-source-map 内联
1:每个文件都生成内联source-map 都在eval
2:错误代码的准确信息和原代码的错误位置
4:nosources-source-map 外部 【发布时候,建议开启这个或者关闭】
1:错误代码的准确信息,但是没有任何源代码的信息
5:cheap-source-map:外部
1:错误代码的准确信息和原代码的错误位置 ,准确错误的行位置
6:cheap-module-map :外部
1:错误代码的准确信息和原代码的错误位置
2:module会将loader的source-map加入
source-map 总结
开发环境:速度快,调试友好【推荐eval-source-map】
速度快:(eval>inline>cheap>....)
eval-cheap-source-map
eval-source-map
调试更友好:
source-map
cheap-souce-map
cheap-module-source-map
又友好又快:
eval-source-map /eval-cheap-moudle-source-map
生产环境:源代码要不要隐藏?调试要不要友好【推荐 hidden-source-map】
内联让代码体积更大,所以再生产不要用内联
nosource-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码的错误信息
生产环境的性能优化
A :oneOf优化构建打包速度
注意:oneOf不能有两个loader
B :缓存:bable缓存和文件缓存
1:babel 缓存(让第二次打包构建速度更快)
2:文件资源缓存(contenthash,让上线运行缓存更好使用)
hash:每次webpack 构建时回生成一个唯一的hash值【不适合】 问题: 因为js和css同时使用一个hash值,如果重新打包,会把所有的缓存清除【可能我只改一个文件css】
chunkhash【不适合】:根据chunk生成的hash值,如果打包来源同一个chunk,那么hash值就是一样 问题:js和css的hash值还是一样
contenthash【适合】:根据文件的内容生成hash值,不同文件的hash值一定不一样
C: tree shaking 去除无用的代码
tree shaking 去除无用的代码 前提:1必须使用es6模块化 2 开启production 环境 作用: 减少代码体积
在package.json中配置 “sideEffects”:false 所有代码都没有副作用(都可以进行tree shanking) 问题:可能会把css /@bale/pollyfill(副作用)文件干掉 “sideEffects”:[".css",".less","*.scss"]
D:code split 代码分割
当一个项目慢慢变得复杂的时候会导致这个bundle.js文件越来越大,浏览器加载的速度也会越来越慢,可以使用代码分割来将不同代码单独打包成不同文件。
式一:通过多入口实现代码分割
配置webpack, 将单入口,改为多入口
注意:index.js文件不要引入base.js文件
const path = require('path');
module.exports = {
mode: 'production',
entry: {
index: './src/js/index.js',
base: './src/js/base.js'
},
output: {
filename: 'js/[name][contenthash:8].js',
path: path.resolve(__dirname, 'bulid'),
}
}
方式二:通过optimization将公共代码单独打包 引入js 也单独打包
-
该方式可以将node_modules中代码单独打包一个chunk最终输出
-
会自动分析多入口chunk中,有没有公共的文件,如果有会将公共文件打包成一个单独的chunk
-
该方式可以对单入口文件使用,也可以对多入口文件使用。
-
注意: 被动态导入的文件如base.js文件内容被修改后,hash值会更改,但是index.js打包生成的main156g2sa.js文件中会记录base.js的hash值,导致index.js的打包文件的的hash值也更改。
解决方法:可以使用runtimeChunk选项使index.js打包生成的main.js不记录base.js生成的hash值,而是将hash值单独放入到一个文件中
安装jquery
npm i jquery --save
入口文件index.js
import $ from 'jquery';
function fn(x, y){
return x * y;
}
base.js文件
import $ from 'jquery';
const add = (x, y) => x + y;
add(2, 5);
webpack配置文件
const path = require('path');
module.exports = {
mode: 'production',
entry: {
index: './src/js/index.js',
base: './src/js/base.js'
},
output: {
filename: 'js/[name][contenthash:8].js',
path: path.resolve(__dirname, 'bulid'),
},
optimization:{
//被注释的代码不是必须的,可以根据自己需求开启
splitChunks:{
chunks: 'all',
},
//将当前模块记录其他模块的hash单独打包为一个文件runtime
runtimeChunk:{
name: entrypoint => `runtime-${entrypoint.name}`
},
}
}
进行打包 最终会打包出三个文件,base.js生成的文件,index.js生成的文件,和jquery生成的文件
方式三:import动态导入
通过import动态导入语法,将某个文件单独打包。 可以实现单入口
webpack.config.js文件
const path = require('path');
module.exports = {
mode: 'production',
entry: './src/js/index.js',
output: {
//[name]默认是chunks
filename: 'js/[name][contenthash:8].js',
path: path.resolve(__dirname, 'bulid'),
},
optimization:{
splitChunks:{
chunks: 'all'
},
//将当前模块记录其他模块的hash单独打包为一个文件runtime
runtimeChunk:{
name: entrypoint => `runtime-${entrypoint.name}`
},
}
}
入口文件index.js
import $ from 'jquery';
//加载base.js文件,返回promise
//webpackChunkName是将打包后的文件名以base开头,相同的webpackChunkName将被打包到一个文件中
//base.js文件一定要进行导出(export)
import( /* webpackChunkName: 'base' */ './base').then((result) => {
console.log(result);
}).catch(() => {
console.log('base.js加载失败!');
});
function fn(x, y) {
return x * y;
}
E:懒加载和预加载
1:懒加载
2:预加载
F:PWA 渐进式网页【离线可访问】
workbox-->workbox-wepack -plugin
命令: npm i workbox-webpack-plugin -D
G:多进程打包
npm i thread-loader -D
H:externals 防止第三方包打包js中;如jq 直接用cdn引入
1: 配置修改
2:在页面使用cdn连引入即可
I: dll技术 动态链接库
对某些库(第三方库:jq,react,vue)进行单独打包 当你
-------------------第二部分end------------------------
安装
1:全局安装 npm install -g webpack
2: 项目文件下创建项目配置项 npm init
3:安装依赖项 npm install --save-dev webpack
创建文件并简单运行
拓展改造配置项
package.json改造一: 命令:npm start
package.json改造二: 命令:npm run hd
loaders
1:安装loaders 命令 npm install -save-dev json-loader
文件的变量解析:
webpack.config.js下的
package.json
package.json改造一: 命令:npm start
package.json改造二: 命令:npm run hd
命令后面的--save 和 -D区别