webpack学习记录

注:本文有相当多的内容参考了如下网址!!!另有部分内容是对网络资源的整合
https://segmentfault.com/a/1190000006178770

一. 为什么会产生 webpack ? / 为什么要使用 webpack?

一个前端项目会产生很多的 JavaScript 代码和一大堆依赖包,这些代码肯定是要分作不同的 js 文件来保存,这些 js 文件之间又必须互相导出或导入数据或函数以供别的 js 文件来使用;

同时在项目的 index.html 文件中又必须一条条导入这所有的 js 文件,这些文件的导入又可能必须按照一定的顺序。

如此一来,A文件引用B文件,B文件引用C文件,C文件导出的内容又被D引用,在 index.html 文件中导入 js 文件的顺序也必须非常留心。交错复杂的逻辑,仅靠人工极难理清。项目越庞杂,引用和导出关系就越乱。

除此之外,我们还会写入 TypeScript、Scss 等拓展语言的代码,这些代码在浏览器中是不能直接运行的,我们也必须对其进行转化。

零碎的文件也会占用 http 请求,如果有办法能将这些零碎文件打包在一起,就能减少 http 请求,提高渲染速度。

为了解决这种种问题,webpack 应运而生。

二. webpack 的使用前提

webpack 依赖 node 环境,使用 webpack 之前必须先下载 node,node 内置了 npm

下载 webpack:

// 全局安装 webpack
npm install -g webpack

文件目录说明(注:以下目录会在下文一一出现):

/ 项目根目录

/src 项目源码目录

/src/main.js 项目主要的 js 文件,其它的 js 文件也会放在 src 目录下

/src/index.html

/webpack.config.js webpack 的配置文件

/package.json 运行 npm init 后生成的文件

三. webpack 的使用

1. 简单使用

1)webpack {入口文件名}

在项目文件的命令行中运行:

webpack {入口文件名}

入口文件就是项目的 .js 文件中最主要的那一个。

运行上方的命令后,webpack 会自动整理由入口文件产生的一系列导入导出关系,并将拓展语言的代码转换为浏览器可识别的代码,最后会在项目根目录自动新建 dist 文件夹,并将结果放入自动新建的 dist/main.js 文件中。此时只需要在项目的 index.html 文件中导入 main.js 文件即可。

不推荐使用这种方法。

2)webpack

如果不想在命令行中附带入口文件名,则可以在项目根目录下新建 webpack.config.js 文件,并向其中写入如下内容:

module.exports = {
  // 入口文件名
  entry: __dirname + '\\src\\main.js',
  output: {
    // 定义出口文件的根路径
    path: __dirname + '\\dist',
    // 定义出口文件名
    filename: 'bundle.js'
  },
  // 注:__dirname 是 node.js 中的一个全局变量,它指向当前执行脚本所在的目录
}

此时再重新打包文件时,只需要在命令行中输入:

webpack

即可得到转换后的 dist/bundle.js 文件。

3)npm start

这是另一种打包的命令行写法,个人认为这种写法和 “webpack” 相差无几,但也在这里列出。

配置方法:在项目根目录下新建 package.json 文件(使用 “npm init” 命令,即可根据提示,自动创建 package.json 文件),并向其中写入:

{
  "name": "webpack-sample-project",
  "version": "1.0.0",
  "description": "Sample webpack project",
  "scripts": {
    "start": "webpack" // 修改的是这里,JSON文件不支持注释,引用时请清除
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "3.10.0"
  }
}

webpack.config.js 文件中也要写入:

module.exports = {
  entry: __dirname + '\\src\\main.js',
  output: {
    path: __dirname + '\\dist',
    filename: 'bundle.js'
  },
}

此时再重新打包文件时,只需要在命令行中输入:

npm start

即可得到转换后的 dist/bundle.js 文件。

2. 配置使用

webpack 还有很多更高级的功能,但这些功能仅在命令行中实现的话,就很不方便且容易出错,更好的办法是定义一个 webpack 的配置文件(即上文提到的:webpack.config.js),并在其中写入我们想要的配置

1)Source Maps: 快速定位源文件错误位置
- 作用:

将源文件和编译后产生的文件对应,如果编译后的文件报错,则可以找到对应源文件产生的错误位置,方便调试

- 配置方法:

在 webpack.config.js 文件中,添加 devtool 属性;该属性有 4 个可选属性值,这里不做单独介绍。对于中小型项目,推荐使用的属性值为 “eval-source-map”。

module.exports = {
  entry: __dirname + '\\src\\main.js',
  output: {
    path: __dirname + '\\dist',
    filename: 'bundle.js'
  },

  // Source map
  devtool: 'eval-source-map',
}

注:此选项只应该在开发阶段使用!

2)构建本地服务器: 显示结果随代码实时更新
- 作用:

让浏览器监听代码的修改,并自动刷新显示修改后的结果

- 配置方法:

在 webpack.onfig.js 文件中,添加 devServer 属性;该属性的一些配置选项有:

  • contentBase: 监听哪个文件夹,这里应该写打包后的 dist 目录
  • port: 设置监听端口,如果不写,则默认为 8080 端口
  • inline: 设置是否自动刷新页面,该值应为 true
  • historyApiFallback: 在 SPA 页面中,依赖 HTML5 的 history 模式(不明白,暂不解释
module.exports = {
  entry: __dirname + '\\src\\main.js',
  output: {
    path: __dirname + '\\dist',
    filename: 'bundle.js'
  },
  devtool: 'eval-source-map',

  // 构建本地服务器,实现显示结果随代码同步刷新
  devServer: {
    // 该设置在哪个文件夹生效
    contentBase: './dist',
    // 页面是否实时刷新
    inline: true,
    port: 8080,
    historyApiFallback: true
  }
}

本地服务器开启完毕后,运行项目的命令就不是 “npm run server” 了,而是:

webpack-dev-server

但其实这样是无法执行命令的。通过 npm 下载的包,如果没有在下载中使用 -g(没有下载到全局),那么在使用的时候就要通过局部方法来使用,webpack-dev-server 也是同理。

因此,实际的使用是:

node_modules/.bin/webpack-dev-server

这样写当然也很麻烦,因此我们可以在 package.json 文件中添加一行脚本命令:

// package.json 文件
{
  "name": "webpack",
  "version": "1.0.0",
  ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    // 添加的地方在这里,--open 是另外添加的参数,使用该参数后,运行命令后就会自动打开浏览器了。注意使用的时候不能有这行注释
    "dev": "webpack-dev-server --open"
  },
  ...
}

如此一来,最终的使用命令就是:

npm run dev

等到项目调试完毕,再重新 “webpack” 打包即可。

3)Loders: 处理文件,转换代码
- 作用:

Loders 并不是某个具体的包,而是解决转换问题的包的总类。通过使用不同的 loader,webpack 有能力调用外部的脚本或工具(例如:Babel-loader,css-loader 等),实现对不同格式的文件的处理,比如说将 scss 转换为 css,或者 es6 转换为 es5 等,这也是 webpack 的重要功能之一。

- 说明:

在 webpack 中,所有的文件都被当做模块进行处理,JavaScript 代码、CSS、fonts 以及图片等等,都可以通过合适的 loader 进行处理。注:本文会记录部分 loader。

- 配置:
  1. 单独安装某类 Loader 的依赖包
  2. 配置 webpack.config.js 文件中的 modules 属性,以实现不同的 loader 的功能
    注:具体配置情况请看下文
3-1. Loader——Babel:
- 作用:

你可以无后顾之忧地使用 es6/es7 等最新的 JavaScript 代码,或者使用基于 JavaScript 的拓展语言,比如 React 的 JSX,Babel 有专门的依赖包帮你解决代码转换的问题。

- 说明:

Babel 其实是几个模块化的包,其核心功能位于 babel-core 的 npm 包中,babel-core 算是 babel 的一个依赖,是作为 babel 的核心存在,一些转码操作都是基于它来完成的。webpack 可以把 Babel 的不同的包整合在一起使用,对于每一个需要的功能或拓展,都需要单独安装一个包。(例如:解析 es6 的 babel-env-preset 和 解析 JSX 的 babel-preset-react)。另外在 7.0 版本之后,babel 包名升级为 @babel/core,网络来源的解释是:@babel 相当于一种官方标记,和以前大家随便起名形成区别

- 配置:

1> 在 webpack.config.js 文件中配置

  1. 安装依赖包。

    npm install babel-loader@8.0.0-beta.0 @babel/core @babel/preset-env --save-dev
    

    注:命令中的 --save-dev 选项意为在 package.json 文件中,将我们安装的包设置为“开发时依赖”,因为 webpack 在开发结束之后就没用了,没有必要在交付项目的时候占用空间。

  2. 在 webpack.config.js 中配置 Babel:

module.exports = {
  entry: __dirname + '\\src\\main.js',
  output: {
    path: __dirname + '\\dist',
    filename: 'bundle.js'
  },
  devtool: 'eval-source-map',
  devServer: {
    contentBase: './dist',
    inline: true,
    port: 8080,
    historyApiFallback: true
  },
    
  module: {
    rules: [
      {
        test: /(\.jsx|\.js)$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              "env", "react"
            ]
          }
        },
        // 转换时,排除 node_modules 文件夹下的代码,这些代码是我们下载来的工具,不应该被转换
        exclude: /node_modules/
      }
    ]
  }
}

