webpack使用详解

什么是webpack

官方定义:从本质上来说,webpack是一个现代的JavaScript的静态模块打包工具。
webpack是前端工程化的一个解决方案。
主要功能: 提供了前端模块化功能支持,模块混淆,代码压缩,处理浏览器JS兼容性,性能优化等强大功能。

webpack前提

学webpack的前提是要知道es6的模块化知识或者commonjs的知识(不用太深),两者都提供模块化功能。es6的模块化是官方标准,较新,将来一定是主流,commonjs比较早就有了,资历深。知道一个或者两个都知道比较好。没学过去看看,不然看不懂webpack例子。
其他前提:
javascript的es5基础 (不用太深)
html (不用太深)
css(不用太深)
node.js(不用太深)

webpack的安装

webpack依赖node.js。没有的需要先安装node.js。就不说怎么安装了。

安装webpack3.6.0这个版本。

npm install [email protected] -g

能看到版本号,就表示安装成功了。

webpack -version

webpack的基本使用过程

先把目录创建好,一个main.js文件,这是js的入口文件。dist文件夹是用来放打包后的文件,后面会用到。因为webpack是支持模块化的,所以加了一个mathUtil.js来简单模拟模块化。
webpack使用详解_第1张图片
main.js的代码如下:
调用了mathUtils.js的代码,实现简单输出。第一行代码是commonjs的语法,不了解的只需要知道是这是在引用模块就可以了。

const {add,mult}=require("./mathUtils.js")
console.log(add(1,2))
console.log(mult(1,2))

mathUtils.js定义了两个方法并且导出给别人使用。

function add(a, b) {
    return a+b
}

function mult(a, b) {
    return a*b
}

//使用commonJs导出
module.exports = {
    add,
    mult
}

index.html的内容为空,也就是默认的html文本,里面什么都没写。
分析:
现在我们有一个main.js,他引用了mathUtils.js里面的内容。
js有了,那么我们是不是要在js里面引用呢?答案是不要。如果不用webpack,我们可能就会引用多个js文件,比如main.js和mathUtils.js,当然你可能有很多个js文件,都引用也非常的麻烦。webpack可以将这些js文件打包成一个文件。然后你只需要引入打包好的这一个文件就行了,打包也非常的简单。
执行下面的代码,会在dist目录下面生成一个bundle.js文件,这就是打包好的文件,注意这里我们打包的时候只写了main.js,webpack会帮我们把相关联的js文件全部打包进来。这就是webpack帮我们简化步骤的地方,和我们一个个手动引用相比,这里相当于变成了自动引用,我们只需要提供入口js,也就是我们的main.js这一个js文件就可以了。

webpack ./src/main.js ./disk/bundle.js

我们简单看一下生成的这个bundle.js。前面的代码不用看,主要看后面的几行,后面那几行实际上就是把main.js和mathUtils.js的代码合并带一起了,还有一些commonjs的代码,这些代码浏览器还是不识别的,前面生成的一堆看不懂的代码就是给commonjs做支持用的。

/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

const {add,mult}=__webpack_require__(1)
console.log(add(1,2))
console.log(mult(1,2))

/***/ }),
/* 1 */
/***/ (function(module, exports) {

function add(a, b) {
    return a+b
}

function mult(a, b) {
    return a*b
}

//使用commonJs导出
module.exports = {
    add,
    mult
}

/***/ })
/******/ ]);

最后,只需要在index.html里面引用我们生成的bundle.js就可以了。

<script src="./dist/bundle.js"></script>

打开浏览器是可以看到js的执行输出的。
webpack使用详解_第2张图片

webpack的配置

前面我们使用webpack打包的时候,是手动指定路径的,这些东西可以写到配置文件里面,方便我们管理。

webpack ./src/main.js ./disk/bundle.js

需要在项目根目录下创建webpack.config.js文件。这是webpack的配置文件,默认就叫这个名字。
webpack使用详解_第3张图片
写入下面的内容,然后在终端执行webpack这个命令,不用加任何参数,这时候会报错,告诉你要用绝对路径。实际上,下面path的写法是不对的,需要绝对路径,我们直接写不合适,而是通过node.js提供的path包来获取。

module.exports = {
    entry:"./src/main.js",
    output:{
        path:"./dist",
        filename:"bundle.js"
    }
}

