混子前端带你入门webpack

4,770 阅读13分钟
本文是混子前端之前在学习 webpack 时整理的说明文件 有些枯燥 但整理的很详细,其中代码模块我已图文备注说明,如果你刚开始准备学习 webpack 那本文对你帮助很大,一定要按照文字走完全流程

webpack基本介绍

webpack: 给js准备的一个打包工具,可以把很多模块打包成很少的静态文件,有一个自己的特性:代码分割 (code splitting),还有一个loaders的概念,模块是通过loaders处理各种文件,无论js是用 commonJS、AMD、ES6、CSS、Images、JSON、Coffeescript、LESS... 都可以用loaders 处理,甚至还可以是自己定义的一些文件,如:.vue、.JSX文件都可以通过 loaders 处理

代码分割: 可以使项目加载过程中只去加载一些当时只需要的部分文件

webpack官方:就是一个模块打包器,下图左边箭头表示文件之间依赖关系的模块群,这样的模块群通过 webpack 打包之后,对依赖进行处理把他打包成可以直接运行的 js/css/图片 文件


webpack目标:
  1. 会切分依赖树,会把依赖树切分不同代码块里,然后按需加载依赖,和前端懒加载概念相似
  2. 保持初始化加载时间更少
  3. 任何静态资源都可以视为一个模块在项目中引用,在开发中起到很多便利,整合第三方类库,把第三方类库视做模块在项目中引用
  4. 可以在整个打包过程中,每一步都可以自定义去做事情
和其他打包工具不同 gulp/grount
  1. code Splitting 代码分割
  2. loaders
  3. 插件系统,模块热更新 plugin system:在开发过程中提高开发和调试效率
核心思想:
  1. 一切皆为模块
  2. 按需加载
打包原理:bundle.js 是以模块 id 为记号,通过函数把各个文件 依赖封装 达成分割效果

webpack安装和命令行

> mkdir webpack-test                                              //创建webpack-test文件夹
> npm init                                                                  //初始化项目,创建package.json文件
> npm install webpack@3.0.0 --save-dev            //安装3.0版本webpack,因为4.0版本直接打包会报错,比较严格
> 创建hello.js 和 world.js,用commonJS规范在hello.js require world.js
> webpack hello.js hello.bundle.js                       //打包hello.js文件

观察hello.bundle.js:

可以看到world代码被打包进来了,编号是 1,写在 hello.js 中的 require 被替换成webpack_require,webpack用这种方式去寻找依赖,把文件打包到一起,这是简单工作方式

> 继续创建 style.css 文件,在 hello.js 中 require style.css (不深究为什么),执行打包命令:webpack hello.js hello.bundle.js 开始报错,提示没有安装 css loader,因为他不支持打包 css类型,如果打包 css 文件需要安装 css loader

> npm install css-loader style-loader --save-dev

> 执行打包命令还是会报错,因为在 hello.js 中 require style.css 没有执行loader,需要写成require('css-loader!./style.css'),意思是:在引用style.css文件前必须经过css-loader处理,继续执行打包命令,没有报错

> 创建 index.html 引用hello.js文件,并且在 style.css 中给 body 添加 background 属性,发现执行打包命令后颜色没有效果,要想颜色有效果要结果style-loader,在hello.js中改为require('style-loader!css-loader!./style.css')

可以看出css-loader让webpack去处理css文件,style-loader通过css-loader处理完的文件,把处理完的文件新建一个style标签插入到html head里面

 > 如果在代码里每次 require css 都需要写一长串的话 style-loader!css-loader! 的话会很麻烦,也可以用打包命令:

webpack hello.js hello.bundle.js --module-bind 'css=style-loader!css-loader'

用到了webpack中 --module-bind 的参数,相当于把这个模块绑定:如果是css文件就先交给css-loader,css-loader之前要style-loader

> 如果每次改变都需要打包的话,会很麻烦,可以用--watch参数监听:

webpack hello.js hello.bundle.js --module-bind 'css=style-loader!css-loader' --watch


> 除了--watch还有一些其他参数,如:

         1、--progress : 看到打包的百分比;

         2、 --display-modules:看到打包的模块和用到的loader列出来;

         3、--display-reasons:打包 模块的原因列举出来


建立项目的webpack配置文件

建立 webpack.config.js 原因:如果直接使用 webpack 命令,会直接在项目根目录寻找webpack.config.js 作为默认配置去运行,这时不需要指定任何参数,会直接读取 config 里面的内容,也可以使用 --config 指定其他配置文件

