webpack代码分割&代码大小控制

这些都是和webpack去优化我们的项目有关的。

一切为了优化

要解决的问题
1.减少加载代码大小
2.提取公共资源,减少加载次数

代码分割

这是webpack标榜自己的一个特点,但是使用这个是有场景之分的。

多页面应用

  • 提取公共依赖

把几个页面之中都用到的依赖打包为一个单独的文件。

浏览器有自动把加载过是js、css、图片等静态资源进行缓存。

比如A页面中用到了C模块,B页面中也用到了C模块,这样吧C模块单独的提取出来,虽然多了一次请求,但是加载A页面的时候就顺便把C加载了,再进入B页面就不用重新请求了

单页面应用

  • 减少文件体积,拆分应用

把要异步加载的内容改成异步加载

在单页面应用中,就不存在说把公共的模块提取出来,因为我就这一个页面,那么我把他全部打包在一个页面中是OK的,可能说C模块和D模块同时用到了F模块,那么在webpack打包的时候是不会重复打包的。

单页面的问题是,他把所有东西都打包在一个入口,所以说单页面应用的打包体积是很大的,说以首屏加载会慢,所以要把能异步加载的就异步加载。

为了业务代码的纯净

  • 有时候不希望业务代码里混入第三方代码,或webpack配置代码

把第三方的代码和webpack配置代码拆分为单独的文件。

可能我们写vue的时候,vue这个框架是第三方依赖,在比如我们引入的一些包比如axios这些都属于第三方依赖,他跟我们业务代码没什么关系,那么这个时候我们希望我们的业务代码保持纯净,不希望业务代码中混入第三方代码或者webpack的配置代码,这种情况下,我们就需要把业务代码和第三方代码以及webpack配置代码拆分为单独的文件,比如vue的打包就是这个样子的:

vue打包后生成3个文件:(为了代码的纯净性做的区分)
app.js(自己的业务代码)
vendor.js(第三方库、vue框架本身的代码)
manifest.js(webpack的配置代码)

所以一般这么打包

  • 多页面应用

主业务代码+公共依赖+第三方包+webpack运行代码

  • 单页面应用

主业务代码+异步模块+第三方包+webpack运行代码

如何进行代码分割

版本差异

  • webpack3==》CommonsChunkPlugin
  • webpack4==》SplitChunkPlugin

我们使用的vue脚手架2或者3都是用的CommonsChunkPlugin

一个插件:clean-webpack-plugin:作用是自动帮我们清除上一次打包的代码

npm install --save-dev clean-webpack-plugin

在webpack.config.js中:

const {CleanWebpackPlugin}=require("clean-webpack-plugin")

然后在注册webpack plugins的地方
    new CleanWebpackPlugin()

webpack3的代码分割:

//因为这是webpack自带的,所以不用额外安装
然后在注册webpack plugins的地方
// 假设我们现在根据单页面的需要
// 要分成app.js manifest.js vender.js
// 所以要new 3次
new webpack.optimize.CommonsChunkPlugin({
  name:"vender",
  minChunks:'infinity'
}),
new webpack.optimize.CommonsChunkPlugin({
  name:"app.js",
  minChunks:2
}),
new webpack.optimize.CommonsChunkPlugin({
  name:"manifest",
  minChunks:'infinity'
})
//因为这是webpack自带的,所以不用额外安装

webpack4的代码分割:

项目webpackdemo的目录结构:

webbackdemo
| - src
  | - app.js
  | - app2.js
  | - modulea.js
  | - moduleb.js
  | - mode1.js
| - package.json
| - webpack.config.js

app.js

import ma from "./modulea.js";
import mb from "./moduleb.js";
import lod from "loadsh"
console.log(22);

app2.js

import ma from "./modulea.js";
import jq from "jquery";
import lod from "loadsh";

mode1.js

module.exports=function(){
    console.log("mode1");
}

modulea.js

import m1 from "./mode1.js";
module.exports=function(){
    console.log("a");
}

moduleb.js

import m1 from "./mode1.js";
module.exports=function(){
    console.log("b");
}

以多入口为例:
如果什么都不配置,他会打包出多个结果文件(比如app.bundle.js和app2.bundle.js),它是没有做任何的公共代码区分,也没有吧公共的代码独立的拿出来。

在webpack.config.js中,在webpack4版本中,无论是压缩还是代码分割,都是写在optimization配置项中

