一步步深入学习webpack(入门困惑express和dev-server区别及分别使用dev-server和webpack-hot-middleware实现的热加载区别)

最近需要对webpack详细学习后,给大家分享学习。于是不得不对每一个点进行学习,结果发现webpack涉及到的知识内容好多,自己学习也是一知半解,很多时候脑细胞都死得一片一片的。
注:本文是参考网上多方资料学习后记录的,如有雷同,请联系我。

学习资料:入门Webpack,看这篇就够了WebPack 简明学习教程Webpack飞行手册以及wepack官方文档
一、webpack是什么
Webpack是一款目前非常流行的前端模块打包工具,可将项目中所加载的模块进行打包,以及将一些浏览器不支持的语言进行转换。

两个最核心原理:一切皆模块、按需加载。

打包原理:先找到入口文件,递归探索出所有依赖的模块,最后利用Loader进行不同文件的类型的处理,最终打包为一个文件。

另外,webpack遵循Common.js规范,也就是项目出现的以require()方式的加载模块,读取并执行,最后返回的是每个模块的自由变量exports(是对外输出API的唯一途径)等。
注意:
Common.js是模块化开发的标准(服务端模块规范),加载同步,加载完成后才能执行后面的操作。但Node.js一般用于服务器的编程,加载模块一般已经存在硬盘,加载比较快,不用考虑异步加载的模式!
此外还有浏览器模式下的AMD(RequireJS在推广过程中对模块定义提出的概念)和CMD(SeaJS在推广过程中对模块定义提出的概念)。其中,AMD是预加载,遵循异步模块定义(^ ^ps:这个还没懂)在使用它时,它会尝试第三方类库修改自身来支持 RequireJS,且插件类型比较单一;而后者是并行加载js文件,可以同一时间解析多个文件,但这时加载顺序不一定,插件类型比较丰富。

后来的后来:在安装vue后,发现dist目录下多了许多个文件,查看描述,又有了UMD。
UMD:通用模块定义(优势多个环境下,不用修改就可以使用,兼容AMD/Common.js和一般的全局定义,但是!不支持CMD的!)
UMD的简单实现:
(1)先判断是否支持Node.js模块格式(即exports是否存在),存在就是用Common.js;
(2)再判断是否支持AMD(define是否存在);
(3)如果两个都不存在,则模块公开全局;

补充:在过程中有接触到SASS(css语法超级),后来又有SCSS是SASS的新语法。SASS的基本用法:
(1)允许使用变量,所有变量以 e g : blue:#1875e7;
(2)变量需要嵌在字符串中,必须写在#{}中;
(3)有计算功能,可以在代码中使用算式;
(4)嵌套;
(5)注释风格/* / 和 /!重要注释*/

webpack使用可以使用不同的Loader,调用外部的脚本或者工具,实现对不同格式的文件处理。提供两个工具处理样式表:
(1)css-loader:使你可以使用类似@import和url()实现require()加载;
(2)style-loader:将计算后的样式假如页面,二者组合在一起使用可以将样式嵌入webpack打包后的JS文件中;

Babel则是编译JavaScript的平台,让我们可以使用新的JavaScript标准(如:ES7、ES6),也可使用基于javaScript的扩展语言,如React的JSX。

二、安装步骤
1.新建项目文件,且在终端中进入该目录;
2.全局安装:npm install –g webpack
3.本地安装:npm install –save-dev webpack
4.初始化: npm init
5.安装webpack服务器 npm install –save-dev webpack-dev-server

三、使用
这里写图片描述
在按第二部分安装好后,我们就可以对webpack进行使用了,使用方法可以通过终端命令或者是配置文件来进行。一般采用后者,配置好后,就可以直接在teminal中使用webpack系列命令,前者易出错且难一点。因此,使用webpack进行打包,一般我们需要在项目中新建一个webpack.config.js配置文件,基本结构如上图所示,作用就是告诉webpack需要做些什么。
(1)entry:webpack工作开始的地方,找到所依赖的模块,按顺序利用Loader处理
三种数据类型:字符串、数组(数组中每一项都会被打包,形成互不依赖的文件)、对象(对象中每一个属性都会被打包,形成互不依赖的文件)