即:执行 webpack 会去项目目录找 webpack.config.js 文件,如果配置文件不叫这个名字 (假如修改为webpack.dev.config.js),可以执行  webpack --config webpack.dev.config.js,也可生效
如果想在webpack命令行中加入一些参数,可以借助 package.json 中 script 属性



webpack配置的entry和output

1、entry 打包2个平行的js文件可以写成数组模式


打包后的 bundle.js 文件


这个0模块把两个不相干的模块,require 打包到一起,这样就可以在开发中使用

2、在多页面应用程序,可以用到entry为对象的形式,对象的key是 chunk name,value 是entry,根据不同页面分配不同的 chunk,如果 entry 的 objcet 是多余一个的,那打包好的文件就不可以是一个了,这时候就要修改 output,在 output 中修改 filename 属性,这里如果指定了 hash,如下图:

打包后的多个文件 hash 值都一样,所以引出了 chunk-hash,如下图:

如果每次打包生成的唯一加密(md5)后 chunk-name不一样,那 html 文件中引入的也不一样了,所以这里要加入插件 html-webpack-plugin,在项目中 npm 安装,在 webpack.config.js 中配置,如下图:


这时候运行 npm run webpack,会生成打包好 带有 hash 值的 main.js 和 a.js 文件,还有新的html 文件引入了带有 hash 值的压缩文件,如下图:


插件生成的 index.html 和项目根目录的 index.html 没有任何关系,这不满足需求。如果以根目录index.html去生成dist下index.html,就要在 webpack.config.js 中继续操作


这时就可以把根目录下 index.html 中 script src 标签删掉,打包后的 index.html 会自动引入压缩并带有 hash 值的 js 文件,这时候发现一个问题,所有压缩好的文件都在dist/js下,这和实际生产不符,实际需要 index.html 在 dist/js 目录外的,还要去修改 webpack.config.js,将不是 js 文件都创建到 dist 目录,将js文件 filename 指定相对路径


打包后文件结构如下图:


除了 template 还有一些其他参数:

如何做到在 webpack.config.js 中修改 index.html 文件 title

index.html中:


还能获取到哪些参数:


index.html 中:


打包编译后为:


究竟能从 htmlWebpackPlugin 取到哪些信息?

这时候可以对 htmlWebpackPlugin进行遍历,因为类似于模版文件引擎写法,可以直接编写:


在打包后生成的 index.html 中可以看到:


继续遍历 files 和 options 可以拿到很多配置信息,不一一介绍了,可以去 npmjs.com 搜索html-wekpack.plugin 进行查看


继续探索,假如想把一部分 js 放到 index 的 head 标签里,一部分想放到 body 标签里,只通过配置是做不到的,所以要改变模版:

1、将 webpack.config.js 中 inject 修改为 false


2、编写 script 标签 src 即可


继续探索,假如打包后要上线,上线后的地址和本地的相对路径肯定不一样,这时候要借助output 新属性:[[ publicPath ]]

打包后前缀就变了:


上线要对 html 文件进行压缩,就要用到新属性:[[minify]] 对当前生成的html进行压缩


处理多页面应用:需要有多个页面,需要生成多个html:

plugins是一个数组,可以有多个,每一个对应一个页面,直接 npm run webpack 即可生成多个html,注意这里新的参数 chunks/excludeChunks


如果希望把页面性能达到极致,把一些初始化脚本直接嵌入到页面,而不是链接的形式引入到页面,现在全都是链接,即:script src=***,这样可以达到效果但会增加http请求,现在可以把初始的代码直接inline入到页面,可以大大提高运行速度,因为没有http请求:

通过 htmlWebpackPlugin.files.chunks.main.entry 截取掉占位符


后通过 comilation.assets[*****] 取 source() 方法,这是引用公共类库 mian.js 的方法,a/b/c 还有属于自己的js方法,要引入body标签中,这时候修改 webpack.config.js 是没有用的,要在index.html 中写入 js 引入外部文件



webpack如何处理想要的资源文件

转换es6:npm install --save-dev babel-loader babel-core

bebel-loader: es6不断的修订,通过指定persets来告诉babel-loader转换某一些特性为我们的js,如果指定所有的特性可以直接用latest,怎么指定插件呢?给loader指定参数,这里指定lastest,然后安装。


官网还提供一种方式指定 persets,可以项目根目录创建 .babelrc 文件,然后指定也可以


还有一种方式可以在 package.json 里面,直接定义 babel,可以不在配置文件里指定



babel-loader 打包编译是很慢的,官网给出新的参数

exclude:loader 的排除范围(不明显)

include:loader的处理范围


exclude 比较慢的原因是没有用到绝对路径,在项目中怎么获取到绝对路径?

