前端面试-构建篇(webpack)

基本使用
安装和配置

拆分 dev prod 配置,然后 merge

  • 安装 nodejs
  • 初始化 npm init -y
  • 安装插件 npm i webpack webpack-cli webpack-merge --save-dev
  • 新建 src 及其测试 js 代码(包含 ES6 模块化)
  • 创建配置文件
  • 增加 scripts ,运行
  • 安装 npm i clean-webpack-plugin --save-dev
  • 配置 prod
本地服务和代理
  • 新建 index.html
  • 安装 npm i html-webpack-plugin --save-dev ,并配置
  • 安装 npm i webpack-dev-server --save-dev ,并配置
  • 修改 scriptsdev ,运行
处理 ES6
  • 安装 npm i @babel/core @babel/preset-env babel-loader --save-dev
  • 配置 webpack module
  • 配置 .babelrc
  • 运行 dev
处理样式
  • 安装 npm i style-loader css-loader less-loader less --save-dev (注意要安装 less)
  • 配置 webpack module
  • 新建 css less 文件,引入 index.js
  • 运行 dev

postcss

  • 安装 npm i postcss-loader autoprefixer -D
  • 新建 postcss.config.js
  • 配置 webpack module ,增加 postcss-loader
  • 增加 css transform: rotate(-45deg);
  • 运行 dev
处理图片
  • 安装 npm i file-loader url-loader --save-dev
  • 分别配置 webpack.dev 和 webpack.prod
  • 新建图片文件,并引入到 js 中,插入页面
  • 运行 dev
  • 运行 build
高级应用
多入口
  • 新建 other.htmlother.js
  • 修改 entry
  • 修改 output
  • 修改 HtmlWebpackPlugin
  • 运行 dev
  • 运行 build
抽离 & 压缩 css 文件

抽离

  • 安装 npm i mini-css-extract-plugin -D
  • 将之前 common 中的 css 处理,移动到 dev 配置中
  • 配置 prod (配置 module ,配置 plugin)
  • 运行 build

压缩

  • 安装 npm i terser-webpack-plugin optimize-css-assets-webpack-plugin -D
  • 配置 prod
抽离公共代码
  • 配置 splitChunks
  • 修改 HtmlWebpackPlugin 中的 chunks 。重要!!!
  • 安装 lodash npm i lodash --save 做第三方模块的测试,引用 lodash
  • 运行 build
懒加载
  • 增加 dynamic-data.js 并动态引入
  • 运行 dev 查看效果(看加载 js)
  • 运行 build 看打包效果

至此,可以总结一下:

  • module:就是js的模块化webpack支持commonJS、ES6等模块化规范,简单来说就是你通过import语句引入的代码。
  • chunk: chunk是webpack根据功能拆分出来的,包含三种情况:
  1. 你的项目入口(entry)
  2. 通过import()动态引入的代码
  3. 通过splitChunks拆分出来的代码
  4. chunk包含着module,可能是一对多也可能是一对一
  • bundle:bundle是webpack打包之后的各个文件,一般就是和chunk是一对一的关系,bundle就是对chunk进行编译压缩打包等处理之后的产出。
处理 react 和 vue
  • vue-loader
  • jsx 的编译,babel 已经支持,配置 @babel/preset-react
打包效率
优化 babel-loader
  • babel-loader cache 未修改的不重新编译
  • babel-loader include 明确范围
{
    test: /\.js$/,
    use: ['babel-loader?cacheDirectory'], // 开启缓存
    include: path.resolve(__dirname, 'src'), // 明确范围
    // // 排除范围,include 和 exclude 两者选一个即可
    // exclude: path.resolve(__dirname, 'node_modules')
},
IgnorePlugin 避免引入哪些模块

以常用的 moment 为例。安装 npm i moment -d 并且 import moment from 'moment' 之后,monent 默认将所有语言的 js 都加载进来,使得打包文件过大。可以通过 ignorePlugin 插件忽略 locale 下的语言文件,不打包进来。

