为什么需要使用打包工具?
开发时使用的框架、es6 语法 、less 等浏览器无法识别。
需要经过编译成浏览器能识别的css、js才可以运行。
打包工具可以帮我们编译,号可以做代码压缩、兼容处理、性能优化。
常见的打包工具有什么?
vite、webpack、glup、grunt
webapck最基本的使用?
是一个静态资源打包工具,以一个或多个文件为打包入口,将项目中所有文件编译组合成输入一个或多个文件。这个输出的文件我们交budle,他就是经过编译可以在浏览器运行的文件。
webpack本身的功能是有限的?
开发模式:仅仅编译js modle语法
生产模式:编译js modle语法、压缩js代码
package.json 是什么文件?
通常我们需要安装一些依赖包,而在下载这个包之前,我们需要包描述文件。通常描述这个项目所需要的各种模块,以及项目的配置信息(比如名称、版本、许可证、如何启动项目、运行脚本等元数据。
package.json 一般在项目创建之初创建。创建方式可以手动(直接在项目根目录新建一个 package.json 文件)、也可以npm init -y 初始化一个,y 就是要使用配置的默认值
package.json文件就是一个JSON对象,该对象的每一个成员就是当前项目的一项设置
练习步骤:
1、新建一个包,名称不能和webpack重复
2、创建基本的mani.js 文件、src、public文件、写入es6 模块化语法,报错,浏览器无法识别。
3、试试使用webpack 后能否识别呢?无法解析modle语法
4、引入之前需要初始化一个包描述文件 npm init -y
5、npm install webpack webpack-cli --save-dev
6、安装成功后执行npx webpack ./src/main.js --mode=development ,npx 会将node—module 的bin 下面的内容临时添加到环境变量。
7、执行上面即可打包了。默认输出到dist,可以观察输出文件,是编译后的文件。
8、npx webpack ./src/main.js --mode=production 生产模式,会压缩代码
webapck的五大核心概念
入口entry
输入output
loader webpack本身只能处理js、json等资源,其他资源需要借助loader
plugin 拓展功能
mode 模式:生产、开发
练习步骤:
1、安装好之后建一个webpack的配置文件,在根路径下,并且文件名必须是webpack.config.js
2、添加基本配置
3、我们之前打包执行的是npx webpack ./src/main.js --mode=development 这个命令,写了webpack的配置文件中设置了入口后,可以直接使用npx webpack 执行了。上面是使用cli的方式运行本地的webpack,但这样还是有些麻烦。
4、在 package.json 文件中添加一个 npm script: “build”: “webpack”
现在,可以使用 npm run build 命令替代之前使用的 npx 命令。注意,使用 npm scripts 便可以像使用 npx 那样通过模块名引用本地安装的 npm packages。
拓展,可以学习下npm中文文档
资源管理
1、使用style样式,需要借助loader
2、npm install --save-dev style-loader css-loader
3、添加配置,参考官网
1、使用less资源
2、npm install less less-loader --save-dev
3、添加配置,参考官网
1、图片资源的使用
2、不需要要下载,使用内置的asset即可。资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。``关于这块配置webpack官网介绍
3、添加配置,
{
test: /.(png|svg|jpg|jpeg|gif)$/i,
type: ‘asset/resource’,
},
1、对文件的加载,如 JSON 文件、CSV、TSV 和 XML。
2、npm install --save-dev csv-loader xml-loader
3、添加配置,参考官网
调整文件打包输出目录
每一次打包都会在dist下多一些文件,而且很混乱。我们在下一次打包之前手动删除dist,再重新打包。
并设置输出路径,文件资源可设置输出路径。
generator: {filename: 'asset/[hash:10][ext][query]'}
可不可以不每次都需要手动删除呢?
能不能自动清除dist文件上次内容?可以,在output 对象中配置clean:true 即可。
处理js资源
webpack对于js资源的处理是有限的,只能处理model语法,对于es6新语法不兼容,我们需要使用babel
在转换之前先介绍一下eslint ,用于检查jx和jsx语法的工具。更多介绍eslint官网
在webpack中使用eslint
1、npm install eslint-webpack-plugin --save-dev
2、npm install eslint --save-dev
3、添加配置,参考官网
const ESLintPlugin = require('eslint-webpack-plugin');module.exports = {// ...plugins: [new ESLintPlugin({context:path.resolve(__dirname,'src'),})]// ...
};
4、运行起来报错,缺少eslint的配置文件。我们在根目录下新建 .eslintrc.js 文件名不能改。
然后具体的配置可以参考官网,可以继承推荐的默认配置。
5、发现dist下定义的文件也被eslint 检查了,但我们并不需要检查这些文件。在根目录下创建一个 .eslintignore 文件,写上需要忽略的文件路径即可。
在webpack中使用babel
关于bable的介绍可以看bable官网
1、npm install -D babel-loader @babel/core @babel/preset-env webpack
2、配置,参考官网 bable配置
3、配置可以直接写在webpack的配置文件中,也可以在根目录创建 babel.config.js 配置。
处理html资源
1、原本html中引入的js资源是这么写的
<script src="../dist/main.js"></script>
引入的是打包后的文件,如果更改入口起点的名称,或者添加一个新的入口,那么会在构建时重新命名生成的 bundle,但是 index.html 仍然在引用旧的名称!而且如果需要引入很多资源,手动维护非常困难。
我们可以使用 HtmlWebpackPlugin 来解决这个问题。
安装:
npm install --save-dev html-webpack-plugin
配置:
new HtmlWebpackPlugin({title: '管理输出',// 模板:以public/index.html 文件创建新的html 这样打包之后的html不会把之前的东西丢掉template:path.resolve(__dirname,'public/index.html')}),
使用了这个插件,我们无需在html中手动引入,他会自动帮我们引入并且在输出文件中新增一个html文件。
搭建开发服务器
我们之前每次改动了src的内容,都需要编译之后才能看到效果,需要手动执行npx webapck ,我们希望webpack能自动化,可以使用
webpack-dev-server,他相当于webpack 启动了一个服务器,他会帮我们监听src下所有的改动,只有有改动会帮我们自动打包编译。
安装
npm install --save-dev webpack-dev-server
配置
devServer:{host:'localhost',// 启动服务器的域名port:'8080',// 启动服务器端口号,open:true,// 是否自动打开浏览器static: './dist', // 将 dist 目录下的文件 serve 哒服务器},
执行命令
npx webpack serve
添加一个可以直接运行 dev server 的 script:
"scripts": {"test": "echo \"Error: no test specified\" && exit 1","watch": "webpack --watch","start": "webpack serve --open","build": "webpack"},
在命令行中运行 npm start,会看到浏览器自动加载页面。
eee,报错了!
报错内容:
Error: Cannot find module ‘ajv/dist/compile/codegen’
解决办法:
npm i ajv
npm install ajv-errors@1.0.1
再次启动
eee,又报错了!
报错内容:
Error: Conflict: Multiple chunks emit assets to the same filename static/js/bundle.js (chunks main and vendors-node_modules_react-hot-loader_patch_js-node_modules_react_jsx-dev-runtime_js-node_mod-4610d2)
解决办法:
让它工作不得不改变filename: "static/js/bundle.js"至filename: "static/js/[name].js"output: {path: undefined,publicPath: "/",filename: "static/js/[name].js",chunkFilename: "static/js/[name].chunk.js",
}
再次启动可以了
webpack-dev-server 在编译之后不会写入到任何输出文件,而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果你的页面希望在其他不同路径中找到 bundle 文件,可以通过 dev server 配置中的 devMiddleware.publicPath 选项进行修改。
意思就是开发模式下不会生成打包后的文件,尝试把dist删除,保存后自动打包确实没有生成dist文件了
修改js文件内容页面自动更新了。
生产和开发环境两套配置
首先我们新建一个config文件夹,分别新建webpack.dev.js 和 webpack.prod.js 两个文件
将文件中使用相对路径的地方加个…/
template:path.resolve(__dirname,'../public/index.html')
但是入口文件路径不改变,因为配置文件运行时在根目录
entry:'./src/main.js',
生产环境不需要dev-server
分别添加两个命令
"build": "webpack --config ./config/webpack.prod.js","start": "webpack serve --config ./config/webpack.dev.js"
执行对应的命令,生产环境下的budle 有压缩。
提取css成单独文件
我们可以看到,现在我们的css文件,是被打包到了js中,当js文件加载时,会创建一个style 标签来生产样式。
只有js加载完了,页面才会出现样式,给用户闪屏的感觉,体验不好。
应该是将样式抽离到单独的css文件中,通过link标签引入,加载的性能才好
借助插件MiniCssExtractPlugin来实现,官网介绍
1、安装:
npm install --save-dev mini-css-extract-plugin
2、配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
将styel-loader 替换为MiniCssExtractPlugin.loader,因为插件会将 CSS 提取到单独的文件中,
为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。
不再需要创建style 标签了use: [MiniCssExtractPlugin.loader, 'css-loader'],
现在已经被打包到main.scc 中了
css 兼容性处理
postcss-loader 官网介绍
可以帮我们处理一些样式兼容性问题
1、安装
npm install --save-dev postcss-loader postcss postcss-preset-env
2、配置,必须写在css-loder 后面
{loader: 'postcss-loader',options: {postcssOptions: {plugins: [['postcss-preset-env',{// 其他选项},],],},},
3、在package.json 中添加配置
设置浏览器兼容需要做到什么程度。
"browserslist" :["last 2 version","< 1%","not dead"]
css压缩处理
通过观察可以发现,我们dist目录下的资源,js和html都压缩了。但css文件没有压缩处理。
我们可以借助CssMinimizerWebpackPlugin 插件,官网介绍
1、安装
npm install css-minimizer-webpack-plugin --save-dev
2、配置
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");optimization: {minimizer: [// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释// `...`,new CssMinimizerPlugin(),],},
再次打包已经被压缩了
打包优化
1、提升开发体验
2、提升打包构建速度
3、减少代码体积
4、优化代码性能
sourceMap
开发时我们运行的代码是编译之后的,如果有报错,只能定位到编译后的位置,不便于我们排查问题。
sourceMap(源代码映射) 是一个用来生成源代码和映射代码构建的方案
他会生成xx.map 文件,里面包含源代码码和构建好的代码每一行,每一列的关系。从而让浏览器提示源文件的出错位置,从而帮我们快速定位错误。
使用devtool?
配置
开发模式:mode:'development',devtool:'cheap-module-source-map',生成模式:
mode:'production',devtool:'source-map',
经过测试,已经定位到具体一行了
HotModlueReplcaement
当我们在开发时只修改了某一个模块的代码,webpack在打包时会重新打包,速度很慢,页面整体重新刷新了。我们希望她值编译需要打包的模块,其他模块不变。
HotModlueReplcaement(热模块替换)在程序运行中,替换、添加、删除模块,而无需加载整页面,
如何使用?
devServer:{host:'localhost',// 启动服务器的域名port:'8080',// 启动服务器端口号,open:true,// 是否自动打开浏览器static: './dist',hot:true // 只应用与开发环境,生产环境不需要,默认开启},
但是css/html 可以做到,对于js 需要在入库文件manjs中
if(module.hot) {// 判断是否支持热模块替换功能,有的话需要一个个js文件添加
module.hot.accept('./js/sum.js')
}
当然了,vue-loader,react-hot-loder 已经帮我们做了这些事情。
oneOf
打包时每个文件都会经过所以loader处理,我们希望她找到对应的loader之后剩下的就不匹配了
eslint 和babel 缓存
每次打包的时候js文件都需要经过eslint检查和babel编译,速度很慢。
我们如果能缓存之前的结果,第二次打包的速度就会快很多
对babel的配置
{test:/\.js$/,include:path.resolve(__dirname,'../src'),loader:'babel-loader',options:{cacheDirectory:true, // 开启babel缓存cacheCompression:false // 关闭缓存文件压缩}}
对eslin的配置
new ESLintPlugin({context:path.resolve(__dirname,'../src'),exclude:"node_modules",// cache:true,// 开启缓存// cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslintcache')}),
多进程打包
我们打包的速度大部分时候是处理js文件的速度,而处理js文件主要就是eslint、bable、terser这三个工具,我们要提升他们的运行速度,可以开启多线程同时处理。
但是需要注意的是,仅在特别耗时的项目中使用,因为每个进程启动大约有600ms的开销
我们启动进程的数量就是cpu的核数。
如何获取自己电脑cpu的核数?
const os = require('os');
const threads = os.cpus().length;
还需要借助
npm i thread-loader -D
配置
{test:/\.js$/,include:path.resolve(__dirname,'../src'),use:[{loader:'thread-loader',// 开启多进程options :{works:threads // 进程数量}},{loader:'babel-loader',options:{cacheDirectory:true, // 开启babel缓存cacheCompression:false // 关闭缓存文件压缩}}],}
还需要引入一个插件
optimization: {minimizer: [// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释// `...`,new CssMinimizerPlugin(),new TerserWebpackPlugin({parallel:threads})],},
什么是tree-shaking
我们定义或引入了一些第三方工具函数或组件库,如果没做特殊处理的话,打包的时候会引入整个库,即使我们使用的是很小一部分功能。
依赖es model ,移除没有被引用的代码
webpacl 默认开启了这个功能,无需配置
减少babel的体积
babel 会为每个编译后的文件添加辅助代码
Babel 对一些公共方法使用了非常小的辅助代码,比如 extend 。默认情况下会被添加到每一个需要它的文件中。
你可以将这些辅助代码作为一个独立模块,来避免重复引入。
babel/plugin-transform-runtime是什么
禁用了 Babel 自动对每个文件的 runtime 注入,而是引入babel/plugin-transform-runtime并且使所有辅助代码从这里引用babel/plugin-transform-runtime
1、安装
npm i @babel/plugin-transform-runtime -D
2、配置
{loader:'babel-loader',options:{cacheDirectory:true, // 开启babel缓存cacheCompression:false, // 关闭缓存文件压缩plugins:["@babel/plugin-transform-runtime"],// 减少代码体积}}
图片压缩
插件官网介绍,使用方式直接参考官网,这里不记录了
代码分割,优化代码运行性能
我们打包时会将所有的js文件都打包到一个文件中,体积太大。如果我们只渲染首页,那么应该只加载首页的js文件,其他文件不需要加载。
所以我们就需要将打包生成的文件进行代码分割,生成多个js文件,渲染哪个页面就只加载某个页面的js文件,这样加载的资源减少,速度就更快。
代码分割主要做了两件事情:
1、将打包生成的文件分为多个js文件
2、按需加载,需要哪个文件就加载哪个文件。
多入口文件
entry:{main:'./src/main.js',app:'./src/app.js'},output:{// path 是所有文件输出的路径path:path.resolve(__dirname,'../dist'),// 当前文件的文件夹目录的dist下面filename:'static/js/[name].js',clean:true},
设置了多个入口文件,就会有多个输出文件。
但是如果多个文件中引入了公共的函数,我们可以发现打包后的两个js文件中都引入了一边。
我们希望,将公共的代码抽离出来,只需要写一次。
可以在webpack的配置文件中添加配置,官网介绍
optimization: {splitChunks:{chunks:'all',// 对所有模块都进行分割//修改配置cacheGroups: {// 组,哪些模块要打包到一个组default:{// 没有写的都是使用默认配置,单独写的会覆盖默认配置minSize:0 }}},/* 以下都是默认配置minSize:20000 分割代码最小的大小minRemainingSize: 0,// 类似于minsize,最后确保提取的文件大小不能为0,minChunks: 1,// 至少被引用的次数,满足条件才会代码分割maxAsyncRequests: 30,// 按需加较时并行加战的文件的最大数量maxInitialRequests: 30,// 入口js文件最大并行请求数量enforceSizeThreshold: 50000,// 超过5kb一定会单独打包(此时会忽略minRemainingSizecacheGroups: {// 组,哪些模块要打包到一个组defaultVendors:{ // 组名test: /[\V]node_modules[\V]/,// 需要打包到一起的模块 priority: -10,// 权重《越大越高》reuseExistingChunk: true,// 如果当前 chunk 包含已从主 bundle 中拆分出的模块},default:{// 其他没有写的配置会使用上面的默认值minChunks:2,// 这里的minChunks权重更大priority: -20,reuseExistingChunk: true.}}*/minimizer: [// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释// `...`,new CssMinimizerPlugin(),new TerserWebpackPlugin({parallel:threads})],},
添加完配置之后再次打包,多了一些文件