webpack

164 阅读5分钟

简介

Webpack 是⼀个现代 JavaScript 应⽤程序的静态模块打包器(module bundler),当 webpack 处理应⽤程序时,它会递归地构建⼀个依赖关系图(dependency graph),其中包含应⽤程序需要的每个模块,然后将所有这些模块打包成⼀个或多个 bundle。

(Webpack是⼀个打包模块化JavaScript的⼯具,它会从⼊⼝模块出发,识别出源码中的模块化导⼊语句,递归地找出⼊⼝⽂件的所有依赖,将⼊⼝和其所有的依赖打包到⼀个单独的⽂件中)

是⼯程化、⾃动化思想在前端开发中的体现。

安装

  1. 环境准备 nodejs 版本参考官网发布的最新版本,可以提升webpack的打包速度

  2. 安装方式 局部安装(推荐)

npm init -y # 初始化npm配置⽂件 
npm install --save-dev webpack # 安装核⼼库 
npm install --save-dev webpack-cli # 安装命令⾏⼯具 

# 安装最新的4.x稳定版本 
npm i -D webpack@4.44.0 
# 安装指定版本 npm i -D webpack@<version>

npm i webpack@4.43.0 webpack-cli@3.3.12 -D
// 检查版本
npx webpack -V

全部安装(不推荐)

# 安装webpack V4+版本时,需要额外安装webpack-cli 
npm install webpack webpack-cli -g 

# 检查版本 webpack -v

# 卸载 
npm uninstall webpack webpack-cli -g

全局安装webpack,这会将你项⽬中的webpack锁定到指定版本,造成不同的项⽬中因为webpack依赖不同版本⽽导致冲突,构建失败

启动webpack

webpack默认配置
  • webpack默认⽀持JS模块和JSON模块
  • ⽀持CommonJS Es moudule AMD等模块类型
  • webpack4⽀持零配置使⽤,但是很弱,稍微复杂些的场景都需要额外扩展
准备执行构建
  • 新建src文件夹
  • 新建 src/index.js、src/index.json、src/other.js
### index.js 
const json = require("./index.json");//commonJS 
import { add } from "./other.js";//es module 
console.log(json, add(2, 3)); 

### index.json 
{ "name": "JOSN" } 

### other.js 
export function add(n1, n2) { 
    return n1 + n2; 
}
执行构建
# npx⽅式 
npx webpack 

# npm script 
npm run test

修改package.json文件:

"scripts": { 
    "test": "webpack" 
 },

原理就是通过shell脚本在node_modules/.bin⽬录下创建⼀个软链接。

构建成功

我们会发现⽬录下多出⼀个 dist ⽬录,⾥⾯有个 main.js ,这个⽂件是⼀个可执⾏的JavaScript⽂件, ⾥⾯包含webpackBootstrap启动函数。

默认配置
const path = require('path');
moudle.exports = {
    // 必填 webpack执行构建入口
    entry: './src/index.js',
    output: {
        // 将所有依赖的模块合并并输出到main.js
        filename: 'main.js',
        // 输出文件存放路径,必须是绝对路径
        path: path.resolve(__dirname,'./dist')
    }
};

webpack配置核心概念

  • chunk: 指代码块,一个chunk可能由多个模块组合而成,也用于代码合并与分割。
  • bundle: 资源经过 Webpack 流程解析编译后最终输出的成果文件。
  • entry: 顾名思义,就是入口起点。⽤来告诉 webpack ⽤哪个⽂件作为构建依赖图的起点。 webpack会根据entry递归的去寻找依赖,每个依赖都将被它处理,最后输出到打包成果中。
  • output: output配置描述了webpack打包的输出配置,包含输出文件的命名、位置等信息。
  • loader: 默认情况下,webpack仅⽀持 .js .json ⽂件,通过loader,可以让它解析其他类型的⽂件,充当翻译官的⻆⾊。理论上只要有相应的loader,就可以处理任何类型的⽂件。
  • plugin: loader主要的职责是让webpack认识更多的文件类型,而plugin的职责则是让其可以控制构建流程,从而执行一些特殊的任务。插件的功能⾮常强⼤,可以完成各种各样的任务。
  • mode:4.0开始,webpack⽀持零配置,旨在为开发⼈员减少上⼿难度,同时加⼊了mode的概念,⽤于指定打包的⽬标环境,以便在打包的过程中启⽤webpack针对不同的环境下内置的优化。

loader: file-loader:处理静态资源模块

loader: file-loader

原理是把打包⼊⼝中识别出的资源模块,移动到输出⽬录,并且返回⼀个地址名称

场景:就是当我们需要模块,仅仅是从源代码挪移到打包⽬录,就可以使⽤file-loader来处理, txt,svg,csv,excel,图⽚资源啦等等

npm install file-loader -D

案例:

module: {
    rules: [
        {
            test: /\.(png|jpe?g|gif)$/,
            //use使⽤⼀个loader可以⽤对象,字符串,两个loader需要⽤数组
            use: {
                loader: "file-loader",
                // options额外的配置,⽐如资源名称
                options: {
                // placeholder 占位符 [name]⽼资源模块的名称
                // [ext]⽼资源模块的后缀
                // https://webpack.js.org/loaders/file-loader#placeholders
                    name: "[name]_[hash].[ext]",
                    //打包后的存放位置
                    outputPath: "images/"
                }
            }
        }
    ]
},
import pic from './logo.png';

var img = new Image();
img.src = pic; 
img.classList.add("logo");

var root = document.getElementById("root"); 
root.append(img);

css

@font-face {
    font-family: "webfont";
    font-display: swap;
    src: url("webfont.woff2") format("woff2");
}

body {
    background: blus;
    font-family: "webfont" !important;
}

// webpack.config.js
{
    test: /\.(eot|ttf|woff|woff2|svg)$/,
    use: "file-loader"
}
url-loader file-loader加强版本

url-loader内部使⽤了file-loader,所以可以处理file-loader所有的事情,但是遇到jpg格式的模块, 会把该图⽚转换成base64格式字符串,并打包到js⾥。对⼩体积的图⽚⽐较合适,⼤图⽚不合 适。

npm install url-loader -D
案例
module: {
    rules: [
        {
            test: /\.(png|jpe?g|gif)$/,
            use: {
                loader: "url-loader",
                options: {
                    name: "[name]_[hash].[ext]",
                    outputPath: "images/",
                    //⼩于2048,才转换成base64
                    limit: 2048
                }
            }
        }
    ]
}
样式处理:
  • Css-loader 分析css模块之间的关系,并合成⼀个css
  • Style-loader 会把css-loader⽣成的内容,以style挂载到⻚⾯的heade部分
npm install style-loader css-loader -D
{
    test: /\.css$/,
    use: ["style-loader", "css-loader"]
}

{
    test:/\.css$/,
    use: [
        {
            loader:"style-loader",
            options: {
                injectType: "singletonStyleTag" // 将所有的style标签合并成⼀个
            }
        },
        "css-loader"
    ]
}

Less 样式处理

less-load 把less语法转换成css

$ npm install less less-loader --save-dev
案例:

loader有顺序,从右到左,从下到上

{
    test: /\.scss$/,
    use: ["style-loader", "css-loader", "less-loader"]
}
Postcss-loader
npm i postcss-loader autoprefixer -D
新建postcss.config.js
// webpack.config.js
{
    test: /\.css$/,
    use: ["style-loader", "css-loader", "postcss-loader"]
},

// postcss.config.js
module.exports = {
    plugins: [
        require("autoprefixer")({
            overrideBrowserslist: ["last 2 versions", ">1%"]
        })
    ]
};
  • loader 处理webpack不⽀持的格式⽂件,模块
  • ⼀个loader只处理⼀件事情
  • loader有执⾏顺序

如何⾃⼰编写⼀个Loader

⾃⼰编写⼀个Loader的过程是⽐较简单的, Loader就是⼀个函数,声明式函数,不能⽤箭头函数 拿到源代码,作进⼀步的修饰处理,再返回处理后的源码就可以了

案例
  • 创建⼀个替换源码中字符串的loader
// index.js
console.log("hello kkb");

// replaceLoader.js
module.exports = function(source){
    console.log(source, this, this.query);
    return source.replace('kkb','开课吧')
}

// 需要⽤声明式函数,因为要上到上下⽂的this,⽤到this的数据,该函数接受⼀个参数,是源码
  • 在配置⽂件中使⽤loade
// 需要使⽤node核⼼模块path来处理路径
const path = require('path')
moodule: {
    rules: [
        {
            test: /\.js$/,
            use: path.resolve(__dirname, "./loader/replaceLoader.js")
        }
    ]
}
  • 如何给loader配置参数,loader如何接受参数?
    • this.query
    • loader-utils
// webpack.config.js
module: {
    rules: [
        {
            test: /\.js$/,
            use: [
                {
                    loader: path.resolve(__dirname, "./loader/replaceLoader.js"),
                    options: {
                        name: "开课吧"
                    }
                }
            ]
        }
    ]
},

// replaceLoader.js
// const loaderUtils = require("loader-utils");//官⽅推荐处理loader,query的⼯具

module.exports = function(source){
    // this.query 通过this.query来接受配置⽂件传递进来的参数
    // return source.replace("kkb", this.query.name);
    const options = loaderUtils.getOptions(this);
    const result = source.replace("kkb", options.name);
    return source.replace("kkb", options.name);
}
this.callback :如何返回多个信息,不⽌是处理好的源码呢,可以使⽤this.callback来处理
// replaceLoader.js
const loaderUtils = require("loader-utils");//官⽅推荐处理loader,query的⼯具

module.exports = function(source){
    const options = loaderUtils.getOptions(this);
    const result = source.replace("kkb", options.name);
    this.callback(null, result);
};