plugins: [
    // 忽略 moment 下的 /locale 目录
    new webpack.IgnorePlugin(/\.\/locale/, /moment/)
]
import moment from 'moment'
import 'moment/locale/zh-cn' // 手动引入中文语言包
moment.locale('zh-cn')
noParse 避免重复打包

module.noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能。 原因是一些库,例如 jQuery 、ChartJS, 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。

module.exports = {
  module: {
    // 独完整的 `react.min.js` 文件就没有采用模块化
    // 忽略对 `react.min.js` 文件的递归解析处理
    noParse: [/react\.min\.js$/],
  },
};
  • IgnorePlugin 直接不引入,代码中不存在
  • noParse 引入,但不再打包编译
happyPack 多进程打包

大型项目,构建速度明显变慢时,作用才能明显。否则,反而会有副作用。

webpack 是基于 nodejs 运行,nodejs 是单线程的,happyPack 可以开启多个进程来进行构建,发挥多核 CPU 的优势。

const path = require('path')
const HappyPack = require('happypack')

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        // 把对 .js 文件的处理转交给 id 为 babel 的 HappyPack 实例
        use: ['happypack/loader?id=babel'],
        exclude: path.resolve(__dirname, 'node_modules')
      }
    ]
  },
  plugins: [
    new HappyPack({
      // 用唯一的标识符 id 来代表当前的 HappyPack 是用来处理一类特定的文件
      id: 'babel',
      // 如何处理 .js 文件,用法和 Loader 配置中一样
      loaders: ['babel-loader?cacheDirectory'],
      // ... 其它配置项
    })
  ]
}
ParallelUglifyPlugin 多进程压缩 js

webpack 默认用内置的 uglifyJS 压缩 js 代码。大型项目压缩 js 代码时,也可能会慢。可以开启多进程压缩,和 happyPack 同理。

const path = require('path')
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')

module.exports = {
  plugins: [
    // 使用 ParallelUglifyPlugin 并行压缩输出的 JS 代码
    new ParallelUglifyPlugin({
      // 传递给 UglifyJS 的参数
      // (还是使用 UglifyJS 压缩,只不过帮助开启了多进程)
      uglifyJS: {
        output: {
          beautify: false, // 最紧凑的输出
          comments: false, // 删除所有的注释
        },
        compress: {
          // 在UglifyJs删除没有用到的代码时不输出警告
          warnings: false,
          // 删除所有的 `console` 语句,可以兼容ie浏览器
          drop_console: true,
          // 内嵌定义了但是只用到一次的变量
          collapse_vars: true,
          // 提取出出现多次但是没有定义成变量去引用的静态值
          reduce_vars: true,
        }
      },
    }),
  ],
};
自动刷新
module.export = {
  watch: true, // 开启监听,默认为 false
  // 注意,开启监听之后,webpack-dev-server 会自动开启刷新浏览器!!!

  // 监听配置
  watchOptions: {
    ignored: /node_modules/, // 忽略哪些
    // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高
    // 默认为 300ms
    aggregateTimeout: 300,
    // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的
    // 默认每隔1000毫秒询问一次
    poll: 1000
  }
}
热更新

上文的自动刷新,会刷新整个网页。

  • 速度更慢
  • 网页当前的状态会丢失,如 input 输入的文字,图片要重新加载,vuex 和 redux 中的数据

操作步骤

  • 把现有的 watch 注释掉
  • 修改 css less 实验 —— 热替换生效
  • 修改 js 实验 —— 热替换不生效
const HotModuleReplacementPlugin = require('webpack/lib/HotModuleReplacementPlugin');