默认情况下是没有path这个包的。我们需要将项目初始化为node工程。
提供下面的命令初始化node工程。要在index.html的父目录执行。

npm init

会叫你填一些值,不要写中文,不是中文的一直默认就可以,后面会说具体配置。
完成之后会多出一个package.json的文件,这是node的配置文件。
里面的内容大致如下。

{
  "name": "meetwebpack",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

webpack使用详解_第4张图片
现在就可以使用node提供的path库了。
通过require引入库,通过path.resolve方法获取绝对路径,__dirname是path库提供的项目路径

const path=require("path")
module.exports = {
    entry:"./src/main.js",
    output:{
        path:path.resolve(__dirname,"dist"),
        filename:"bundle.js"
    }
}

现在我们可以直接使用webpack来打包,而不用指定路径了。
webpack使用详解_第5张图片

前面的方式运行webpack已经可以用了。但是还有一种更好的方式。那就是使用node的脚步运行webpack。
前面的问题是我们可能存在多个webpack配置文件,一个是生产环境的一个是开发环境的,那么如果环境变了,我们的webpack打包命令需要指定配置文件。
前面没有指定配置文件名称是因为默认配置文件名称就叫做webpack.config.js。

webpack production.config.js

我们可以使用一种更灵活的方式,就是把这些命令配置到脚本里面。下面的命令就是运行build这个脚本,这个脚本是我们自己定义的。

npm run build

在我们的package.json里面有下面的脚本。我们可以在这里面创建我们自己的build脚本,默认写了一个叫test的脚本。

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  }

现在执行npm run build可以实现和原来一样的效果。
webpack使用详解_第6张图片
这里还存在一个问题,就是我们在终端执行命令的时候,调用的都是全局的webpack命令。这就会造成问题,因为实际项目中,本地和全局可能因为某些原因需要使用不同的版本。都用全局就会造成问题。
这时候我们需要安装一个本地版本就可以了。

 npm install [email protected] --save-dev

执行完后会多出下面的目录。node_modules包含了node的所有模块,有几百个,webpack就在里面。
默认在终端执行webpack都是执行全局的node,除非写成下面的样子

./node_modules/webpack xxxxx

当然现在我们有更好的方式,执行下面的代码就可以了,这就是最终的效果。

npm run build

webpack使用详解_第7张图片
并且package.json会多出下面的节点。

  "devDependencies": {
    "webpack": "^3.6.0"
  }

webpack配置CSS

我们先创建css,目录并且添加normal.css文件,内容如下。

body{
    background-color: lightgreen;
}

调整一下js文件结构,把作为入口的main.js暴露再外面。
webpack使用详解_第8张图片

这时候我们不要在html中引入css代码了,而是交给webpack来做。

webpack本身并没有支持CSS的功能,这些功能通过loader来提供。
我们只需要加载对应的loader,就可以实现对应的功能。
webpack提供非常多的loader,CSS loader只是其中一个。

我们打开文档:
https://www.webpackjs.com/loaders/#%E6%A0%B7%E5%BC%8F

我们需要用到的css loader就两个,一个是css-loader,用于解析css代码,并返回css代码。如果只单独用这个loader是没有效果的,因为这个loader只负责加载,不负责导出显示到dom中。所以我们还需要一个style-loader模块,这个模块就是专门干这个事的。
webpack使用详解_第9张图片
安装模块,webpack 3.6.0对应的0.9.0是可以使用的,太高版本会给警告。
使用npm好像是会直接报错的,但是cnpm只有警告,并且给出提示说当前版本只能对应的版本范围。

npm install --save-dev [email protected]
npm install [email protected] --save-dev

然后需要在配置文件里面添加module节点,并添加rules的内容。
需要注意的是,use里面的两个loader是后面的先加载,顺序错了是会报错的,文档里面有这么说。不知道为什么这样设计。

const path=require("path")
module.exports = {
    entry:"./src/main.js",
    output:{
        path:path.resolve(__dirname,"dist"),
        filename:"bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    { loader: "style-loader" },
                    { loader: "css-loader" }
                ]
            }
        ]
    }
}

这时候执行下面的代码再运行index.html就可以看到css的样式了

npm run build

如果背景色变了就表示成功了。

webpack配置less