现在 webpack 的配置已经允许你使用 ES6 以及 JSX 的语法了。

2> 在单独的文件中配置

Babel 其实可以完全在 webpack.config.js 文件中进行配置,但是考虑到 babel 具有非常多的配置选项,在单一的webpack.config.js 文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把 babel 的配置选项放在一个单独的名为 “.babelrc” 的配置文件中。

我们现在的 babel 的配置并不算复杂,不过这里仍做出演示,现在我们就提取出相关部分,分两个配置文件进行配置(webpack 会自动调用 .babelrc 文件里的 babel 配置选项)。

webpack.config.js 文件内容如下:

module.exports = {
  entry: __dirname + '\\src\\main.js',
  ...
  module: {
    rules: [
      {
        test: /(\.jsx|\.js)$/,
        use: {
          loader: "babel-loader"
        },
        exclude: /node_modules/
      }
    ]
  }
}

在项目根目录下新建 “.babelrc” 文件,并向其中写入:

{
  "presets": ["react", "env"]
}

注:在上面这个配置中,真正起到转换代码作用的是 @babel/core,webpack 负责调用 @babel/core 对代码进行转换,并使用 babel-loader 加载文件。

3-2. Loader——css-loader/style-loader
1> css-loader

由于在 webpack 中,一切文件都可以被当成模块并进行处理,因此只要开发者在别的文件中按照 require(’./xxx.css’) 或者 import xxx from ‘xxx.css’ 或者 @import url(‘xxx.css’) 的格式,表明了要导入某个 .css 文件,那么 css-loader 就会对目标 .css 文件进行处理(webpack.config.js 文件中的设置不要忘了)。

作用:

将 .css 文件内每一行的 css 代码当做数组元素,存储进一个数组里。也可以对这个数组进行操作,比如将其转换为字符串之类的。

配置:

npm 下载 css-loader

npm install --save-dev css-loader

webpack.config.js:

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

base.css:

html {
  background: #bfa;
}

main.js:

const css = require('./base.css')	
console.log(css)  // 打印 css

注:如果有不清楚的地方,可以去 “webpack中文网” 搜索 css-loader,查看官方文档

运行 index.html 后的结果:

在这里插入图片描述

2> style-loader

虽然有了 css-loader,但这还不够,因为 css-loader 只是把 .css 文件转换为数组,页面是无法直接使用数组的。这时我们就需要 style-loader 来配合处理。

作用:

将 css-loader 打包好的 css 代码以 style 标签的形式插入到 html 文件中

配置:

npm 下载 style-loader

npm install --save-dev style-loader

webpack.config.js:

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.css$/,
        // 可以简写成这样,但要注意 style-loader 要在 css-loader 之前,因为使用多个 loader 时,是从右向左(或从下向上)读取的
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

此时重新打包一下 main.js 文件,得到的 bundle.js 文件中会出现类似如下的代码:

o.push([e.id,"html {\r\n  background: #bfa;\r\n}",""])

这说明 style-loader 已经将 css-loader 转换好的代码用 style 标签包裹,并放入 html 文件中了,此时浏览器运行 index.html 文件:

在这里插入图片描述

给 html 设置的背景颜色已经生效。

3> css-loader 的单独使用:避免样式的全局污染

暂未记录

3-3. vue-loader/vue-template-compiler
- 作用:

vue-loader 用来加载 .vue 文件

vue-template-compiler 用来编译 .vue 文件

详情待记录

4)Plugin

plugin 是插件,通常是用于对某个现有的框架进行扩展。webpack 中的插件,就是对 webpack 现有功能的各种扩展,比如打包优化,文件压缩等。

1> loader 和 plugin 的区别

loader 主要用于转换某些类型的模块,它是一个转换器(或加载器);

plugin 是插件,它是对 webpack 本身的扩展,是一个扩展器。

2> 使用
  1. npm 安装需要的插件(某些插件是 webpack 内置的,不需要安装)
  2. 在 webpack.config.js 文件的 plugins 属性中配置插件

例如:使用 BannerPlugin 插件,给打包的 .js 文件加一个版权声明

// webpack.config.js 文件
const webpack = require('webpack')		// 要使用的 BannerPlugin 插件是 webpack 自带的,只需直接导入 webpack 即可

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有')	// 此时打包的 bundle.js 文件最上方就会出现这个中文字符串
    ]
}

翻车记录:插件自动在 dist 目录下创建了一个 bundle.js.LICENSE.txt 文件,然后:

// bundle.js.LICENSE.txt 文件
/*! 版权所有 */

为什么没有加到 .js 文件中?原因不明,留待解决

3> 常用插件
- webpack 自带插件——BannerPlugin

这个插件之前举例介绍过了,不再赘述

- html-webpack-plugin

说明:

在最终提交项目的时候,我们提交的是 dist 文件夹,但该文件夹中并没有 index.html 文件,此时我们就可以利用这个插件。

作用:

自动生成一个 index.html 文件(可以指定模板来生成);在生成的 index.html 文件中自动添加 script 标签,引入打包后的 .js 文件。

使用:

html-webpack-plugin 这个插件并不是 webpack 自带的,因此需要先 npm 下载:

npm install --save-dev html-webpack-plugin

然后在 webpack.config.js 文件的 plugins 属性中进行设置:

// webpack.config.js 文件
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
    ...
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究')
        // 可以这样什么都不写
        new HtmlWebpackPlugin()
        // 也可以传入一个参数,设置生成的 index.html 文件的模板
        // new HtmlWebpackPlugin({
        //   template: './src/index.html'
        // })
    ]
}

此时 dist 目录下就会自动创建一个 index.html 文件,该文件只有一行,文件的内容是这样的:

<!doctype html><html><head><meta charset="utf-8"><title>Webpack App</title><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><script src="bundle.js"></script></body></html>

如果 src 目录下的 index.html 文件中有你需要的代码,(比如 div id=‘app’),那就要给 HtmlWebpackPlugin 设置模板了

- uglifyjs-webpack-plugin——代码压缩(丑化)插件

说明:

该插件的作用是将生成的 bundle.js 文件中的不必要的东西(比如空格,注释)全部去掉,但我这里生成的 bundle.js 已经是不含空格和注释的最简略版的了,我也不知道是什么原因,所以这里的这个插件也就没用上,但仍然将其记录下来。注:uglifyjs 这个插件会把注释去掉,因此之前添加的“new webpack.BannerPlugin(‘版权所有,翻版必究’)”,这条代码就没用了(当然其实我本来就没用上 QAQ)。可能我之前 BannerPlugin 没有显示到 bundle.js 文件中也和这个有关系吧。

使用:

// webpack.config.js 文件
...
const uglifyJsPlugin = require("uglifyjs-webpack-plugin")
...

module.exports = {
    ...
    plugins: [
        ...
        new uglifyJsPlugin()
    ]
}
- 配置分离

分离的原因:
开发时和生产时的部分配置是冲突的,有的配置是开发时所必须,但生产并不需要的配置;而有的配置又必须在生产时设置,但对开发毫无用处。在项目很小,配置不多的时候,可以手动的注释掉不需要的配置。但如果项目很大,所需配置很多,并且开发生产的配置冲突也很多的时候,对配置进行分离就是最方便的选择了。
分离的步骤:

  • step1.
    抽离 webpack.config.js 文件为三个文件:
    base.config.js 存放开发和生产时都需要的配置
    prod.config.js 只存放生产时需要的配置
    dev.config.js 只存放开发时需要的配置

  • step2.
    安装 merge 包,它可以将 base.config.js 文件与 prod.config.js 文件结合起来,从而生成生产时的配置文件,同样的,它可以将 base.config.js 文件与 dev.config.js 文件结合起来,生成开发时的配置文件。

    npm install -D webpack-merge
    
  • step3.
    在项目下新建 build 文件夹,存放这三个文件

  • step4.
    在 prod.config.js 文件中写入:

    const baseConfig = require("./base.config.js")
    const webpackMerge = require("webpack-merge")
    module.exports = webpackMerge(baseConfig, {
    	// 生产时需要的配置
    })
    

    在 dev.config.js 文件中写入:

    const baseConfig = require("./base.config.js")
    const webpackMerge = require("webpack-merge")
    module.exports = webpackMerge(baseConfig, {
    	// 开发时需要的配置
    })
    
  • step5.
    删除项目中原有的 webpack.config.js 文件。此时会出现问题,因为删除 webpack.config.js 文件后,package.json 文件中的 script 属性就不能够再识别我们在命令行中输入的 webpack 命令了,此时需要在 script 属性中手动添加 webpack 配置文件的路径。

    // package.json 文件
    {
        ...
        "script": {
            ...
            "build": "webpack --config ./build/prod.config.js",
            "dev": "webpack-dev-server --open --config ./build/dev.config.js"
        }
        ...
    }
    
  • step6.
    此时可能还会产生另外一个问题:打包产生的 dist 文件就会默认保存在 build 文件夹下了。这是因为此时的 base.config.js 文件中的 output 的 path 属性(dist 目录的出口路径)写的是当前目录,如果产生了这个问题,自己手动调试一下就行。_dirname 代表当前项目的根路径,用 __dirname + ‘xxx’ 的形式修改到想要放置的位置

    ...
    module.exports = {
      ...
      output: {
        // 这里
        path: __dirname + '\\dist',
        filename: 'bundle.js'
      },
    }
    

未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值