webpack 总结

143 阅读11分钟

webapck 用法

不像大多数的模块打包机,webpack 是收把项目当作一个整体,通过一个给定的的主文件,webpack 将从这个文件开始找到你的项目的所有依赖文件,使用 loaders 处理它们,最后打包成一个或多个浏览器可识别的 js 文件

1. 安装 webpack

npm install webpack webpack-dev-server --save-dev

2. webpack-dev-server

webpack 是我们需要的模块打包机,webpack-dev-server 用来创建本地服务器,监听你的代码修改,并自动刷新修改后的结果。这些是有关 devServer 的配置

// 为文件提供本地服务器
contentBase, 
// 监听端口,默认8080
port,
// 设置为true,源文件发生改变自动刷新页面
inline,
// 依赖HTML5 history API,如果设置为true,所有的页面跳转指向index.html
historyApiFallback  
devServer:{
    // 本地服务器所加载的页面所在的目录
    contentBase: './src' 
    // 不跳转
    historyApiFallback: true,
    // 实时刷新
    inline: true 
}
然后我们在根目录下创建一个'webpack.config.js',在'package.json'添加两个命令用于本地
开发和生产发布

"scripts": {
    "start": "webpack-dev-server",
    "build": "webpack"
}

在使用 webpack 命令的时候,他将接受 webpack 的配置文件,除非我们使用其他的操作

3. entry

entry 可以是字符串,也可以是对象和数组。

entry: 用来写入口文件,它将是整个依赖关系的根

var baseConfig = {
    entry: './src/index.js'
}

当我们需要多个入口文件的时候,可以把entry写成一个对象

var baseConfig = {
  entry: {
     main: './src/index.js'
  }
}

我建议使用后面一种方法,因为他的规模会随你的项目增大而变得繁琐

4. output

output: 即使入口文件有多个,但是只有一个输出配置

var path = require('path')
var baseConfig = {
    entry: {
        main: './src/index.js'
    },
    output: {
        filename: 'main.js',
        path: path.resolve('./build')
    }
}
module.exports = baseConfig

如果你定义的入口文件有多个,那么我们需要使用占位符来确保输出文件的唯一性

output: {
    filename: '[name].js',
    path: path.resolve('./build')
}

如今这么少的配置,就能够让你运行一个服务器并在本地使用命令 npm start 或者 npm run build 来打包我们的代码进行发布

5、Loader

loader的作用: 1、实现对不同格式的文件的处理,比如说将 scss 转换为 css,或者 typescript 转化为 js
2、转换这些文件,从而使其能够被添加到依赖图中
loader 是 webpack 最重要的部分之一,通过使用不同的 Loader,我们能够调用外部的脚本或者工具,实现对不同格式文件的处理,loader 需要在 webpack.config.js 里边单独用 module 进行配置,配置如下:

test: 匹配所处理文件的扩展名的正则表达式(必须)
loader: loader的名称(必须)
include/exclude: 手动添加处理的文件,屏蔽不需要处理的文件(可选)
query: 为loaders提供额外的设置选项
ex: 
    var baseConfig = {
        // ...
        module: {
            rules: [
                {
                    test: /*匹配文件后缀名的正则*/,
                    use: [
                        loader: /*loader名字*/,
                        query: /*额外配置*/
                ]
         }
    ]
}
}

要是 loader 工作,我们需要一个正则表达式来标识我们要修改的文件,然后有一个数组表示
我们表示我们即将使用的 Loader 当然我们需要的 loader 需要通过 npm 进行安装。例如我们需要解析 less 的文件,那么 webpack.config.js 的配置如下:

var baseConfig = {
    entry: {
        main: './src/index.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve('./build')
    },
    devServer: {
        contentBase: './src',
        historyApiFallBack: true,
        inline: true
    },
    module: {
        rules: [
            {
                test: /.less$/,
                use: [
                    {loader: 'style-loader'},
                    {loader: 'css-loader'},
                    {loader: 'less-loader'}
                ],
                exclude: /node_modules/
            }
        ]
    }
}

