WebPack学习汇总

Webpack用于前端工程化代码的底层构建,angular、react、vue三大框架都采用了webpack来做构建。webpack具备的功能:tree shaking、懒加载、代码分割等等。其核心定义是一个模块打包工具;
能够识别所有模块引入方式,例如:

  • es6 module import引入;
  • commonJs 中module.exports=Header;
  • AMD;
  • CMD;

Webpack 4的构建速度更快,在构建大型的项目时,甚至可以提升90%的构建速度。Nodejs和Webpack版本尽可能用最新稳定(LTS)版本,也可以提高打包速度;

会涉及的知识点如下图:


image.png

安装

新建一个,webpack01文件夹,并cd webpack01进入文件夹,安装好需要的包webpack和webpack-cli,并指定版本安装(新版本尚未研究差异,暂且用老版本),其中webpack-cli用于实现命令行功能。
当没有使用-g进行全局安装时,执行webpack -v会提示命令不存在;需要使用npx webpack -v来执行,npx会进入到项目中的node_module目录,寻找到webpack包来执行;

//不推荐全局安装-g
npm i webpack@4.43.0 webpack-cli@3.3.12 --save-dev
npm i webpack@4.43.0 webpack-cli@3.3.12-D

设置淘宝镜像

在根目录下创建.npmrc配置文件:

registry=https://registry.npm.taobao.rog/

其他方法:
1、npm config set registry https://registry.npm.taobao.org
2、npm config set registry https://registry.npm.taobao.org/

重置webpack打包命令**

修改package.json文件的script属性值,执行npm run dev命令时,等同于npx webpack

  "scripts": {
    "dev":"npx webpack",
    ...
  },

看一个入门小例子

  • 目录结构
    image.png
  • 代码
    index.html的代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>首页</title>
</head>

<body>
    <div id="root"></div>
    <script src="./index.js"></script>
</body>

</html>

index.js的代码

import Header from "./header";
import Content from "./content";
import Footer from "./footer";

new Header();
new Content();
new Footer();

header.js的代码

function Header() {
    var dom = document.getElementById("root");

    var header = document.createElement("div");
    header.innerText = "header";
    dom.append(header);
}
export default Header;

content.js的代码

function Content() {
    var dom = document.getElementById("root");

    var content = document.createElement("div");
    content.innerText = "content";
    dom.append(content);
}
export default Content;

footer.js的代码

function Footer() {
    var dom = document.getElementById("root");

    var footer = document.createElement("div");
    footer.innerText = "footer";
    dom.append(footer);
}
export default Footer;
  • 运行
    执行命令npx webpack index.js完成后,会在项目根目录下生成一个dist目录,里面 有个main.js文件。打开index.html,页面显示如下:
    image.png

Webpack默认配置文件webpack.config.js

1. 入口(entry)
当没有创建webpack.config.js文件,或者此文件中为空时,直接执行npx webpack,终端控制台报错;写入如下代码:

module.exports = {
    entry: "./index.js"//等同于{main:"./index.js"},会在输出目录中打包生成main.js文件。
};

再次执行npx webpack,则在根目录生成'/dist/main.js',效果等同于npx webpack index.js
2. 出口(output)

const path = require('path');
module.exports = {
    entry: "./index.js",
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'my-first-webpack.bundle.js'
    }
};

执行npx webpack,生成文件如下:

image.png

常用配置:

  • publicPath:"http://cdn.com.cn",打包完成后,会在index.html中引入的script路径中加上这个域名;
    image.png

3. mode模式
production:默认,代码会被压缩
development:代码不会被压缩
4. loader
webpack不能识别非 JavaScript 文件,需要loader来识别;如果看到引入的模块的后缀不是.js,就需要想到用对应的loader来引入;loader中支持配置各种options参数,例如:file-loader的选项值;loader的执行顺序是由右往左的。

常用的loader:

  • file-loader:指示webpack发出所需的对象文件,并返回其公共URL;可用于加载iconfont中的字体文件。
  • url-loaderurl-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
test: /\.(png|jpg|gif)$/, use: {
        loader: "url-loader",
        options: {
            name: 'images/[name].[ext]',
            limit: 8192//单位B,如果不设置,则会把所有图片都用base64
        }
    }
//js引入图片需要使用require,直接写相对路径无效
var img = new Image();
img.src = require('./image/img1.jpg').default;
document.getElementById("main").appendChild(img);
  • style-loader:将css文件中的内容解析提取出来,并挂载到head中style里面;
  • css-loader:解释@importurl() ,会import/require()后再解析(resolve)它们。例如css文件中使用@import "./avator.css";
  • sass-loader:加载一个SASS / SCSS文件,编译成 CSS。安装npm install sass-loader node-sass --save-dev
  • postcss-loaderPostCSS利用 JavaScript 的强大编程能力对 CSS 代码进行转换。数以百计的 PostCSS 插件可以用来为 CSS 属性添加特定于浏览器厂商的前缀、压缩工具cssnano、支持未来 CSS 语法、模块。如果给class添加厂商前缀
  • babel-loader:用来处理ES6语法,将其编译为浏览器可以执行的js语法;使用userBuildIns:'usage'配置,可以有效减少打包文件大小(只打包用到的es6语法)。使用target设定目标浏览器版本,打包的时候就会对这些版本下支持的es6语法免打包;
    再结合"@babel/polyfill",可以完全解决es6在老浏览器中运行问题,但是打包文件也会因此而变大,因为系统会把很多老浏览器不支持的方法,以自定义代码的形式打包到文件中。所以要使用useBuiltIns:'usage',设置如果用到了就打包,没用到就不打包。高阶前端工程师需要学习babel各种配置,了解抽象语法树。

