WebPack

155 阅读8分钟

本质上,webpack是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。

安装webpack:npm i webpack@4.26.1 webpack-cli@3.1.2 -D

npm是一个包管理工具,可以通过npm下载使用各种包(代码模块)。它是通过NodeJS编写的,所以要使用npm,必须要安装NodeJS。安装Node的时候,会自动安装npm。

常用包:clean-webpack-plugin@1.0.0 css-loader@2.0.0 file-loader@2.0.0 html-webpack-plugin@3.2.0 style-loader@0.23.1 url-loader@1.1.2npm

要注意不同版本的webpack API可能不一样,平常用自己最熟悉的就好了,没必要进行升级。

webpack -h 查看所有webpack命令

核心概念:

  1. 入口(entry):进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。每个依赖项随即被处理,最后输出到称之为bundles的文件中。可以有多个入口文件。
  • 如果不是用默认的index.js作为默认入口文件,就需要自己配置入口和出口文件。
  1. 出口(output):output 属性告诉 webpack 在哪里输出它所创建的bundles,以及如何命名这些文件,默认值为 ./dist
  • filename 用于输出文件的文件名。
  • path 为输出目录的绝对路径。
  • 如果配置了多个入口,就需要占位符来配置不同的名称filename: '[name].bundle.js'
  1. loader:loader让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
  • test 属性,阐明需要进行转换的文件。
  • use 属性,表示进行转换时,应该使用哪个 loader。
  1. 插件(plugins):loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

    想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。

  2. 模式(mode):

  • production生产模式:会将生成的代码进行压缩
  • development开发模式:不会被压缩
  • none:不会被压缩

配置文件

package.json:定义了项目所需的各种模块,以及项目的配置信息,npm install会根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。是管理本地npm包的最佳方式。

npm init命令会自动生成这个文件

属性说明:

  • name:项目名称
  • scripts:指定了运行脚本命令的npm命令行缩写,比如"build": "webpack --mode production",通过npm run build可以运行webpack

参考文章:javascript.ruanyifeng.com/nodejs/pack…

webpack.config.js:webpack配置文件

