首先,声明本次笔记是整理表严肃(biaoyansu.com)老师Webpack精讲课程视频内容。
1.背景介绍:
a.由于web技术相对其他技术其历史更长即其历史包袱更重。
b.智能机造在2008年前基本也就是一些发烧友在玩,在2008年后才慢慢红火起来然后才有了整个开发的生态。不管是IOS还是Android向,它向后兼容的压力要小太多了。
c.Web开发就与上述IOS和Android开发不同。浏览器一大把,版本一大把,那就意味着各种各样的标准一大把(即它们浏览器基于自己的想法开发出来的标准就一大把)。后来慢慢的这些标准就统一了然后大家又开始朝着这个方向前进。
d.虽然浏览器的标准后来统一了但是之前所做过的工作。比如:一个网站(流量很大),现在有100个用户访问,然后网站更新后造成一半用户不能访问因为那一半用户的浏览器不支持这个功能或接口。那老板肯定是不接受这么做的。但是又有一大批用户需要一些新功能,所以这个"新需求"和"旧标准"之间的矛盾是一直存在并且愈演愈烈。
e.基于"新需求"和"旧标准"之间的矛盾,Web前端出现了太多补丁以及类似补丁的工具。这些工具就在前面两者之间发挥"润滑剂"作用。但是问题依然没有解决,给我们感觉整个Web前端生态就像一个巨人在一个鸡窝里跳芭蕾舞,还要跳得优雅还不能踩死小鸡。非常辛苦,有太多历史包袱。
f. webpack作为一个润滑剂其初衷就是希望我们填Web前端生态的坑的过程不要太辛苦。当然为了理解这个填坑工具就已经够辛苦了,但是当我们理解并掌握好这个工具后就会发现它是很好用的(至少在当下而言它是最好最完善的工具)。
g.学习webpack,要不就有很多项目经验(即填过无数坑)这样你一看到它就知道该怎么用,要不就要了解Web前端"新需求"和"旧标准"之间的矛盾。
2.是什么和为什么:
a.链接内容:
浏览器在解析Js时会有个盲点即未像其他语言那样做到Js语言的模块化。也就是在Js语言里,一个文件要想给另一个文件暴露出去部分数据或变量那么只有把它定义为全局变量)。
比如:
b.js文件代码:
(function () {
var msg = ‘Yo.’;
})();
a.js文件代码:console.log(‘msg:’,msg);
结果:在浏览器控制台报"msg is not defined"错误。
分析:a.js不能访问b.js中的msg变量。必须将该变量定义为全局变量。
修改:
b.js文件代码:
(function(){
window.msg=‘Yo.’;
})();
结果:在浏览器控制台显示"msg:Yo." 。
修改后虽解决了问题。但会带来新的问题或隐患,你不知道什么时候用什 么方式会把一个全局变量给覆盖掉。如果所有的东西所有的依赖都是我们自己写的那还好,可能我们都知道自己定义了哪些全局变量。如果引入的是一个Web的库那该库中的全局变量就不再受我们控制因为人家这个库也是随时更新的,可能到后面就变了而且我们很难绕开这个东西。无论用何种方式定义全局变量总有一定可能性会发生冲突,这个问题直到出现Vue时都没办法解决(因为Vue代码"new Vue();“就是一个全局变量)。
但是后来出现node,它可以运行Js,同时浏览器也可以运行Js。两者区别是:node可以直接运行在操作系统上。
node中有个非常好的浏览器中不存在的机制,这是现在很多人会把前端代码先写成后端代码然后再编译成前端代码的原因。上面已经说过如果要共享两个Js文件中的数据在浏览器模式下只能把该数据写成全局变量,但是在node模式下不需要写成全局变量只需要把该数据暴露出去即可。
比如:
b.js文件代码:
var msg=‘Yo.’;
module.exports={msg:msg};
注释: “module.exports”是个比较特殊的东西。因为在node眼中所有文件都是一个模块,任何一个模块都可以有两个口一个是入水口一个是出水口,这里"module.exports"就是一个出水口。”{msg:msg}“是一个对象,在其中写入要共享的内容。*********
a.js文件代码:
var msg=require(’./b.js’).msg;
注释: 执行方法require后可获得b.js暴露出来的”{msg:msg}“对象
console.log(‘msg’:msg);
*** 结果***:在a.js当前目录运行"node a.js"命令回车得到"msg:Yo.”。
分析:每个文件只需要管理好两个地方一个入水口一个出水口 。入水口表示当前文件要用什么数据然后语法实现就是a.js代码,出水口表示当前文件要暴露什么数据出去然后语法实现就是b.js代码。
结论:归根结底,浏览器或web标准有问题。一开始未考虑这么多即文件间的代码共享或数据共享,这个就是历史包袱本来应该有的功能浏览器却没开发出来就只能依赖后来的node来解决。
b.webpack引入
初衷:为了解决上述问题。具体是基于已经实现用写后端语言的方式去写前端代码出现的浏览器无法解析node语法问题,webpack可做到动态把后端代码变成浏览器能读懂的代码。
原理:比如上述用node语法实现的a.js文件中"var msg=require(’./b.js’).msg"代码,webpack就可以把b.js中用到的部分代码提取出来两个文件打包为一个文件(体积小,性能好)。最后在页面中只需依赖webpack打包出来的文件。
用法:以上述node语法实现文件为例。change directory到a.js所在目录下运行"webpack a.js bundle.js"(命令中不用写a.js的依赖webpack会动态加载另外写上打包后的文件bundle.js)命令回车可在a.js当前目录下出现"bundle.js"文件,最后在页面中依赖“bundle.js”
3.安装和配置:
a.准备工作:
由于webpack是基于node,所以安装webpack前先安装node。
b.安装工作
首先,分两种安装方式。其中第一种是安装在全局,第二种是安装在局部。
全局安装:
运行命令"npm i webpack -g"(“i"为install缩写,”-g"代表global即全局范围)在全局范围内通过npm来安装webpack。之后可在任何目录运行webpack相关打包命令。但是有可能会出现问题,即局部安装webpack的项目到其他电脑上遇到全局安装的webpack可能会出现版本不一致这个就很有可能出问题(虽然一般情况下不会但还有特殊情况)。
局部安装:
进入项目目录,然后运行命令"npm init -y"在当前目录下生成"package.json"文件,只要有该文件npm就认为整个目录就是一个模块一个项目,然后在该目录下运行命令"npm i webpack -D"("-D"为"–save-dev"缩写)之后npm就会把webpack放到当前项目中即局部安装。区别全局安装的是,它会把版本信息写在"package.json"中以后当前项目运行在其他平台上也没有任何问题只需要通过npm安装下webpack即可。
c.webpack本地工作
c-1.首先,在当前项目目录下找到文件夹"node_modules",打开它并在其下找到子目录".bin"(该子目录指“binary”意思是:可执行的),在该子目录下找到webpack文件。
c-2.找到webpack文件后,在webpack当前目录下运行命令"webpack a.js bundle.js"之后也会产生bundle.js文件。
c-3.由于webpack文件所在目录有点长造成每次执行打包命令过于繁锁。所以可在package.json中"scripts"对象里配置webpack打包命令的快捷方式,如下图所示:
其中,”pack“为webpack打包命令的快捷方式,执行“npm run pack”命令即可实现webpack打包命令。
c-4.最后在页面引入pack.js即可。
d.webpack配置工作
d-1.首先,由于webpack功能强大其配置项非常多不管是在webpack之前还是之后还是中间的工作都有相当多的参数可以配置,如果每次都打一行命令那会变得非常长而且有些配置项没法在命令中添加。
d-2.基于上述情况,我们会指定"webpack.config.js"这个文件来写入webpack的配置,然后在当前文件我们要暴露一些东西出去,代码如下图:
注意在引入webpack.config.js文件后应去掉package.json中用于配置快捷方式的webpack打包命令的参数。
d-3.直接运行命令"npm run pack"回车也可生成打包文件pack.js.
d-4.在页面中依赖上pack.js文件运行项目即可。
4.entry和output(即多入口和多出口)
a.首先:
由于项目逻辑提高复杂度(即每个页面既依赖相同js又依赖不同js),可能会出现多个入口文件和出口文件,代码如下图:
当然这里有整个项目依赖的js文件,用于控制当前项目是否开放注册。代码如下图:
如上述文件,如果变量值为true则出现注册页面且点击可进入注册成功页面,否则不出现注册页面可出现暂不开放注册页面。两个js文件代码如下(是上述的home.js以及signup.js):
最后,在两个页面中分别引入webpack打包过后的js文件(即上述:home.bundle.js,signup.bundle.js)。
b.着重理解点:
在页面中只需引入home.bundle.js和signup.bundle.js文件即可。依赖base.js文件过程在webpack打包期间已经动态加载到home.js和signup.js两个文件中最后分别打包生成home.bundle.js和signup.bundle.js文件。
5.loader(路由)
a.首先,配置webpack。
b.路由用途:
一般,在一个文件使用另一个文件暴露出来的东西时会使用到路由。
c.举个例子:
c-1.如果某js文件依赖css文件时仍需使用webpack被打包那么由于webpack不理解css文件所以使用路由来编译一下。代码如下:
c-2.验证一下上述”style-loader“路由的效果:如下图:
编译之前,两个文件base.css以及base1.css:
c-3. 如果在js文件中依赖两个或以上css文件时,越往下优先级越高。
c-4. loader其实有很多(可在“https://v4.webpack.js.org/concepts/loaders/查看”),但是真正用到的并不多,可能经常用到的几个或十几个。
6.如何动态加载样式:
a.首先,配置webpack,较之前新增了一个属性"mode"(可避免出现警告)代码如下:
js依赖好css文件后,在当前目录下运行命令"npx webpack"后刷新页面即可
b.由于项目提高样式复杂度后,js文件中要依赖的css文件就会增多。如果界面中某个元素样式代码出现问题我要知道它来自哪个样式文件的哪一行这样才能第一时间解决问题。
b-1. 这里以界面body元素为例,代码如下图:
b-2.为解决上述问题,可在webpack配置module属性中引入一个options,代码如下图:
再次在当前目录下运行命令"npx webpack"进行打包后调式即可,成功解决问题,代码如下图: