【云+社区年度征文】webpack 学习笔记系列01-基础命令与常见配置

Write By CS逍遥剑仙 我的主页: www.csxiaoyao.com GitHub: github.com/csxiaoyaojianxian Email: sunjianfeng@csxiaoyao.com

1. webpack 命令

webpack 命令可以在 package.json 中的 script 字段中添加命令,再使用 npm 执行:

"scripts": { 
    "dev": "webpack --mode development ./src/es/index.js --module-bind js=babel-loader", 
    "build": "webpack --mode production ./src/es/index.js --module-bind js=babel-loader" 
}

webpack-cli 命令的选项比较多,详细可以通过 webpack-cli 的文档进行查阅,常用的有:

--config:指定 webpack 配置文件的路径

--mode:指定打包环境 development / production

--json:输出打包结果,可用 webpack --json > stats.json 将打包结果输出到指定文件

--watch, -w:watch 模式打包,监控文件变化

--hot:开启 Hot Module Replacement 模式

--progress:显示打包进度

--color / --no-color:开启/关闭控制台输出内容的颜色

--profile:详细输出每个环节的用时,方便排查打包速度瓶颈

2. webpack 配置基础

2.1 常见概念

webpack 是一个模块打包工具,能够从一个 JavaScript 文件开始,构建一个依赖关系图(dependency graph)映射项目中每个模块,然后将这个依赖关系图输出到一个或者多个 bundle 中。webpack.config.js 配置中的一些概念:

module:每个文件都可以看做模块,模块不局限于 js,也包含 css、图片等

chunk:代码块,一个 chunk 可以由多个模块 module 组成

bundle:最终打包生成的文件,一般和 chunk 对应,是对 chunk 进行压缩打包等处理后的产出

loader:模块 module 源代码的处理器,对模块进行转换处理

plugin:扩展插件可以处理 chunk 或 bundle,可以完成 loader 不能完成的任务

2.2 占位符

2.2.1 常见占位符

webpack 支持占位符,可灵活用于后面介绍的配置中,常见的有:

hash:模块 module 标识符的 hash

chunkhash:代码块 chunk 内容的 hash

name:模块名称

id:模块标识符

query:模块的 query,如文件名 ? 后的字符串

function:一个能 return 出一个 string 作为 filename 的函数

2.2.2 三种 hash 对比

关于 hash 和 chunkhash,二者都可以指定长度,如 hash:16(默认20),区别在于:

hash 在整个项目唯一,每次修改任何文件编译都会生成新的 hash,因此无法实现前端静态资源在浏览器上的长缓存;

chunkhash 根据不同的入口文件 entry 进行依赖文件解析,构建对应的 chunk 生成相应的 hash,对于变动较少的公共库代码,使用 chunkhash 可以发挥最长缓存的作用;

contenthash 使用 chunkhash 存在一个问题,当一个 JS 文件中引入了 CSS 文件,编译后它们的 hash 是相同的,只要 JS 文件内容发生改变,与其关联的 CSS 文件 hash 也会改变,针对这种情况,可以把 CSS 从 JS 中使用 mini-css-extract-plugin 或 extract-text-webpack-plugin 插件抽离出来并使用 contenthash。

3. webpack 常见配置项

3.1 entry 入口配置

entry 入口支持多种类型:字符串对象数组

3.1.1 单文件入口

module.exports = {
    // context 上下文在实际开发中一般不需要配置,默认为 process.cwd() 工作目录,必须是一个绝对路径,代表项目的相对路径上下文
    context: '/Users/test/webpack',

    // 字符串,直接把该 string 指定的模块(文件)作为入口模块
    entry: 'path/to/my/entry/file.js',
    // 对象
    entry: {
        main: 'path/to/my/entry/file.js'
    },
    // 数组,实际只有一个入口,会自动生成另外一个入口模块并加载数组指定的模块
    entry: ['./src/app.js', './src/home.js'],
};

3.1.1 多文件入口

相对于单文件入口,具有较高的灵活性,例如多页应用、页面模块分离优化等。

module.exports = {
    entry: {
        home: 'path/to/my/entry/home.js',
        search: 'path/to/my/entry/search.js',
        list: 'path/to/my/entry/list.js'
    }
};

3.2 output 出口配置

3.2.1 核心属性

output 出口指定了 entry 对应文件编译打包后的输出 bundle。一个 webpack 的配置,可以包含多个 entry,但是只能有一个 output,但可以通过 name 占位符语法来区分:

module.exports = {
    entry: {
        home: 'path/to/my/entry/home.js',
        search: 'path/to/my/entry/search.js',
        list: 'path/to/my/entry/list.js'
    },
    output: {
        // 输出 bundle 的名称
        filename: '[name].js',
        // 输出 bundle 的存放路径
        path: __dirname + '/dist',
        // 指定在浏览器中引用地址,如静态资源CDN等
        publicPath: '/assets/'
    }
};

当不指定 output 时,默认输出到 dist/main.js ,即 output.path 是 dist , output.filename 是 main 。

3.2.2 output.library 输出为库

可以使用 output.library 生成库供第三方使用。

module.exports = {
    output: {
        library: 'myLib' // 也可使用占位符,如 '[name]'
    }
};

3.2.3 output.libraryTarget 输出规范对比

使用 output.libraryTarget 可指定库打包出来的规范,可选值有:varassignthiswindowglobalcommonjscommonjs2commonjs-moduleamdumdumd2jsonp ,默认是 var,libraryTarget = global 时,如果 target = node 才是 global,默认 target = web 下 global 为

window,保险起见可以使用 this 。

module.exports = {
    output: {
        library: 'myLib' // 也可使用占位符,如 '[name]',
        filename: 'var.js',
        libraryTarget: 'var'
    }
};

下面是各种规范打包后的代码:

// var
var myLib = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// assign,相比var规范,缺少一个var
myLib = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// this
this["myLib"] = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// window,target=web
window["myLib"] = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// global,target=node
global["myLib"] = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// commonjs
exports["myLib"] = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// commonjs2 / commonjs-module
module.exports = (function(modules) {})({
    './src/index.js': function(module, exports) {}
});

// amd
define('myLib', [], function() {
    return (function(modules) {})({
        './src/index.js': function(module, exports) {}
    });
});

// umd
(function webpackUniversalModuleDefinition(root, factory) {
    if (typeof exports === 'object' && typeof module === 'object') module.exports = factory();
    else if (typeof define === 'function' && define.amd) define([], factory);
    else if (typeof exports === 'object') exports['myLib'] = factory();
    else root['myLib'] = factory();
})(window, function() {
    return (function(modules) {})({
        './src/index.js': function(module, exports) {}
    });
});

// umd2
(function webpackUniversalModuleDefinition(root, factory) {
    if (typeof exports === 'object' && typeof module === 'object') module.exports = factory();
    else if (typeof define === 'function' && define.amd) define([], factory);
    else if (typeof exports === 'object') exports['myLib'] = factory();
    else root['myLib'] = factory();
})(window, function() {
    return (function(modules) {})({
        './src/index.js': function(module, exports) { }
    });
});

// jsonp
myLib((function(modules) {})({
    './src/index.js': function(module, exports) {}
}));

3.3 externals 外部模块配置

externals 配置项用于去除输出的打包文件中依赖的某些第三方 js 模块(例如 jquery,vue 等),由使用者主动引入,例如开发 jquery 插件等,引入方式如下:

js-lib 导出方式(libraryTarget)

使用者引入方式

被依赖模块的提供方式

var (默认,包含非js库的普通方式)

<script> 标签形式引入

以全局变量形式引入

commonjs

按 commonjs 规范引入

按 commonjs 规范引入

amd

按 amd 规范引入

按 amd 规范引入

umd

<script> 、commonjs、amd 引入

按对应方式引入

3.4 target 构建目标配置

module.exports = {
  	// 构建目标,可以传入字符串,默认是 web,可以省略
    target: 'web',
    // 也可以传入 function,接收 compiler 作为参数
    target: compiler => {
        compiler.apply(new webpack.JsonpTemplatePlugin(options.output), new webpack.LoaderTargetPlugin('web'));
    }
};

构建目标 target 支持以下类型:

node:编译为类 Node.js 环境可用(使用 node.js require 加载 chunk)

async-node:编译为类 Node.js 环境可用(使用 fs 和 vm 异步加载分块)

electron-main:编译为 Electron 主进程

electron-renderer:编译为 Electron 渲染进程

node-webkit:编译为 Webkit 可用(使用 jsonp 加载分块)

webworker:编译成一个 WebWorker

3.5 devtool 配置 sourcemap

devtool 参数用来控制如何显示 sourcemap:

devtool