如果是写的项目代码,那么可以用babel+@babel/polyfill来;如果是发布上线的组件或插件,就不能用这种方式,会污染全局。需要改用@babel/plugin-transform-runtime

image.png
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    "presets": [
                        ["@babel/preset-env", {
                            "targets": {
                                "chrome": "67"
                            },
                            "useBuiltIns": "usage"
                        }]
                    ]
                }
            },

babel-loaderoptions配置项的代码也可以单独放入跟目录下的.babelrc文件中,执行效果是一样的。

//vue init webpack my-project脚手架项目默认.babelrc文件代码
{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
  "env": {
    "test": {
      "presets": ["env", "stage-2"],
      "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"]
    }
  }
}

5. plugins
插件目的在于解决 loader 无法实现的其他事;可以在webpack运行到某个时刻的时候,帮你做一些事情;类似于生命周期钩子;例如html-webpack-plugin查看,就是在webpack打包完成的时刻,自动生成一个index.html到打包目录中去。
常用plugins:

  • html-webpack-plugin:自动生成一个HTML文件
  • clean-webpack-plugin:清空打包目录
  • webpack.ProvidePlugin,例如引入jquery
//安装
npm install jquery --save-dev
//引用,修改webpack.config.js文件。
const webpack = require('webpack')
plugins: [
        new webpack.ProvidePlugin({
            jQuery: "jquery",
            $: "jquery"
        })
    ],
//使用
$("#header").html("init");

6. sourceMap

devtool用于打包前和打包后文件代码位置映射,方便代码出错时定位到源代码位置;

当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js, b.jsc.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到 bundle.js。这并通常没有太多帮助,因为你可能需要准确地知道错误来自于哪个源文件。

为了更容易地追踪错误和警告,JavaScript 提供了 source map 功能,将编译后的代码映射回原始源代码。如果一个错误来自于 b.js,source map 就会明确的告诉你。 devtool详细配置JavaScript Source Map 详解

module.exports = {
    entry: {
      app: './src/index.js',
      print: './src/print.js'
    },
    devtool: 'cheap-module-eval-source-map',  //调试版本建议用
    //devtool: 'cheap-module-source-map',      //打包后版本建议用
    plugins: [
      new CleanWebpackPlugin(['dist']),
      new HtmlWebpackPlugin({
        title: 'Development'
      })
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

7. devServer
用于调试阶段项目热重载;

  • a.安装;npm i webpack-dev-server -D
  • b.修改webpack.config.js
    devServer: {
        contentBase: path.join(__dirname, "dist"),//
        compress: true,
        open: true,
        port: 9000
    },
  • c.修改package.json
  "scripts": {
    "watch": "webpack --watch",//观察者模式。源代码更改后,dist中代码会自动打包;
    "start": "webpack-dev-server"//npm run start启动后,会自动打开一个端口为9000的页面,并且源代码修改后,页面会热重载;
  }

package.json配置

  1. 修改打包命令
  "scripts": {
    "bundle": "webpack"//可以不用写npx webpack,因为script脚本命令,会优先到当前工程目录中查找是否支持此命令,没找到再到全局查找;
    "watch": "webpack --watch",//监听模式,如发现源代码改变了,则将文件自动打包到dist目录(页面不会自动刷新)
    "start": "webpack-dev-server"//自动热重载,如果发现域代码改变了,则自动打包到内存,并自定刷新浏览器页面
  },

执行npm run bundle,等同于执行npx webpack

附件

webpack.config.js文件代码:

const path = require('path');
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: {
        app: './src/index.js'
    },
    mode: "development",//production
    devtool: 'cheap-module-eval-source-map',  //调试版本建议用
    plugins: [
        new CleanWebpackPlugin(),
        new webpack.HotModuleReplacementPlugin(),//热更新
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        }),
        new webpack.ProvidePlugin({
            jQuery: "jquery",
            $: "jquery"
        })
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    "presets": [
                        ["@babel/preset-env", {
                            "targets": {
                                "chrome": "67"
                            },
                            "useBuiltIns": "usage"
                        }]
                    ]
                }
            },
            {
                test: /\.(png|jpg|gif)$/, use: {
                    loader: "url-loader",
                    options: {
                        name: 'images/[name].[ext]',
                        limit: 8192
                    }
                }
            },
            {
                test: /\.css$/, use: ["style-loader", "css-loader"]
            },
            {
                test: /\.scss$/, use: ["style-loader", "css-loader", "sass-loader", "postcss-loader"]
            }
        ],
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    devServer: {
        contentBase: path.join(__dirname, "dist"),
        compress: true,
        open: true,
        port: 9000,
        hot:true,          //改了哪个文件,就更新哪个文件,最小化更新,提交开发效率;
        hotOnly: true,  //启用热模块替换,而无需页面刷新作为构建失败时的回退。
        proxy: {
            "/api": {
                target: "http://localhost:8081"//dev环境设置代理,方便测试;
            }
        }
    },

};

package.json代码

{
  "name": "jquerytowebpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.4",
    "@babel/preset-env": "^7.10.4",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^3.6.0",
    "file-loader": "^6.0.0",
    "html-webpack-plugin": "^4.3.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^9.0.2",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  },
  "dependencies": {
    "babel-loader": "^8.1.0",
    "jquery": "^3.5.1"
  }
}

问题汇总:

1、安装 xxx-loader成功后,打包报错:this.getOption is not function
由于xxx-loader版本过高导致,降版本就行了,可以看npm上的版本记录,找前个版本的稳定版;

image.png

2、配置browserslist
node项目中不同位置设置browserslist对postcss-loader影响的权重
前端工程基础知识点--Browserslist (基于官方文档翻译)

资料推荐
如何发布一个库到npm仓库
webpack 中文文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容