这里介绍几个常用的loader:
babel-loader: 让下一代的 js 文件转换成现代浏览器能够支持的 JS 文件。babel有些复杂,所以大多数都会新建一个 .babelrc 进行配置
css-loader, style-loader: 两个建议配合使用,用来解析 css 文件,能够解释 @import,url() 如果需要解析 less 就在后面加一个 less-loader
file-loader: 生成的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名
url-loader: 功能类似 file-loader,但是文件大小低于指定的限制时,可以返回一个DataURL事实上,在使用 less,scss,stylus 这些的时候,npm 会提示你差什么插件,差什么,你就安上就行了

6、Plugins

plugins 和 loader 很容易搞混,说都是外部引用有什么区别呢? 事实上他们是两个完全不同的东西。这么说 loaders 负责的是处理源文件的如 css、jsx,一次处理一个文件。而 plugins 并不是直接操作单个文件, 它直接对整个构建过程起作用下面列举了一些我们常用的plugins和他的用法\

ExtractTextWebpackPlugin: 它会将入口中引用 css 文件,都打包都独立的 css 文件中,而不是内嵌在 js 打包文件中。下面是他的应用

plugins: [
    new ExtractTextPlugin('main.css')
]

HtmlWebpackPlugin: 依据一个简单的 index.html 模版,生成一个自动引用你打包后的 js 文件的新 index.html

var HTMLWebpackPlugin = require('html-webpack-plugin')
var baseConfig = {
    // ...
    plugins: [
        new HTMLWebpackPlugin()
    ]
}

HotModuleReplacementPlugin: 它允许你在修改组件代码时,自动刷新实时预览修改后的结果注意永远不要在生产环境中使用HMR。这儿说一下一般情况分为开发环境,测试环境,生产环境。用法如 new webpack.HotModuleReplacementPlugin()

webapck.config.js的全部内容
    

const webpack = require("webpack")
const HtmlWebpackPlugin = require("html-webpack-plugin")
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var lessRules = {
    use: [
        {loader: 'css-loader'},
        {loader: 'less-loader'}
    ]
}
module.exports = {
    entry: {
            main: './src/index.js'
        },
        output: {
            filename: '[name].js',
            path: path.resolve('./build')
        },
        devServer: {
            contentBase: '/src',
            historyApiFallback: true,
            inline: true,
            hot: true
        },
        module: {
            rules: [
                {test: /.less$/, use: ExtractTextPlugin.extract(lessRules)}
            ]
        },
        plugins: [
        new ExtractTextPlugin('main.css')
    ]
}
    

7. 产品阶段的构建

目前为止,在开发阶段的东西我们已经基本完成了。但是在产品阶段,还需要对资源进行别的处理,例如压缩,优化,缓存,分离css和js。首先我们来定义产品环境

var ENV = process.env.NODE_ENV
var baseConfig = {
    // ... 
    plugins: [
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(ENV)
        })
    ]
}

然后还需要修改我们的script命令

"scripts": {
    "start": "NODE_ENV=development webpack-dev-server",
    "build": "NODE_ENV=production webpack"
}

process.env.NODE_ENV 将被一个字符串替代,它运行压缩器排除那些不可到达的开发代码分支。当你引入那些不会进行生产的代码,下面这个代码将非常有用。

if (process.env.NODE_ENV === 'development') {
    console.warn('这个警告会在生产阶段消失')
}

8. 优化插件

下面介绍几个插件用来优化代码
OccurenceOrderPlugin: 为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多 的模块,然后为他们分配最小的ID
UglifyJsPlugin: 压缩代码
下面是他们的使用方法\

var baseConfig = {
    // ...
     new webpack.optimize.OccurenceOrderPlugin()
     new webpack.optimize.UglifyJsPlugin()
}

然后在我们使用 npm run build 会发现代码是压缩的

二、webpack 小计

