ES6模块化和webpack

Lorem ipsum dolor sit amet, consectetur adipisicing elit.

Featured image

模块化介绍

什么是模块化

模块化,就是把一个大的文件拆分成若干小文件,而且还能把小文件通过特定的语法组合到一起的实现过程。
比如手机、电脑….等等几乎所有,都是模块化的设计,拿电脑来说,可以把电脑拆分成显示器、键盘、硬盘、内存等一个一个的小模块,当然也能够组装到一起。

优点

模块化的优势:

Node中,规定每个JS文件都是一个小模块。一个项目由许许多多的小模块(JS文件)组合而成。
Node中模块化的优势:

前端模块化的分类 (了解)

实现模块化的语法不止一种。
在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了 AMD (国外 requirejs)、CMD (国内 seajs 淘宝)、CommonJS (nodejs) 等模块化规范
但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通用的模块化标准
例如:

太多的模块化规范给开发者增加了学习的难度与开发的成本。因此,大一统的  ES6 模块化(ES Module)  规范诞生了!

ES6模块化

Node环境使用ES6模块化的要求

node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,可以按照
如下两个步骤进行配置:

  1. 确保安装了 v13.0.0 或更高版本的 node.js
  2. 在 package.json 的根节点中添加 "type": "module" 节点

ES6模块语法

ES6 的模块化,英文 ES Module ,主要包含如下几种用法:

默认导出与默认导入

默认导出的语法: export default 默认导出的成员
默认导入的语法: import 接收名称 from '模块路径'

const a = 10
const b = 20
const fn  = () => {
  console.log('这是一个函数')
}
// 默认导出
// export default a  // 导出一个值
export default {
  a,
  b,
  fn
}
// 默认导入时的接收名称可以任意名称,只要是合法的成员名称即可
import result from './xxx.js'
console.log(result)

注意点:

按需导入与按需导出

按需导出的语法: export const s1 = 10
按需导入的语法: import { 按需导入的名称 } from '模块标识符'

export const a = 10
export const b = 20
export const fn = () => {
  console.log('内容')
}

按需导入的语法

import { a, b as c, fn } from './xxx.js'

注意事项:

将所有内容全部导入

import * as obj from './03-按需导出.js'
console.log(obj)
/**
 * {
 *   uname: 'zhangsan',
 *   age: 20,
 *   fn: function ......,
 *   default: 'hello world'  // 叫做default这个,是默认导出的内容
 * }
 */

直接导入模块(无导出)

如果只想单纯地执行某个模块中的代码,并不需要得到模块中向外共享的成员。
此时,可以直接导入并执行模块代码,示例代码如下:
import '模块的路径'

//xxx.js
for (let i = 0; i < 10; i++) {
  console.log(i)
}


// 导入该模块
import './xxx.js'

实际的项目中,我们使用这种语法,导入css、less、图片等等资源

导入内置模块和第三方模块

// 内置模块,支持默认导入
import fs from 'fs'

// 内置模块,支持按需导入
import { readFile, writeFile } from 'fs'

// 第三方模块,肯定支持默认导入(按需导入不一定支持,因为我们并不知道别人的模块是怎么写的)
import dayjs from 'dayjs'

导入导出总结


总结:

前端工程化

学习目标
① 转变对前端开发的认知
② 在脑海中形成画面,知道企业中真正的前端开发是什么样子的

前端工程化

前端小白眼中的前端开发 vs 实际的前端开发

什么是前端工程化

注意: 企业中的 Vue 和 React 项目,都是基于工程化的方式进行开发的 前端工程化

前端工程化的实现

webpack 的基本概念

什么是 webpack

webpack 是前端项目工程化的具体解决方案。
webpack也是第三方模块,使用之前,需要下载安装。
参考文档:https://webpack.docschina.org/

为什么要学习 webpack

webpack 让前端开发变得更高效
① 代码压缩混淆
② 处理浏览器端 JavaScript 的兼容性
③ 以模块化的方式处理项目中的资源

注意:目前绝大部分的 Vue/React 等前端项目,都是基于 webpack 进行工程化开发的。

安装全部所需的包

目的:一次性把今天所需的包全部安装好。