1、需要借助 path 方法


2、path.resolve(__dirname,'相对路径'),resolve解析


同理 include也是写绝对路径

如何在项目里处理css文件?

首先要在命令行中:npm install style-loader css-loader --save-dev

创建一个common.css文件,import到app.js文件中

在webpack.config.js中添加loaders


最后npm run webpack即可,接下来查看,如果有一个需要兼容性的样式怎么处理?假如说:display:flex要我们手动添加前缀进行兼容,现在有一个loader去处理

npm install postcss-loader --save-dev

postcss-loader是css的一个后处理器,安装好之后,需要继续安装 autoprefixer 这是给postcss的插件,是用来处理css前缀的

npm install autoprefixer --save-dev


这时候 npm run webpack,可以在网页源码中看到生成出来兼容性的css


如果在 css 中使用@import一个文件,会发现display:flex没有生成兼容性的前缀,也就是说他经过了css-loader和style-loader,没有经过postcss-loader,处理办法是借助precss插件,在项目中安装:npm install precss --save-dev


在 postcss-loader 添加配置即可

如何在项目里处理less文件,先安装 less-loader 和 less,进行配置

npm install less-loader --save-dev npm install less --save-dev

和处理css文件的配置一样,不过多了 less-loader,同样要处理css代码兼容性,官方指定,postcss-loader 处理要在 less-loader 和 css-loader 中间进行,顺序不能变


这里如果在 less 中使用 import 写法,可以不用写 require('precss'),因为 less-loader 已经帮我们做了处理,如果使用sass作为项目预处理器使用方法与less一样


如何处理项目中模版文件,通常是:

1、用webpack把模版文件当作字符串处理 

2、webpack把模版文件 当成已经编译好的模版处理函数;首先安装html-loader

npm install html-loader --save-dev

将模版js文件引入css less 及 html模版,我们在入口app.js文件中引入html模版文件,在webpack.config.js 中添加 html-loader 语法规范即可运行


现在如果,模版文件不是以html结尾,而是以.tpl结尾的模版文件,那要如何处理 ?

首先要引入新的loader:

npm install ejs-loader --save-dev

然后在webpack.config.js中添加规则,让.tpl结尾的文件用 ejs-loader 处理


修改模版文件(是ejs模版引擎的语法,在模版区间内可以自由编写js语法):


然后规则不变,在layer.js引用,这时tpl就不是字符串了,而是返回的函数:


所以在app.js中可以进行传参的方式对模版内name 和 arr进行定义


打包后页面输出:


现在比较流行的loader,比如JSX是react框架的loader,现在这个loader已经被集成到了babel,只需要进行一些设置就可以支持JSX,现在VUE2.0也可以通过直接写render函数,render函数也支持JSX语法

如何处理项目中的图片文件,分几种情况,

    1、css中很多的background图片

     2、模版结构里引用了img文件

     3、根部index.html引用了img文件

首先npm install file-loader --save-dev
处理第一种情况,在 webpack.config.js 中添加 file-loader 规则:


当给项目增加一个对图片的处理规则,在项目中根目录下 index.html 相对路径图片的引用,还是css背景中 相对路径图片引用,loader都会帮我们处理
还有一种情况是模版组件中引用了img文件,会在控制台中看到index.html中img src路径 和 css中 background url 都被替换了,但模版中的img src路径没有被替换,

解决办法:

    1、尽可能在模版中使用绝对地址 

    2、可以在src中使用${ }的形式把图片引用进来,这时可以在控制台中看到图片地址被替换了


图片打包以后的输出地址,默认放到了dist目录下,可以给loader添加一些参数:


介绍一个新的loader,npm install url-loader --save-dev,当图片或者文件大小大于指定的limit 指定的大小,会丢给 file-loader 去处理,当小于limit设置值的时候会把图片或者文件转为base64 位编码


这两种方式各有优劣:通过 http 请求载入过来的图片,可以让浏览器享受一个优势就是图片的缓存,当图片重复性很高的时候,通过http request下次访问会很快,利用这个缓存;如果是base64,相当于在任何地方用到这个图片时候,在这个地方会有一份同样大小的 base 64 编码存在,会导致代码体积。真正在项目中要平衡的去看。

怎么去压缩图片呢?官方给出了 image-webpack-loader,最好配合 url-loader 或 file-loader一起使用

这时可以在命令窗口看到,原本18k的图片被压缩很小的图片被打包进项目

注意: loader处理方式是从右到左,从下到上的,比如(老规则)


先调用postcss-loader 然后 css-loader 然后 style-loader

最后老规矩,欢迎点赞和纠错,祝大家工作愉快!