预热面试季-webpack基础篇

544 阅读6分钟

阅前须知

  • 适用人群:初级前端,不了解webpack人群
  • 本文目标:
    • 学习了解常用webpack配置的核心概念
    • 熟悉webpack常用的loader和plugin
    • 学会不依赖脚手架运行简易项目并打包

webpack简介

中文文档:webpack官方网站

概念:Webpack是⼀一个打包模块化JavaScript的⼯工具,它会从入口模块出发,识别出源码中的模块化导⼊入语句句,递归地找出⼊入文件的所有依赖,将⼊口和其所有的依赖打包到⼀一个单独的⽂文件中。

初试牛刀

安装webpack

npm init

全局安装(不推荐)

如果全局安装webpack的话,就会将你项目中的webpack版本锁定到你全局安装的版本,造成不同的项目中因为webpack版本依赖不同而导致冲突,从而构建失败

npm install webpack webpack-cli -g
// 检查版本
webpack -v
// 卸载
npm uninstall webpack webpack-cli -g

项目安装

npm install webpack webpack-cli -D
// 检查版本
webpack -v
// 卸载
npm uninstall webpack webpack-cli -D

尝试构建

  • webpack4+版本是支持零配置使用的,但是功能很弱,因此项目中是需要额外的扩展
  • webpack支持commonJS、ESmodule等模块类型

构建准备

  • 新建src文件夹
  • 创建index.js、a.js、b.json 3个文件
  • 用来校验commonJS、ESmodule两种规范
//a.js
export function add(a,b){
	return a+b
}
// b.json
{
	"name": "aaaa"
}
// index.js
// 这里分别用commonJS、ESmodule引用两个文件
const json = require("./index.json");//commonJS
import { add } from "./other.js";//es module

console.log(json);
console.log(add(2, 3));

构建文件

  • 修改package.json里的运行脚本
  • 然后运行脚本
// package.json
"scripts": {
"test": "webpack"
}
// 控制台
npm run test

可以发现在当前目录下会多出一个dist目录,里面有main.js,这个文件就是webpack打包出来的可执行文件

自定义webpack配置

因为零配置是很弱的,特定的需求,总是需要自己进行配置。 在webpack有一个默认的配置文件,我们可以新建webpack.config.js,对这个文件进行修改,个性化配置。

因为webpack是基于nodeJs的,因此对webpack.config.js进行导出的配置需要使用CommonJS规范

webpack.config.js配置基础结构

module.exports = {
  entry: "./src/index.js", //打包⼊入口文件
  output: "./dist", //输出结构
  mode: "production", //打包环境
  module: {
    rules: [
    //loader模块处理
    ]
  },
  plugins: [] //插件配置
};

entry

指定webpack打包入口文件:如Vue的main.js

//单⼊入⼝ SPA,本质是个字符串
entry: "./src/index.js"
// 多入口 这是entry是个对象
entry:{
  index:"./src/index.js",
  login:"./src/login.js"
}

output

打包转换后的文件输出到的位置以及文件名称

output: {
  filename: "index.js",// 输出⽂件的名称
  path: path.resolve(__dirname, "dist")// 输出文件到磁盘的目录,必须是绝对路径
},
//多⼊入⼝口的处理理
output: {
	filename: "[name][chunkhash:8].js",// 利用占位符,⽂件名称不要重复
	path: path.resolve(__dirname, "dist")// 输出⽂件到磁盘的目录,必须是绝对路径
},

mode

Mode⽤用来指定当前的构建环境,有3种环境:

  • production
  • development
  • none 这是文档的解释

因此开发阶段的开启会有利于热更新的处理,识别哪个模块变化;而生产阶段的开启会有帮助模块压缩,处理副作用等⼀些功能

loader

模块解析,模块转换器,用于把模块原内容按照需求转换成新内容,可以理解为翻译官的意思。webpack是模块打包工具,⽽模块不仅仅是js,还可以是css,图片或者其他格式,但是webpack默认只支持处理js和json模块,那么其他格式的模块就需要loader处理了。

常用的loader

  • style-loader
  • css-loader
  • less-loader
  • sass-loader
  • ts-loader
  • babel-loader:讲es6等js新特性语法转成es5
  • file-loader:处理文件、图片等静态资源
  • eslint-loader

有三种使用loader的方法

  • 在 webpack.config.js 文件中指定 loader(推荐)
  • 在每个 import 语句中显式指定 loader
  • 在 shell 命令中指定它们