(2)output:打包后文件的具体配置,打包文件所在路径及打包后文件的名字,常用的属性配置如下:
path:打包后文件所在路径;
filename:打包后文件的名字(四种常见写法:自定义、[name].js即入口的文件名、[hash].js打包后的hash值、[chunkhash]该块打包后的hash值);
publicPath:上线时的公共路径,主要应用于线上(eg:如果是设置了为static,则线上运行时,http://localhost:port/static/入口路径);
chunkFilename:’js/[name].js’:按需加载模块时输出的文件名称;

(3)module——Loader: 将不同格式的模块转换为浏览器统一可识别的,利用test进行正则匹配。两种写法:loaders:[{loader:’style-loader’},{loader:’ ‘},{loader:’ ‘}]或者是使用!号拼接的方法:loader:”loader1!loader2!…”

(4)plugins:插件用于扩展更多的功能需求;
使用较多的是html-webpack-plugin插件,用于生成HTML(如果需要多页面,有几个页面就需要几个htmlWebpackPlugin);
还有webpack-dev-middleware中间件,好处就是不会向硬盘写文件,而在内存中,文件改变时保存后,只要直接刷新浏览器就可以看到最新结果;
如果想要不用刷新就能检测到变化自动更新,则需要webpack-hot-middleware,进行页面的热重载的;

(5)devtools:可设置生成不同的sourcemap类型,方便调试定位;

(5)devServer:本地服务器,可以监听代码修改,并自动刷新显示修改后的结果;

(6)resolve: 设置模块被解析的一些约定(如引入方式等,extention:[“.js”,”.html”,”css”,…],这表明我们在require加载对应后缀名的文件时,可以省略后缀名);

四、手把手跟着前辈脚步走
正是通过对这篇文章——一步步构造自己的vue2.0+webpack环境的反复实践,终于对webpack更熟悉了解了。然后由于这篇文章是博主16年底写的,如今的新版运行,在写法和使用上可能又很大的差别,因此,虽然博主手把手地写下了这篇博客,我也踩了大半天的坑。
1.简要记录下自己修改的几个地方:
(1)安装未安装的依赖;
(2)修改现在的loader写法,module/rules:[];
(3)且在书写匹配不同的loader时,不能省略后缀eg:vue-loader;
(4)去掉没有的Fevlist对象属性;
(5)另外还需要新建出口文件output;

2.途中的小疑问
(1)利用node.js中的express服务器和webpack中dev server实现开启服务有什么不同吗?
经查询,dev server也是一个轻量级的node.js express服务器,实际上相当于是一个封装好的express的http服务器+调用webpack-dev-middleware。
例如一般可能会有如下的配置,当服务器启动时,启动命令是:webpack-dev-server –config wepack.config.js(配置文件路径),在解析模板列表前会有如图所示的提示信息。

devServer: {
  historyApiFallback: true, // 404的页面会自动跳转到/页面
  inline: true, // 文件改变自动刷新页面
  port: 3800, // 服务器端口 
  },

这里写图片描述

而我们可以使用express服务器进行更多的扩展,结合使用其他的中间件来响应http请求及其他的功能,扩展性更好,较为灵活。这种方式下的编译命令一般是:node build/dev-server.js(配置文件路径)。
补充:中间件,是一个函数,用于访问请求对象、响应对象和web应用中处于请求-响应循环流程中的中间件。它的功能包括:执行任何代码、修改请求和响应对象、终结请求-响应循环和调用堆栈中的下一个中间件。

(2)热加载/热更新,使用devServer和使用webpack-dev-middleware+webpack-hot-middleware区别
devServer正如(1)中一样,express+webpack-dev-middleware,源文件改动后,会进行实时编译,再用webpack-dev-middleware将编译后文件输出到内存中.
而后面两种,如果单用前面一种需要手动刷新浏览器,加上webpack-hot-middleware才可以实现浏览器的无刷新更新,也就是HMR,不需要刷新整个页面。热加载却要刷新整个页面。

(⊙o⊙)我天我在说啥,我也搞不清楚了,上面两个问题仅限于当下的理解,避免不了有错误之处。

(3)path.resolve( ),到底是怎么进行路径转换的?

path.resolve('/foo','/bar','baz');     // /bar/baz
path.resolve('/foo/bar','./baz');      // /foo/bar/baz
path.resolve('/foo/bar','/tmp/file/'); // /tmp/file

查询了官方API,写得比较隐晦,只写了结果没有具体原理,还有看到了这篇文章《JavaScript 标准参考教程(alpha)》,by 阮一峰,其中列了一个小实例来说明。

path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile')

其执行效果类似于:

cd foo/bar
cd /tmp/file
cd ..
cd a/../subfile
pwd       //展示整个路径名

在代码里验证打印输出为: /tmp/subfile。最开始进入foo/bar路径目录下,然后又进入tmp/file目录下(末尾的/自动隐藏),又进入当前路径的上层路径即tmp下,接着就是又进入当前目录下的a即tmp/a,然后又是上级tmp/,最后就是subfile,因此最终路径为tmp/subfile。
即resolve进行路径转换时,同级目录参数,前面的直接被覆盖!正如例子中(1),bar覆盖了foo.

猜你喜欢

转载自blog.csdn.net/bonjourjw/article/details/79703123
今日推荐