# webpack配置

# module chunk bundle区别

//文件结构
src/
├── index.css
├── index.html  //  这个是 HTML 模板代码
├── index.js
├── common.js
└── utils.js
//index.css
body {
    background-color: red;
}
//utils.js
export function square(x) {
    return x * x;
}
//common.js
export function log(x) {
    console.log(x)
}
//index.js代码,引用了index.css common.js
import './index.css';
const { log } = require('./common');
log('webpack');
{
    entry: {
        index: "../src/index.js",
        utils: '../src/utils.js',
    },
    output: {
        filename: "[name].bundle.js", // 输出 index.js 和 utils.js
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader, // 创建一个 link 标签
                    'css-loader', // css-loader 负责解析 CSS 代码, 处理 CSS 中的依赖
                ],
            },
        ]
    }
    plugins: [
        // 用 MiniCssExtractPlugin 抽离出 css 文件,以 link 标签的形式引入样式文件
        new MiniCssExtractPlugin({
            filename: 'index.bundle.css' // 输出的 css 文件名为 index.css
        }),
    ]
}

打包后的输出结果如下:

1 index.css 和 common.js 在 index.js 中被引入。打包后都属于chunk0 ,utils.js单独打包构成chunk1.

2 我们书写的每个源文件( js css等)称为一个module

3 webpack 处理好 chunk 文件后,最后会输出 bundle 文件,这个 bundle 文件包含了经过加载和编译的最终源文件,所以它可以直接在浏览器中运行。

一般来说一个 chunk 对应一个 bundle,该配置正常生成 index.bundle.js 和 utils.bundle.js。但是 通过MiniCssExtractPlugin插件把index.css在chunk0中提出来了。所以最后应该是

utils.bundle.js index.bundle.js index.buidlel.css

module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字。

直接写出来的是 module,webpack 处理时是 chunk,

最后生成浏览器可以直接运行的 bundle。

QQ截图20200128111606.png

# hash contenthash chunkhash

标题 描述
hash hash是整个项目的hash值,根据每次编译后的内容计划生成,每次编译任何文件的修改都会导致hash值的变化
chunkhash chunk模块内的hash,chunk模块内的代码有改动 hash值变化
contenthash 单个文件根据content决定hash值变化

# 打包后的内容简单版

  • 1 新建a.js 和 b点js两个页面
  • 2 在a.js中引入b.js的某个模块并输出,
  • 3 查看webpack打包后的结果。
a.js内容 

improt { goPlay  } from './b.js'
console.log(goPlay())

b.js内容

export function goPlay(){
    return 'hello'
}

webpack打包输出的结果 npm run build
//发现webpack打包结果是通过一个自执行函数包裹。

(() => { "use strict"; console.log("hello") })();

 "scripts": {
    "build": "webpack"//实际查找的即是./node_modules/.bin/webpack
  },

# entry指定打包入口

webpack是一个模块打包器,根据文件的依赖关系进行模块化打包。

//单入口
module.exports = {
    entry:'./path/file1.js'
}

//多入口
module.exports = {
    entry:{
        file1:'./src/file1.js',
        file2:'./src/file2.js'
    }
}

# output指定打包入口

指定结果代码输出的位置

//在weboack.config.js中配置 

'use strict'
const path = require('path')
console.log('path')
module.exports ={
    //entry配置多个入口
    entry:{
        file1:'./src/index.js',
        file2:'./src/index.js'
    },
    output: {
        path:path.join(__dirname,'dist'),
        //'[name].js'占位符作用,代表输出的文件名
        filename:'[name].js'
    },
    mode:'production'
}

//输出结果 在dist目录下会生成file1.js 和 file2.js两个文件 

# babel兼容的处理

npm i babel-loader @babel/core @babel/preset-env @babel/preset-react -D
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D

转换过程
 # babel-loader调用@babel/core
 # @babel/core识别js代码,调用@babel/preset-env预设进行转换 

转换配置
module:{
    rules:[
        {
            test:/\.js$/,
            use:[
                'babel-loader',
                {
                    options:{
                        presets:[
                            "@babel/preset-env","@babel/preset-react"
                        ],
                        plugins:[
                            ["@babel/plugin-proposal-decorators",{legacy:true}],
                            ["@babel/plugin-proposal-class-properties",{legacy:true}]
                        ]
                    }
                }
            ]
        },
    ]
},