1. 什么是 webpack,与 grunt 和 gulp 有啥不同

  1. webpack 是一个模块打包工具,在 webpack 里面一切皆模块, 通过 loader 转换文件,通过 plugin 注入钩子,最后输出有多个模块组合成的文件。

  2. WebPack 可以看做是模块打包机:它做的事情是,分析你的项目结构,找到 Js 模块以及其它的一些浏览器不能直接运行的拓展语言,并将其打包为合适的格式以供浏览器使用

  3. Gulp/Grunt 是一种能够优化前端的开发流程的工具,而 WebPack 是一种模块化的解决方案,不过 Webpack 的优点使得在很多场景下可以替代 Gulp/Grunt 类的工具

  4. Grunt 和 Gulp 的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务

  5. Webpack 的工作方式是:把你的项目当做一个整体,通过一个给定的主文件 (如:index.js),Webpack 将从这个文件开始找到你的项目的所有依赖文件,使用  loaders 处理它们,最后打包为一个(或多个)浏览器可识别的 JavaScript 文件

  6. gulp 和 grunt 需要开发者将整个前端构建过程拆分成多个 Task ,并合理控制所有 Task 的调用关系

  7. webpack 需要开发者找到入口,并需要清楚对于不同的资源应该使用什么 Loader 做何种解析和加工

2. webpack 的优缺点

优点:

  1. 专注于处理模块化的项目,能做到开箱即用,一步到位

  2. 可通过plugin扩展,方便、灵活  

  3. 社区庞大活跃,经常引入新特性

  4. 良好的开发体验

缺点: 是只能用于采用模块化开发的项目

3、 bundle,chunk,module 是什么

bundle: 是由 webpack 打包出来的文件

chunk: 代码块,一个 chunk 由多个模块组合而成,用于代码的合并和分割

module: 是开发中的单个模块,一个模块对应一个文件,webpack 会从配置的 entry 中递归开始找出所有依赖的模块

4、什么是 loader? 什么是 plugin,两者区别

loader:模块转换器,
     用于把模块原内容按照需求转换成新内容, 通过使用不同的 Loader,Webpack       
     可以要把不同的文件都转成 JS 文件,比如 CSS、ES6/7、JSX 等

plugin:扩展插件
  在 Webpack 构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要的事情, 一个插件是含有 apply 方法的一个对象,通过这个方法可以参与到整个 webpack 打包的各个流程

loader: 是用来对模块的源代码进行转换,而插件目的在于解决 loader 无法实现的其他事,
  因为 plugin 可以在任何阶段调用,能够跨 Loader 进一步加工 Loader 的输出

5、什么是模块热更新?

  模块热更新是 webpac k的一个功能,他可以使得代码修改过后不用刷新浏览器就可以更新,是高级版的自动刷新浏览器

devServer中通过 hot 属性可以实时模块的热替换

//通过配置文件
const webpack = require('webpack');
const path = require('path');
let env = process.env.NODE_ENV == "development" ? "development" : "production";
const config = {
   mode: env,
   devServer: {
      hot:true
   }
}
plugins: [
     new webpack.HotModuleReplacementPlugin(), //热加载插件
],
module.exports = config;

6. 几个常见的 plugin 和常见的 loader

plugin:
  html-webpack-plugin 为 html 文件中引入的外部资源,可以生成创建 html 入口文件
  mini-css-extract-plugin 分离 css 文件
  clean-webpack-plugin 删除打包文件
  HotModuleReplacementPlugin 热更新应用
  copy-webpack-plugin 拷贝静态文件
  terser-webpack-plugin 通过 TerserPlugin 压缩 ES6 代码


  loader:
  css-loader 加载 CSS,支持模块化、压缩、文件导入等特性
  image-loader 加载并且压缩图片文件
  babel-loader 把 ES6 转换成 ES5
  style-loader 把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS
  file-loader 把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  source-map-loader 加载额外的 Source Map 文件,以方便断点调试

7、如何可以自动生成webpack配置?

