webpack series - Principle Analysis of webpack

I. Introduction

Now, with the complexity and scale of the growing front-end development, the eagle can not put aside engineered to independent development, such as: react the jsx code must be compiled in order to use in the browser, such as sass and less code that is not browser It supports. If you abandon these development framework, development efficiency will decline.

Among the many front-end engineering tools, webpack emerged as the most popular front-end build tool.

 

Two, webpack principle

Know these know why.

1, the core concept

(1) entry: an inlet library or executable module.

(2) chunk: a plurality of block files. Executable modules and can he relies modules are combined into a chunk, which is packaged.

(3) loader: file converter. For example the es6 into es5, scss into css, etc.

(4) plugin: Extensions webpack function. Join expansion hook on the life cycle node webpack build, add functionality.

 

2, webpack build process (principle)

Built from the start to the output of a series of processes:

(1) initialization parameters: resolution parameters webpack configuration parameters, and the combined shell webpack.config.js incoming file configuration, to form the final configuration.

(2) start the compilation: compiler initialization parameter objects previous entry, registration of all plug-in configuration, plug-in monitor webpack build lifecycle event node, react accordingly, run method executes the object started to compile.

(3) determine the entrance: the entrance from entry configuration starts parsing the file to build AST syntax tree to find the dependence recursion.

(4) compilation module: recursive and based on the file type loader configuration, all calls loader configured to convert the file, and then find the module depends module, this step recursively until all documents have been dependent on the inlet of the steps of the present deal with.

(5) completion module compiled and output: When done recursively, results obtained for each file, each module contains the dependencies between them and arranged to generate a code block according chunk entry.

(6) complete output: the output of all of the chunk to the file system.

Note: There is a series of plug-in to do right time to do the right thing in building life cycle, such as UglifyPlugin converted recursive finished using the results of the previous UglifyJs compression results in coverage of the loader.

 

Third, business scenarios and the corresponding solutions

1, single-page application

A one-page application needs one entry specifies execution entry, Web-WebPACK-plugin where WebPluginyou can automatically complete these tasks: webpack will provide entry is generated that contains this entry chunk all dependent files, but also need a html to load the chunk generated js, if required further extracted css css the HTML file extracted introduced.

A simple configuration file chestnuts webpack