构建速度

重新构建速度

生产环境支持

品质(quality)

留空,none

+++

+++

yes

打包后的代码

eval

+++

+++

no

生成后的代码

cheap-eval-source-map

++

no

转换过的代码(仅限行)

cheap-module-eval-source-map

o

++

no

原始源代码(仅限行)

eval-source-map

––

no

原始源代码

cheap-source-map

o

no

转换过的代码(仅限行)

cheap-module-source-map

o

no

原始源代码(仅限行)

inline-cheap-source-map

o

no

转换过的代码(仅限行)

inline-cheap-module-source-map

o

no

原始源代码(仅限行)

source-map

––

––

yes

原始源代码

inline-source-map

––

––

no

原始源代码

hidden-source-map

––

––

yes

原始源代码

nosources-source-map

––

––

yes

无源代码内容

+++ 非常快速 ++ 快速 + 比较快 o 中等 - 比较慢 -- 慢 推荐生产环境不使用或者使用 source-map,开发环境使用 cheap-module-eval-source-map

3.6 resolve 配置依赖查询解析规则

配置 resolve 参数可以帮助 webpack 快速查找依赖。

3.6.1 resolve.extensions 扩展名解析

配置后载入模块可以省略对应等扩展名

module.exports = {
    resolve: {
        extensions: ['.js', '.json', '.css']
    }
};

3.6.2 resolve.alias 路径解析

module.exports = {
    resolve: {
        alias: {
            src: path.resolve(__dirname, 'src'),
            // 使用特殊字符 @ ! ~,便于区分
            '@lib': path.resolve(__dirname, 'src/lib'),
            // 根据环境加载不同的库
          	aLib: process.env.NODE_ENV === 'production' ? 'aLib/dist/aLib.min.js' : 'aLib/dist/aLib.dev.js',
            // 支持在名称末尾添加 $ 实现精准匹配
            // 如能匹配 import vue from 'vue';
            // 只触发普通解析 import file from 'vue/file.js';
            vue$: '/path/to/vue.min.js'
        }
    }
};

设置了 alias 就可以在任意文件中使用短路径来定位模块,如 require('@lib/utils')require('src/lib/utils')

3.6.3 其他配置

resolve.mainFields:设置使用的模块代码版本,如 ['browser', 'module', 'main']

resolve.mainFiles:解析目录时的默认文件名,默认 index,即查找目录下的 index + resolve.extensions 文件

resolve.modules:模块依赖名,默认是 node_modules

resolve.plugins:添加解析插件,数组格式

resolve.cachePredicate:是否缓存,支持 boolean 和 function(path:string,require:object):boolean。

3.7 module 配置模块解析规则

3.7.1 module.noParse 忽略非模块化文件

忽略对部分没采用模块化的文件的递归解析和处理,能提高构建性能。需要确定被排除出去的模块代码中不能包含 import 、require 、define 等内容,以保证webpack的打包包含了所有的模块,否则打包后的代码会因为缺少模块报错。

module.exports = {
    module: {
        // 使用正则表达式
        noParse: /jquery|lodash/,
        // 使用函数,从 Webpack 3.0.0 开始支持
        noParse: (content) => {
            // content 代表一个模块的文件路径
            return /jquery|lodash/.test(content); // 返回 true or false
        }
    }
}

3.7.2 module.rules.parser 控制模块化语法解析

noParse 只能控制哪些文件不进行解析,而 parser 属性可以更细粒度地从语法层面配置模块的解析。

module: {
    rules: [{
        test: /\.js$/,
        use: ['babel-loader'],
        parser: {
            amd: false, // 禁用 AMD
            commonjs: false, // 禁用 CommonJS
            system: false, // 禁用 SystemJS
            harmony: false, // 禁用 ES6 import/export
            requireInclude: false, // 禁用 require.include
            requireEnsure: false, // 禁用 require.ensure
            requireContext: false, // 禁用 require.context
            browserify: false, // 禁用 browserify
            requireJs: false, // 禁用 requirejs
        }
    }]
}

3.7.3 module.rules 模块解析规则配置

webpack 处理模块时将符合规则条件的模块,提交给对应的处理器来处理。

3.7.3.1 条件匹配

通过 test、include、exclude 等配置来命中可以应用规则的模块文件。如下述 rule 规则匹配来自 src 和 test 文件夹,不包含 node_modules 和 bower_modules 子目录,模块的文件路径为 .tsx 和 .jsx 结尾的文件。

