webpack与group、gulp有什么不同?
三者都是前端构建工具,group和gulp在早期比较流行,不过一些轻量级的任务还是可以用gulp来处理。比如单独打包css文件等。
group和gulp是基于任务和流去执行任务。类似jQuery,找到一个(或类)文件,对其做一系列链式操作,更新流上的数据,整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的loader理不同的文件,用Plugin来扩展webpack功能。
- 从构建思路上看
gulp和grunt需要开发者将整个前端构建过程拆分成多个Task,并合理控制所有Task的调用关系
webpack需要开发者找到入口,并需要清楚对于不同资源应该使用什么loader做哪种解析和操作
- 从知识背景上看
gulp更像后端开发者的思路,需要对于整个流程都了如指掌
webpack更倾向于前端开发者的思路,不同文件使用不同的东西去处理
loader和plugin的不同?
- Loader-加载器:webpack将一切文件视为模块,但是webpack原生只能解析js文件,如果想将其他文件也打包的话,就需要用loader去解析。所以loader的作用是让webpack拥有了加载和解析非js文件的能力
- Plugin-插件:Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。在webpack运行的生命周期中会广播出很多事件,Plugin可以监听这些事件的触发,并在合适的时机通过webpack提供的API改变输出的结果。
webpack的构建流程
webpack的运行流畅是个串行的过程,从启动到结束会依次以下流程:
- 初始化参数:从配置文件和Shell语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化
Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译; - 确定入口:根据配置中的entry找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的Loader对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用Loader编译完所有模块后,得到了每个模块被编译后的最终内容以及他们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,将文件内容写入到文件系统。
在以上过程中,webpack会在特定的时间点广播出特定的时间,插件在监听到感兴趣的事件后执行特定的逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果。
如何配置单页应用和多页应用?
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可。
多页应用的话,可以使用webpack的AutoWebPlugin来完成简单自动化的构建,但是前提是项目结构必须遵守它预设的规范。多页应用中要注意的是:
- 每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如每个页面都引用了同一套css样式表。
- 随着业务的不断扩张,页面会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
npm打包时需要注意哪些?如何利用webpack来更好的构建?
注意事项:
- 要支持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则。
- Npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传。
- Npm包体积应尽可能的小(有些仓库会限制包大小)
- 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。
- UI组件类的模块应该将依赖的其他资源文件,例如.css文件也需要包含在发布的模块里。
基于以上需要注意的问题,我们可以对于webpack配置做以下扩展和优化:
- CommonJS模块化规范的解决方案:设置output.libraryTarget=’commonjs2’使输出的代码符合CommonJS2的模块化规范,以供给其他模块导入使用。
- 输出ES5代码的解决方案:使用babel-loader把ES6代码转换成ES5的代码。再通过开启devtool:’source-map’输出SourceMap以发布调试。
- Npm包体积尽量小的方案:Babel在把ES6代码转换成ES5代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件。
- 不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉webpack哪些模块不需要被打包。
- 对于依赖的资源文件打包的解决方案:通过css-loader和extract-text-webpack-plugin来实现,配置如下:
const ExtractTextPlugin = require('extract-text-webpack-plugin');module.exports = {module:{rules:[{test: /\.css/,use: ExtractTextPlugin.extract({use:['css-loader']})}]},plugins: [new ExtractTextPlugin({filename: 'index.css'})]}
如何在vue项目中实现按需加载?
Vue UI组件库的按需加载
为了快速开发前端项目,经常会引入现成的UI组件库如ElementUI、iView等,但是他们的体积和他们所提供的功能一样,是相对庞大的。而通常情况下,我们仅仅只需要少量的几个组件就足够了,但是我们却将庞大的组件库打包到我们的源码中,造成了不必要的开销。
不过很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component和AntDesign的babel-plugin-import安装以上插件后,在.babelrc配置中或babel-loader的参数中进行设置,即可实现组件按需加载了。
单页应用的按需加载{"presets":[["es2015",{"modules":false}]],"plugins":[["component",{"libraryName":"element-ui","styleLibraryName":"theme-chalk"}]]}
现在很多前端项目都是通过单页应用的方式开发的,但是随着业务的不断扩展,会面临一个严峻地问题——首次加载的代码量会越来越大,影响到用户的体验。
通过import()语句来控制加载时机,webpack内置了对于import()的解析,会将import()引入的模块作为一个新的入口再生产一个chunk。当代码执行到import()语句时,会去加载Chunk对应生成的文件。import()会返回一个Promise对象,所以为了让浏览器支持,需要事先注入Promise polyfill