// path路径是node提供的,需要引入
var path = require('path');var HtmlWebpackPlugin = require('html-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {    // 入口文件    entry: {        app: './src/app.js'    },    output: {        // 设置出口文件路径,__dirname代表根目录        path: path.resolve(__dirname, 'dist'),        // 设置出口文件名称        filename: '[name].bundle.js'    },
    // 处理第三方文件    module: {        rules: [{            // 用babel-loader处理以js为后缀的文件            test: /\.js$/,            use: 'babel-loader',            // 排除node_modules里的js文件,提高打包效率            exclude: /(node_modules)/        }]    }    plugins: [        new HtmlWebpackPlugin({            template: "./index.html",            filename: "app.html",            chunks: ['app'],            // 将js文件插入到body标签里面            inject: "body"        }),        new CleanWebpackPlugin(['dist'])    ]}

然后在经过npm run build编译

入口文件设置的其他写法:

有多个入口文件可以用下面这两种写法

  • 键值对

    module.exports = { // 入口文件 entry: { app: './src/app.js', new: './src/new.js' }, output: { // 设置出口文件路径,__dirname代表的是当前文件的绝对路径 path: path.resolve(__dirname, 'dist'), // 设置出口文件名称 filename: '[name].bundle.js' } }

  • 数组表示

    entry: ['./src/app.js']

模块化

webpack支持三种模块化:AMD(RequireJS)、ES Modules、CommonJS

以下是三种模块化的不同写法:在index.js文件中引入a.js

  1. ES Modules

     export default function(a, b) {
    	return a + b;
    } 
    
    import a from './a.js';
    
  2. CommonJS

     module.exports =  function(a, b) {
    	return a + b;
    }
    
     var a = require('./a');
    
  3. AMD(RequireJS)

    define( function(require, factory) {
    	'use strict';
    	return function(a, b) {
    		return a + b;
    	}
    }); 
    
     require(['./a'], function(a) {
    	console.log(a(5, 5));
    }) 
    

配置webpack:

  1. 安装nodeJS

  2. npm init 初始化包,一直按回车配置文件,文件目录下会多出来一个package.json文件

  3. node -v;npm -v检查node和npm的版本,确认安装成功;可以用where node查看node安装路径

  4. 安装webpack:npm i webpack@4.26.1 webpack-cli@3.1.2 -D;同样可以用webpack -v检查是否安装成功

  5. 在根目录下新建src文件夹,在该文件下设立index.js入口文件(index.js为默认的入口文件)

  6. 输入webpack命令,将index.js文件打包,文件目录下会多出来一个dist文件

  7. 但是控制台会出现一个警告,在webpack 4中需要设置mode模式。例:webpack --mode development。

  • 但是每次都要设置太烦了,可以在package文件中进行配置

      "scripts": {
      	"build": "webpack --mode production"
      },
    
  • 然后通过npm run build命令构建文件(效果一致)

  1. --watch | -w 检测文件的改变,不需要没改变一次就重新运行一次。在package文件里配置即可。ctrl + c 取消监听

      "scripts": {
      	"build": "webpack --mode production --watch",
      	"dev": "webpack --mode development"
      },
    
  2. 在引入模块的时候,可以再通过一些命令行,得到详细的打包信息,看到打包的信息

  • --progress 观察打包进度

  • --display-reasons 显示原因

  • --colors 以多样的颜色显示

    "scripts": {  	"build": "webpack --mode production --watch --progress --display-reasons --colors",  	"dev": "webpack --mode development"  },
    
  • single entry 入口文件:在src文件夹下面

  • 出口文件:main

  • 最后三行的白色文字部分,展示打包原因

  • 最后三行的红色文字部分(index.js)引入青色文字部分的模块(a.js),以彩色的形式显示

dev配置

安装dev:npm install webpack-dev-server@3.1.10 -D

  • inline:内联/iframe模式,一般都不设定,设定为false的时候会有一个状态条

  • open:在服务器启动后打开浏览器

  • openPage:打开的页面,如果不是index.html,是找不到页面的,除非设置了这个属性

  • hot:启用webpack的热模块替换功能

  • port:自定义端口

  • historyApiFallback:使用HTML5历史记录API时,index.html会代替任何404回复,也可以设定跳转到什么页面上

  • 输入http://localhost:3000/a,会跳转到a.html,相当于重定向

        devServer: {        open: true,        port: 3000,        historyApiFallback: {            rewrites: [{                from: '/a',                to: '/a.html'            }]        }    },
    
  • proxy:拥有单独API后端开发服务器后,希望在同一域上发送API请求时,代理某些URL会很有用

  • target:目标接口

  • changeOrigin:如果不加这个就无法跳转请求

  • logLevel:日志

  • overlay:是否显示错误在页面中

Eslint语法检测

是ECMAScript/JavaScript代码中识别和报告模式匹配的工具,也就是检查代码规范的工具

配置相关插件:

npm i eslint@5.10.0 eslint-config-standard@12.0.0 eslint-loader@2.1.1 eslint-plugin-promise@4.0.1 eslint-plugin-node@8.0.0 eslint-plugin-html@5.0.0 eslint-plugin-standard eslint-plugin-import@2.14.0 -D

新建一个eslint配置文件:.eslintrc.js

module.exports = {    // 在当前目录层级上筛选    root: true,    // 筛选标准    extands: 'standard',    // 在哪些文件中筛选js文件    plugins: ['html', 'promise', 'node'],    // 设置环境    ebv: {        browser: true,        node: true    }}

babel 转换编译

加载ES2015+ 代码,然后使用 Babel 转译为 ES5

配置步骤:

  1. npm init

  2. npm i webpack webpack-cli -D

  3. 新建src文件夹,在该文件夹下设置入口文件

  4. npm i babel-loader@7.1.5 babel-core -D

  5. 新建webpack.config.js文件,进行配置,是专门给webpack打包工具配置的文件

    var path = require('path');module.exports = {
        entry: {
            app: './src/app.js'
        },
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: "[name].bundle.js"    },
        // 处理第三方文件
        module: {
            rules: [{
                // 用babel-loader处理以js为后缀的文件
                test: /\.js$/,
                use: 'babel-loader',
                // 排除node_modules里的js文件,提高打包效率
                exclude: /(node_modules)/
            }]
        }
    }
    
  6. npm i babel-preset-env -D 不同的浏览器和平台对ES6,ES7,ES8的支持情况不一致, babel-preset-env可以按需加载插件,比起preset的其他版本更加灵活

  7. 新建.babelrc文件,配置babel

  • targets指定运行环境

  • targets.node 指定node版本

  • targets.browsers 指定浏览器版本

  • modules 指定何种形式的模块,设置为false表示不转码模块

    {    "presets": [        ["env", {            // 指定运行环境            "target" : {                // 浏览器最新的两个版本运行                "browsers": ["lasty 2 versions"]            }        }]    ]}
    
  1. npm i babel-polyfill -D 在有部分代码无法转码时使用垫片
  • 在入口文件最顶部引入垫片import 'babel-polyfill';
  1. webpack --mode none

全局 | 局部垫片

polyfill

babel默认只转换新的js语法,而不转换新的API(即方法和函数,比如generator、set、maps、symbol、promise等全局对象,以及一些定义在全局对象上的方法【Object.assign】,还有ES6新增的Array.from方法,babel也不会转码),所以需要使用babel-polyfill,为当前环境提供一个垫片,使各个浏览器保持相同的API:

  • npm i babel-polyfill -D
  • 在入口文件最顶部引入垫片:import 'babel-polyfill';
  • 优点:一次性解决所有兼容性问题,而且是全局的

transform-runtime

在webpack中,babel-plugin-transform-runtime实际上是依赖babel-runtime,因为在babel编译过程中,babel-plugin-transform-runtime这个插件会自动polyfill ES5不支持的特性。

  • 这些polyfill包就在babel-runtime这个包里。
  • babel-runtime和babel-transform-runtime的区别是,前者是手动挡,每当转译一个API都需要手动加上require(‘babel-runtime’);后者是自动挡,会由工具自动添加babel-transform-runtime

使用步骤:

  1. 如果之前下载了polyfill,先卸载 npm uninstall babel-polyfill

  2. 下载 npm i babel-runtime babel-plugin-transform-runtime -D 和 npm i babel-runtime -D。下载完之后package会自动添加babel-runtime 和 babel-plugin-transform-runtime的依赖

  3. 在.babelrc中配置插件

        "plugins": [        "transform-runtime"    ]
    
  4. webpack --mode none 

优点:

  1. 无全局污染
  2. 依赖统一,按需引入,无重复引入,无多余引入(polyfill是各个模块共享)
  3. 适合用来编写lib(第三方库)类型的代码

处理CSS

style-loader

配合css-loader使用,将模块的导出作为样式添加到 DOM 中

分类:

  1. style-loader:以标签的形式插入到页面中
  2. style-loader/url:以link标签的形式在html中插入。这种方式不推荐,因为在js文件中引入多个css文件会生成多个link标签,而html每个link标签都会发送一个网络请求。
  • 这种方式配合使用的就不是css-loader了 ,而是file-loader
  1. style-loader/useable:这种方式会有use()和unuse()两个方法,决定是否引入样式

options配置:

  • singleton:将嵌入的多个css文件样式合并到一个样式里
  • attrs:在样式标签上添加属性
  • insertInto:将style标签插入到特定标签中
  • insertAt:top | bottom,决定样式标签插入的位置
  • transform

css-loader

解析CSS 文件后,使用 import 加载,并且返回 CSS 代码

options配置:

  • modules:是否模块化 modules: true

  • 模块化之后,会将类名变成哈希值,不会污染全局的类名

  • 给元素设置类名就在入口文件中设置即可

但是className方法只能设定一个类名,如果要引入多个类名,就需要composes

.box1 {    composes: box2 from './b.css';    width: 400px;    height: 400px;    background-color: red;}

document.getElementById('app').className = a.box1;

这样就两个类都引入了

编译顺序是先用css-loader将css代码编译,然后交给style-loader插入到html中去

file-loader

将文件(一般以图片文件为主,还包括字体文件等),在进行一些处理后,移动到输出文件夹

配置:webpack.config.js

var path = require('path');
module.exports = {
    entry: {
        app: './src/app.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].bundle.js'
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [{
                loader: 'style-loader'
            },{
                loader: 'css-loader'
            }],
            exclude: /(node_modules)/
        }]
    }
}

在webpack中解析是从下到上解析的,所以先写style-loader,再写css-loader

在app.js文件顶部引入css文件,通过css-loader将文件引入,style-loader将文件动态插入到html中

import a from './css/a.css';

Postcss

将css解析成JavaScript可以操作的抽象语法树结构(Abstract Syntax Tree),可以使用插件去转换css

常用插件:

Autoprefixer:用于添加浏览器特定前缀,处理兼容性

postcss-cssnext:使用css将来版本中可能会加入的新特性(包含Autoprefixer)

cssnano:压缩优化css

Autoprefixer插件安装步骤:

  1. 将上一个项目复制过来,有了package.json的配置信息,可以直接npm install安装

  2. 下载postcss:npm i postcss@7.0.6 postcss-cssnext@3.1.0 post-loader@3.0.0 cssnano@4.1.7 autoprefixer@9.3.1 -D

  3. 新建postcss配置文件,postcss.config.js,配置插件

  4. webpack.config.js中也添加配置信息

    {    loader: 'postcss-loader'}
    
  5. 运行之后可以看到兼容性已自动添加

postcss-cssnext和cssnano安装:

和上一个插件差不多,把postcss.config.js的配置修改一下即可

要注意的是,postcss-cssnext已经包含了autoprefixer了,之前autoprefixer的配置可以删除

可以看到设置的自定义属性,还是可以正常使用的

:root {    --fontSize: 30px;}

.box {    width: 400px;    height: 400px;    font-size: var(--fontSize);    background-color: red;    transition: width .2s linear;    transform: rotate(10deg);}

Sass、Less

安装: npm i less less-loader sass sass-loader -D

同样在webpack配置文件中添加配置

loader: 'sass-loader'

使用less语法,能够被正常解析

@homecolor: pink;html {    background-color: @homecolor;}

sass同理,要注意的是引入的包是sass-loader,sass文件后缀是scss

图片处理

url-loader

会将引入的图片编码,根据需求选择性把某些小图片编码成base64格式,写进页面,从而减少服务器请求,优化性能。

也就是说有两种工作情况:

  1. 文件大小<limit参数,将文件转为DataURL
  2. 文件大小>limit参数,url-loader会调用file-loader进行处理

是增强版的file-loader,url-loader包含了file-loader

使用步骤:

  1. 安装

  2. 配置webpack,在rules最顶部配置,防止出错

  3. 在入口文件中往html动态插入图片

    var app = document.getElementById('app');
    app.innerHTML = "<div class='item1'></div><div class='item2'></div><div class='item3'></div><div class='item4'></div>";
    
  4. 运行文件,file-loader会将图片放到dist文件夹中。

  • 此处我们设置了文件路径,图片会挡在dist下面的imgs中,因为publicPath和outputPath结合起来就是dist/imgs
  • 但是对于file-loader来说,limit是没有什么用的。换成url-loader就可以生效

url-loader和file-loader配置方法一致

image-webpack-loader压缩图片

npm i image-webpack-loader -D

配置:

插件

生成HTML - html-webPack-plugin@3.2.0

options:

  • template:本地模板文件的位置,重写一份和模板一样的文件
  • filename:输出文件的文件名称
  • minify:压缩代码
  • chunks: 允许插入到模板中的一些chunk,允许引入哪个入口文件所生成的js文件
  • inject:向template或templateContent注入所有静态资源

**处理html文件中的图片 :**html-loader@0.5.5

 自动处理文件路径

options:attrs:[img:src]

清除文件插件:clean-webpack-plugin@1.0.0

由于每次运行完都会生成新的js文件,使用这个插件可以清除dist文件夹,再重新生成文件,使用方法很简单,传入要清理的文件夹即可

var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
    entry: {
        app: './src/app.js',
        main: './src/main.js'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].[hash:5].js"
    },
    module: {
        rules: [{
            test: /\.html$/,
            use: {
                loader: "html-loader",
                options: {
                    // 对img标签的src进行处理
                    attrs: ["img: src"]
                }
            }
        },{
            test: /\.jpg$/,
            use: {
                loader: "file-loader",
                options: {
                    name: "[name]-[hash:5].[ext]"
                }
            }
        }]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./index.html",
            filename: "app.html",
            chunks: ['app'],
            // 将js文件插入到body标签里面
            inject: "body"
        }),
        new HtmlWebpackPlugin({
            template: "./index.html",
            filename: "main.html",
            chunks: ['main']
        }),
        new CleanWebpackPlugin(['dist'])
    ]
}