const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
  entry: {
    app: './src/doc/index.js',     home: './src/doc/home.js' }, plugins: [ // 一个WebPlugin对应生成一个html文件 new WebPlugin({ //输出的html文件名称 filename: 'index.html', //这个html依赖的`entry` requires: ['app','home'], }), ], };

Description: require: [ 'app', 'home'] which indicates the html dependent entry, entry and css js generated automatically injected into the html.

Also supports configuring these resources into ways to support the following attributes:

(1) _dist only resource in the production environment was introduced;

(2) _dev only resource in the development environment was introduced;

(3) _inline dive into the contents of the html resource;

(4) _ie only resource IE browser only need to be introduced.

These attributes can be configured in js in, take a simple example:

new WebPlugin({
    filename: 'index.html',
    requires: {
         app:{
              _dist:true,
              _inline:false,
         }
    },
}),

These attributes can also be set in the template, use the template advantage is the flexibility to control the injection point resources.

new WebPlugin({
      filename: 'index.html',
      template: './template.html',
}),

//template模板
<!DOCTYPE html>
<html lang="zh-cn"> <head> <link rel="stylesheet" href="app?_inline"> <script src="ie-polyfill?_ie"></script> </head> <body> <div id="react-body"></div> <script src="app"></script> </body> </html>

WebPluginPlug-in draws on fis3the idea complements HTML-entry function webpack missing . For WebPluginmore features, see the document .

 

2, a project managing multiple single-page

A project will include more than a single page application, although more than a single-page application can be synthesized, but doing so will cause some users do not have access also loaded, if the project has a lot of one-page application. Configuration entry and WebPlugin a single page for each application? If they add, but also new webpack configuration, this trouble, this time there is a plug-in web-webpack-plugin in the AutoWebPlugin ways to solve these problems.

module.exports = {
    plugins: [
        // 所有页面的入口目录
        new AutoWebPlugin('./src/'),
    ]
};

Analysis: 1, AutoWebPluginwill ./src/catalog all of each folder as a single page entry page, automatically for all the pages inlet disposed WebPlugin output a corresponding html.

2. To add a page in ./src/the folder that contains the one-page application relies on a file under the new code, AutoWebPluginautomatically generates html file named folder name.

 

3, the partition Code Optimization

A good code to split-screen effect to enhance the browser preferences great.

The most common system react:

(1) the first out basis libraryreact react-dom redux react-redux to a separate file and other files, rather than put together a package file, the benefits of doing so as long as you do not upgrade their version of the file will never be refreshed. If you take these basic libraries and business code packaged in a file where each change will lead to business code file hash value change causing the browser cache invalidation repeat download the code contains the base library. So the foundation libraries packaged into a file .

// vender.js 文件抽离基础库到单独的一个文件里防止跟随业务代码被刷新
// 所有页面都依赖的第三方库
// react基础
import 'react'; import 'react-dom'; import 'react-redux'; // redux基础 import 'redux'; import 'redux-thunk'; // webpack配置 { entry: { vendor: './path/to/vendor.js', }, }

(2) by CommonsChunkPlugin may extract a plurality of code blocks formed depend on a separate code chunk. Extraction at the scene with a plurality of pages of application all common code pages reduce code a single page, is loaded before switching over between different code pages common for all pages without having to reload. Therefore, by CommonsChunkPlugin forming a plurality of separate blocks of code can be extracted is dependent codechunk。

 

4, building a service-side rendering

Server-side rendering code to be run in different nodejs environment, and the browser, server-side rendering code need commonjs specification should not simultaneously contain files other than such as js css.

webpack configuration is as follows:

module.exports = {
  target: 'node',
  entry: {
    'server_render': './src/server_render',
  },
  output: {
    filename: './dist/server/[name].js', libraryTarget: 'commonjs2', }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader', }, { test: /\.(scss|css|pdf)$/, loader: 'ignore-loader', }, ] }, };

Analyze:

(1) target: 'node' indicates a code to be constructed in a node operating environment.

(2) libraryTarget: 'commonjs2' indicates that the output of the code if the commonjs specification.

(3) {test: /\.(scss|css|pdf)$/,loader: 'ignore-loader'} is not performed to prevent the rendering server have no access to files in the node are packed into the inside.

 

5, fis3 migrate to webpack

fis3 and webpack have a lot of similar places there are different places, like places: commonjs specifications are used, different places: Import the way these non-js css resources.

fis3 by @require './index.scss', and by webpack require ( './ index.scss').

If you want a smooth migration to fis3 webpack, you can comment-require-loader.

For example: You want to build in webpack is adopted fis3 way of using imuimodules

loaders:[{
     test: /\.js$/,
     loaders: ['comment-require-loader'],
     include: [path.resolve(__dirname, 'node_modules/imui'),]
}]

 

Fourth, expand custom webpack

If you can not find the solution to your application scenarios in the community, it would need to write yourself a loader or a plugin.
You write custom extensions before webpack want to understand you need to do in the end is a loaderstill pluginof it? It could judge:

如果你的扩展是想对一个个单独的文件进行转换那么就编写loader剩下的都是plugin。

Which converts the file can be like:

1, babel-loader into the es6 ES5;

2, file-loader into the corresponding file and replace url;

3, raw-loader injection text file contents in the code.

1, write webpack loader

Write loadervery simple to comment-require-loader as an example:

module.exports = function (content) {
    return replace(content); };

loaderEntrance need to export a function that you want to do things is to convert the contents of a file.
Function received parameter contentis a string file content before the conversion, the need to return a string of new content as a result of the conversion, all files are modular poured through loader. From here you can see loaderonly handle one single file can not process the code block. You can refer to the official documentation

 

2, write webpack plugin

pluginA wide range of application scenarios, it is slightly more complex. At end-webpack-plugin as an example:

class EndWebpackPlugin {

    constructor(doneCallback, failCallback) {
        this.doneCallback = doneCallback; this.failCallback = failCallback; } apply(compiler) { // 监听webpack生命周期里的事件,做相应的处理 compiler.plugin('done', (stats) => { this.doneCallback(stats); }); compiler.plugin('failed', (err) => { this.failCallback(err); }); } } module.exports = EndWebpackPlugin;

Entrance loader needs to export a class, the new EndWebpackPlugin () when passing parameters required by this plugin constructor, when webpack startup will first instantiate the plugin, and then apply the method to call the plugin, plug-in function listens apply webpack life cycle events, handled accordingly.

webpack plugin two core concepts:

(1) compiler: start to withdraw from webpack there is only a Compiler, compiler kept the webpack configuration.

(2) compilation: Because webpack monitor file changes automatically compile the mechanism, on behalf of compilation time compilation.

Compiler And  Compilation will broadcast a series of events. webpack life cycle, there are a lot of events

These are just a simple demo, the more complex you can view  how to write a plugin or refer to Web-WebPACK-plugin .

 

V. Summary

webpack actually relatively simple, one sentence the essence:

webpack是一个打包模块化js的工具,可以通过loader转换文件,通过plugin扩展功能。

If webpack makes you feel complex, it must be the cause of various loader and plugin.

 

Sixth, some problems

1, webpack and grunt, gulp different?

Three are front-end build tools, grunt and gulp more popular in the early days, now webpack relatively mainstream, but some of the weight of the task will still be treated with a gulp, such as CSS files packaged separately.

grunt and gulp is task-based and streaming (Task, Stream) of. Similarly the jQuery, a find (or class) file, a series of chain do its operations, the updated data stream, the whole constitutes a task chaining multiple tasks comprise the entire web build process.

webpack based inlet. webpack automatically parsed recursively all the resources needed to load the file entry, the file is then processed with various different Loader, with webpack Plugin to extend functionality.

Summary: (1) the construction approaches for: gulp and grunt will need to develop the entire front end of the build process into multiple `Task` and reasonable control of all calls` Task` relationship webpack require the developer to find the entrance, and the need clearly for different resources should be used to parse and process what Loader what to do;

(2) knowledge of the background: the idea gulp more like a back-end developer, you need ideas for the whole process well known webpack prefer front-end developers.

 

2, and webpack similar tools What? Talk about why you chose (or abandon) use webpack?

Also it based on entrance packaging tools as well as several mainstream: webpack, rollup, parcel.

From the point of view scenarios: (1) webpack front site for large complex building; (2) rollup package for foundation libraries, such vue, react; (3) parcel is suitable for simple laboratory project, but it is hard to packaging errors debugging.

 

3. What are the common Loader have? They solve any problem?

(1) babel-loader: the es6 converted to ES5;

(2) css-loader: loading css, modular support compression, file import and other characteristics;

(3) style-loader: the code into the css js by css dom to load operation;

(4) eslint-loader: js code by checking Eslint;

(5) image-loader: Picture compression load and evening;

(6) file-loader: the output file to a folder in the code to reference the file output by the relative url;

(7) url-loader: and file-loader Similarly, when you can file small file contents base64 way it is injected into the code.

(8) source-map-loader: load additional source map file for easy debugging.

 

4. What are the common Plugin there? They solve any problem?

(1) uglifyjs-webpack-plugin: js code by decompression UglifyJS;

(2) commons-chunk-plugin: extracting common code;

(3) define-plugin: define environment variables.

 

5, different plugin loader and the

Different roles: (1) loader make webpack non-js ability to load and parse; (2) plugin can be extended webpack function, webpack operating cycle will broadcast many events, Plugin can listen to some of the events, to change the result by api webpack of.

Usage of different: (1) loader disposed in the module.rule. Array type, each of which is Object; (2) plugin is configured separately, an array type, each of which is plugin example, the parameter passed by the constructor.

 

6. What webpack build process? From the output file to read the configuration to try to say this whole process

Webpack running process is a serial process, the following procedures will be followed from start to finish:

(1) initialization parameters: reading parameters from the configuration file and merge statements Shell, obtain the final parameters;

(2) start the compilation: Compiler initialization parameters spend target step obtained load all plug-in configuration, run method performs object started to compile;

(3) determining entry: find all entries in the entry according to the configuration file;

(4) compile the module: From the entrance file, calling all configured Loader module to translate, then find the module dependent module, and then recursively this step until all files have been dependent on the entrance of the process in this step;

(5) complete compilation module: after Step 4 using the End Loader translate all modules, each module obtained after the final content is translated and the dependencies between them;

(6) Output Resources: The dependencies between the inlet and the modules are assembled into a plurality of modules containing one Chunk and Chunk then converting each into a single output file on the list, this step can be modified output content last chance;

(7) output is completed: after determining good outputting content, determined according to the configuration of the output path and file name, the file contents are written to the file system.

In the above process, Webpack will broadcast a special event at a specific point in time, after listening to plug in the event of interest will perform a specific logic, and plug-ins can call the API Webpack provide a change of operating results Webpack.

 

7, whether written Loader and Plugin? Describe the idea to write a loader or plugin?

To follow the principle of a single time of writing Loader, Loader each only a kind of "escape" work. Each Loader to get the contents of the source file (Source), the content output by way of the return value of the process may also call this.callback () method returns the content to the webpack. May also generate a callback function by this.async (), then the contents of the process output callback out.

Plugin written on a lot of flexibility. Webpack many events in the life cycle will run out of broadcasting, Plugin can listen to these events, change the output in the right time API provided by Webpack.

 

8, hot update webpack is how to do it? How this works?

webpack known as thermal heat update replacement (Hot Module Replacement), abbreviated as HMR. This mechanism can be done without refreshing the browser and the new changes will replace the module off the old block .

principle:

analysis:

(1) a first step in the WebPACK watch mode, a file in a file system modification occurs, WebPACK monitored file changes, re-compiled based on the profile of the package module, and packaged by a simple code JavaScript object stored in memory.

(2)第二步是 webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。

(3)第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念。

(4)第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。

(5)webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。

(6)HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。

(7)而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。

(8)最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。

 

9、如何利用webpack来优化前端性能?(提高性能和体验)

用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。

(1)压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css。使用webpack4,打包项目使用production模式,会自动开启代码压缩。

(2)利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径

(3)删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现或者使用es6模块开启删除死代码

(4)优化图片,对于小图可以使用 base64 的方式写入文件中

(5)按照路由拆分代码,实现按需加载,提取公共代码。

(6)给打包出来的文件名添加哈希,实现浏览器缓存文件

 

10、如何提高webpack的构建速度?

(1)多入口的情况下,使用commonsChunkPlugin来提取公共代码;

(2)通过externals配置来提取常用库;

(3)使用happypack实现多线程加速编译;

(4)使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用多核并行压缩来提升压缩速度;

(5)使用tree-shaking和scope hoisting来剔除多余代码。

 

11、怎么配置单页应用?怎么配置多页应用?

单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可。

多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。

 

12、npm打包时需要注意哪些?如何利用webpack来更好的构建?

NPM模块需要注意以下问题:

(1)要支持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则

(2)Npm模块使用者的环境是不确定的,很有可能并不支持ES6,所以打包的最后结果应该是采用ES5编写的。并且如果ES5是经过转换的,请最好连同SourceMap一同上传。

(3)Npm包大小应该是尽量小(有些仓库会限制包大小)

(4)发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。

(5)UI组件类的模块应该将依赖的其它资源文件,例如.css文件也需要包含在发布的模块里。

基于以上需要注意的问题,我们可以对于webpack配置做以下扩展和优化:

(1)CommonJS模块化规范的解决方案: 设置output.libraryTarget='commonjs2'使输出的代码符合CommonJS2 模块化规范,以供给其它模块导入使用;

(2)输出ES5代码的解决方案:使用babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map'输出SourceMap以发布调试。

(3)Npm包大小尽量小的解决方案:Babel 在把 ES6 代码转换成 ES5 代码时会注入一些辅助函数,最终导致每个输出的文件中都包含这段辅助函数的代码,造成了代码的冗余。解决方法是修改.babelrc文件,为其加入transform-runtime插件

(4)不能将依赖模块打包到NPM模块中的解决方案:使用externals配置项来告诉webpack哪些模块不需要打包

(5)对于依赖的资源文件打包的解决方案:通过css-loader和extract-text-webpack-plugin来实现,配置如下:

 

13、如何在vue项目中实现按需加载?

经常会引入现成的UI组件库如ElementUI、iView等,但是他们的体积和他们所提供的功能一样,是很庞大的。

不过很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component和AntDesign出品的babel-plugin-import 安装以上插件后,在.babelrc配置中或babel-loader的参数中进行设置,即可实现组件按需加载了

单页应用的按需加载 现在很多前端项目都是通过单页应用的方式开发的,但是随着业务的不断扩展,会面临一个严峻的问题——首次加载的代码量会越来越多,影响用户的体验。

 

七、参考

1、https://github.com/webpack/docs/wiki/how-to-write-a-plugin

2、https://webpack.js.org/api/compiler-hooks/

3、https://webpack.js.org/concepts/loaders

4、https://webpack.js.org/concepts/plugins

5、手把手教你撸一个简易的 webpack

 

 【谢谢关注和阅读,后续新的文章首发:sau交流学习社区:https://www.mwcxs.top/

Guess you like

Origin www.cnblogs.com/chengxs/p/11022842.html