1、什么是 webpack?说一下你对 webpack 的理解?webpack的工作原理?

webpack 是一种模块打包工具,可以将各类型的资源,例如 img、CSS、JS 等,转译组合为 JS 格式的 bundle 文件。
是一个模块化打包工具,将不同的资源和文件,进行打包,合并在一个文件里。

2、为什么要使用 wp?好处?优点?

Webpack 可以将多种静态资源 js、css、less 转换成一个静态文件,减少了页面的请求。
重新加载编译,将浏览器不认识的语法编译成浏览器认识的语法。less 编译成 css,ES6 语法转换成 ES5。
语法兼容,插件多,功能多。

3、在 webpage 之前,前端是怎么打包的?

引入外部第三方库

4、浏览器是不支持模块化的?

5、如何让浏览器支持模块化?

早期做法:
browserify、requirejs 打包工具,来编写能在浏览器中运行 commonjs 的模块的代码。
es6 模块化(现在)

6、跨域请求的安全问题?

npx http-server  // 起一个端口服务

7、自动引入资源——使用 wp 插件——HtmlWebpackPlugin

// npm install html-webpack-plugin -D // 本地环境安装

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js", // 入口
  output: {
    // 出口
    filename: "bundle.js", // 指定输出文件的文件名
    path: path.resolve(__dirname, "./dist"), // 文件的输出路径
    clean: true, // 打包的时候清除上一次打包的遗留垃圾!!!!
  },

  mode: "none",
  plugins: [new HtmlWebpackPlugin()],  // 插件
};

plugins 是什么?有什么作用?

Plugin 功能更强大,主要目的就是解决 loader 无法实现的事情,比如打包优化和代码压缩等。
Plugin 加载后,在 webpack 构建的某个时间节点就会触发 plugin 定义的功能,帮助 webpack 做一些事情。实现对 webpack 的功能扩展。

常见的 Plugins 有哪些?

html-webpack-plugin 处理 html 资源,默认会创建一个空的 HTML,自动引入打包输出的所有资源(js/css)
mini-css-extract-plugin 打包过后的 css 在 js 文件里,该插件可以把 css 单独抽出来
clean-webpack-plugin 每次打包时候,CleanWebpackPlugin 插件就会自动把上一次打的包删除

8、wp 实时重新加载

只要 js 文件发生变化,wp 都会自动打包。
使用 watch mode——实时监测 js 代码的变化,不用手动 npx wp 打包。

npx webpack --watch

webpack-dev-server 插件提供了一个基本的 web server,且有实时重新加载的功能。
// npm install webpack-dev-server -D  // 本地环境安装

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.js", // 入口
  output: {
    // 出口
    filename: "bundle.js", // 指定输出文件的文件名
    path: path.resolve(__dirname, "./dist"), // 文件的输出路径
    clean: true, // 打包的时候清除上一次打包的遗留垃圾
  },

  mode: "development", // 开发模式
  devtool: "inline-source-map", // 编译工具,定位错误代码
  plugins: [
    new HtmlWebpackPlugin({
      template: "./index.html", // 模板
      filename: "app.html",
      inject: "body",
    }),
  ],

  devServer: {
    static: "./dist", // devServer指向的物理路径!!
  },
};

// npx webpack-dev-server 

9、webpack的构建流程是什么? 从读取配置到输出文件这个过程【原理】

  • 初始化参数:

从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
  • 开始编译:

用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
  • 确定入口:

根据配置中的 entry 找出所有的入口文件;

  • 编译模块:

从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
  • 完成模块编译:

在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
  • 输出资源:

根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
  • 输出完成:

在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

10、如何利用 webpack 来优化前端性能?(提高性能和体验 --按需加载)

压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
利用CDN加速。在构建过程中,将引用的静态资源路径修改为 CDN 上对应的路径。可以利用 webpack 对于output参数和各 loader 的publicPath参数来修改资源路径。
删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动 webpack 时追加参数--optimize-minimize来实现。tree-shaking 是一种基于 ES Module 规范的 Dead Code Elimination 技术打包,在打包过程中检测工程中没有引用过的模块并进行标记,删除没有引用过的模块,提高构建速度,较少程序运行时间。
提取公共代码。
代码分割 splitChunks - 在 optimization 配置项中配置,可以将 node__mudules 中代码单独打包成一个chunk 输出(比如使用了jqury?);会自动分析多入口 chunk 中,有没有公共的文件,如果有会打包成单独的一个 chunk 不会重复打包。
Dll 进行分包。正常情况下 node_module 会被打包成一个文件。dll 技术,对可以将那些不常更新的框架和库进行单独打包,生成一个 chunk。
路由懒加载。在代码中所有被 import() 函数引用的模块,都将打成一个单独的包,放在 chunk 存储的目录下。在浏览器运行到这一行代码时,就会自动请求这个资源,实现异步加载。