less的配置和css的配置是类似的。
这里存在一个困难是版本问题,你根本不知道什么版本和你的webpack匹配。
而且版本号是没有统一的,一个loader是一个独立的版本号。
解决办法就是到github直接搜索你要安装的loader,比如这里的less-loader,随便点击一个tag版本。查看里面的package.json,里面是可以查看到webpack的版本范围的。
对于任意的loader都是类似的,这些loader都是webpack官方发布的。
使用下面的版本对应webpack3.6.0是可以的,只是会告诉你已经废弃,math方法有bug。实际项目还是不要用了,应该升级webpack版本。

npm install --save-dev [email protected] [email protected]

在webpack.config.js下面的rules节点下面添加下面的代码。

{
                test: /\.less$/,
                use: [{
                    loader: "style-loader" // creates style nodes from JS strings
                }, {
                    loader: "css-loader" // translates CSS into CommonJS
                }, {
                    loader: "less-loader" // compiles Less to CSS
                }]
            }    

创建css文件夹,在里面准备我们的special.less测试文件。
webpack使用详解_第10张图片

@fontSize:50px;
@fontColor:lightblue;
body{
  font-size: @fontSize;
  color: @fontColor;
}

在main.js中引用less

require("./css/special.less")

在index.html中添加如下内容:

<div id="box">
  Hello Webpack
</div>

最后运行。

npm run build

效果正确就表示配置成功了。

webpack对于图片的处理

图片也是需要loader的,在webpack里面图片分为两种情况,在配置文件里面可以配置一个limit属性来判断图片大小是否超过这个指,如果小于这个值,就会被当做base64来处理,如果大于这个值,就当正常的图片文件来处理。
版本一定要写1.1.2,1.0.0是不能显示图片的,对于webpack3.6.9。

npm install --save-dev [email protected]

这里的关键点是多了一个limit,也就是我们上面说的图片大小限制。

{
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          }
        ]
}

引入两张图片,gun.jpg的大小为4kb,baidu.png的大小为15kb.。webpack使用详解_第11张图片

把body的背景指定为gnu.jpg,因为图片大小小于limit,所以图片会以base64的形式传递。

body{
    /*background-color: lightgreen;*/
    background: url("../img/gnu.jpg");
}

webpack使用详解_第12张图片
执行下面代码,就可以看到效果了。

npm run build


如果把图片换成baidu.jpg,就会直接报错。告诉你找不到file-loader。前面讲过,webpack处理图片有两种方式,当图片大小小于limit的时候采用的是base64的字符串方式。这时候就需要用到url-loader,而当图片大于limit的时候,就不能用url-loader,而是要用file-loader.

Module build failed: Error: Cannot find module 'file-loader'

下载file-loader。

 npm install [email protected] --save-dev

配置file-loader。
前面的url-loader是基于file-loader实现的,两者只能写其中一个,同时写是不能生成图片的。
前面的url-loader是基于file-loader实现的,两者只能写其中一个,同时写是不能生成图片的。
前面的url-loader是基于file-loader实现的,两者只能写其中一个,同时写是不能生成图片的。

{
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'file-loader',
            options: {}
          }
        ]
      }

编译运行后发现图片不显示。但是dist目录下是生成了图片的。
在这里插入图片描述
我们看下浏览器body的url地址,路径是不对的,应该在前面加一个dist/。这是可以提供配置实现的。

url(d9c8750bed0b3c7d089fa7d55720d6cf.png)

需要子啊webpack配置文件里面添加publicPath这个节点。

    output:{
        path:path.resolve(__dirname,"dist"),
        filename:"bundle.js",
        publicPath:"dist/"
    },

现在baidu.jpg就可以被当做图片显示了。
我们不太可能将所以图片都放在dist根目录下,给dist创建一个img目录是比较合理的,可以提供下面的代码实现。[name]表示原来的文件名[hash:8]表示8位hash值,不写默认32位,[ext]表示扩展名。这样就非常的工程化了。

options: {
       name:"img/[name]-[hash:8].[ext]"
}

输出效果:
webpack使用详解_第13张图片

将ES6语法转成ES5语法

webpack的一个最大的优点就是他帮我们做了一些兼容性问题的处理。可以通过babel-loader来实现。

npm install [email protected] @babel/core @babel/preset-env

在webpack配置文件里面添加下面内容:

{
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }

观察输出的js文件,之前的const等这些es6语法就都转化成es5的语法了。

你可能感兴趣的:(webpack,前端,前端,webpack)