webpack5入门基础
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
webpack 是代码编译工具,有入口、出口、loader 和插件;webpack 是一个用于 JavasScript 应用程序的静态模块打包工具;当 webpack 处理应用程序时会递归构建一个依赖关系图(dependency graph),其中包含应用程序的每一个模块,然后将这个模块打包成一个或者多个 bundle。 webpack 的本质是模块化打包工具,前端所有的资源都应当看成是一个模块,通过 webpack 的核心机制 loader 来处理,然后借助插件机制 plugin 来形成一个繁荣的生态; 学习可参考 webpack 中文文档;webpack 版本:5.73.0 文章目录
一、为什么使用 webpack1、解决作用域问题问题原因:
上面是一个立即执行函数,如果在立即执行函数外面调用 test,test 是 ‘is not defined’ ,说明在立即执行函数中的变量是不能够在外部访问的,这样就不会污染到 window;如果想暴露一些东西给 window,则可以使用一个变量,将自执行函数赋值给这个变量,然后就可以在 window 中访问自执行函数的返回值; 2、代码拆分问题问题原因:
上面代码我们通过 3、让浏览器支持模块1、借助 require.js (不够简洁) 定义 add 方法,通过 define 暴露出来,define 第一个参数是一个数组,里面填写所需要依赖的文件路径;在 html 页面引入 require.js 文件,通过 data-main 来绑定入口文件;在入口文件 main 中,使用require 方法,第一个参数是所需要的依赖文件,第二个参数是个方法,你可以在方法中使用add方法,并返回,这个返回值在浏览器中是可以查看的; //add.jsconst add =(a,b)=> {return a+b}export default add;//html <script type="module>import add from './add.js'</script> 或者 //add.jsexport const add =(a,b)=> {return a+b}//html <script type="module">import { add } from './add.js'</script> 这里需要声明 script 的 上面这些手段虽然都能解决对应的问题,但是会比较麻烦,这里我们就引出 了更加强大的工具 webpack ;它可以打包 JavaScript 应用程序,支持 ES 模块化标准和 commonJS,可以扩展支持图片、字体文件、样式文件等静态资源打包; 4、构建工具对比1、Webpack:适合一些复杂的应用,可以集成很多第三方库,可以拆分代码,使用静态资源文件,支持 commonJS、esmodule 等模块化模式; 二、webpack 学习起步1、安装安装 webpack 之前需要确保已经安装了 node.js 的最新版本(参考:node版本升级);然后使用 npm 包管理工具来安装 webpack: //全局安装webpack webpack-clinpm install webpack webpack-cli --global //查看webpack是否安装成功webpack -v 不建议使用全局安装 webpack,那样不利于不同项目中使用不同版本的 webpack,也不利于项目的协调开发; //安装npm包管理配置文件package.jsonnpm init -y//局部安装webpack webpack-clinpm install webpack webpack-cli --save-dev 本地项目安装之前需要闲创建一个 npm 包管理配置文件;安装好 webpack 之后本地目录中会生成 node_modules 文件夹,里面就我们引入的依赖包;(切记,文件名不可以是webpack) webpack-cli 不是必须的,只是用来处理命令行参数的工具; 2、运行打包1、如果是在全局安装的 webpack 直接在控制台执行 webpack 2、如果是局部安装的 webpack,上面的运行命令就不行了,因为局部安装的并没有加入到系统环境变量中,所以控制台找不到 npx webpack npx 依托于 npm ,有了 npm 就可以直接使用 npx;npx 的作用是表示我们可以观察当前文件夹里面是否有我们想要去运行的命令,如果没有就会在这个目录的上一层目录中查找; 注意:运行打包可以在任意文件夹下面运行,运行之后生成的 dist 文件夹会在运行打包的文件夹下面; 3、自定义 webpack 配置在根目录下创建 webpack.config.js 文件,用来配置 webpack 的配置项; const path = require('path')module.exports = { entry:'./src/index.js', //入口文件路径 output:{ filename:'bundle.js',//打包后的文件名 path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置 clean:true //每次打包前清空dist文件夹}} 为了获取绝对路径,我们需要引入 node.js 的 path 模块;通过 resolve 来解析路径,_dirname (两个下划线)表示当前文件的物理路径,也就是 webpack.config.js 文件的上一级文件夹;第二个参数是指定打包文件保存的文件夹; 打包之后的 bundle.js 在 html 文件中通过标签引入就可以正常使用了; 4、自动引入资源我们可以使用 webpack 插件 npm install --save-dev html-webpack-plugin 2、配置 const path = require('path')var HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = { entry:'./src/index.js', output:{ filename:'bundle.js',//打包后的文件名 path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置 clean:true //每次打包前清空dist文件夹}, plugins:[new HtmlWebpackPlugin({ tempalte:'./index.html',// 模板,根据指定模板生成新的html filename:'app.html',//生成文件的名称 inject:'body'//指定script标签位置})]} 这样 dist 文件夹中就会生成 bundle.js 的同时还会生成一个 app.html 文件,这个 app.html 文件就是根据 index.html 文件为模板生成的,并且文件自动引入 bundle.js ; 5、mode 选项为了能在每次修改之后能够自动编译,并且让浏览器自动刷新,我们可以搭建一个开发环境来实现; devtool:'inline-source-map' 2、自动编译 npx webpack --watch 3、webpack-dev-server npm install webpack-dev-server -D 配置 devServer:{ statis:"./dist" //server根目录} 启动 npx webpack server//或者npx webpack-dev-server//自动打开浏览器npx webpack-dev-server --open 这里可以启动一个服务,一般是 http://localhost:8080/,然后在浏览器访问这个地址就可以实现自动更新浏览器了; webpack-dev-server 实际上并没有输出任何的物理文件,它把打包后的 bundle 文件保存在内存里面,这也我们的开发效率提高了,webpack 的编译效率也提高了; 6、资源模块 module在 webpack 出现之前,前端人员会使用 Grunt 、Gulp 等工具来处理资源,将 src 文件夹的文件移动到 dist 或者 build 目录中;然而 webpack 最出色的功能除了引入 js 还可以使用内置的资源模块;asset modules 来引入任何的其他类型资源,它允许 webpack 打包其他的文件(字体、图标); 资源模块有四种类型:asset modules type
6.1、resource 资源在 webpack.config.js 新增 module 配置项;添加 rules 规则,通过 test 加上正则匹配指定类型的文件; module:{ rules:[{//规则 test:/\.png$/, //正则定义加载文件的类型 type:'asset/resource'}]}//页面使用import imgSrc from './assets/test.png' 这个时候在页面上引用的时候就会获取到图片的路径;并且在 dist 文件夹下面可以看到我们导出的图片资源; 如果想修改图片存放位置和文件名可以进行如下操作: output:{ filename:'bundle.js',//打包后的文件名 path: path.resolve(__dirname,'./dist'),//打包后文件放置的位置 clean:true, //每次打包前清空dist文件夹 assetModuleFilename:'images/[contenthash][ext]' //contenthash 根据文件的内容生成一个hash字符串,ext表示扩展名}, 或者在 module 中加一个 generator module:{ rules:[{//规则 test:/\.png$/, //正则定义加载文件的类型 type:'asset/resource', generator:{ filename:'images/[contenthash][ext]'}}]} 注意:如果两处同时设置了,那么 generator 的优先级会更高; 6.2、inline 资源在 dist 文件夹下面是看不到图片资源的,因为这种模式只导出了资源的 URL;这个 URL 是 base64 格式的资源路径; 6.3、source 资源可以获取文本的内容,常用来获取 txt 文件的内容; 6.4、通用资源类型 asset在 inline 和 resource 之间自由选择,默认情况下小于 8kb 的文件将会视为 inline 模块类型,否则视为 resource 模块类型;也可以通过设置 module:{ rules:[{//规则 test:/\.png$/, //正则定义加载文件的类型 type:'asset', parser:{//自定义解析器里面的时间 dataUrlCondition:{ maxSize:4*1024*1024}}, generator:{ filename:'images/[contenthash][ext]'}}]} 7、loaderwebpack 除了可以使用资源模块来引入外部资源,还可以使用 loader 来引入其他类型的文件;webpack 只能理解 js 和 json 类型的文件,这是 webpack 自带的能力, loader 可以让 webpack 去解析其他类型的文件并且将这些文件转化为有效的模块,供应用程序使用; loader 的定义在 module rules 下面定义一个 test 来识别那些文件被转换,use 属性定义在转化的时候使用那个 loader 来进行转化; module:{ rules:[{ test:/\.text/, use:'raw-loader'}]} 上面这段配置的意思是:webpack 在通过 import、require 去解析一个 .test 文件的时候,在对文件进行打包之前先使用 row-loader 转化一下; 7.1、加载 css1、处理 css //将css识别转化,让webpack可识别npm i css-loader -D//把css放置到页面 header 标签里面npm i style-loader -D 安装成功之后在 webpack.config.js 文件的 module 下新增 rules : rules:[{ test:/\.css$/, use:['style-loader','css-loader']}] 多个 loader 可以在 use 里面以数组的形成传入,loader 执行顺序从 use 数组的后面往前面执行,先执行的 loader 会将结果返回传递给下一个 loader;并且这个先后执行顺序必须正确,否则不生效;需要先转化 css ,然后将 css 放到页面上面; 2、处理 less npm i less-loader less -D 安装成功之后在 webpack.config.js 文件的 module 下新增 rules : rules:[{ test:/\.(css|less)$/, use:['style-loader','css-loader','less-loader']}] 7.2、抽离和压缩 css1、抽离 //webpack5 下才有这个插件npm i mini-css-extract-plugin -D 在 webpack.config.js 引入插件 const MiniCssExtractPlugin = require('mini-css-extract-plugin')//插件使用plugins:{new MiniCssExtractPlugin({ filename:'styles/[contenthash].css' //制定打包后的css存放位置})},module:{ rules:[{ test:/\.(css|less)$/, use:['MiniCssExtractPlugin.loader','css-loader','less-loader']}]} 使用插件的 loader 2、压缩 npm i css-minimizer-webpack-plugin -D 在 webpack.config.js 引入插件: const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')//这个插件不是在plugins中,而是在优化配置中做设置mode:'production',optimization:{ minimizer:[new CssMinimizerPlugin()]} 注意:这个时候的 mode 必须是 production; 7.3、加载 fonts 字体可以直接借助 asset module 来接收和载入任何类型的资源; module:{ rules:[{ test:/\.(woff|woff2|eot|ttf|otf)$/i, type:'asset/resource'}]} 7.4、加载数据json 是默认可以正常导入的,但是要导入 CSV、TSV 和 XML 类型的文件数据则需要 loader 来帮忙; npm i csv-loader xml-loader 安装成功之后在 webpack.config.js 文件的 module 下新增 rules : module:{ rules:[{ test:/\.(csv|tsv)$/i, use:['csv-loader']},{ test:/\.xml$/i, use:['xml-loader']}]} 然后页面引入这些类型的文件,就可以正常访问了,XML文件会转化成 js 对象,CSV 文件会转化成一个数组; 7.5、自定义 JSON 模块 parser通过使用自定义 parser 替换特定的 webpack loader ,将 toml、yaml、json5 文件作为 json 模块导入; npm i toml yaml json5 -D 安装成功之后在 webpack.config.js 文件的 module 下新增 rules : const toml = require('toml')const yaml= require('yaml')const json5 = require('json5')module:{ rules:[{ test:/\.toml$/i, type:'json', parser:{ parse: toml.parse }},{ test:/\.yaml$/i, type:'json', parser:{ parse: yaml.parse }},{ test:/\.json5$/i, type:'json', parser:{ parse: json5.parse }}]} 8、babel-loaderwebpack 只能做 js 打包,但是无法转化 js 代码; babel-loader 的主要任务是将 ES6 转化成低版本浏览器可以使用的代码;这里需要先安装三个包:
npm i babel-loader @babel/core @babel/parset-env -D 安装成功之后还需要安装 //包含regeneratorRuntime 插件运行的时候需要的内容npm i @babel/runtime -D//需要regeneratorRuntime 的地方自动require导包,然后编译的时候需要它npm i @babel/plugin-transform-runtime -D 在 webpack.config.js 文件的 module 下新增 rules : module:{ rules:[{ test:/\.js$/i, exclude:/node_module/,//不打包node_module里面的js use:{ loader: 'babel-loader', options:{//参数 presets:['@babel/preset-env'], plugins:[['@babel/plugin-transform-runtime']]}}}]} 9、代码分离代码分离是 webpack 最主要的特性之一,可以将代码分离到不同的 bundle 中;分离后的文件我们可以按需加载、并行加载;代码分离可以获取最小的 bundle ,可以控制资源加载的优先级,如果使用合理可以极大的节省加载时间;常用分离方式有三种: 9.1、配置入口节点使用 entry 配置手动的分离代码;这种方法的问题是如果有多个入口,那么这些多个入口共享的文件会分别在每个包里重复打包; const path = require('path')entry:{ index:'./src/index.js', other:'./src/other.js'},output:{ filename:'[name].bundle.js', //name可以获取到entry里面入口的key path:path.resolve(__dirname,'./dist'),} 多个入口,对应打包就会打包出多个出口,但是如果 index 和 other 同时使用了 lodash 包,那么在打包的时候会分别将 lodash 包加到 index 和 other 文件中; 9.2、防止重复使用 Entry dependencies 或者 const path = require('path')entry:{ index:{import:'./src/index.js', dependOn:'shared'}, other:{import:'./src/other.js', dependOn:'shared'}, shared:'lodash' //配置需要共享的模块},output:{ filename:'[name].bundle.js', //name可以获取到entry里面入口的key path:path.resolve(__dirname,'./dist'),} 将 lodash 模块单独打包在 shared 包中,让 index 和 other 共享; const path = require('path')entry:{ index:'./src/index.js', other:'./src/other.js'},output:{ filename:'[name].bundle.js', //name可以获取到entry里面入口的key path:path.resolve(__dirname,'./dist'),},optimization:{ splitChunks:{ chunks:'all'}} 这种方法会自动帮们做代码分割处理; 9.3、动态导入当涉及到动态代码拆分时,webpack 提供了两种方法: function get(){return import('lodash').then((default:_)=>{return _.join(['hello','webpack'],' ')})}get.then(res=>{ console.log(res)}) import 函数调用完成之后返回的是一个 Promise,所以可以直接使用 then 来链式调用;静态导入和动态导入是可以同时工作的; 下面是动态导入的两个比较好的应用: 9.3.1、懒加载懒加载也叫按需加载,是优化网页的一种方式;它主要是将代码在一些逻辑断点处分离开,在完成某些操作之后立即引入需要的代码模块;这也能加快应用程序初始加载速度,也能减轻代码的体积; //math.jsexport add(a,b){return a+b}//页面使用const button = document.createElement('button')button.textContent = '+'button.addEventListener('click',()=>{import(/*webpackChunkName:'math'*/'./math.js').then({add}=>{ //注释这一段是修改打包后文件的名称 console.log(add(1,2))})})document.body.appendChild(button) 上面这个例子,webpack 会将 math.js 打包成一个公共文件 math.bundle.js,但是在页面初始化的时候这个文件不回被加载,当点击按钮的时候才会被加载出来; 9.3.2、预加载模块webpack4.6.0 以上版本增加了对预获取和预加载的支持;在声明 import 时,使用下面指令可以让 webpack 输出资源提示,来告诉浏览器:
//math.jsexport add(a,b){return a+b}//页面使用const button = document.createElement('button')button.textContent = '+'button.addEventListener('click',()=>{import(/*webpackChunkName:'math', webpackPrefetch:true*/'./math.js').then({add}=>{ //注释这一段是修改打包后文件的名称 console.log(add(1,2))})})document.body.appendChild(button) 在引入注释处加上
10、缓存由于获取资源比较耗费时间,浏览器会使用一个缓存机制,通过命中缓存以降低网络流量,是网站加载速度更快;然而在部署新版本的时候不改变资源文件名浏览器可能会认为你没有更新,就会使用缓存版本; 10.1、配置输出文件名output:{ filename:'[name].[contenthash].js'} 在打包时输出文件名增加一个动态 hash 字符串,这也每次打包的文件名就不回重复了; 10.2、缓存第三方库将第三方库(lodash)单独提取到一个固定名称的文件中,因为这些库一般不回做修改,所以可以利用缓存机制消除请求,减少向 server 获取资源;(目标是第三方共享文件) optimization:{ splitChunks:{ cacheGroups:{ vendor:{ test:/[\\/]node_module[\\/]/, name:'vendors', chunks:'all'}}}} 这样所有第三方的包就都被放到 vindors.bundle.js 中了; 10.3、将所有的 js 文件放到一个文件夹中output:{ filename:'scripts/[name].[contenthash].js'} 11、拆分开发环境和生产环境的配置11.1、公共路径(publicPath)我们可以使用公共路径来指定应用程序中所有资源的基础路径;默认值是空字符串:“” ,webpack-dev-server 也会默认从 publicPath 为基准,使用它来决定在哪个目录下启用服务,来访问 webpack 输出的文件。 output:{ publishPath:'/' //也可以是其他路径} 11.2、环境变量环境变量可以消除 webpack.config.js 在开发环境和生产环境之间的差异;webpack 的命令行 npx webpack --env production//也可以携带一个 key 、valuenpx webpack --env production --env global=local 在 webpack.config.js 文件中获取环境变量: module.exports = (env) =>{ console.log(env)return { mode: env.production ? 'production' : 'development'}} 结果: { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true }//携带参数{ WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, production: true, global: 'local'} js 压缩 npm i terser-weboack-plugin -D 在 webpack.config.js 文件中引入使用: //压缩const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')const TerserPlugin = require('terser-weboack-plugin')optimization:{ minimizer:{new CssMinimizerPlugin()new TerserPlugin()}} 上面这俩压缩插件只会在生产环境中生效,开发环境中不会压缩; 11.3、拆分配置文件拆分配置文件的目的就是将生产环境和开发环境的配置文件分开,单独配置:webpack.config.dev.js、webpack.config.prod.js ;然后统一放到配置文件夹中; //webpack.config.dev.jsmodule.exports = { ebtry:{ index:'./src/index.js'}, output:{ filename:'scripys/[name].js', path:path.resolve(__dirname,'../dist'), clean:true, assetModuleFilename:'images/[contenthash][ext]'}, mode:'development', devtool:'inline-source-map', devServer:{static:'./dist'}, optimization:{ splitChunks:{ cacheGroups:{ vendor:{ test:/[\\/]node_module[\\/]/, name:'vendors', chunks:'all'}}}}}12345678910111213141516171819202122232425262728 开发环境不需要清理服务器缓存,不需要 publicPath,mode 可以直接设置为 development,需要 devtool,不需要压缩相关配置; 我们可以在控制台运行这个配置: npx webpack -c ./config/webpack.config.dev.js 由于提前设置的 //webpack.config.prod.jsconst CssMinimizerPlugin = require('css-minimizer-webpack-plugin')const TerserPlugin = require('terser-weboack-plugin')module.exports = { ebtry:{ index:'./src/index.js'}, output:{ filename:'scripys/[name].js', path:path.resolve(__dirname,'../dist'), clean:true, assetModuleFilename:'images/[contenthash][ext]', publicPath:'/'}, mode:'production', optimization:{ minimizer:{new CssMinimizerPlugin()new TerserPlugin()}, splitChunks:{ cacheGroups:{ vendor:{ test:/[\\/]node_module[\\/]/, name:'vendors', chunks:'all'}}}}, performance:{//关闭提示信息 hints:false}}12345678910111213141516171819202122232425262728293031323334 11.4、合并配置文件由于配置文件开发环境和生产环境中有很多相同的配置,我们可以把相同配置提取到 webpack.config.common.js 文件中;然后再将配置合并: npx i webpack-merge -D 在 config 文件夹下新增 webpack.config.js 文件: const { merge } = require('webpack-merge')const common = require('./webpack.config.common.js')const prod= require('./webpack.config.prod.js')const dev= require('./webpack.config.dev.js')module.exports = (env) => {switch(true){case env.development:return merge(common,dev);case env.production:return merge(common,prod);}} 11.5、npm 脚本每次打包或者启动服务时,都需要在命令行输入一长串的命令,这里我们配置 npm 来简化命令行; "scripts":{"start": "npx webpack serve -c ./config/webpak.config.js --env development"} 在配置 npm 的时候,我们可以省略 npm 或者 npx "scripts":{"start": "webpack serve -c ./config/webpak.config.js --env development"} 2、在命令行运行 npm run start 由于改写命令行的时候在后面传入了环境变量,所以这个时候 start 就代表执行 development 环境的打包; 入门部分就到这里了,后面会继续深入学习 webpack! 该文章在 2024/4/3 14:25:59 编辑过 |
关键字查询
相关文章
正在查询... |