# devServer相关

devServer:{
    //额外静态资源目录(默认开发模式 只有dist是静态资源目录)
    contentBase:path.resolve('public'),
    compress:true,//是否启用压缩gzip
    //webpack serve 启用的端口
    port:8888,
    //webpack serve 运行自动打开网页
    open: true
},

# loader相关

# 常用loader

loader名 作用
babel-loader 转换es6 es7
css-loader 支持css文件加载和解析
scss-loader 把scss转换为css
ts-loader 把ts转换为js
file-loader 文件字体相关处理
thread-loader 多线程打包
raw-loader 将文件以字符串形式导入

# babel-loader的使用

通过该方式就可以对es6语法进行支持

1 npm install @babel/core @babel/preset-env babel-lader -d

2 配置.babellrc文件

{
    "presets": ["@babel/preset-env"],
    "plugins": ["@babel/proposal-class-properties"]
}

3 babel-loader配置

module:{
    rules:[{
        test: /.js$/,
        use: 'babel-loader'
    }]
}

4 babel增加对 react的支持

// 1 npm i react react-dom @babel/preset-react -d

// 2 .babellrc中增加对react的babel

{
    "presets": ["@babel/preset-env","@babel/preset-react"],
    "plugins": ["@babel/proposal-class-properties"]
}

// 3 在要build的文件中增加一段react结构

import React from 'react'
import ReactDOM from 'react-dom'

class Index extends React.Component{
     render(){
         return <div>121212</div>
     }
}

ReactDOM.render(
    <Index/>,
    document.getElementById('app')
)

# css-loader相关处理

作用

  • style-loader 用于加载.css文件,并转换为commonjs对象,将css插入到head中
  • css-loader 解析css中url() 和 import
  • 1 npm i style-loader css-loader -d
  • 2 loader解析scss
module:{
        rules:[{
            test: /.js$/,
            use: 'babel-loader'
        },{
            test: /.css$/,
            use: ['style-loader','css-loader']
        },{
            test: /.scss$/,
            use: ['style-loader','css-loader','sass-loader']
        }]
    }

module:{
        rules:[
            {
                test:/\.txt$/,
                use:'raw-loader'
            },
            {
                test:/\.css$/,
                use:[
                    'style-loader',//css转换为js
                    {
                        loader:'css-loader',//解析 url import
                        options:{
                            importLoaders:1
                        },
                    },
                    'sass-loader'
                ]
            }
        ]
    },

# file-loader相关

  • 通过使用file-loader可以解析 png jpg gif svg等的解析。用file-loader打包的图片会给每张图片都生成一个随机的hash值作为图片的名字,并可以通过outputPath参数指定其所打包后存放的位置。
  • 项目中如果想要const img = require('./assets/test.png'),就需要用到file-loader解析

使用过程

  • 通过npm i file-loader -d 安装file-loader
  • file-loader配置
  • file-loader的常用配置项: name配置项是配置打包生成的文件的名字,使用的是placeholder语法, [name] 表示的是原文件的名字;[hash] 表示的是这次打包的hash值 [ext]表示的是原文件的后缀;
{
    test:/\.(png|jp?g|gif)$/,
    use:{
        loader:'file-loader',
        options:{
            name:"[name]_[hash:10].[ext]",
            outputPath:"images/"
        }
    },
}
文件 hash生成的代码
let crypto = require('crypto');
let content = fs.readFileSync('demo.png');
crypto.createHash('md5').update(content).digest('hex').slice(0,10);

# url-loader相关

  • 通过npm i file-loader url-loader -d
  • url-loader是file-loader的加强版,在file-loader的基础上增加了一些功能。同时拥有file-loader的全部功能。
  • url-loader优势主要是支持limit(在业务中如果有一些小图icon建议打包成svg,如果是大图不建议打包svg,因为会增大html代码体积)。

# plugins相关

plugins作用于整个构建过程。用于bundle文件的优化,资源管理以及环境变量的注入等。

# 常用plugins