module.exports = {
  entry:{
    // 为每个入口都注入代理客户端
    index:[
        'webpack-dev-server/client?http://localhost:8080/',
        'webpack/hot/dev-server',
        path.join(srcPath, 'index.js')
    ],
    // other 先不改了
  },
  plugins: [
    // 该插件的作用就是实现模块热替换,实际上当启动时带上 `--hot` 参数,会注入该插件,生成 .hot-update.json 文件。
    new HotModuleReplacementPlugin(),
  ],
  devServer:{
    // 告诉 DevServer 要开启模块热替换模式
    hot: true,
  }  
};

js 热替换不生效,是因为我们要自己增加代码逻辑。

// 增加,开启热更新之后的代码逻辑
if (module.hot) {
    module.hot.accept(['./math'], () => {
        const sumRes = sum(10, 20)
        console.log('sumRes in hot', sumRes)
    })
}

最后,热替换切勿用于 prod 环境!!!

DllPlugin

Dll 动态链接库,其中可以包含给其他模块调用的函数和数据。

要给 Web 项目构建接入动态链接库的思想,需要完成以下事情:

  • 把网页依赖的基础模块抽离出来,打包到一个个单独的动态链接库中去。一个动态链接库中可以包含多个模块
  • 当需要导入的模块存在于某个动态链接库中时,这个模块不能被再次被打包,而是去动态链接库中获取。
  • 页面依赖的所有动态链接库需要被加载。

为什么给 Web 项目构建接入动态链接库的思想后,会大大提升构建速度呢?

  • 前端依赖于第三方库 vue react
  • 其特点是:体积大,构建速度慢,版本升级慢
  • 同一个版本,只需要编译一次,之后直接引用即可 —— 不用每次重复构建,提高构建速度

Webpack 已经内置了对动态链接库的支持,需要通过2个内置的插件接入,它们分别是:

  • DllPlugin 插件:打包出 dll 文件
  • DllReferencePlugin 插件:使用 dll 文件

打包出 dll 的过程

  • 增加 webpack.dll.js
  • 修改 package.json scripts "dll": "webpack --config build/webpack.dll.js"
  • npm run dll 并查看输出结果

使用 dll

  • 引入 DllReferencePlugin
  • babel-loader 中排除 node_modules
  • 配置 new DllReferencePlugin({...})
  • index.html 中引入 react.dll.js
  • 运行 dev
总结 - 提高构建效率的方法
  • 优化 babel-loader(可用于线上)
  • IgnorePlugin 避免引入哪些模块(可用于线上)
  • noParse 避免重复打包(可用于线上)
  • happyPack 多进程打包(可用于线上)
  • ParallelUglifyPlugin 多进程压缩 js(可用于线上)
  • 自动刷新(仅开发环境)
  • 热更新(仅开发环境)
  • DllPlugin(仅开发环境)
产出代码优化
使用 production
  • 开启压缩代码
  • 开启 tree shaking(必须是 ES6 Module 语法才行)

ES6 Module 和 commonjs 的区别

  • ES6 Module 是静态引入,编译时引入
  • commonjs 是动态引入,执行时引入
// commonjs
let apiList = require('../config/api.js')
if (isDev) {
    // 可以动态引入,执行时引入
    apiList = require('../config/api_dev.js')
}
import apiList from '../config/api.js'
if (isDev) {
    // 编译时报错,只能静态引入
    import apiList from '../config/api_dev.js'
}
小图片 base64 编码

webpack4.x为图片资源配置base64编码规范_webpack4 confogurewebpack配置图片为base64-CSDN博客

bundle 加 hash

动手写webpack配置--7.webpack加hash值命名。_webpack打包hash命名-CSDN博客

 使用 CDN
配置 publicPath

https://juejin.cn/post/6977628582419890206

提取公共改代码 

Webpack高级配置「四」-- 抽离公共代码和第三方模块 ***_webpack 抽取公共配置-CSDN博客

懒加载

https://juejin.cn/post/6990565311955075085?searchId=202407250628002FBB6F4BF86C4762E067

scope hosting 将 module 合并到一个函数中