11、使用 tree-shaking (删除死代码)需要注意什么?

默认 mode = production ,生产环境默认开启 tree-shaking 功能。
需要是使用 ES6 规范编写模块代码,ES6 的模块依赖关系是确定的,和运行时状态无关。
尽量不写带有副作用的代码。如编写了立即执行函数,在函数里使用了外部变量等。

12、怎么配置单页应用?怎么配置多页应用?

单页应用:

可以理解为 webpack 的标准模式,直接在entry中指定单页应用的入口即可;

多页应用:

可以使用 webpack 的 AutoWebPlugin 来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。
每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套 css 样式表;
随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置。

13、Webpack 中 loader 是什么?的作用?loader 机制的作用是什么?

webpack 默认只能打包 js 文件,配置里的 module.rules 数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换打包成 js。
注意:
use 属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的;
每一个 Loader 都可以通过 URL querystring 的方式传入参数,例如 css-loader? minimize 中的 minimize 告诉css-loader 要开启 CSS 压缩。

【官网】有两种使用 loader 的方式:

  • 配置方式(推荐):在 webpack.config.js 文件中指定 loader。

  • 内联方式:在每个 import 语句中显式指定 loader。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true,
            },
          },
          { loader: 'sass-loader' },
        ],
      },
    ],
  },
};

14、常见的 loader 有哪些?

less-loader: 将 less 文件编译成 css 文件;开发中,我们常常会使用 less 预处理器编写 css 样式,使开发效率提高。
css-loader: 将 css 文件变成 commonjs 模块加载到 js 中,模块内容是样式字符串。
style-loader: 创建 style 标签,将 js 中的样式资源插入标签内,并将标签添加到 head 中生效。
ts-loader: 打包编译 Typescript 文件。
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  entry: "./src/index.js", // 入口
  output: {                // 出口
    filename: "bundle.js",                       // 指定输出文件的文件名
    path: path.resolve(__dirname, "./dist"),     // 文件的输出路径
    clean: true,                                 // 打包的时候清除上一次打包的遗留垃圾
    assetModuleFilename: "images/[contenthash][ext]",    // 图片打包并命名(方法一),[contenthash]根据文件的内容生成哈希字符串,[ext]文件扩展名
  },
  mode: "development",                            // 开发模式
  devtool: "inline-source-map",                   // 编译工具,定位错误代码
  plugins: [
    // 插件
    new HtmlWebpackPlugin({
      template: "./index.html",                   // 模板
      filename: "app.html",
      inject: "body",
    }),

//   new MiniCssExtractPlugin({
//        filename: "styles/[contenthash].css",
//    }),

  ],

  devServer: {                                    // 浏览器自动更新,不用在手动刷新浏览器
    static: "./dist",                             // devServer 指向的物理路径
  },

  module: {                                       // 配置模块,来加载各种资源
    rules: [                                      // 规则
      {
        test: /\.png$/,                           // 正则表达式,test 定义加载的文件类型
        type: "asset/resource",                   // 资源模块的类型叫 asset module type 4 种
        generator: {                              // 图片打包并命名(方法二),generator 优先级更高
          filename: "images/test.png",
          filename: "images/[contenthash][ext]",
        },
      },
      {
        test: /\.svg$/,
        type: "asset/inline",
      },
      {
        test: /\.txt$/,
        type: "asset/source",
      },
      {
        test: /\.jpg$/,
        type: "asset/source",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024 * 1024,
          },
        },
      },

      {
        test: /\.(css|less)$/,
        use: ["style-loader", "css-loader", "less-loader"],   // css less !!!!11111111111111111111111···
       },

 //  npm install mini-css-extract-plugin -D   抽离和压缩 css
 //    {
 //       test: /\.(css|less)$/,
 //       use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],  // 抽离 css,原来的 css loader那个就没有用了
 //     },

    ],
  },

 // optimization: {
 //   minimizer: [new CssMinimizerPlugin()],
 //  },

};

15、为什么需要 babel-loader?

webpack 自身可以自动加载 JS文件,就像加载 JSON 文件一样,无需任何 loader。
可是,加载的 JS 文件会原样输出,即使你的 JS 文件里包含 ES6+ 的代码,也不会做任何的转化。
这时我们就需要 Babel 来帮忙。
Babel 是一个 JavaScript 编译器,可以将 ES6+转化成 ES5。
在 Webpack 里使用 Babel,需要使用 `babel-loader`。

hello-world.js

function getString() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("hello world!!!!");
    }, 2000);
  });
}

async function helloWrld() {
  let string = await getString();
  console.log(string);
}

// 导出
export default helloWrld;

index.js