plugins名 作用
commonChunkPlugin 将chunks相同的代码模块提取到公共
clearWebpackPlugin 清理dist目录
UglifyWebpackPlugin 压缩js
HtmlWebpackPlugin 创建HTML结构并引入bundle
ZipWebpackPlugin 将打包后的文件压缩为zip
thread-loader 多线程打包
ExtractTextWebpackPlugin 将css从bundle里提取出成为一个独立的css文件

# 热更新相关问题

  • 方法1 通过watch参数配置更新(使用该方式需要每次修改完配置之后都需要刷新页面)
// 启动webpack 设置--watch 或者 在webpack.config.js中设置watch

scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch",//增加watch 参数
    "dev": "webpack-dev-server --open"
}
  • 方法2 webpack-dev-server热更新
    • 不需要刷新浏览器。
    • 不需要输出文件,放在内存中。
    • 可以使用HotModuleReplacementPlugin(webpack自带)
    • 要将mode模式改为development

使用

  • 1 安装 sudo npm install webpack-dev-server -g
  • 2 配置package.json
 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --watch",
    "dev": "webpack-dev-server --open"
 },

  • 3 webpack.config.js配置
module.exports ={
    entry:{
        file1:'./src/index.js',
    },
    output: {
        path:path.join(__dirname,'dist'),
        filename:'[name].js'
    },
    mode:'development',
    module:{
        rules:[{
            test:/.(png|jpg|gif|jpeg|svg)$/,
            use:'file-loader'
         }]
    },
    plugins:[
        new webpack.HotModuleReplacementPlugin()
    ],
    devServer:{
        contentBase:'./dist',
        hot:true
    }
}

# sliptChunksPlugin进行公共脚本分离

# 文件指纹

  • hash跟整个项目的构建相关,只要项目有修改,整个项目构建的hash就会发生改变。
  • chunkhash 和webpack打包 的chunk有关,不同的entry对应的chunk不一样。
  • contenthash 根据文件内容定义hash,内容不同hash值不同。

# HTML CSS JS压缩

  • webpack内置uglifyjs-webpack-plugin

(由于浏览器在生产环境默认开启了uglifyjs-webpack-plugin,js会被自动压缩)

  • optimize-css-assets-webpack-plugin + cssnano

用来压缩css

//第一步导入相关资源
npm i  optimize-css-assets-webpack-plugin -d
npm i cssnano -d 
//引入资源+配置,即可以把css压缩
const optimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') 

plugins:[
    new optimizeCssAssetsPlugin({
        assetNameRegExp:/\.css$/g,
        cssProcessor:require('cssnano')
    })
],
  • 设置html-webpack-plugin设置压缩参数,压缩html文档
npm i html-webpack-plugin -d

# Mode内置函数

通过 process.en.NODE_ENV值为development/production

模式 描述
developement 会开启NamedChunksPlugin 和 NameModulesPlugin
production 会开启FlagDependencyUsagePlugin,FlagIncluedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin 和 TerserPlugin
none 不开启任何优化

# source map

推荐

关键字 描述
devtool: 'eval' build在生成的js文件中 通过eval包裹,(优势方便缓存,速度能快一些)
devtool: 'source-map' 会生成行 列的对应关系 生成main1.js 和 main1js.map文件
devtool: 'cheap-source-map' 会生成行 不包含列信息 生成main1.js 和 main1js.map文件,不包含babel-loader转换的代码
devtool: 'module-source-map' 指包含babel-loader转译之后的那些信息
devtool: 'inline' 把.map文件作为DataURL内嵌,不单独生成.map文件
  • source-map和cheap-module-source-map的区别?

    source-map能够定位到行 列的信息,并且包含经过babel-loader转换的代码

    cheap-module-source-map只能够定位到行的信息,包含babel-loader转换的代码

 devtool: 'eval',
//生成的main1.js文件
(() => { var __webpack_modules__ = { 966: () => { eval("var a = 1;\nconsole.log(a);\n\n//# sourceURL=webpack://webpack5/./src/index.js?") } }, __webpack_exports__ = {}; __webpack_modules__[966]() })();