webpack-cli /vue-cli /etc ...脚手架工具

 

8、webpack-dev-server 和 http 服务器如 nginx 有什么区别?

webpack-dev-server 使用内存来存储 webpack 开发环境下的打包文件
并且可以使用模块热更新,他比传统的 http 服务对开发更加简单高效。

 

9、webpack构建过程

从 entry 里配置的 module 开始递归解析 entry 依赖的所有 module\
每找到一个 module,就会根据配置的 loader 去找对应的转换规则\
对 module 进行转换后,再解析出当前 module 依赖的 module\
这些模块会以 entry 为单位分组,一个 entry 和其所有依赖的 module 被分到一个组Chunk\
最后 webpack 会把所有 Chunk 转换成文件输出\
在整个流程中 webpack 会在恰当的时机执行 plugin 里定义的逻辑

 

10、什么是entry,output

entry 入口,告诉 webpack 要使用哪个模块作为构建项目的起点,默认为./src/index.js

output 出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist

11、webpack如何配置单页面和多页面的应用程序?

复制代码

        //单个页面
        module.exports = {
            entry: './path/to/my/entry/file.js'
        }
        //多页面应用程序
        module.entrys = {
            entry: {
                pageOne: './src/pageOne/index.js',
                pageTwo: './src/pageTwo/index.js'
            }
        }    

复制代码

 

12、什么是长缓存?在 webpack 中如何做到长缓存优化?

  浏览器在用户访问页面的时候,为了加快加载速度会对用户访问的静态资源进行存储,但是每一次代码升级或更新都需要浏览器下载新的代码,最简单方便的方式就是引入新的文件名称
webpack中可以在 output 中指定 chunkhash,并且分离经常更新的代码和框架代码。通过 NameModulesPlugin 或 HashedModuleIdsPlugin 使再次打包文件名不变

三、webpack 4 和 webpack 5 区别

1. 压缩代码

a. webpack4

webpack4 上需要下载安装 terser-webpack-plugin 插件,并且需要以下配置:

const TerserPlugin = require('terser-webpack-plugin')
 
