从0到1实现一个 Hello Webpack

389 阅读6分钟

前言 - 阅读《深入浅出webpack》一书实践

目录

  • 安装与使用
  • Loader
  • Plugin
  • Devserver

源码参考

安装与使用

# npm i -D 是 npm install --save-dev 的简写,
--save 是指安装模块并保存到 package.json 的 devDependencies

# 安装最新稳定版
npm i -D webpack

# 安装指定版本
npm i -D webpack@<version>

# 安装最新体验版本
npm i -D webpack@beta

# 全局安装
npm i -g webpack

下面通过 Webpack 构建一个基于 CommonJS 模块化编写的项目,该项目会使用JavaScript通过webpack编译在网页中显示 Hello,Webpack。

创建一个HTML

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <!--引入 Webpack 输出的 JavaScript 文件暂时命名 bundle.js -->
  <script src="./dist/bundle.js"></script>
</head>

<body>
  <div id="app"></div>
</body>

</html>

创建一个执行入口文件 main.js

var showText = require("./utils.js");
showText('Webpack');

main.js 里边的代码很简单,就是 require 导入一个模块儿 utils.js 在commonJs模块儿规范里边一个Js文件就相当于一个模块儿,紧跟着就是调用这个模块儿里的函数;好,那么此时我需要创建这个模块儿文件utils.js

创建utils.js

// 操作 DOM 元素,把 content 显示到网页上
function showText(content) {
  window.document.getElementById('app').innerText = 'Hello,' + content;
}
// 通过 CommonJS 规范导出 show 函数
module.exports = showText;

内容也很简单就是一个函数 showText ,函数体就是在页面显示一段文本,紧跟着后边就是导出这个函数。

好,入口文件以及执行的操作都有了,接下来我们需要一个webpack配置文件,紧接着,我们再创建一个webpack.config.js 文件

创建webpack.config.js

const path = require('path');
module.exports = {
  entry: './main.js', // 入口文件,很重要,运行webpack构建唯一执行的文件
  output: {
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
    filename: 'bundle.js',
    // 输出文件都放到 dist 目录下
    // __dirname 只当前指向目录
    path: path.resolve(__dirname, './dist'),
  }
};

内容其实也很少啊,我们简单分析一下,path 基于node模块的path模块儿,主要就是路径(顾名思义啊),然后就是导出一个对象,这个对象里边包含了入口文件和出口文件配置;具体的解释都写在代码注释了。

截至到这里,我们的准备文件都已经就绪了,目录暂时如下:

webpack // 工程目录
	index.html
	main.js
	utils.js
	webpack.config.js

看到这里,小伙伴们是不是感觉还差点什么,对,package.json 文件,那么我们初始化一下 npm install , 会发现命令行里会让我们填写一些东西,例如:webpack项目名字,作者,版本,入口文件等等。我们一一确认一下就好。 在这里插入图片描述 package.json 文件,我们需要在 scripts 对象里新增一条执行命令,执行我们的 webpack 配置文件,也就是 webpack.config.js文件

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "HiSen",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    // 新增命令
    "build":"webpack --config webpack.config.js"
  },
  "author": "",
  "license": "ISC"
}

准备就绪了,我们安装 webpack npm install -D webpack , 然后执行我们的webpack构建命令 npm run build 其实就是执行的 "webpack --config webpack.config.js" 看上边的scripts对象。结果不尽人意啊(如下图示),还让我们安装 webpack-cli 理由既然是作为单独的包提供,好吧,照着来吧。 在这里插入图片描述 这里有一个小插曲,选择 yes 安装居然会报错,我没有去查看到底是因为什么啊,我直接自己命令装了一下 在这里插入图片描述 好了,这一下算是全部文件准备就绪了,此时我们的目录和package.json文件都发生了一点点小小的改变;如下:

webpack  // 工程目录
	index.html
	main.js
	utils.js
	webpack.config.js
	package.json
	node_modules

