WebPack学习汇总

Posted IT飞牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.org/

其他方法:

1、npm config set registry https://registry.npm.taobao.org

2、npm install cnpm -g --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.js2. 出口(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. loaderwebpack不能识别非 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、配置browserslistnode项目中不同位置设置browserslist对postcss-loader影响的权重前端工程基础知识点--Browserslist (基于官方文档翻译)

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

以上是关于WebPack学习汇总的主要内容,如果未能解决你的问题,请参考以下文章

webpack学习小结

在 webpack 或汇总模块捆绑过程中是不是保证保留单例?

webpack4常用片段

webpack4知识汇总2

Vue基础自学系列 | webpack中的插件

Vue基础自学系列 | webpack的基本使用