module.exports = { 
// ...other config
optimization: {
  minimize: !isDev,
  minimizer: [
    new TerserPlugin({
      extractComments: false, 
      terserOptions: { 
        compress: { 
          pure_funcs: ['console.log'] 
        }
      }
    }) ]
 }

b. webpack5

内部本身就自带 js 压缩功能,他内置了 terser-webpack-plugin 插件,我们不用再下载安装。而且在 mode=“production” 的时候会自动开启 js 压缩功能。

如果你要在开发环境使用,就用下面:

  // webpack.config.js中
  module.exports = {
     optimization: {
       usedExports: true, //只导出被使用的模块
       minimize : true // 启动压缩
     }
  }
 

2. webpack 缓存

a. webpack4 缓存配置

npm install hard-source-webpack-plugin -D

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') 
 
module.exports = { 
plugins: [
  // 其它 plugin... 
  new HardSourceWebpackPlugin(), 
] }
 

b. webpack5 缓存配置

webpack5 内部内置了 cache 缓存机制。直接配置即可。

cache 会在开发模式下被设置成 type: memory 而且会在生产模式把cache 给禁用掉。 type 的可选值为: memory 使用内容缓存,filesystem 使用文件缓存。

// webpack.config.js
module.exports= {
  // 使用持久化缓存
  cache: {
    type: 'filesystem',
    cacheDirectory: path.join(__dirname, 'node_modules/.cac/webpack')
  }
}

3. 启动服务的差别

a. webpack4 启动服务

通过 webpack-dev-server 启动服务

b. webpack5 启动服务

内置使用 webpack serve 启动,但是他的日志不是很好,所以一般都加都喜欢用 webpack-dev-server 优化。

4. devtool 的差别

sourceMap需要在 webpack.config.js里面直接配置 devtool 就可以实现了。而 devtool有很多个选项值,不同的选项值,不同的选项产生的 .map 文件不同,打包速度不同。

一般情况下,我们一般在开发环境配置用“cheap-eval-module-source-map”,在生产环境用‘none’。

devtool在webpack4和webpack5上也是有区别的

v4: devtool: 'cheap-eval-module-source-map'

v5: devtool: 'eval-cheap-module-source-map'

四、webpack 初体验

1. webpack.config.js

const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');


module.exports = {
  // 打包入口文件 entry 可以为一个字符串,一个数组或者一个对象
  // entry: './src/index.js',  // 这里可以用相对路径
  // entry: ['./src/base.js', './src/index.js'],

  // 当 entry 为对象或者数组时, output 中的 filename 值需为 '[name][hash:8].js'
  entry: {
    index: './src/index.js',    // 这里的 key 值 index 对应下面的 HtmlWebpackPlugin 的chunk元素
    base: './src/base.js',      // 这里的 key 值 base 对应下面的 HtmlWebpackPlugin 的chunk元素
    // 这里是一个入口,引入 jquey 库
    // vendor: 'jquery',
    // 引入公共文件
    common: './src/common.js'

  },


  // 打包后的文件要放在哪里,及打包后的文件要命名为什么名称
  output: {
    path: path.join(__dirname, 'dist'),   // 输出的文件夹,只能是绝对路径
    // filename: 'bundle.js',                // 打包后的文件名
    // name 是 entry 名字 main, hash 根据打包后的文件内容计算出来的一个 hash 值
    filename: '[name][hash:8].js'
  },

  // loader工具,所有要用到的 loader 工具都需要经过安装
  module: {
    rules: [
      {
        test: /\.css$/,                 // 匹配 .css文件
        // 多个 loader 是有顺序要求的,需从右往左写,因为转换的时候是从右往左进行转换
        // css-loader 用来解析 CSS 文件中的 url 路径,把 CSS 文件变成一个模块
        // style-loader 可以把 CSS 文件变成 style 标签插入到 head 中
        // 安装 npm install css-loader@1 style-loader@0 -D,
        // 安装时如报错需指定版本: css-loader 设置版本为 1, style-loader 设置版本为 0 
        loader: ['style-loader', 'css-loader'],

      }
    ]
  },

  // 注意:插件执行顺序没有先后之分
  plugins: [
    // 打包时清除 dist 文件夹,重新生成一个 dist 文件夹
    new CleanWebpackPlugin(),

    /**
     *  当 new HtmlWebpackPlugin 时需要 引入 jquery 这个库的代码块时,需提供 jquery 
     *  用来自动向模块内部注入这个 $ 变量
     */
    new webpack.ProvidePlugin({
      $: 'jquery'
    }),
     
    // 此插件可以自动产出 html 文件
    new HtmlWebpackPlugin({
      template: './src/index.html',   // 指定产出的 HTML 模板
      filename: 'index.html',         // 产出的 HTML 文件名
      title: 'index学习',
      /**
       * 在产出的 HTML 文件里引入了哪些代码块, 
       * 这里的 index 指的是 entry 上面的 index: './scr/index.js' 表示引入这个代码块
       * 这里的 vendor 指的是 entry 上面的 jquery
       * 这里的 chunks 中的元素对应 上面 entry 中的 key 值
      */ 
      chunks: ['index', 'common'],             
      hash: true,                     // 会在引入的 js 里加入查询字符避免缓存
      minify: {
        removeAttributeQuotes: false,  // 移除 html 标签上 和 script 标签上的 引号
      }
    }),

     // 此插件可以自动产出 html 文件
     new HtmlWebpackPlugin({
      template: './src/index.html',   // 指定产出的 HTML 模板
      filename: 'base.html',         // 产出的 HTML 文件名
      title: 'base学习',
      /**
       * 在产出的 HTML 文件里引入了哪些代码块, 
       * 这里的 base 指的是 entry 上面的 index: './scr/base.js' 表示引入这个代码块
       * 这里的 vendor 指的是 entry 上面的 jquery
       * 这里的 chunks 中的元素对应 上面 entry中的 key 值
      */ 
      chunks: ['base', 'common'],            
      hash: true,                     // 会在引入的 js 里加入查询字符避免缓存
      minify: {
        removeAttributeQuotes: false,  // 移除 html 标签上 和 script 标签上的 引号
      }
    })
  ],


  // 配置此静态文件服务器,可以用来预览打包后项目
  devServer: {
    contentBase: 'dist',
    host: 'localhost',
    port: 8080,           //  不设置的话,默认为 8080
    compress: true,       // 服务器返回给浏览器的时候是否启动 gzip 压缩
  }

}

2. package.json

{
  "devDependencies": {
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^1.0.1",
    "expose-loader": "^1.0.3",
    "html-webpack-plugin": "^3.2.0",
    "style-loader": "^0.23.1",
    "webpack": "^4.43.0",
    "webpack-dev-server": "^3.11.3"
  },
  "dependencies": {
    "jquery": "^3.6.0",
    "webpack-cli": "^3.3.12"
  },
  "scripts": {
    // --open 指的是启动的时候自动在浏览器打开页面
    "dev": "webpack-dev-server --open --mode development",
    "build": "webpack --mode development"
  }
}

3. memo.txt

## 前提摘要
1. npm 与 npx 的区别
  node_moudels/.bin 目录下放了一些命令, npm 执行不了,需要通过 npx 来执行。

# 一、准备工作
1. 来创建一个 package.json
  npm init

2. 安装 webpack
  npm install webpack@4.43.0  webpack-cli@3.3.12 -D

3. package.json 配置 打包命令
  "build": "webpack --mode development"



# 二、把一个 js 文件进行打包
1. 创建打包入口文件
   src/index.js

2. 创建打包入的文件夹放置位置
  dist/bundle.js

4. 配置 webpack.config.js 文件


5. 执行 打包命令
  npm run build


# 三、把 一个 css 文件打包
1. 需安装 对应的依赖包
  npm i style-loader css-loader -D

2. 在 webpack.config.js 中 的 module 进行文件配置


# 四、对打包后的文件在本地进行预览
1. 需安装 webpack-dev-server 包
  npm i webpack-dev-server@3 -D

2. 在 webpack.config.js 中 的 devServer 进行文件配置

3. package.json 中配置 运行命令, --open表示命令运行完,自动帮我们在浏览器里运行打包后的文件
  这里需注意:所以打包过后的文件都是放在内存里的
  "dev": "webpack-dev-server --open --mode development"



# 五、对打包后的文件名进行自动命名,加上 hash 值,以防再次更改,浏览器用的缓存文件
1. webpack.config.js 文件 outpout 中 filename 修改值


# 六、自动产出 html 模板
1. 需安装 html-webpack-plugin
  npm i html-webpack-plugin@3 -D

2. 在 webpack.config.js 中 引入 html-webpack-plugin 并在 plugins 中配置

3. 在 html 模板中 设置 title 的值,可以让 title 不固定
    src/index.html 设置 <title><%=htmlWebpackPlugin.options.title%></title>


# 七、执行打包命令时删除之前打包好的目录 删除 dist 文件夹
1. 安装 clean-webpack-plugin
  npm i  clean-webpack-plugin@3 -D

2. 在 webpack.config.js 先引入插件 clean-webpack-plugin
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');

3. 同时在 webpack.config.js 中 plugins 中 配置该插件
  new CleanWebpackPlugin()

# 八、多入口进行打包,并且打包后一个 html 文件调用对应的 js 文件
1. entry 需为一个对象

4. index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%=htmlWebpackPlugin.options.title%></title>
</head>
<body>
  <p>文字</p>
  <div id="box"></div>
  <div id="base"></div>
</body>
</html>

5. index.js

console.log(3)
$('#box').html('box 页面中的内容');
require('./i2')


// 引入css
// require('./index.css')
import './index.css';

6.目录结构

image.png