// 引入
import helloWrld from "./hello-world";
// img 资源加载进来后,会生成一个url
import imgsrc from "./assets/ma.png";
// svg 资源
import logoSvg from "./assets/webpack-logo.svg";
// txt 资源
import exampleTxt from "./assets/example.txt";
// jpg 资源
import jpgMap from "./assets/qianfeng-sem.jpg";
// css
import "./style.css";
// less
import "./style.less";

helloWrld();

// img
const img = document.createElement("img"); // 创建一个dom节点
img.src = imgsrc; // 路径
document.body.appendChild(img); // 把img资源放到页面上

// svg
const imgsvg = document.createElement("img"); // 创建一个dom节点
imgsvg.style.cssText = "width:600px; height:200px"; // style
imgsvg.src = logoSvg;
document.body.appendChild(imgsvg); // 把img资源放到页面上

// text
const block = document.createElement("div");
block.style.cssText = "width:200px; height:200px; background: aliceblue";
block.classList.add('block-bg');
block.textContent = exampleTxt;
document.body.appendChild(block);

// jpg
const imgjpg = document.createElement("img");
imgjpg.style.cssText = "width:600px; height:240px; display: block";
imgjpg.src = jpgMap;
document.body.appendChild(imgjpg);

// css
document.body.classList.add("hello");

config

// 引用插件
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
  entry: "./src/index.js", // 入口
  output: {
    // 出口
    filename: "bundle.js", // 指定输出文件的文件名
    path: path.resolve(__dirname, "./dist"), // 文件的输出路径
    clean: true, // 打包的时候清除上一次打包的遗留垃圾
    assetModuleFilename: "images/[contenthash][ext]", 
    // 图片打包并命名(方法一),[contenthash]根据文件的内容生成哈希字符串,[ext]文件扩展名
  },

  mode: "development", // 开发模式 development\production
  devtool: "inline-source-map", // 编译工具,定位错误代码
  plugins: [
    // 插件
    new HtmlWebpackPlugin({
      template: "./index.html", // 模板
      filename: "app.html",
      inject: "body",
    }),

    new MiniCssExtractPlugin({
      filename: "styles/[contenthash].css",
    }),
  ],

  devServer: {
    // 浏览器自动更新,不用在手动刷新浏览器
    static: "./dist", // devServer 指向的物理路径
  },

  module: {
    // 配置模块,来加载各种资源
    rules: [
      // 规则
      {
        test: /\.png$/, // 正则表达式,test 定义加载的文件类型
        type: "asset/resource", // 资源模块的类型叫 asset module type 4 种
        generator: {
          // 图片打包并命名(方法二),generator优先级更高
          filename: "images/test.png",
          filename: "images/[contenthash][ext]",
        },
      },

      {
        test: /\.svg$/,
        type: "asset/inline",
      },
      {
        test: /\.txt$/,
        type: "asset/source",
      },
      {
        test: /\.jpg$/,
        type: "asset/source",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024 * 1024,
          },
        },
      },
      {
        test: /\.(css|less)$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
      },
      {
        test: /\.(woff|woff2|ttf|otf)$/,
        type: "asset/resource",
      },
    ],
  },

  optimization: {
    minimizer: [new CssMinimizerPlugin()],
  },
};

16、使用 babel-loader

安装:

npm install babel-loader @babel/core @babel/preset-env -D
  • babel-loader: 在 webpack 里应用 babel 解析 ES6 的桥梁

  • @babel/core: babel 核心模块

  • @babel/preset-env: babel 预设,一组 babel 插件的集合

在 webpack 配置中,需要将 `babel-loader` 添加到 `module` 列表中:

module: { 
 rules: [    {      test: /\.js$/,      exclude: /node_modules/,      
use: {        loader: 'babel-loader',        
options: {          presets: ['@babel/preset-env']        }      }    }
  ]}

执行编译:

 npx webpack

17、说说 webpack 与 grunt、gulp 的不同?

三者都是前端构建工具;grunt 和 gulp 在早期比较流行;
现在 wp 相对来说比较主流,不过一些轻量化的任务还是会用 gulp 来处理,比如单独打包 CSS 文件等。
grunt 和 gulp 是基于任务和流(Task、Stream)的。类似 jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据,整条链式操作构成了一个任务,多个任务就构成了整个 web 的构建流程。
webpack 是基于入口的。webpack 会自动地递归解析入口所需要加载的所有资源文件,然后用不同的 Loader 来处理不同的文件,用 Plugin 来扩展 webpack 功能。

18、什么是 bundle?什么是 chunk?什么是 module?

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

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

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

Webpack Proxy 工作原理

代理

在项目开发中不可避免会遇到跨越问题,Webpack中的Proxy就是解决前端跨域的方法之一。所谓代理,指的是在接收客户端发送的请求后转发给其他服务器的行为,webpack中提供服务器的工具为webpack-dev-server。

