webpack打包原理

作者:好奇男孩
链接:https://www.jianshu.com/p/37ff752d0f97
来源:简书


1. webpack核心概念

  • entry: 一个可执行模块或库的入口文件。
  • chunk :多个文件组成的一个代码块,例如把一个可执行模块和它所有依赖的模块组合和一个 chunk 这体现了webpack的打包机制。
  • loader :文件转换器,例如把es6转换为es5,scss转换为css。
  • plugin :插件,用于扩展webpack的功能,在webpack构建生命周期的节点上加入扩展hook为webpack加入功能。

2. webpack构建流程

从启动webpack构建到输出结果经历了一系列过程,它们是:

  1. 解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,生产最后的配置结果。
  2. 注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应。
  3. 从配置的entry入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件,递归下去。
  4. 在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换。
  5. 递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk。
  6. 输出所有chunk到文件系统。

3 概括

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

4.Webpack的Code Splitting实现按需加载

4.1 什么是Code Splitting?

在最开始使用Webpack的时候, 都是将所有的js文件全部打包到一个build.js文件中(文件名取决与在webpack.config.js文件中output.filename), 但是在大型项目中, build.js可能过大, 导致页面加载时间过长. 这个时候就需要code splitting, code splitting就是将文件分割成块(chunk), 我们可以定义一些分割点(split point), 根据这些分割点对文件进行分块, 并实现按需加载.

4.2 Code Splitting的作用?

  1. 第三方类库单独打包:
    由于第三方类库的内容基本不会改变,可以将其与业务代码分离出来,这样就可以最大化的利用浏览器的缓存机制,减少请求。
  2. 按需加载:
    Webpack支持定义分割点,通过require.ensure进行按需加载。

4.3 如何进行Code Splitting?

下面的代码是基于vue-cliwebpack-simple模板生成的演示文档

//cmd
vue init webpack-simple code_spliting_demo
(一) 第三方类库单独打包

我们假设项目中引入了jquery.jsrespond.js, 那么我们可以在webpack.config.js中配置多入口来进行将这两个第三方类库单独打包.

  • 在webpack.config.js进行配置
//webpack.config.js

//在entry中添加相应第三方类库
entry: {
    bundle: './src/main.js',
    vendor: ['./src/lib/jquery-1.10.2.min.js', './src/lib/respond.min.js']
}

 //在plugins中添加CommonChunkPlugin
plugins:[
    new webpack.optimize.CommonsChunkPlugin({ 
        name: 'vendor',  
        filename: 'vendor.bundle.js'  
    })
]
  • 执行npm run build, 此时dist目录下生成了两个文件, 分别是build.jsvendor.bundle.js
    npm run build后的生成文件

  • index.html中引入, 注意: vendor.bundle.js优先于build.js引入

//index.html

<script src="/dist/vendor.bundle.js"></script>
<script src="/dist/build.js"></script>
(二) 按需加载

我们可以在router中进行配置,实现组件的按需加载,在一些单个组件文件较大的时候,采用按需加载能够减少build.js的体积,优化加载速度(如果组件的体积较小,那么采用按需加载会增加额外的http请求,反倒增加了加载时间。

  • 这里, 我们增加3个组件,分别是A.vue, B.vue, C.vue
//A.vue
<template>
    <h1>这里是A.vue组件</h1>
</template>

//B.vue
<template>
    <h1>这里是B.vue组件</h1>
</template>

//C.vue
<template>
    <h1>这里是C.vue组件</h1>
</template>
  • 在路由中进行配置 (注意:这里是为了方便, 是在app.js中添加的路由, 在实际的项目中, 路由应该单独抽取出来)
//app.js

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

//AMD规范的异步载入
const ComA = resolve => require(['./components/A.vue' ], resolve);
const ComB = resolve => require(['./components/B.vue' ], resolve);
const ComC = resolve => require(['./components/C.vue' ], resolve);

const router = new VueRouter({
  routes: [
    {
      name: 'component-A',
      path: '/a',
      component: ComA
    },
    {
      name: 'component-B',
      path: '/b',
      component: ComB
    },
    {
      name: 'component-C',
      path: '/c',
      component: ComC
    }
  ]
})

new Vue({
  el: '#app',
  router: router,
  render: h => h(App)
})
  • webpack.config.js中进行配置output.chunkFilename
//webpack.config.js

output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'build.js',
    //添加chundkFilename
    chunkFilename: '[name].[chunkhash:5].chunk.js'
}
  • 执行npm run build, 此时dist目录下生成了5个文件, 多出的3个文件,就是对应的A.vue, B.vue, C.vue这三个组件
    npm run build后生成的文件

CMD规范的异步载入

刚才在路由引入的时候,使用的是AMD规范的异步载入。webpack提供了require.ensure()这个方法实现CMD规范的异步载入。这同样也是webpack推荐的载入方式。

  • 下面的代码是使用require.ensure()方法对路由进行配置
//app.js

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)

//AMD风格的异步加载
// const ComA = resolve => require(['./components/A.vue' ], resolve);
// const ComB = resolve => require(['./components/B.vue' ], resolve);
// const ComC = resolve => require(['./components/C.vue' ], resolve);

//CMD风格的异步加载
const ComA = resolve => require.ensure([], () => resolve(require('./components/A.vue')));
const ComB = resolve => require.ensure([], () => resolve(require('./components/B.vue')));
const ComC = resolve => require.ensure([], () => resolve(require('./components/C.vue')));

const router = new VueRouter({
  routes: [
    {
      name: 'component-A',
      path: '/a',
      component: ComA
    },
    {
      name: 'component-B',
      path: '/b',
      component: ComB
    },
    {
      name: 'component-C',
      path: '/c',
      component: ComC
    }
  ]
})

new Vue({
  el: '#app',
  router: router,
  render: h => h(App)
})
  • 执行npm run build后, dist目录下同样生成5个文件
    npm run build后生成的文件

webpack Tree Shaking

随着缩小和树摇动,我们的捆绑现在变小了几个字节!虽然在这个人为的例子中看起来似乎并不多,但是当处理具有复杂依赖树的较大应用程序时,树抖动会导致束大小显着减少。清除无用代码,减少文件体积。

webpack scope hoisting 范围提升

为了检测这些导入链可以在哪里展平并转换为一个内联函数,而不会影响我们的代码。我们不仅保存了额外的函数调用,还访问了模块数组,因此我们的代码运行速度比以前更快。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值