Webpack是一个打包模块化javascript的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注于构建模块化项目。
本质上,webpack是javascript应用程序的静态模块打包器。当webpack处理应用程序时,它会递归地构建一个依赖图,其中包含应用程序所需的每个模块,然后将所有这些模块打包成一个或多个bundle。
webpack可以看做是模块打包机,他做的事情是分析项目结构,找到js模块以及其他一些浏览器不能直接被运行的拓展语言(scss、typescript)等,并将其打包为合适的格式以供浏览器使用。
webpack的几个概念:
(1)Entry:指示webpack应该使用哪个模块来作为构建其内部依赖图的开始。进入入口起点后,webpack会找出哪些模块和库是入口起点依赖的。
(2)Output:告诉webpack在哪里输出它所创建的bundles,以及如何命名这些文件。
(3)Loader:模块转换器,让webpack能够处理那些非javascript文件(webpack自身只理解js)。loader可以将所有的类型文件转换为webpack能够处理的有效模块,然后你可以利用webpack的打包能力,对它们进行处理。
(4)Plugins:扩展插件,loader被用来转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。
(5)Chunk :代码块,一个Chunk由多个模块组合而成,用于代码合并与分割。
webpack配置:
const path=require('path'); module.export={ entry:'./main.js', output:{ //将所有依赖的模块合并输出到bundle.js文件中 filename:'bundle.js', //将输出文件都放到dist目录下 path:path.resolve(__dirname,'./dist') }, module:{ rules:[ { //用正则去匹配要用该loader转换的css文件 test:/.css$/, use:['style-loader','css-loader?minimize']
}
]
}//module.rules数组配置了一组规则,告诉webpack在遇到哪些文件时使用哪些loader去加载和转换
}
use:[ { loader:'style-loader' }, { loader:'css-loader', options:{ minimize:true } } ]
1、常用的Loader有哪些?分别有什么作用
- style-loader:将css代码注入到js中,通过DOM操作去加载css;
- css-loader:允许将css文件通过require的方式引入;
- less-loader:将less代码转换为css代码;
- sass-loader:将sass代码转换为css代码;
- image-loader:加载并且压缩图片文件;
- babel-loader:用babel来转换ES6文件到ES5;
- ts-loader:将ts文件转换为js文件
2、几个常见的Plugin
html-webpack-plugin:为html文件中引入的外部资源,可以生成创建html入口文件。
commonChuckPlugin:提取公共代码。
3、webpack与grunt、gulp有什么不同?
webpack与Grunt、Gulp没有什么可比性,它可以看做模块打包机,通过分析项目结构,找到js文件以及其他一些浏览器不能直接运行的拓展语言,并将其转换和打包为合适的格式供浏览器使用。
Grunt/Gulp是一种能够优化前端的开发流程的工具,而Webpack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具
(1)他们的工作方式也有较大的区别:
Grunt/glup的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。
webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(main.js),Webpack将从这个文件开始找到你的项目所有的依赖文件,然后使用loaders处理他们,最后打包为一个(或多个)浏览器可识别的js文件。
(2)三者都是前端构建工具,grunt和glup在早期比较流行,现在webpack相对来说比较主流,不过一些轻量级的任务还是会用glup来处理,比如单独打包CSS文件等。
(3)grunt/glup强调的是前端开发的工作流程,我们可以通过一系列的task,定义task处理事务(例如文件压缩、雪碧图、处理server)然后定义执行顺序,来让grunt/glup执行这些task,从而构建项目的整个前端开发流程。
虽然它们都是前端自动化构建工具,但是严格上讲,grunt/glup旨在规范前端开发流程,模块化不是它强调的东西;而webpack更明显强调模块化开发,而那些文件压缩合并、预处理等功能,不过是他附带的功能。
4、Webpack的构建流程是什么?从读取配置到输出文件的过程
初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数;
开始编译:用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译;
确定入口:根据配置中的entry找出所有的入口文件;
编译模块:从入口文件出发,调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,递归本步骤直到所有依赖的文件都经过本步骤的处理;
完成模块编译:在使用loader翻译完所有模块后,得到每个模块被翻译后的最终内容以及它们之间的依赖关系;
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
一个个独立的模块文件被合并到了一个单独的bundle.js的原因是:浏览器不能像node.js那样快速地在本地加载一个个模块文件,而必须通过网络请求去加载还未得到的文件。如果模块数量很多,则加载时间会很长,因此将所有模块都存放在了数组中,执行一次网络加载。
5、编写Loader
Webpack是运行在Node.js上的,一个Loader其实就是一个Node.js模块,这个模块需要导出一个函数,这个导出函数的工作就是获得处理前的原内容,对原内容执行处理后,返回处理后的内容。
//一个最简单的Loader源码 module.export=function(src){//src为文件的原内容 //该函数需要返回处理后的内容,这里直接返回了原内容,相当于没有做任何转换 return src; }
如果一个文件可以使用多个loader,loader的执行顺序和本身的顺序是相反的,即最后一个loader先执行,第一个loader最后一个执行;
第一个执行的loader接收源文件内容作为参数,其他loader接收前一个执行的loader的返回值作为参数,最后执行的loader会返回此模块的js源码。
编写Loader的思路:
编写Loader时需要遵循单一原则,每个Loader只需要完成一种转换。每个Loader拿到的是源文件内容,可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。还可以通过this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。
//需求 //1.处理.txt文件,2.对字符串进行反转操作,3.首字母大写 //reverse-loader.js module.export=function(src){ if(src){ src=src.split('').reverse().join(); } return src; } //upperCase-loader.js module.export=function(src){ src=src.charAt(0).toUpperCase()+src.slice(1); return `module.export='${src}'`; } //配置webpack module:{ rules:[ { test:/.txt$/, use:[ './upperCase-loader.js', './reverse-loader.js' ] } ] }
6、编写Plugin
webpack通过Plugin机制让其更灵活,以适应各种应用场景,在webpack运行的生命周期中会广播许多事件,Plugin可以监听这些事件,在合适的时机通过webpack提供的API改变输出结果。
//一个最基础的Plugin源码 class BasicPlugin{ //在构造函数中获取用户为该插件传入的配置 constructor(options){ } //Webpack会调用BasicPlugin实例的apply方法为插件传入compiler对象 apply(compiler){ compiler.plugin('compilation',function(compilation){ }) } } module.exports=BasicPlugin; //在使用这个Plugin时,相关的配置代码如下: const BasicPlugin=require('./BasicPlugin.js'); module.exports={ plugins:[ new BasicPlugin(options), ] }
PS:
Compiler对象:包含了webpack环境的所有配置信息,包含options、loaders、plugins等信息。这个对象在webpack启动时被实例化,它是全局唯一的,可以理解为webpack实例。
Compilation对象:包含了当前模块资源、编译生成资源、变化的文件等。当webpack以开发模式运行时,每当检测到一个文件发生变化,便有一个新的Compilation被创建。
7、Webpack的热替换是如何做到的?说明其原理?
webpack --watch需要刷新整个页面。
模块热替换能做到在不重新加载整个网页的情况下,将已更新的模块替换老模块,再重新执行一次来实现实时预览。 webpack-dev-server --hot
8、如何利用webpack来优化前端性能(提高性能和体验)
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
(1)剔除不必要的代码(引入但未使用) tree shaking
比如引入lodash包,里面有许多和js相关的遍历方法,但是实际中我们只用到了其中一两个,此时打包的时候如果把所有方法都打进去,会很浪费,tree shaking就是去除多于代码。具体使用方法是用UglifyJsPlugin可以在构建的过程中对冗余的代码进行删除。
(2)利用CDN加速,在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径。
(3)删除死代码,将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现。
(4)提取公共代码。
(5)资源压缩
js压缩、css压缩、图片资源压缩(配合url-loader使用)
9、html-webpack-plugin
作用是当使用webpack打包时,创建一个html文件,并把webpack打包后的静态文件自动插入到这个html文件当中。
html-webpack-plugin默认将会在output.path的目录下创建一个index.html文件,并在这个文件中插入一个script标签。
也可以生成多个html文件。
loader与plugin的区别:
【loader】对模块源码的转换,loader描述了webpack如何处理非javascript模块,可以将文件从不同语言转成js。
【plugin】目的在于解决loader无法实现的其他事情,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共代码,避免首屏加载的bundle文件或者按需加载的bundle文件体积过大,导致加载时间过长。