webpack-dev-server

webpack-dev-server是 webpack 官方推出的一款开发工具,将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在了一起。同时,为了提高开发者日常的开发效率,只适用在开发阶段。在webpack配置对象属性中配置代理的代码如下:

// ./webpack.config.js
const path = require('path')

module.exports = {
    // ...
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        compress: true,
        port: 9000,
        proxy: {
            '/api': {
                target: 'https://api.github.com'
            }
        }
        // ...
    }
}

其中,devServetr里面proxy则是关于代理的配置,该属性为对象的形式,对象中每一个属性就是一个代理的规则匹配。

属性的名称是需要被代理的请求路径前缀,一般为了辨别都会设置前缀为 /api,值为对应的代理匹配规则,对应如下: - target:表示的是代理到的目标地址。 - pathRewrite:默认情况下,我们的 /api-hy 也会被写入到URL中,如果希望删除,可以使用pathRewrite。 - secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false。 - changeOrigin:它表示是否更新代理后请求的 headers 中host地址。

原理

proxy工作原理实质上是利用http-proxy-middleware 这个http代理中间件,实现请求转发给其他服务器。比如下面的例子:

const express = require('express');
const proxy = require('http-proxy-middleware');

const app = express();

app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
app.listen(3000);

// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar

在上面的例子中,本地地址为http://localhost:3000,该浏览器发送一个前缀带有/api标识的请求到服务端获取数据,但响应这个请求的服务器只是将请求转发到另一台服务器中。

跨域

在开发阶段, webpack-dev-server 会启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 localhost 的一个端口上,而后端服务又是运行在另外一个地址上。所以在开发阶段中,由于浏览器同源策略的原因,当本地访问后端就会出现跨域请求的问题。

解决这种问题时,只需要设置webpack proxy代理即可。当本地发送请求的时候,代理服务器响应该请求,并将请求转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,最终再由代理服务器将数据响应给本地,原理图如下:

v2-9b93cf979ed4b55d9ff592b3ebe2baff_720w.webp

在代理服务器传递数据给本地浏览器的过程中,两者同源,并不存在跨域行为,这时候浏览器就能正常接收数据。

注意:服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制。

你是如何提高 webpack 构件速度的?

多入口情况下,使用CommonsChunkPlugin来提取公共代码通过externals配置来提取常用库利用DllPlugin和DllReferencePlugin预编译资源模块通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。使用Happypack 实现多线程加速编译使用webpack-uglify-paralle来提升uglifyPlugin的压缩速度。原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度

使用Tree-shaking和Scope Hoisting来剔除多余代码

npm 打包时需要注意哪些?如何利用 webpack 来更好的构建?

Npm 是目前最大的 JavaScript 模块仓库,里面有来自全世界开发者上传的可复用模块。你可能只是 JS 模块的使用者,但是有些情况你也会去选择上传自己开发的模块。

NPM 模块需要注意以下问题:

  • 要支持 CommonJS 模块化规范,所以要求打包后的最后结果也遵守该规则。

  • Npm 模块使用者的环境是不确定的,很有可能并不支持 ES6,所以打包的最后结果应该是采用 ES5 编写的。并且如果 ES5 是经过转换的,请最好连同 SourceMap 一同上传。

  • Npm 包大小应该是尽量小(有些仓库会限制包大小)

  • 发布的模块不能将依赖的模块也一同打包,应该让用户选择性的去自行安装。这样可以避免模块应用者再次打包时出现底层模块被重复打包的情况。

  • UI 组件类的模块应该将依赖的其它资源文件,例如 .css 文件也需要包含在发布的模块里。

前端为什么要进行打包和构建?

代码层面:

  • 体积更小(Tree-shaking、压缩、合并),加载更快

  • 编译高级语言和语法(TS、ES6、模块化、scss)

  • 兼容性和错误检查(polyfill,postcss,eslint)

研发流程层面:

  • 统一、高效的开发环境

  • 统一的构建流程和产出标准

  • 集成公司构建规范(提测、上线)

sourceMap

是一个映射关系,将打包后的文件隐射到源代码,用于定位报错位置配置方式:例如:devtool:‘source-map’

加不同前缀意义:

  • inline:不生成映射关系文件,打包进main.js

  • cheap: 1.只精确到行,不精确到列,打包速度快 2.只管业务代码,不管第三方模块

  • module:不仅管业务代码,而且管第三方代码

  • eval:执行效率最快,性能最好

最佳实践:

  • 开发环境:cheap-module-eval-source-map

  • 线上环境:cheap-mudole-source-map

Webpack 的基本功能有哪些?

  • 1. 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等

  • 2. 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等

  • 3. 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载

  • 4.模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件

  • 5. 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器

  • 6. 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过

  • 7. 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。