目录在安装webpack的时候会自动生成一个依赖文件,node_modules 在这里插入图片描述 package.json文件 devDependencies 对象也增加了我们安装的 webpackwebpack-cli

最后,我们来尝试构建打包一下:npm run build 在这里插入图片描述 恭喜你 ,结果很明显,成功了,我们简单的实现了一个webpack构建打包的实例。

代码结构目录

webpack  // 工程目录
	dist // 构建生成目录
	   bundle.js
	index.html
	main.js
	utils.js
	webpack.config.js
	package.json
	node_modules

打开本地的HTML就可以看到,我们的页面,可能会遇到一个小小的错误 Cannot set property 'innerText' of null 因为JavaScript运行时,id="app"的那个div元素可能还没解析和加载,只需把整个搬到前面即可,就像这样:在这里插入图片描述 最后结果: 在这里插入图片描述

Loader

很重的一个知识点,接下来我们能创建一个css文件 main.css,然后同样在我们的入口文件main.js 文件引入,结果如下:

main.css文件内容

#app{
  text-align: center;
  color:red;
}

main.js 导入

require("./main.css"); // 导入main.css 文件
var showText = require("./utils.js");
showText('Webpack');

显然这样直接构建,Webpack 构建是会报错的,因为 Webpack 不原生支持解析 CSS 文件。要支持非 JavaScript 类型的文件,需要使用 Webpack 的 Loader 机制。所以我们修改webpack.config.js 文件,添加 Loader 配置(Loader 可以理解为是文件解析器,解析类似 css,less,scss等)

module.exports = {

  entry: './main.js', // 入口文件
  output: {
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
    filename: 'bundle.js',
    // 输出文件都放到 dist 目录下
    // __dirname 只当前指向目录
    path: path.resolve(__dirname, './dist'),
  },
  // 添加如下配置
  module: {
    rules: [
      {
        test: /\.css$/, // 用正则去匹配要用该 loader 转换的 CSS 文件
        use: ['style-loader', 'css-loader'] // 注意这里顺序不能错
      }
    ]
  }
};

user 还有另外一种写法,数组对象形式

use: [
 'style-loader', 
  {
    loader:'css-loader',
    options:{
      minimize:true, // minimize 告诉 css-loader 要开启 CSS 压缩。
    }
  }
]

配置里的 module.rules 数组配置了一组规则,告诉 Webpack 在遇到哪些文件时使用哪些 Loader 去加载和转换。

这里的配置告诉 Webpack 在遇到以 .css 结尾的文件时先使用 css-loader 读取 CSS 文件,再交给 style-loader 把 CSS 内容注入到 JavaScript 里。

注意: use 属性的值需要是一个由 Loader 名称组成的数组,Loader 的执行顺序是由后到前的; 每一个 Loader 都可以通过 URL querystring (或者数组对象)的方式传入参数。

好,接下来,我们来实践一下,在构建之前,首先需要安装 Loader,在这里使用到了 style-loader,css-loader ,安装一下

npm i -D style-loader css-loader

package.json 文件会显示出来,后续大家请自己关注,我就不贴图了。 在这里插入图片描述 效果如下 在这里插入图片描述

你会发现 bundle.js 文件被更新了,里面注入了在 main.css 中写的 CSS,而不是会额外生成一个 CSS 文件。 但是重新刷新 index.html 网页时将会发现 Hello,Webpack 居中了,样式生效了! 也许你会对此感到奇怪,第一次看到 CSS 被写在了 JavaScript 里!这其实都是 style-loader 的功劳,它的工作原理大概是把 CSS 内容用 JavaScript 里的字符串存储起来, 在网页执行 JavaScript 时通过 DOM 操作动态地往 HTML head 标签里插入 HTML style 标签。 也许你认为这样做会导致 JavaScript 文件变大并导致加载网页时间变长,想让 Webpack 单独输出 CSS 文件。接着往下看

Plugin