module.exports = {
  mode:"development",
  optimization:{
    splitChunks:{
      // name设为true是说
      // 根据模块名字来命名打包的结果
      name:true,
      // 所有的结果都进行打包
      chunks:"all"
    }
  },
  entry: {
    app: "./src/app.js",
    app2:"./src/app2.js"
  },……

这样在多入口的情况下,进行代码分割之后,将第三方代码提取出来一个单独的vendor.js

打包后的dist文件夹

| - dist
  | - app.bundle.js
  | - app2.bundle.js
  | - vendors~app~app2.bundle.js
  | - vendors~app2.bundle.js

| - vendors~app~app2.bundle.js(内面主要是loadsh)
| - vendors~app2.bundle.js(主要是jQuery)

发现他把modulea和moduleb都引用的loadsh提取出了一个单独的文件,把jQuery提取出一个单独的文件,但是app和app2公共的modulea没有提取。webpack把公共的代码提取出来是好的,但是也产生了一个负面效应,就是多了一个http请求,那我们就要做一个均衡,如果公共的代码只有1kb,再把他单独提出来没有必要为此多一次请求。

所以可以设置minsize

  optimization:{
    splitChunks:{
      name:true,
      chunks:"all",
      // 默认是1000就是10kb
      // 生产模式下是30000就是30kb
      minSize:0
      // 因为我们的modulea很小
      // 所以设为0
    }
  },

然后打包文件就多了一个app~app2.bundle.js,内容就是mode1.js的内容和modulea.js的内容

  optimization:{
    splitChunks:{
      name:true,
      chunks:"all",
      minSize:0
    },
    // 把运行代码,在vue打包出的manifest拿出来
    runtimeChunk:true
  },

加了runtime之后就多了两个runtime~app.bundle.js和runtime~app2.bundle.js文件,内面就是webpack运行时的配置文件。

有时有这样的需求,需要把modulea和mode1分开打包成两个单独的文件,可以设置cacheGroup:

splitChunks:{
  name:true,
  chunks:"all",
  minSize:0,
  cacheGroups:{
    mode1:{
      // 什么样的文件名需要单独打包
      test:/mode1/
    }
  }
},

这样在原来的基础上就多了mode1~app~app2.bundle.js文件,内面是mode1的内容。

webpack4比webpack3更灵活,webpack4多了minsize和cacheGroup这样的功能。

单页面的分割需要

webpack在打包的时候,无论是单页面还是多页面,如果使用异步加载,都会把异步加载的东西单独打一个包。

现在修改app.js

// import ma from "./modulea.js";
import mb from "./moduleb.js";
import lod from "loadsh"
// 异步加载有两种方式
// 方法一:
import("./modulea").then(function(res){
    // res就是拿到的modulea模块
    // 因为本身import("./modulea")
    // 返回的是promise,所以可以then
})
// 方法二:
// 第一个参数是一个数组
// 意思是如果在modulea中需要使用moduleb
require.ensure(["./moduleb.js"],function(){
    require("./modulea.js");
})
console.log(22);

使用方法一或者是方法二然后我们打包,发现多了一个0.bundle.js内容就是异步加载的modulea。所以这就是异步组件的原理。

可以通过这样的方式改变异步加载的模块名
import(/*webpackChunksName:'ma'*/"./modulea").then(function(res){})

代码体积控制

(无非就是压缩和tree-shaking)

压缩

  • webpack3
    optimizeUglifyPlugin()

就是在注册webpack plugins的地方加上

//也是webpack自带的,不用额外安装
new webpack.optimize.UglifyPlugin();
  • webpack4
    optimization.minimize
optimization:{
    //是否压缩
    minimize:true,
    splitChunks:{
      name:true,
      chunks:"all",
 }

其实只要让mode为production就可以了

module.exports = {
  mode:"production",
  optimization:……

所以说webpack4的目的就是干掉配置文件,很多生产和开发模式要用到的东西先做一个预制,所以很多情况下用webpack4打包,我们只要设置mode、entry和output就可以了。

tree-shaking

假设modulea.js

export const a=function (){
    console.log("i am a");
}
export const b=function (){
    console.log("i am b");
}

在app.js

import {a} from "./modulea.js"
a();

在mode:"development"的模式下打包,会发现在打包代码中b方法也被打包进来。但是本可以不打包进来的。

tree-shaking的作用就是,在文件中export多个内容时,他就是监听模块流,然后在引入的时候只引入部分,那使用tree-shaking的时候就不会把没有引入的内容打包进去。这个功能对export暴露多个内容时效果好,如果像jQuery是一个自执行函数,那他就没办法tree-shaking,因为他的原理是监听export

将mode设为production就开启了tree-shaking。

但是tree-shaking会受到babel的影响,因为babel会把export编译掉,而tree-shaking是根据export做事情的,所以会失败。

解决方法:我们可以改.babelrc文件

{
  "presets":[
    [
      "env",{
//不去编译模块化的东西
          "modules":false
        }
    ]
  ]
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342