# 切换镜像源
nrm use taobao
# 初始化
npm init
# 安装项目依赖包
npm install echarts
# 安装项目开发依赖包
npm install webpack@5.73.0 webpack-cli@4.10.0 webpack-dev-server@4.9.2 style-loader@3.3.1 less-loader@11.0.0 less@4.1.2 html-webpack-plugin@5.5.0 html-loader@3.1.0 css-loader@6.7.1 cross-env@7.0.3 clean-webpack-plugin@4.0.0 babel-loader@8.2.5 @babel/core@7.18.2 @babel/plugin-proposal-decorators@7.18.2 @babel/preset-env@7.18.2 -D

演示webpack 的作用

创建折线图项目

目的:以模块化的方式实现echarts图表的项目,为 webpack 的学习做准备。
具体步骤:
创建项目文件夹 webpack-demo,初始化,安装上面的这些包。
① 在 webpack-demo 中,新建 src 源代码目录
② 在 webpack-demo 中,新建 public 静态资源目录
③ src 目录中创建 index.js
④ public 目录中创建 index.html,引入index.js
⑤ index.html中创建一个具有宽高的div,id=”main”(准备放图表)
⑥ index.js 中,通过 ES6 模块化的方式导入echarts,实现一个简单的图表(参考echarts官方网站)

import * as echarts from 'echarts'

;(function () {
  var chartDom = document.getElementById('main')
  var myChart = echarts.init(chartDom)
  var option = {
    xAxis: {
      type: 'category',
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        data: [150, 230, 224, 218, 135, 147, 260],
        type: 'line'
      }
    ]
  }
  option && myChart.setOption(option)
})()

分析项目中遇到的问题

打开页面,发现报错了:
image.png

安装和配置webpack

目标:

安装 webpack 相关的两个包: webpackwebpack-cli (前面已经统一安装过了)
① 在 package.json 的 scripts 节点下,新增 build脚本如下:

"scripts": {
  "build": "webpack"
},

③ 在终端中运行 npm run buildyarn buildpnpm run build 命令,启动 webpack 进行项目的打包构建。

打包结果

执行 npm run build 命令后,会在项目根目录生成 dist文件夹,并在 dist 文件夹中生成 main.js
main.js 就是 echarts.js 和 index.js 的结合体。
index.html 中引入 main.js ,就可以看到最终的效果了。
image.png

Webpack配置

webpack.config.js配置文件

webpack.config.js是什么

作用

被读取的时机

配置打包模式 mode

目标:清楚每个 mode 值的作用,知道每个值的具体应用场景。
mode 节点的可选值有两个,分别是:
① development

② production

webpack-demo文件夹中,创建 webpack.config.js,代码如下:

// 需要导出一个配置对象
module.exports = {
  // 这里写所有的配置项
  // 配置打包模式
  mode: 'development' // development 开发模式 | production 生产模式(默认)
}

webpack 中的默认入口和出口

在 webpack 4.x 和 5.x 的版本中,有如下的默认约定:
① 默认的打包入口文件为 src -> index.js
② 默认的输出文件路径为 dist -> main.js
注意:可以在 webpack.config.js 中修改打包的默认约定

自定义打包的入口与出口

在 webpack.config.js 配置文件中,通过 entry 节点指定打包的入口。通过 output 节点指定打包的出口。
示例代码如下:

const { join } = require('path');

module.exports = {
  // 打包模式
  mode: 'development', //  production:生成  development:开发
  // 入口文件
  entry: join(__dirname, 'src', 'index.js'),
  // 出口文件
  output: {
    path: join(__dirname, 'dist'), // 要使用绝对路径,否则报错
    filename: 'bundle.js',
  }
}

重新运行 npm run build ,即可得到新的打包结果 bundle.js .
index.html 中引入新的打包结果 bundle.js。

webpack 中的插件

概述

webpack 插件的作用
通过安装和配置第三方的插件,可以拓展 webpack 的能力,从而让 webpack 用起来更方便
最常用的 webpack 插件有如下3个:
① clean-webpack-plugin

② webpack-dev-server

③ html-webpack-plugin

clean-webpack-plugin

作用:每次打包构建的时候,自动清理 dist 目录下的旧文件,保证 dist 目录的代码是最新的。
安装依赖包:clean-webpack-plugin_@_4.0.0 (前面已经统一安装过)
在 webpack.config.js 中增加配置:

// 1. 配置自动清理插件(在打包的时候,插件就会自动清理 dist 中没用的文件)
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  // 其他项省略......
  // 插件配置
  plugins: [ 
    new CleanWebpackPlugin()
  ]
}

重新运行 npm run build ,即可将 dist 文件夹中没用的文件清理掉.

html-webpack-plugin

html-webpack-plugin 是 webpack 中的 HTML 插件。
作用:自动把生成好的 bundle.js 注入到 HTML 页面中,并且会把html文件打包进dist文件夹。
安装包 html-webpack-plugin_@_5.3.2 (前面已经统一安装过)
在webpack.config.js中配置 html-webpack-plugin

const HtmlPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  // 其他项省略......
  // 插件配置
  plugins: [ 
    new CleanWebpackPlugin(), // 前面的自动清理插件
    new HtmlPlugin({
      template: join(__dirname, 'public', 'index.html'), // public中,你的html叫什么
      filename: 'index.html' // 打包后的html叫什么(这个文件会生成到dist文件夹)
    })
  ]
}

其他说明:
① 会将初始的模板(public/index.html) 打包进 dist文件夹
② 生成的 dist/index.html 页面,自动注入了打包的 bundle.js 文件(所以去掉 public/index.html 中所有的js引入代码,重新打包)
③ 浏览器应该打开 dist/index.html 看效果。

webpack-dev-server

基本使用

作用:可以让 webpack 监听项目源代码的变化,从而进行自动打包构建。
安装包 webpack-dev-server@4.3.1(前面已经统一安装过)
配置 webpack-dev-server
① 在 package.json -> scripts 中新增一个命令,命令如下:

"scripts": {
  "serve": "webpack serve" // 注意这里是 serve ,不是 server
},

② 在 webpack.config.js 配置文件中,增加 devServer 节点对 webpack-dev-server 插件进行更多的配置

module.exports = {
  mode: 'development',
  //.......
  // .....
  devServer: {
    // port: 9000, // 实时打包所用的端口号
    open: true // 初次打包完成后,自动打开浏览器
  }
}

③ 运行 npm run serve 命令,会创建一个本地服务。
④ 在浏览器中访问 http://localhost:8080 地址,查看效果

注意:凡是修改了 webpack.config.js 配置文件,或修改了 package.json 配置文件,必须重启实时打包的服务器,否则最新的配置文件无法生效!

打包生成的文件哪儿去了?

① 不配置 webpack-dev-server 的情况下,webpack 打包生成的文件,会存放到实际的物理磁盘上

② 配置了 webpack-dev-server(自动打包插件) 之后,打包生成的文件存放到了内存中

webpack 中的 loader

loader 概述

loader加载器有什么用?

所以,loader 加载器的作用是协助 webpack 打包处理特定的文件模块。比如:

loader 的调用过程

打包处理 css 文件

在src中,创建了 abc.css ,里面随便写点。

#main {
  border: solid 5px #c67b24;
}

在 index.js 中,通过 import 导入css文件。

import './abc.css'