首先我们需要下载插件 extract-text-webpack-plugin

npm i -D extract-text-webpack-plugin

然后直接看向 webpack.config.js 配置文件,进行如下配置:

  1. 引入 extract-text-webpack-plugin 插件, 作用是提取出 JavaScript 代码里的 CSS 到一个单独的文件
  2. module 里添加 plugin 数组,通过插件的 filename 属性,告诉插件输出的 CSS 文件名称是通过 [name]_[contenthash:8].css 字符串模版生成的,里面的 [name] 代表文件名称, [contenthash:8] 代表根据文件内容算出的8位 hash 值,
  3. 修改 rules 里边 use,使用ExtractTextPlugin 插件转换 css-loader,这里不需要再使用 style-loader 解析了。
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 引入 plugin 插件 

module.exports = {

  entry: './main.js', // 入口文件
  output: {
    // 把所有依赖的模块合并输出到一个 bundle.js 文件
    filename: 'bundle.js',
    // 输出文件都放到 dist 目录下
    // __dirname 只当前指向目录
    path: path.resolve(__dirname, './dist'),
  },
  // 添加如下配置
  module: {
    rules: [
      {
        // 用正则去匹配要用该 loader 转换的 CSS 文件
        test: /\.css$/,
        // use: ['style-loader', 'css-loader'],
        use: ExtractTextPlugin.extract({
          // 转换 .css 文件需要使用的 Loader
          use: ['css-loader']
        }),
      }
    ]
  },

  plugins: [
    new ExtractTextPlugin({
      // 从 .js 文件中提取出来的 .css 文件的名称
      filename: `[name]_[contenthash:8].css`,
    }),
  ]
};

以上修改完成后,我们开始构建,索嘎,成功的构建出来了,如果发现报错,请检查webpack版本和ExtractTextPlugin版本是否匹配。

构建出来的 dist 目录如下:

dist
	bundle.js // js 里边也不含有 css 样式代码了
	main_69bba57c.css // 而这个css文件就是我们的 main.css 文件

DevServer

前面的几节只是让 Webpack 正常运行起来了,但在实际开发中你可能会需要:

  1. 提供 HTTP 服务而不是使用本地文件预览;
  2. 监听文件的变化并自动刷新网页,做到实时预览;
  3. 支持 Source Map,以方便调试。

当然这些 webpack 都为你想好了,DevServer 会启动一个 HTTP 服务器用于服务网页请求,同时会帮助启动 Webpack ,并接收 Webpack 发出的文件更变信号,通过 WebSocket 协议自动刷新网页做到实时预览。

好了,我们实践一下,安装 webpack-dev-server 这里有版本兼容问题,我用的是2.9.4,webpack@3.6.0,大家需要注意以下。

npm i -D webpack-dev-server

运行命令 webpack-dev-serve , 如下运行成功,此时有一个地方,注意看,第三行,四行 在这里插入图片描述 这意味着 DevServer 启动的 HTTP 服务器监听在 http://localhost:8081/ ,DevServer 启动后会一直驻留在后台保持运行。

细心的小伙伴已经发现了问题,我们的打开 http://localhost:8081/ 发现网页内容没错,可是样式却没了,而且 ./dist/bundle.js 加载404了;原因是 DevServer 会把 Webpack 构建出的文件保存在内存中,在要访问输出的文件时,必须通过HTTP服务访问。 由于DevServer不会理会 webpack.config.js 里配置的output.path属性,所以正确的 index.html 应该修改为:

<link rel="stylesheet" href="main_69bba57c.css">
<script src="bundle.js"></script>

然后大家再尝试着修改任何文件,会发现,犹如热更新一样,随着修改,终端和网页都在随之刷新。 好了,第一章节就到这了,我们下章见。

注释:这里我们学习打包的是 js 文件,当然也可以打包 vue 文件或者 jsx 文件,只是需要 bebal-loader 编译和一些 loader 解析

相关链接