https://juejin.cn/post/6850418110983241741

构建流程

几个核心概念

  • Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入
  • Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  • Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
  • Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
  • Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。

Webpack 的构建流程可以分为以下三大阶段:

  • 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
  • 编译:从 Entry 发出,针对每个 Module 串行调用对应的 Loader 去翻译文件内容,再找到该 Module 依赖的 Module,递归地进行编译处理。
  • 输出:对编译后的 Module 组合成 Chunk,把 Chunk 转换成文件,输出到文件系统。
babel
初始化环境
  •  安装 babel 插件 npm i @babel/cli @babel/core @babel/preset-env -D

  • 新建 .babelrc,配置 preset-env

  • 新建 src/index.js ,写一个箭头函数

  • 运行 npx babel src/index.js ,看结果

使用 polyfill
  • 什么是 polyfill ?—— 即一个补丁,引入以兼容新 API(注意不是新语法,如箭头函数),如搜索“Object.keys polyfill” 和 “Promise polyfill”
  • core-js 集合了所有新 API 的 polyfill
  • regenerator 是 generator 的 polyfill
  • babel-polyfill 即 core-js 和 regenerator 的集合,它只做了一层封装而已。

基本使用

  • src/index.js 中写一个 Promise,打包看结果
  • npm install --save @babel/polyfill 【注意】要 --save
  • 然后引入 import '@babel/polyfill'
  • 再打包,看结果
  1. 解释:babel 仅仅是处理 ES6 语法,并不关心模模块化的事情。模块化归 webpack 管理
  2. 全部引入 polyfill ,体积很大

按需加载

  • 新增 "useBuiltIns": "usage" (注意要改写 preset 的 json 结构)
  • 删掉入口的 import '@babel/polyfill'
使用 runtime

babel-polyfill 的问题 —— 会污染全局变量

  • 如果是一个网站或者系统,无碍
  • 如果是做一个第三方工具,给其他系统使用,则会有问题
  • 不能保证其他系统会用 Promise 和 includes 做什么,即便他们用错了,那你也不能偷偷的给更改了
// 源代码
Promise.resolve(100).then(data => data);
[10, 20, 30].includes(20);

// 结果 —— 可以看到,Promise 和 includes 都未改动,因为以注入全局变量了
"use strict";
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");

Promise.resolve(100).then(function (data) {
  return data;
});
[10, 20, 30].includes(20);

使用 babel-runtime

  • npm install --save-dev @babel/plugin-transform-runtime`
  • npm install --save @babel/runtime,注意是 --save
  • 配置 "plugins": ["@babel/plugin-transform-runtime"]
  • 删掉 "useBuiltIns": "usage"
  • 运行代码

手把手教你在webpack中,配置两种常用的polyfills配置,以ie11为例,配置babel-loader-CSDN博客

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前端面试中,关于webpack的问题通常包括以下几个方面: 1. webpack的基本配置:这个问题涉及到webpack的入口、输出、加载器、插件等基本配置项。可以参考引用中的相关内容进行回答。 2. 如何利用webpack优化前端性能:这个问题主要是针对如何通过webpack进行性能优化,包括代码分割、懒加载、缓存等方面。可以通过使用插件和配置优化策略来实现。具体的优化方法可以参考引用中的相关内容进行回答。 3. 提高webpack构建速度:这个问题涉及到如何提高webpack构建速度,包括优化配置、使用缓存、并行处理等方面。可以参考引用中的相关内容进行回答。 4. 如何在vue项目中实现按需加载:这个问题主要是针对在vue项目中使用webpack进行按需加载,可以使用webpack的code splitting功能和Vue的异步组件来实现。具体的实现方法可以参考引用中的相关内容进行回答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [前端面试webpack](https://blog.csdn.net/Lycoriy/article/details/105835946)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [前端面试--webpack](https://blog.csdn.net/weixin_47964837/article/details/124557803)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值