直接这样使用,会报错。
image.png
下面配置loader,解决上面的问题:
① 安装包 style-loader@_3.3.0 ](/3.3.0 ) 和 css-loader[@_6.4.0 (前面已经统一安装过)
② 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:

module.exports = {
  mode: 'development',
  // 其他项省略......
  module: {
    rules: [
      // 处理css文件
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
    ]
  }
}

其中,test 表示匹配的文件类型, use 表示对应要调用的 loader
注意:

打包处理 less 文件

① 安装 less-loader_@_10.1.0 和 less@4.1.2(前面已经安装过)
② 在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:

module.exports = {
  mode: 'development',
  // 其他项省略......
  module: {
    rules: [
      // 处理css文件
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      // 处理less文件
      { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
    ]
  }
}

打包处理img标签引入的图片

public/index.html ,通过 <img src="1.png" /> 如果有srcset属性,去掉
配置

module.exports = {
  mode: 'development',
  // 其他项省略......
  module: {
    rules: [
      // 处理css文件
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      // 处理less文件
      { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
      // 处理html中img标签引入的图片
      { test: /\.html$/, use: 'html-loader' }
    ]
  }
}

打包处理asset资源

目前为止,我们还不能在 js 中导入图片。如果项目中有这种需要,还需要下面的配置

使用 asset 来代替 webpack4中的 url-loader 、file-loader、raw-loader。

module.export = {
  output: {
    path: join(__dirname, 'dist'),
    filename: 'bundle.js',
    assetModuleFilename: 'images/[hash:8][ext]', // 配置文件的输出路径
  },
  module: {
    rules: [
      // 处理css文件
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      // 处理less文件
      { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
      // 处理html中的img标签引入的图片
      { test: /\.html$/, use: 'html-loader' },
      // 处理图片资源
      {
        test: /\.(jpg|png|gif)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 12000, // 图片超过这个大小,使用原图片,否则使用base64字符串
          }
        }
      }
    ]
  }
}

至此,所有图片的使用方式,都可以使用了:

打包处理 js 文件中的高级语法

webpack 只能打包处理一部分高级的 JavaScript 语法。对于那些 webpack 无法处理的高级 js 语法,需要借 助于 babel-loader 进行打包处理。
例如 webpack 无法处理下面的 JavaScript 代码:

// js装饰器
function info(target) {
  target.abc = '哈哈哈哈哈哈哈,这是新语法';
}
// 下面的语法,表示给Person加了一个abc属性
@info
class Person { }
console.log(Person.abc);

安装 babel-loader 相关的包

在 webpack.config.js 的 module -> rules 数组中,添加 loader 规则如下:

module.exports = {
  mode: 'development',
  // 其他项省略......
  module: {
    rules: [
      // 处理css文件
      { test: /\.css$/, use: ['style-loader', 'css-loader'] },
      // 处理less文件
      { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
      // ...... 其他配置略
      // .js 文件使用 babel-loader去处理。但是不要处理 node_moduels文件夹中的第三方模块
      { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
    ]
  }
}

配置 babel-loader
在项目根目录下,创建名为 babel.config.js 的配置文件,定义 Babel 的配置项如下:

module.exports = {
  // 声明 babel 可用的插件
  plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]]
}

打包发布

为什么要打包发布

项目开发完成之后,需要使用 webpack 对项目进行打包发布,主要原因有以下两点:
① 开发环境下,打包生成的文件存放于内存中,无法获取到最终打包生成的文件
② 开发环境下,打包生成的文件不会进行代码压缩和性能优化
为了让项目能够在生产环境中高性能的运行,因此需要对项目进行打包发布。

配置 webpack 的打包发布

在 package.json 文件的 scripts 节点下,新增 build 命令如下:

"scripts": {
  "serve": "webpack serve",
  "build": "webpack --mode production"
},

注意:通过 –model 指定的参数项,会覆盖 webpack.config.js 中的 model 选项。

把 JavaScript 文件统一生成到 js 目录中

output: {
  path: join(__dirname, 'dist'),
  filename: 'js/bundle.js', // 这里加入 js 文件夹。
  assetModuleFilename: 'images/[hash:8][ext]' // 配置文件(图片)的输出路径
},

最终,npm run build 打包结果。最终的产出就是 dist 文件夹

base64格式说明

结论:实际开发中,小图可以使用base64格式;大图还是使用原图片。

Source Map

目前的问题

上述问题,可以通过sourceMap解决。

什么是 Source Map

Source Map 就是一个信息文件,里面储存着位置信息。
也就是说,Source Map 文件中存储着压缩混淆后的代码,所对应的转换前的位置。
有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码,能够极大的方便后期的调试。

配置

开发环境

推荐在 webpack.config.js 中添加如下的配置,即可保证运行时报错的行数与源代码的行数 保持一致:

module.exports = {
  // source-map 仅限在开发模式中使用
  //(开发中,程序员需要排错,需要准确的定位错误行号)
  devtool: 'source-map',
}

生产环境

module.exports = {
  // nosources-source-map 适合生产环境。
  //(生产环境,我们不希望别人看到我们的源码,这个配置项只显示行号,但不会显示源码)
  devtool: 'nosources-source-map',
}

总结

实际开发中需要自己配置 webpack 吗?
答案:不需要!
实际开发中会使命令行工具(俗称 CLI)一键生成带有 webpack 的项目
开箱即用,所有 webpack 配置项都是现成的!
我们只需要知道 webpack 中的基本概念即可!