Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack编译流程漫谈 #1

Open
slashhuang opened this issue Aug 16, 2016 · 17 comments
Open

webpack编译流程漫谈 #1

slashhuang opened this issue Aug 16, 2016 · 17 comments

Comments

@slashhuang
Copy link
Owner

slashhuang commented Aug 16, 2016

webpack编译流程漫谈

前言

weback在web构建工具的激烈竞争中逐渐脱引而出。 无论是编译速度、报错提示、可扩展性等都给前端开发者耳目一新的感觉。本篇文章是个人对webpack的一点小研究总结。

webpack在开发者社区的反馈

类似gulp把自己定位为stream building tools一样,webpack把自己定位为module building system。
在webpack看来,所以的文件都是模块,只是处理的方式依赖不同的工具而已。

webpack同时也把node的IO和module system发挥的淋漓尽致。 webpack在配合babel(ES6/7)tsc(typescript)等类似DSL语言预编译工具的时候,驾轻就熟,为开发者带来了几乎完美的体验。

webpack整体架构(以webpack.config主要部分进行划分)

  1. entry: 定义整个编译过程的起点
  2. output: 定义整个编译过程的终点
  3. module: 定义模块module的处理方式
  4. plugin 对编译完成后的内容进行二度加工
  5. resolve.alias 定义模块的别名

webpack的核心module

无论你是jsx,tsx,html,css,scss,less,png文件,webpack一视同仁为module。并且每个文件[module]都会经过相同的编译工序 loader==> plugin。

关于以上这点,以如下一个简单的webpack.config文件为例。看下webpack会做什么

module.exports =  {
        watch: true,
        entry: './index.js',
        devtool: 'source-map',
        output: {
            path: path.resolve(process.cwd(),'dist/'),
            filename: '[name].js'
        },
        resolve: {
            alias:{ jquery: 'src/lib/jquery.js', }
        },
        plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                _: 'underscore',
                React: 'react'
            }),
            new WebpackNotifierPlugin()
        ],
        module: {
            loaders: [{
                test: /\.js[x]?$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },  {
                test: /\.less$/,
                loaders:['style-loader', 'css-loader','less-loader']
            }, {
                test: /\.(png|jpg|gif|woff|woff2|ttf|eot|svg|swf)$/,
                loader: "file-loader?name=[name]_[sha512:hash:base64:7].[ext]"
            }, {
                test: /\.html/,
                loader: "html-loader?" + JSON.stringify({minimize: false })
            } ]
        }
    };

webpack是如何处理如上webpack.config文件解析

1. 确定webpack编译上下文context

默认情况下就是node启动的工作目录process.cwd(),当然也可以在配置中手动指定context。

webpack在确定webpack.config中entry的路径依赖时,会根据这个context确定每个要编译的文件(assets)的绝对路径。

2.entry和output 确定webpack的编译起点和终点

顾名思义,entry定义webpack编译起点,入口模块。 对应的结果为compolation.assets

output定义webpack编译的终点,导出目录

3. module.loaders 和 module.test 确定模块预编译处理方式

以babel为例,当webpack发现模块名称匹配test中的正则/js[x]?的时候。

它会将当前模块作为参数传入babel函数处理,babel([当前模块资源的引用])

函数执行的结果将会缓存在webpack的compilation对象上,并分配唯一id

以上的这一步,非常非常关键。唯一的id值决定了webpack在最后的编译结果中,是否会存在重复代码。
而缓存在compilation对象上,则决定了webpack可以在plugin阶段直接拿取模块资源进行二度加工。

4. plugin阶段贯穿于webpack的整个编译流程,一般用来做一些优化操作。

比如webpack.ProvidePlugin,它会在对编译结果再加工的操作过程中进行自定义的变量注入,当模块中碰到比如_这个变量的时候,webpack将从缓存的module中取出underscore模块加载进引用_的文件(compilation.assets)。
比如WebpackNotifierPlugin,它会在编译结果ready的时通知开发者,output已经就绪。

5.resolve.alias的作用就是对module模块提供别名,并没有什么特殊的。

【副作用】 webpack编译过程中的电脑卡慢?

在weback经历以上流程的时候,查看你的内存,你会发现,内存飙升!!!

这一般都是loader阶段,对DSL进行AST抽象语法树分析的时候,由于大量应用递归,内存溢出的情
况也是非常常见。

output目录不是一个渐进的编译目录,只有在最后compilation结果ready的时候,才会写入,造成开发者等待的时候,output目录始终为空。

【webpack编译对象compilation】 webpack将编译结果导出到output是怎么做到的

如上,webpack在plugin结束前,将会在内存中生成一个compilation对象文件模块tree

这个阶段是webpack的done阶段 : webpack写入output目录的分割点。

这棵树的枝叶节点就是所有的module[由import或者require为标志,并配备唯一moduleId],

这棵树的主枝干就是所有的assets,也就是我们最后需要写入到output.path文件夹里的文件内容。

最后,这个compilation对象也是所有webpackPlugin的处理的时候的arguments。

总结

对于开发者来说,整体而言webpack的编译过程细节比较多,但是大体的框架还是比较直观。

里面涉及到的类似DSL,AST的概念及模块缓存等等,在构建工具中还是比较常见的,配合watch模式,debug模式,对于开发者来说实在是一大利器。

一切文件皆为模块也和react的一切dom都可以变为JS一样,对前端世界带来了新的开发理念。

webpack官方文档

@ckmilse
Copy link

ckmilse commented Aug 29, 2016

点赞,介绍的很基础。谢谢

@slashhuang
Copy link
Owner Author

@ckmilse 有帮助就好啊!关于webpack的hash、chunk这块,这篇文章还没有完全覆盖,它们也是webpack中非常重要的一块

@renaesop
Copy link

唔……插件理解有误

@slashhuang
Copy link
Owner Author

@renaesop 愿闻其详,松神

@MontageD
Copy link

很赞..

@MeFirebird
Copy link

@onlycxue
Copy link

onlycxue commented May 4, 2017

赞,清晰明了

@airenqingqing
Copy link

process.cwd()这个函数是干啥的呀?

@viking-Cao
Copy link

process.cwd()表示返回运行当前脚本的工作目录的路径

@minghuli
Copy link

minghuli commented Aug 6, 2017

@webjohnjiang
Copy link

mark

@MeFirebird
Copy link

@ZhyCong
Copy link

ZhyCong commented Sep 2, 2017

nice

@lie-sun
Copy link

lie-sun commented Nov 29, 2017

不cuo

@hanruto
Copy link

hanruto commented Mar 1, 2018

干脆利索

@slashhuang
Copy link
Owner Author

相关讲的比较细的资料: https://juejin.im/entry/5b0e3eba5188251534379615

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

14 participants