{
    test: [/\.jsx?$/, /\.tsx?$/],
    include: [
        path.resolve(__dirname, 'src'),
        path.resolve(__dirname, 'test')
    ],
    exclude: [
        path.resolve(__dirname, 'node_modules'),
        path.resolve(__dirname, 'bower_modules')
    ]
}
3.7.3.2 loader 配置

在使用对应的 loader 之前需要先安装。如在 JavaScript 中引入 less,则需要安装 less-loader:

$ npm i -D less-loader

然后配置 loader,这样 less 文件都会被 less-loader 处理成对应的 css 文件。

module.exports = {
    module:{
        rules:[
            test: /\.less$/,
            // string 类型,结果会被作为 require() 的参数直接使用
            use: 'less-loader'
        ]
    }
}

还可以直接在 js 文件中使用 loader 内联配置方式:

const html = require('html-loader!./loader.html');
// or
import html from 'html-loader!./loader.html';

console.log(html);

效果等同于:

const html = require('./loader.html');

console.log(html);

加上 webpack 配置文件:

module.exports = {
    module: {
        rules: [{
            test: /\.html$/,
            use: ['html-loader']
        }]
    }
};

注意:如果没有 html-loader,直接 require 一个 html 文件,会被当作 js 模块来执行,会报错。

3.7.3.3 loader 参数

loader 传参支持 options 和 query 两种方式:

// inline 内联写法,通过 query 传入
const html = require("html-loader?attrs[]=img:src&attrs[]=img:data-src!./file.html");

// config 内写法,通过 query 传入
module: {
    rules: [{
        test: /\.html$/,
        use: [{
            loader: 'html-loader?minimize=true&removeComments=false&collapseWhitespace=false'
        }]
    }]
}

// config 内写法,通过 options 传入
module: {
    rules: [{
        test: /\.html$/,
        use: [{
            loader: 'html-loader',
            options: {
                minimize: true,
                removeComments: false,
                collapseWhitespace: false
            }
        }]
    }]
}
3.7.3.4 loader 解析顺序

简单配置一个 loader 往往不能满足一些模块的需求,如 less 模块文件,除了将 less 语法转换成 CSS 语法,还需要添加 css-loader 等处理为 js 能直接使用的模块,webpack 的 loader 解析顺序是从右到左(从后到前)的:

// query 写法从右到左,使用!隔开
const styles = require('css-loader!less-loader!./src/index.less');

// 数组写法,从后到前
module.exports = {
    module: {
        rules: [{
            test: /\.less$/,
            use: [{
                loader: 'style-loader'
            },{
                loader: 'css-loader'
            },{
                loader: 'less-loader'
            }]
        }]
    }
};

enforce 参数可以调节 loader 的执行顺序,post 表示该 loader 最后执行,而 pre 表示该 loader 最先执行。

use: [{
    loader: 'babel-loader',
    enforce: 'post'
}];

oneOf 参数可以设置只应用第一个匹配的规则,一般结合 resourceQuery。

module.exports = {
    //...
    module: {
        rules: [{
            test: /\.css$/,
            oneOf: [{
                resourceQuery: /inline/, // foo.css?inline
                use: 'url-loader'
            },{
                resourceQuery: /external/, // foo.css?external
                use: 'file-loader'
            }]
        }]
    }
};

3.8 plugin 插件

loader 面向的是解决某个或者某类模块的问题,而 plugin 面向的是项目整体,解决的是 loader 解决不了的问题。webpack 本身内置了很多插件,可以直接通过 webpack 对象的属性来直接使用:

module.exports = {
    plugins: [
        // 压缩js
        new webpack.optimize.UglifyJsPlugin();
    ]
}

除了内置插件,还可以通过安装 NPM 包的方式来使用插件:

const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
    plugins: [
        // 导出css内容到单独的文件
        new ExtractTextPlugin({
             filename: 'style.css'
        })
    ]
};

4. 总结

本文是对系统化学习 webpack 到工程化优化实践过程中的一些细节的总结记录。webpack 早已成为前端开发不可或缺的脚手架工具,因此系统化学习 webpack 是前端er成长路上的必修课。

5. 附录

  1. webpack-cli 常用命令官方文档:https://webpack.js.org/api/cli/

sign.jpg

原文地址:https://cloud.tencent.com/developer/article/1763255

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340