插件配置完之后dist文件就会多出来index.html和图片文件

提取css样式

1. extract-text-webpack-plugin@next

fallback:当不提取的时候用什么方式加载到页面中
use:提取的方式处理css

使用这个插件之前,首先应该要在路口文件将css文件引入进来

import a from './css/a.css';import b from './css/b.less';

webpack配置如下:

var ExtractLess = require('extract-text-webpack-plugin');var CleanWebpackPlugin = require('clean-webpack-plugin');

        {            test: /\.css$/,            use: ExtractCss.extract({                // 将css文件以require的形式加载到入口文件中                fallback: {                    loader: 'style-loader'                },                use: {                    loader: 'css-loader'                }                            })        },{            test: /\.less$/,            use: ExtractLess.extract({                // 将css文件以require的形式加载到入口文件中                fallback: {                    loader: 'style-loader'                },                use: [{                    loader: 'css-loader'                },{                    loader: 'less-loader'                }]            })        }

        new ExtractCss({            filename: '[name]-[hash:5]-one.css'        }),        new ExtractLess({            filename: '[name]-[hash:5]-two.css'        }),

**2. mini-css-extract-plugin@**0.4.5

使用方法:

var MinicssEctractPlugin = require('mini-css-extract-plugin');

        {            test: /\.css$/,            use: [{                loader: MinicssEctractPlugin.loader            },{                loader: 'css-loader'            }]        },{            test: /\.less$/,            use: [{                loader: MinicssEctractPlugin.loader            },{                loader: 'css-loader'            },{                loader: 'less-loader'            }]        }

        new MinicssEctractPlugin({            filename: '[name]-[hash:5]-one.css'        }),

使用成功后就会在dist文件夹下出现打包后的文件

引入库

1. bootstrap@3.3.7

使用npm i命令下载

在入口文件通过import引入css文件:

import 'bootstrap/dist/css/bootstrap.css';

我们可以在引入的库中看到文件目录:

字体文件需要相关的loader解析,也就是file-loader

var path = require('path');var HtmlWebpackPlugin = require('html-webpack-plugin');var CleanWebpackPlugin = require('clean-webpack-plugin');module.exports = {    entry: {        app: './src/app.js'    },    output: {        path: path.resolve(__dirname, 'dist'),        filename: '[name].bundle.js'    },    module: {        rules: [            {                test: /\.css$/,                use: {                    loader: 'style-loader'                }            },{                test: /\.css$/,                use: {                    loader: 'css-loader'                }            },{                // 利用file-loader处理字体文件                test: /\.(eot|svg|ttf|woff|woff2)$/,                use: [{                    loader: 'file-loader',                    options: {                        name: 'app.[ext]'                    }                }]            }        ]    },    plugins: [        // 以index.html为模板,在dist中创建app.html        new HtmlWebpackPlugin({            template: 'index.html',            filename: 'app.html'        }),        new CleanWebpackPlugin(['dist'])    ]

效果如下:

2. jQuery

通过npm i下载jQuery库

引入jQuery

import $ from 'jquery';

webpack.ProvidePlugin

上例中必须要通过import引入模块,webpack.ProvidePlugin不需要,它是webpack的内置模块,使用webpack.ProvidePlugin加载的模块在使用时不再需要import和require进行引入

var webpack = require('webpack');

        new webpack.ProvidePlugin({            $: 'jquery'        })

没有通过npm下载库,找不到路径没有关系,可以通过手动修改路径,引入本地文件

    resolve: {        alias: {            jquery$: path.resolve(__dirname, 'libs/jquery.min.js')        }    }

imports-loader

允许使用依赖于特定全局变量的模块,对依赖全局变量$,或this作为window对象的第三方模块非常有用

但是这种方法还是要用import引入,resolve也要配置路径,还是第一种方法更简单点

                test: path.resolve(__dirname, 'libs/jquery.min.js'),                use: {                    loader: 'imports-loader',                    options: {                        $: 'jquery'                    }                }

Tree Shaking

摇晃掉枯萎的叶子,也就是清除掉没有用到的文件

JS tree shaking

uglifyjs-webpack-plugin@2.1.1 可以更加精细的压缩代码

在minimizer中可以用,在plugins也可以用。

minimizer:true和webpack --mode production作用一样,也是可以压缩代码的。但是它的优先级更高,是在mode production压缩的基础上进行更精细的压缩配置,设定minimizer,要在mode production的前提下才能生效。

实际上mode production设置了之后,minimizer默认为true,如果minimizer为false,mode production无效。

optimization专门用来配置优化webpack打包后的文件,最好在这里进行专门处理
module.exports = {    optimization: {        // minimize: true        minimizer: [            new UglifyJS({                uglifyOptions: {                    ecma: 5                }            })        ]    }}

在插件中使用,要注意如果mode不是production,会全部压缩,只有mode production才会按需压缩,没有使用到的内容不会打包

var UglifyJS = require('uglifyjs-webpack-plugin');module.exports = {    plugins: [        new UglifyJS({            uglifyOptions: {                ecma: 5            }        })    ]}

两种方法都可以

CSS true shaking

压缩:同样有两种方法

npm i optimize-css-assets-webpack-plugin@5.0.1 cssnano@4.1.8 -D

var UglifyJS = require('uglifyjs-webpack-plugin');var MiniCss = require('mini-css-extract-plugin');var OptimizeCss = require('optimize-css-assets-webpack-plugin');module.exports = {    module: {        rules: [            {                test: /\.css$/,                use: [{                    // 插入                    loader: 'style-loader'                },{                    // 压缩                    loader: MiniCss.loader                }, {                    // 打包                    loader: 'css-loader'                }]            }        ]    },    optimization: {        // minimize: true        minimizer: [            new UglifyJS({                uglifyOptions: {                    ecma: 5                }            }),            new OptimizeCss({                cssProcessor: require('cssnano')            })        ]    },    plugins: [        new MiniCss({            filename: 'app.min.css'        }),        // new OptimizeCss({        //     // 加载器        //     cssProcessor: require('cssnano')        // })        // new UglifyJS({        //     uglifyOptions: {        //         ecma: 5        //     }        // })    ]}

去除无效样式

npm i purifycss-webpack@0.7.0 purify-css@1.2.5 glob@7.1.3 glob-all@3.1.0 -D

purifycss-webpack

purify-css 筛选没有用到的类名,对应的样式

glob-all,帮助purifyCSS进行路径处理,定位要做Tree Shaking的路径文件

var purifyCss = require('purifycss-webpack');var path = require('path');var glob = require('glob-all');
        new purifyCss({            paths: glob.sync([                path.join(__dirname, './index.html'),                path.join(__dirname, './src/index.js')            ])        })

提取公共代码

提取了公共代码之后,虽然用户在第一次打开网站的速度得不到优化,但是当用户访问该网站的其他页面时,就可以直接在缓存中提取公共代码。这样做网页加载速度得到优化,网络传输流量得以减少,服务器成本也会降低。

webpack4.x会默认对代码进行拆分,规则如下:

  • 模块被重复引用或者模块来自node_modules
  • 在压缩前最小为30kb
  • 在按需加载时,请求数量<=5
  • 在初始加载时,请求数量<=3

但是一般情况下都是自定义查分规则,用下列工具细分代码

缓存组 Cache Groups

缓存组默认将node_modules中的模块拆分到一个叫做vendors的代码块中,将最少重复引用两次的模块放入default中

bundle-analyzer可视化工具,可以查看文件结构

npm i webpack-bundle-analyzer@3.0.3 -D

var path = require('path');var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;module.exports = {    entry: {        pageA: './src/pageA.js',        pageB: './src/pageB.js'    },    output: {        path : path.resolve(__dirname, 'dist'),        filename: '[name].bundle.js'    },    plugins: [        new BundleAnalyzerPlugin()    ],    optimization: {        // 分隔模块        splitChunks: {            cacheGroups: {                // 定义处理公共模块的内容,都是业务代码,没用其他库,都是自己写的                common: {                    // 拆分出来的块的信息                    name: 'common',                    // 在所有模块中寻找                    chunks: 'all',                    // 压缩前的最小模块大小                    minSize: 1,                    // 优先级                    priority: 0                },                vendor: {                    name: 'vendor',                    test: /[\\/]node_modules[\\/]/,                    chunks: 'all',                    // 先提取体积比较大的第三方库的代码,再提取业务代码                    priority: 10                }            }        }    }}

第三方库的代码另外提取到vendor中

代码分割

单页面的代码分割和懒加载不是通过webpack配置来实现的,而是通过webpack的写法和内置函数实现的

import()

进行动态加载,如果单独文件太大的话,加载可能会很慢,这个方法可以将打包后的js文件拆分成一个个小的js文件,然后通过script标签直接加载要页面里就可以了。

使用方法很简单,用import方法异步调用

import(/* webpackChunkName: 'subPageA'*/'./subPageA').then(function(subPageA) {    console.log(subPageA);});

注释设定的是拆分的chunk的名称

后缀名再通过webpack配置文件配置一下,和打包的budle文件以示区分

    output: {        path: path.resolve(__dirname, 'dist'),        filename: '[name].bundle.js',        chunkFilename: '[name].chunk.js',    }

第三方文件:

import(/* webpackChunkName: 'jquery'*/'jquery').then(function($) {    console.log($);});

require.ensure()

webpack在编译时 ,会静态的解析代码中的require.ensure(),同时将模块添加到一个分开的chunk中。这个新的chunk会被webpack通过jsonp来按需加载

和import()一个个拆分不同,require.ensure()是将多个引用资源打包在一起

// 入口文件打包的结果会包括module模块require.include('./module');// subPageA和subPageB合在一起另外打包require.ensure(['./subPageA', './subPageB'], function() {    var subPageA = require('./subPageA');    var subPageA = require('./subPageB');}, 'subPage');

第三方文件:

require.ensure(['jquery'], function(){    var $ = require('jquery');    console.log($);}, 'vendor')