关键字 描述
source-map 单独在外部生成完成的.map文件,能准确定位代码的行列信息
inline-source-map 内联在build的文件中以base64的形式生成.map文件,能准确定位代码的行列信息(内联构建速度能快一些)
eval-source-map 会为每个模块单独生成一个source-map文件,使用eval执行
cheap-source-map 外部生成source-map,不包含列信息,不包含loader的map
cheap-module-source-map 在外部生成source-map,不包含列,包含loader的map
hidden-source-map 生成source-map文件,但是不跟原文件关联
devTool:false 不生成source-map文件

在开发环境中

-eval-cheap-source-map 重复构建有缓存,速度最快,cheap不包含列的信息,只能定位到行

-cheap-module-source-map 只能够定位到行的信息,包含babel-loader转换的代码

-eval-source-map 包含行和列的信息,重复构建速度快(以上2种折中方法)

在生产环境中 (肯定要排查inline 减少源代码的体积) 调试友好 source-map > cheap-source-map > cheap-module-source-map > 速度更快 选择 cheap

 //开发环境
 devtool:"cheap-module-eval-source-map"
 //生产环境
 devtool:"cheap-module-souce-map"

source map设置等级,可以方便开发环境的调试。

# webpack打包速度

# 指定loader作用的范围

{
    test: /.js$/,
    //在指定目录查找
    include:path.resolve("__dirname","./src"), 
    //不查找某个目录
    exclude: /node_modules/,
    use: {
        loader:'babel-loader',
        options: {
            presets: ["@babel/preset-env"]
        }
    }
}

# 指定项目中查找插件的规则

//默认规则是先在本地路径中查找,如果没有在项目node_modules中查找,如果项目node_modules中没有则在本地全局环境中查找。
resolve:{
    modules:[path.resolve(__dirname,"./node_modules")]
},

# externals引用cdn资源

如 import jquery from 'jquery',我们引入了jquery资源,但是在打包的过程中我们不想把jquery打包到boundle.js中,而是通过cdn引入。可以通过externals配置。

//在html中引入资源
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
//在webpack配置中
module.exports = {
    externals:{
        //jquery通过script方式引入
        'jquery':'jquery'
    }
}

# 简单webpack从0搭建

package.json

   {
  "name": "webpackStudy",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --config  config/webpack.dev.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "html-webpack-plugin": "^4.5.0",
    "webpack": "^4.44.2",
    "webpack-cli": "^3.3.12",
    "webpack-merge": "^4.2.2",
    "webpack-dev-server": "^3.11.0"
  },
  "dependencies": {}
}

  },

新建config

webpack是模块化打包工具,通过webpack可以把 .vue .sass .less .jsx .js等编译成 .js .css .png .jpg

##  全局安装webpack
npm i webpack  webpack-cli -g

## 版本检查
webpack -v

# webpack打包后的文件

  • 首先打包后的结果,该函数是自执行函数
(function(){

})('key':function(){})

// key 代表文件路径 value:是一个函数(执行当前文件的代码),通过eval执行字符串代码
webpack把相互依赖的多个文件,  打包成为一个文件。


//多个文件打包成一个文件,通过__webpack_require__ 该函数通过递归自己调用自己,引入依赖的文件。  把所有文件打包形成一个文件。
function __webpack_require__(moduleId){
}


 (function(modules) { // webpackBootstrap
  // The module cache
  var installedModules = {};

  // The require function
  function __webpack_require__(moduleId) {

   // Check if module is in cache
   if(installedModules[moduleId]) {
    return installedModules[moduleId].exports;
   }
   // Create a new module (and put it into the cache)
   var module = installedModules[moduleId] = {
    i: moduleId,
    l: false,
    exports: {}
   };

   // Execute the module function
   modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

   // Flag the module as loaded
   module.l = true;

   // Return the exports of the module
   return module.exports;
  }
    (function(module, exports) {
    eval("console.log('hello')\n\n\n\n//# sourceURL=webpack:///./src/index.js?");
    })

 });

webpack优化点,使用懒加载 热更新 不用的不引入 大的包放在CDN上
速度优化
体积优化
自带的优化
- treeSharking 不用的代码不打包 (生产环境有效)
- scope-hoisting 作用域提升,用变量来计算一个结果,如果其他地方未使用到该变量,怎只会打印结果
自己实现的优化

比如moment插件,包含我们不需要语言包, 通过 ignorePlugin

一些资源包如 jquery不打包,通过cdn引入。如果打包进来体积爆炸。