module.rules 允许你在 webpack 配置中指定多个 loader。 这是展示 loader 的一种简明方式,并且有助于使代码变得简洁

案例

// 先安装
npm install url-loader -D

// 在webpack.config中使用
module: {
  rules: [
    {
    test: /\.(png|jpe?g|gif)$/,
    // user使用一个loader时可以用对象或者字符串,两个loader需要用数组
      use: {
      loader: "url-loader",
      	// 需要额外的配置项
        options: {
        name: "[name]_[hash:8].[ext]", // 文件名字
        outputPath: "images/", // 打包后放的文件位置
        //小于2048,才转换成base64
        limit: 2048
        }
      }
    }
  ]
},

其他的loader使用大致相同 ,可参照官网。

plugin

plugin可以在webpack运行到某个阶段的时候,帮你做⼀些事情,类似于生命周期的概念。扩展插件,在 Webpack 构建流程中的特定时机注⼊入扩展逻辑来改变构建结果或做你想要的事情,可以解决 loader 无法实现的其他事。

  • HtmlWebpackPlugin htmlwebpackplugin会在打包结束后,自动生成⼀个html⽂件,并把打包生成的js模块引入到该html中
// 安装
npm install --save-dev html-webpack-plugin

// 使用
const path = require("path");
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
...
  plugins: [
    new htmlWebpackPlugin({
    title: "My App",
    filename: "app.html",
    template: "./src/index.html"
    })
  ]
};

WebpackDevServer

每次改完代码都需要重新打包一次,然后手动刷新浏览器才是最新的代码,这样很影响开发效率。我们可以安装WebpackDevServer来帮助我们。

// 安装
npm install webpack-dev-server -D

// webpack.config.js
module.exports = {
    ...
    devServer: {
      contentBase: "./dist", // 告诉服务器内容的来源。仅在需要提供静态文件时才进行配置,默认情况下,它将使用当前的工作目录来提供内容
      port: 8081,
      open: true
  }
}

// package.json
"scripts": {
   "server": "webpack-dev-server"
}

然后就可以执行npm run server命令,启动服务后,会发现dist目录没有了,这是因为devServer把打包后的模块不会放在dist目录下,⽽而是放到内存中,从⽽提升速度。

  • devServer代理解决跨域问题

联调期间,前后端分离,直接获取数据会跨域,上线后我们使用nginx转发,开发期间,webpack就可以搞定这件事。

module.exports = {
    ...
    devServer: {
      contentBase: "./dist", // 告诉服务器内容的来源。仅在需要提供静态文件时才进行配置,默认情况下,它将使用当前的工作目录来提供内容
      port: 8081,
      open: true,
      proxy: {
      "/api": {
        target: "http://192.168.12:9092" // 接口的域名和端口,axios请求接口时,用/api代替接口的域名前缀
      }
    }
  }
}

// axios请求接口
axios.get("/api/info").then(res => {
   console.log(res);
});

Hot Module Replacement (HMR:热更新)

启动HMR

// 先引入webpack
const webpack = require("webpack");

module.exports = {
    ...
    devServer: {
      contentBase: "./dist",
      open: true,
      hot: true,
      hotOnly: true //即便HMR不⽣效,浏览器也不自动刷新,就开启hotOnly
  },
  // 添加热更新插件
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
}

这时当你改变某个css样式的时候,保存文件;浏览器就会自动更新,因为借助于 style-loader,使用模块热替换来加载 CSS 实际上极其简单。此 loader 在幕后使用了 module.hot.accept。但是要注意的是:js模块需要特殊处理:js模块 HRM 需要手动监听需要HRM的模块,当该模块的内容发生改变,会触发回调

需要使用module.hot.accept来观察模块更新

// add.js 点击一下按钮 就加10
function add() {
  var div = document.createElement("div");
  div.setAttribute("id", "add");
  div.innerHTML = 1;
  div.onclick = function() {
    div.innerHTML = parseInt(div.innerHTML, 10) + 1;
  };
  document.body.appendChild(div);
}
export default add;

// show.js 用来校验改变改变改文件,是否是局部更新,只更新show.js
function show() {
  var div = document.createElement("div");
  div.setAttribute("id", "show");
  div.innerHTML = 10000;
  document.body.appendChild(div);
}
export default show;

// index.js
import add from "./add";
import show from "./show";
add();
show();
if (module.hot) {
  module.hot.accept("./show.js", function() {
  console.log(show模块更新了)
  document.body.removeChild(document.getElementById("show"));
  show();
});
}

其他框架的热更新 webpack官网