项目说明

笔记来源:拉勾教育 大前端高薪训练营
阅读建议:内容较多,建议通过左侧导航栏进行阅读

模块化打包工具解决的是前端整体的模块化,并不单指 JavaScript 模块化。

产生原因

  • ES Modules 存在环境兼容问题;
  • 模块文件过多,网络请求频繁;
  • 所有的前端资源都需要模块化。

Webpack

基本介绍

webpack,常用的模块打包器(Module bundler),可以将零散的JavaScript 代码打包到一个 JS 文件中。
对于那些存在环境兼容问题的代码,可以在打包过程中通过模块加载器(Loader)对其进行编译转换。
webpack,具有代码拆分(Code Splitting)的能力,可以将所有的代码都按照需要进行打包,可以实现渐进式的打包方式。

适用场景

开发应用程序,使用 Webpack。

快速上手

  • 1,准备三个文件,本次使用 http-server 构建服务器,也可以采用其他的

    代码示例如下(heading.js):

      export default () => {const element = document.createElement('h2')element.textContent = 'hello world'element.addEventListener('click', () => {alert('Hello webpack')})return element}
    

    代码示例如下(index.js):

      import createHeading from './heading.js'const heading = createHeading()document.body.append(heading)
    

    代码示例如下(index.html):

       <script type="module" src="./src/index.js"></script>
    
  • 2,webpack 是一个 NPM 工具模块,需要初始化包管理文件 package.json

      $ yarn init --yes # or npm init -y
    
  • 3,安装 webpack核心模块 ,以及对应的 webpack-cli模块

      $ yarn add webpack webpack-cli --dev # or npm i webpack webpack-cli --save-dev
    
  • 4,查看 webpack 是否安装成功

      $ yarn webpack --version
    

    运行结果,如下图所示:

  • 5,使用 webpack ,进行打包

      $ yarn webpack
    

    运行结果,如下图所示:


    可以看到,打包后,会生成一个 dist 文件夹,里面包含一个 main.js

  • 6,在 package.json 中,使用 NPM Scripts 对打包命令进行包装

    配置代码如下:

      {"scripts": {"build": "webpack"}}
    
  • 7,将 index.html 中引入的 js,进行修改

    代码示例如下(index.html):

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

基本使用

webpack 4.0+ 支持按照约定的内容,进行打包,即 src/index.js(默认入口文件) 打包到 dist/main.js

配置文件

在项目根目录添加 webpack.config.js 配置文件。

工作模式

  • development(开发模式),优化打包的速度

      $ yarn webpack --mode development
    
  • production(生产模式),启动多个插件,进行代码的压缩等
      $ yarn webpack --mode production
    
  • none,运行最原始状态的打包,不会做任何的处理
      $ yarn webpack --mode none
    

基本配置

  • 指定 webpack 打包的入口文件

    配置代码如下(webpack.config.js):

      module.exports = {entry: './src/main.js' // 相对路径时,./ 不能省略}
    
  • 设置输出文件的位置

    配置代码如下(webpack.config.js):

      const path = require('path')module.exports = {output: {filename: 'bundle.js', // 设置输出文件的名称// 指定输出文件所在的目录,必须是绝对路径// 利用 node 的 path 模块,组合生成绝对路径path: path.join(__dirname, 'output')     }}
    
  • 配置打包的工作模式 值为:development | production | none

    配置代码如下(webpack.config.js):

      module.exports = {mode: 'development',}
    

导入资源模块

JavaScript 驱动整个前端应用的业务,因此需要把 打包入口 设置为 js 文件,它也相当于是 运行入口。在 js 代码中 通过 import 导入其他资源文件。

  • 1,在 main.js 中引入 css 文件

    代码示例如下(main.js):

      import createHeading from './heading.js'import './main.css'const heading = createHeading()document.body.append(heading)
    
  • 2,在 webpack.config.js 中设置入口文件

    配置代码如下(webpack.config.js):

      module.exports = {entry: './src/main.js' // 相对路径时,./ 不能省略}
    

    页面效果,如下图所示:


    注意

    一般需要根据代码的需要动态导入资源,因为需要资源的不是应用,而是此时正在编写的代码。

    优势

    1)逻辑合理,JS 确实需要这些资源文件;
    2)确保上线资源不缺失,都是必要的。

模块加载

webapck 进行打包过程中,会使用 loader 进行编译转换。下面,来看一下都有那些编码方式会触发对应的 loader ,并最终将其打包为 资源模块?

Js 代码

webpack 兼容多种模块化标准,但是建议不要混合使用标准

  • 1,遵循 ES Modules 标准的 import 声明

    语法代码如下:

      import ... from 'module_path'
    
  • 2,遵循 CommonJs 标准的 require 函数

    语法代码如下:

      require('module_path')//通过 require 函数载入 ES Modules,对于 ES Modules 的默认导出// 需要通过导入require 结果的default 属性进行获取const xxx = require('module_path').default
    
  • 3,遵循 AMD 标准的 define 函数 和 require 函数

    语法代码如下:

      define(['module_path', 'module_path', ...], ( mod1, mod2) => { })require(['module_path', 'module_path', ...], ( mod1, mod2) => { })
    
样式代码
  • @import 指令 和 url 函数,会触发相应的模块加载

      @import url('./reset.css');body {background-color: url('./u0.png');}
    
HTML 代码
  • 图片标签的 src 属性,会触发相应的模块加载

      <footer><img src="./u0.png"><a:href></a:href></footer>
    
      import footerHtml from './footer.html'document.write(footerHtml)
    

Loader

Loader (加载器) 是 webpack 的核心特性,借助于 Loader 就可以加载任何类型的资源,从而实现资源模块加载的功能。

webpack 内部内置的 loader 只能对 JS 文件进行打包,其他的资源文件需要引入其他的 loader 进行处理,最终都会转换成 js 模块。

Loader Kinds

编译转换类

编译转换器,会把加载到的资源模块转换为 JavaScript 代码,如 css-loader。

css-loader

css-loader,用来对 css 文件进行转换的加载器,将资源文件转换成 js 代码。

基本使用

  • 1,安装 loader 模块

      $ yarn add css-loader --dev # or npm i css-loader --save-dev
    
  • 2,在 css 文件中,书写 body 的样式

    样式代码如下(main.css):

      body {margin: 0 auto;padding: 0 20px;max-width: 800px;background-color: #000000;}
    
  • 3,在 webpack.config.js 中进行配置

    配置代码如下(webpack.config.js):

      module.exports = {entry: './src/main.css',module: {rules: [  // rules 数组,是指针对其他资源模块的加载规则的配置{   test: /.css$/,  // 正则表达式,用来匹配在打包过程中遇到的文件路径use: 'css-loader' // 用来指定匹配到的文件,需要去使用的 loader}]}}
    

    页面效果,如下图所示:

    可以看到,此时页面中并没有对应的样式。这是因为还需要 style-loader 将转换后的结果追加到页面中。

style-loader

style-loader,用来把 css-loader 转换后的结果,通过 style 标签的形式追加到页面中。

基本使用

  • 1,安装 loader 模块

      $ yarn add style-loader --dev # or npm i style-loader --save-dev
    
  • 2,修改 webpack.config.js 中的配置

    配置代码如下(webpack.config.js):

      module.exports = {entry: './src/main.css',module: {rules: [ // rules数组,是指针对其他资源模块的加载规则的配置{   test: /.css$/, // 正则表达式,用来匹配在打包过程中遇到的文件路径// 用来指定匹配到的文件,需要去使用的 loaderuse: [ // 若配置多个loader,执行顺序是 从后往前 的'style-loader', 'css-loader'   // 先将 css 代码转换成 js 模块]}]}}
    
babel-loader

babel-loader,用来将 ES6+ 的新特性,转换为 ES5,需要 @babel/core 核心模块,以及用于去完成具体特性转换插件的集合 @babel/preset-env

基本使用

  • 1,安装 loader 模块

      $ yarn add babel-loader @babel/core @babel/preset-env --dev
    
  • 2,在 webpack.config.js 中添加配置规则

    配置代码如下(webpack.config.js):

      module.exports = {module: {rules: [{ // 通过 babel-loader 取代默认的加载器,处理代码中的新特性test: /.js$/,use: 'babel-loader', options: {presets: ['@babel/preset-env']}},]}}
    

    总结

    1)Webpack 只是 打包工具;
    2)加载器可以用来编译转换代码。

html-loader

html-loader,用来处理 HTML 中制定的标签属性。

基本使用

  • 1,安装 loader 模块

      $ yarn add html-loader --dev
    
  • 2,在 HTML 中编写代码

    代码示例如下(footer.html):

      <footer><img src="./u0.png"><a:href></a:href></footer>
    
  • 3,在 JS 文件中进行导入

    代码示例如下(main.js):

      import footerHtml from './footer.html'document.write(footerHtml)
    
  • 4,在 webpack.config.js 中,添加配置

      module.exports = {module: {// 针对其他资源模块的加载规则的配置rules: [{test: /.html$/,use: {loader: 'html-loader',options: {// 指定哪个标签属性组合应该被此 loader 处理// 这个属性默认只有 'img:src'attrs: ['img:src', 'a:href']}}}]}}
    
文件操作类

文件操作类加载器,会把加载到的资源模块拷贝到输出的目录,同时会将文件的访问路径向外导出,如 file-loader。

file-loader

file-loader,文件资源加载器,这里主要指图片、字体等资源。

基本使用

  • 1,安装 loader 模块

      $ yarn add file-loader --dev # or npm i file-loader --save-dev
    
  • 2,在 main.js 引入图片

    代码示例如下(main.js):

      import createHeading from './heading.js'import './main.css'import u from './u0.png'const heading = createHeading()document.body.append(heading)const img = new Image()img.src = udocument.body.append(img)
    
  • 3,在 webpack.config.js 中添加配置规则

    配置代码如下(webpack.config.js):

      module.exports = {output: {filename: 'main.js',path: path.join(__dirname, 'dist'),publicPath: 'dist/'  // 设置网站的根目录  / 不能省略,默认为 ''},module: {rules: [{   // 配置图片规则test: /.png$/, use: 'file-loader'}]}}
    
url-loader

url-loader,用来将资源(图片、字体等)文件转换为 Data URL 的形式,Data URLs 是一种特殊的 url 协议,url 可以直接去表示文件内容的方式,也就是说,这种 URL 中的文本就已经包含了文件内容。

在使用过程中,不会再去发送任何的 HTTP请求。如果要将图片、字体等二进制的文件进行编译时,会将文件的内容进行 base64 编码,然后以 base64编码 过后的结果(一个字符串)去表示文件的内容。

url-loader,适合转换体积比较小的文件资源。

基本使用

  • 1,安装 loader 模块

      $ yarn add url-loader --dev # or npm i url-loader --save-dev
    
  • 2,在 webpack.config.js 中进行配置

    配置代码如下(webpack.config.js):

      module.exports = {module: {rules: [{test: /.png$/,use: {loader: 'url-loader',options: { // 设置配置选项limit: 10 * 1024 // 10 KB 单位字节,只匹配 10 KB 以下的}}}]}}
    

    最佳实践

    1)小文件使用 Data URLs,转换为 Data URLs 嵌入代码中,减少请求次数;
    2)大文件单独提取存放,提高加载速度。

    注意

    在对 url-loader 适合的文件大小进行限制后,需要同时安装 file-loader,因为超出限制的文件,会去查找 file-loader 进行转换。如果不安装 file-loader,将会因为找不到,而引起程序报错。

代码检查类

代码检查类加载器,会对所加载到的资源文件(一般指代码)去进行校验。目的是为了统一代码的风格,从而提高代码质量,这种加载器一般不会修改生产环境的代码,如 eslint-loader。

Loader Dev

开发需求

希望得到的结果是 markdown 转换过后的字符串。

准备工作
  • 1,新建一个名为 markdown-loader 的项目,并创建 src 文件夹;

  • 2,在 src 文件夹下,新建 xxx.md 文件,测试内容随便写;

  • 3,在 src 文件夹下,新建 main.js 入口文件,并引入 xxx.md 文件

    代码示例如下(main.js):

      import about from './about.md'console.log(about);
    
  • 4,在根目录下,新建 index.html,页面展示的入口文件;

  • 5,初始化包管理文件 package.json

      $ yarn init --yes # or npm init -y
    
  • 6,安装 webpack 模块,以及其依赖命令模块 webpack-cli

      $ yarn add webpack webpack-cli --dev # or npm i webpack webpack-cli --save-dev
    
  • 7,在根目录下,新建 markdown-loader.js,即 md 文件的转换 loader

  • 8,在根目录下,新建 webpack.config.js,并设置对 md文件的规则配置

    配置代码如下(webpack.config.js):

      const path = require('path')module.exports = {mode: 'none',entry: './src/main.js',output: {filename: 'bundle.js',path: path.join(__dirname, 'dist'),publicPath: 'dist/'},module: {rules: [{   // 用来转换的 loader 既可以是一个模块,也可以是一个 js 文件test: /.md$/,use: './markdown-loader' }]}}
    
基本配置

markdown-loader.js 文件需求,输入:就是每次加载到的资源文件;输出:就是此次加工后的结果。

  • 1,在 markdown-loader.js 中进行配置

    配置代码如下(markdown-loader.js):

      // 通过 source 参数,接收输入module.exports = source => {console.log(source);return `console.log('hello ~')`}
    

    可以看到,使用的 js 的代码标准进行了输出字符串,这是因为 loader 要求输出结果必须是一段标准的 JavaScript代码

  • 2,若要解析 md 文件,则需要安装 md 文件的解析模块

      $ yarn add marked --dev # or npm i marked --save-dev
    
  • 3,在 markdown-loader.js 中,使用 marked 模块解析 md 文件,并将结果以模块形式导出

    配置代码如下(markdown-loader.js):

      const marked = require('marked') // 导入 md 文件的解析模块module.exports = source => { // 解析 source, 返回值是一串html字符串,即转换后的结果const html = marked(source)// 将其结果进行导出,需要输出 JavaScript 代码return `module.exports = ${JSON.stringify(html)}`// or // return `export default ${JSON.stringify(html)}`}
    
  • 4,返回 html字符串,交给下一个 loader 处理

    配置代码如下(markdown-loader.js):

      const marked = require('marked') // 导入 md 文件的解析模块module.exports = source => { // 解析 source, 返回值是一串html字符串,即转换后的结果const html = marked(source)// 返回 html字符串,交给下一个 loader 处理return html}
    
  • 5,安装用于处理 html 加载的 loader

      $ yarn add html-loader --dev # or npm i html-loader --save-dev
    
  • 6,修改 webpack.config.js 配置文件

    配置代码如下(webpack.config.js):

      const path = require('path')module.exports = {mode: 'none',entry: './src/main.js',output: {filename: 'bundle.js',path: path.join(__dirname, 'dist'),publicPath: 'dist/'},module: {rules: [{test: /.md$/,use: ['html-loader','./markdown-loader' // 按照 从后往前 的顺序进行执行]}]}}
    

Plugins

webpack plugins 可以增强 webpack 自动化能力,可以解决除了资源加载以外的其他自动化工作,例如:打包之前,自动清除 dist 目录;拷贝静态文件至输出目录;压缩输出代码等。

clean-webpack-plugin

clean-webpack-plugin,自动清除输出目录插件。

基本使用

  • 1,安装插件模块

      $ yarn add clean-webpack-plugin --dev
    
  • 2,在 webpack.config.js 中,添加插件配置

    配置代码如下(webpack.config.js):

      // 导入 clean-webpack-plugin 插件,这个插件导出一个 CleanWebpackPlugin 成员const { CleanWebpackPlugin } = require('clean-webpack-plugin')module.exports = {// ...   plugins: [  // plugins 专门用来配置插件的属性// 一般插件导出的都是一个类型,通过这个类型创建实例// 然后将这个实例,放入到 plugins 数组中new CleanWebpackPlugin()]}
    

    注意

    plugins 属性,是一个数组,它与 module 属于同级。

html-webpack-plugin

html-webpack-plugin,自动生成 HTML 插件,不需要在手动书写 html 入口文件。

基本使用

  • 1,安装插件模块

      $ yarn add html-webpack-plugin --dev
    
  • 2,在 webpack.config.js 中,导入插件,并进行配置

    配置代码如下(webpack.config.js):

      const path = require('path')const { CleanWebpackPlugin } = require('clean-webpack-plugin')// html-webpack-plugin 默认导出的就是一个类型,无需解构其内部成员const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'none',entry: './src/main.js',output: {filename: 'bundle.js',path: path.join(__dirname, 'dist'),// publicPath: 'dist/' // 自动生成 html 时,不需配置},plugins: [new CleanWebpackPlugin(),// 添加 HtmlWebpackPlugin 实例对象    // 自定义输出文件内容:给构造函数传入对象参数,指定配置选项new HtmlWebpackPlugin({ // 用于生成 index.htmltitle: 'Webpack Plugin Sample', // 设置 HTML 的标题meta: { // 设置页面中的 元数据标签viewport: 'width=device-width'},// 指定模板文件,使生成的HTML文件根据模板文件进行生成template: './src/index.html'}),// 同时输出多个页面文件, 即可以添加多个 HtmlWebpackPlugin 实例// 每一个 HtmlWebpackPlugin 实例,就是用来生成一个 HTML文件的new HtmlWebpackPlugin({ // 用于生成 about.htmlfilename: 'about.html' // 指定输出的文件名})]}
    

copy-webpack-plugin

copy-webpack-plugin,将不需要编译转换的 静态资源文件 拷贝到输出目录,一般存放在 public 目录下。

基本使用

  • 1,安装插件模块

      $ yarn add copy-webpack-plugin --dev
    
  • 2,在 webpack.config.js 中,导入插件,并进行配置

    配置代码如下(webpack.config.js):

      const path = require('path')const CopyWebpackPlugin = require('copy-webpack-plugin')module.exports = {// ...plugins: [// ...// 传入 数组参数,用于指定需要去拷贝的文件路径new CopyWebpackPlugin([ // 'public/**'  // 可以是一个目录,通配符,文件路径'public'  // 表示会将 public 目录下所有文件全部拷贝到输出目录])]}
    

plugins dev

Plugin 是通过 在webpack 生命周期的钩子中挂载函数实现扩展的。Webpack 要求,插件必须是一个函数或者是一个包含 apply 方法的对象,最后将其挂载到 钩子上。

开发需求

删除 bundle.js 中生成的注释。

基本配置
  • 在 webpack.config.js 中,进行插件的创建和使用

    配置代码如下(webpack.config.js):

      class MyPlugin { // 定义一个插件类型/*** 在 webpack 启动时,自动被调用* @param {*} compiler * compiler 对象参数,是 webpack 工作过程中最核心的一个对象* compiler 对象参数,包含了此次构建的所有配置信息* 通过 compiler 对象注册钩子函数*/apply(compiler) {console.log('MyPlugin 启动');/*** 通过 compiler.hooks 可以访问到钩子* 通过 tap 方法去注册钩子函数* tap方法,接收两个参数:* -- 第一个参数:插件的名称* -- 第二个参数:需要挂载到钩子上的函数*/compiler.hooks.emit.tap('MyPlugin', compilation => {// compilation 对象 => 可以理解为此次打包的上下文// 所有打包的结果,都会放到 compilation 对象当中for (const name in compilation) {// 对象中的键(属性名),代表每一个资源文件的名称// 判断只对 js文件 进行处理if (name.endsWith('.js')) {// assets 获取即将写入目录当中的资源文件信息// source() 拿到对应的资源文件的内容const contents = compilation.assets[name].source()// 使用正则替换代码中的注释const withoutComments = contents.replace(/\/*\**\*\//g, '')// 将结果 覆盖到原有的文件中compilation.assets[name] = {source: () => withoutComments, // 返回新内容size: () => withoutComments.length // 返回内容的大小,必须方法}}}})}}module.exports = {// ...plugins: [// ...// 应用 MyPlugin 插件new MyPlugin()]}
    

体验优化

自动编译

  • 1,直接在命令行启动 watch 工作模式,监听文件变化,自动重新打包。

      $ yarn webpack --watch
    
  • 2,在 package.json 中,配置 NPM Scripts

    配置代码如下(package.json):

      {"scripts": {"build": "webpack --watch"}}
    

    可以看到,开启 watch 工作模式以后,打包命令会一直处于工作状态,当文件修改后,会自动进行打包,直到手动结束 cli 命令。

自动刷新

使用 BrowserSync 启动热更新开发 Web服务器,实现自动刷新浏览器

  • 1,全局安装 BrowserSync 模块

      $ yarn global add browser-sync # or npm i browser-sync -g
    
  • 2,使用命令启动 web服务器

      $ browser-sync dist --files "**/*"
    

    缺点

    1)操作上太麻烦了;
    2)效率上降低了。

Dev Server

Webpack Dev Server,是 Webpack 官方推出的开发工具,提供用于开发的 HTTP Server,集成了 自动编译自动刷新浏览器 等功能。

版本说明

本次安装 Webpack Dev Server 版本,要求 webpack 4 版本以下webpack 5+ 版本已经集成了 Webpack Dev Server 开发工具,无需再进行安装,直接使用 webpack server 即可开启服务。

注意

Webpack Dev Server 的版本,要比 Webpack 的版本低一级,否则无法使用。

准备工作

  • 1,安装 开发工具

      $ yarn add webpack-dev-server --dev
    
  • 2,运行命令,使用 --open 自动唤醒浏览器,并打开运行地址

      $ yarn webpack-dev-server --open
    

    执行说明

    1)运行时,内部自动使用 webpack 进行打包;
    2)启动 HTTP Server,自动运行打包结果;
    3)自动监听代码变化,自动立即重新打包;
    4)打包结果不会写入到磁盘中,打包结果会暂时存放在内存中;
    5)HTTP Server 会从内存中读取这些文件,然后发送给浏览器。

    优势

    Webpack Dev Server,会减少很多磁盘读写操作,从而大大提高构建效率。

    问题

    Dev Server 默认只会 serve 打包输出文件,即只要是 webpack 打包输出的文件,都可以直接被访问。其他静态资源如果也需要 serve,则需要去告知 webpack。

基本配置

  • 1,配置静态资源访问

    配置代码如下(webpack.config.js):

      const path = require('path')const { CleanWebpackPlugin } = require('clean-webpack-plugin')const HtmlWebpackPlugin = require('html-webpack-plugin')// const CopyWebpackPlugin = require('copy-webpack-plugin')module.exports = {// 专门为 webpack-dev-server 指定的配置选项devServer: {// 额外为开发服务器指定查找静态资源目录,可以是字符串或数组,配置一个或多个contentBase: ['./public']},plugins: [new CleanWebpackPlugin(),// 用于生成 index.htmlnew HtmlWebpackPlugin({title: 'Webpack Tutorials',meta: {viewport: 'width=device-width'},template: './src/index.html'}),// 开发阶段最好不要使用这个插件// 一般放在上线前的最后一次打包使用// 开发阶段需要频繁的serve, 多次使用插件会影响运行效率// new CopyWebpackPlugin(['public'])]}
    
  • 2,开发阶段接口跨域问题,需要配置 代理API

    配置代码如下(webpack.config.js):

      module.exports = {// 专门为 webpack-dev-server 指定的配置选项, 开发阶段的配置devServer: {// 额外为开发服务器指定查找静态资源目录,可以是字符串或数组,配置一个或多个contentBase: ['./public'],// proxy属性,用来添加代理服务配置proxy: {/*** 每一个属性就是一个代理规则的配置* 属性名: 需要被代理的请求路径前缀,*        即请求以哪一个地址开始,就会走对应的代理请求* 属性值: 为这个前缀所匹配到的代理规则配置 */ '/api': {// http://localhost:8080/api/users => https://api.github.com/api/userstarget: 'https://api.github.com', // 代理目标// http://localhost:8080/api/users => https://api.github.com/users// 如果代理目标地址中没有‘/api’,则需要重写代理目标地址pathRewrite: {'^/api': '' // 以正则的形式进行匹配,以 ^ 开头},// 不能使用 localhost:8080 作为请求 GitHub 的主机名// 设置改变主机名changeOrigin: true}}}}
    

Source map

Source map,源代码地图,用来映射转换过后的代码与源代码之间的关系。一段转换过后的代码,通过转换过程中生成的 Source Map 文件,可以逆向得到源代码。主要用来在开发阶段进行调试和定位错误。

基本使用

  • 1,引用 Source Map 的注释

      //# sourceMapppingURL=jquery-3.4.3.min.map
    

    在浏览器中,打开开发人员工具,开发人员工具加载到的 js文件最后存在上面的注释,会自动去请求 Source Map 文件,然后根据文件的内容逆向解析出对应的源代码,以便于调试。又因为存在映射关系,所以源代码中如果出现错误,就会很容易定位到源代码中错误的位置。

  • 2,在 webpack.config.js中,配置 Source Map

    配置代码如下(webpack.config.js):

      // 其余代码省略module.exports = {// 配置开发过程中的辅助工具,// 也就是与 Source Map 相关的一些功能配置devtool: 'source-map',}
    
  • 3,运行打包命令,查看 bundle.js 底部是否存在 Source Map 的注释

      $ yarn webpack
    

    查看 bundle.js 底部,如下图所示:


    Webpack 对 Source Map的风格支持

    Webpack 目前支持 12 种不同的方式,每种方式所生成的 Source Map 效果,以及生成 Source Map 的速度都是不一样的。效果最好的,生成速度越慢;反之,生成最快的,生成的 Source Map 效果不好,几乎没有。

  • 12 种方式对比,如下图所示:

eval 模式

eval() 函数,用来运行字符串中的 JavaScript 代码。默认情况下,运行的代码会运行在一个临时的虚拟机环境中,可以通过 sourceURL 来声明这段代码所属的文件路径。

基本使用

  • 1,在浏览器控制台,进行测试

      eval('console.log(123)//# sourceURL=./foo/bar.js')
    

    运行示例,如下图所示:


    点击./foo/bar.js,显示效果,如下图所示:

  • 2,在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      // 其余代码省略module.exports = {  // 配置开发过程中的辅助工具,  // 也就是与 Source Map 相关的一些功能配置  devtool: 'eval',}
    

    浏览器定位错误,如下图所示:


    可以看到,当点击进去的时候,显示的却是打包过后的模块代码。

    在 eval 模式下,会将每个模块的js 代码都放到 eval() 函数中执行,在执行的字符串最后通过 sourceURL 的方式去说明所对应的文件路径,这样浏览器通过 eval 在执行代码时,就会知道代码所对应的源代码是哪一个文件,从而去定位错误所出现的文件。

    总结

    eval 模式下,不会生成 Source Map 文件,因此构建速度最快,但是其效果比较简单,只能知道源代码文件的名称,而不知道具体的行列信息。

模式对比

一次打包过程中,同时生成所有模式下的不同结果。

基本使用

  • 1,在 webpack.config.js 中,进行配置

配置代码如下(webpack.config.js):

  const HtmlWebpackPlugin = require('html-webpack-plugin')const allModes = ['eval','cheap-eval-source-map','cheap-module-eval-source-map','eval-source-map','cheap-source-map','cheap-module-source-map','inline-cheap-source-map','inline-cheap-module-source-map','source-map','inline-source-map','hidden-source-map','nosources-source-map']module.exports = allModes.map(item => {return {devtool: item,mode: 'none',entry: './src/main.js',output: {filename: `js/${item}.js`},module: {rules: [{test: /\.js$/,use: {// 辨别其中一类模式的差异loader: 'babel-loader',options: {presets: ['@babel/preset-env']}}}]},plugins: [// 为每一个打包任务生成一个 HTML文件new HtmlWebpackPlugin({filename: `${item}.html`})]}})
  • 2,使用 http-server 开启服务器

      $ http-server dist
    

    运行结果,如下图所示:

  • 3,通过查看每一个 HTML 文件,得出各个模式的对比结果。

    1)带有 module 的模式,不会经过 loader 的编译转换,生成的 Source Map 就是项目的源代码。而不带有 module 的模式,则是经过 babel 转换后的代码。
    2)hidden-source-map,一般用在生成第三方包的时候,需要生成 Source Map 文件,但又不想在 包 中引入。
    3)nosources-source-map,用在生产环境中,保护源代码不被暴露。

选择合适的 Source Map

下面是建议选择模式,应根据具体情况具体选择。

  • 开发模式,选择cheap-module-eval-source-map

    1)代码每行不会超过 80 个字符;
    2)代码经过 Loader 转换过后的差异较大;
    3)首次打包速度慢无所谓,重写打包相对较快。

  • 生产模式,选择 none

    1)Source Map 会暴露源代码;
    2)调试是开发阶段的事情。

  • 生产模式,方便定位错误,选择 nosource-source-map

    1)不会向外暴露源代码;
    2)出现错误时,可以找到源代码对应的位置。

HMR

HMR(Hot Module Replacement),模块热替换,又叫模块热更新,他是 Webpack 中最强大的功能之一,它能够实现在应用运行过程中实时替换某个模块,而应用运行状态不会改变。HMR 极大程度的提高了开发者的工作效率,因此很受欢迎。

HMR ,已经集成在 webpack-dev-server 中,无需再单独安装模块。

开启 HMR

  • 1,直接在命令中,使用 --hot 开启热更新

      $ yarn webpack-dev-server --hot
    
  • 2,在 webpack.config.js 中,进行配置开启热更新

    配置代码如下(webpack.config.js):

      const webpack = require('webpack')module.exports = {mode: 'development',devServer: {hot: true},plugins: [// 载入 webpack 的内置插件new webpack.HotModuleReplacementPlugin()]}
    

    Webpack 中的 HMR 并不可以开箱即用,他需要手动处理 JS 模块热替换逻辑。

HMR APIs

  • 在 main.js 中,使用 HMR APIs 手动处理热替换

    代码示例如下(main.js):

      // module.hot 对象,是 HMR API 的核心对象 /*** accept(),用于注册模块更新过后的处理函数* 第一个参数,指的是 依赖模块的路径* 第二个参数,指的是 依赖更新过后的处理函数*/ module.hot.accept('./editor', () => {// 热替换逻辑})
    

注意事项

  • 1,处理 HMR 的代码报错会导致自动刷新

    配置代码如下(webpack.config.js):

       const webpack = require('webpack')module.exports = {devServer: {hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading},plugins: [// 载入 webpack 的内置插件new webpack.HotModuleReplacementPlugin()]}
    
  • 2,启用 HMR 的情况下,HMR API 报错

    代码示例如下(main.js):

       if (module.hot) { // 先判断这个对象是否存在,再进行任务的注册module.hot.accept('./editor', () => {// 热替换逻辑})}
    
  • 3,业务中添加的处理代码,会在打包过后自动移除。

环境配置

配置文件根据环境不同导出不同配置

  • 在 webpack.config.js 中,进行导出配置

    配置代码如下(webpack.config.js):

      const webpack = require('webpack')const { CleanWebpackPlugin } = require('clean-webpack-plugin')const HtmlWebpackPlugin = require('html-webpack-plugin')const CopyWebpackPlugin = require('copy-webpack-plugin')/*** 导出一个函数,在这个函数中设置所需的配置对象* 接收两个参数,*    第一个参数 env:即 通过 cli 传递的环境名参数*    第二个参数 argv: 指 运行 cli 过程中传递的所有参数*/module.exports = (env, argv) => {// 设置默认模式:开发模式const config = {mode: 'development',entry: './src/main.js',output: {filename: 'js/bundle.js'},devtool: 'cheap-eval-module-source-map',devServer: {hot: true,contentBase: 'public'},module: {rules: [{test: /\.css$/,use: ['style-loader','css-loader']},{test: /\.(png|jpe?g|gif)$/,use: {loader: 'file-loader',options: {outputPath: 'img',name: '[name].[ext]'}}}]},plugins: [new HtmlWebpackPlugin({title: 'Webpack Tutorial',template: './src/index.html'}),new webpack.HotModuleReplacementPlugin()]}// 生产模式if (env === 'production') {config.mode = 'production'config.devtool = false // 禁用 Source Mapconfig.plugins = [...config.plugins,new CleanWebpackPlugin(),new CopyWebpackPlugin(['public'])]}return config}
    

    启动开发模式,默认模式为 开发模式,无需指定工作模式

      $ yarn webpack
    

    启动生产模式,使用 --env 指定工作模式

      $ yarn webpack --env production
    

    注意

    只适用中小型项目的配置,大型项目不适合。

不同环境对应不同配置文件

  • 1,新建 webpack.common.js,用来存放 公共 配置信息

    配置代码如下(webpack.common.js):

      const HtmlWebpackPlugin = require('html-webpack-plugin')// 公共配置module.exports = {entry: './src/main.js',output: {filename: 'js/bundle.js'},module: {rules: [{test: /\.css$/,use: ['style-loader','css-loader']},{test: /\.(png|jpe?g|gif)$/,use: {loader: 'file-loader',options: {outputPath: 'img',name: '[name].[ext]'}}}]},plugins: [new HtmlWebpackPlugin({title: 'Webpack Tutorial',template: './src/index.html'})]}
    
  • 2,安装 webpack-merge 模块, 合并 webpack 配置

      $ yarn add webpack-merge --dev
    
  • 3,新建 webpack.prod.js ,用来存放 生产模式 配置信息

    配置代码如下(webpack.prod.js):

      // 导入 webpack-mergeconst merge = require('webpack-merge')const { CleanWebpackPlugin } = require('clean-webpack-plugin')const CopyWebpackPlugin = require('copy-webpack-plugin')const common = require('./webpack.common')// webpack-merge模块 导出 merge(), 合并 webpack 配置module.exports = merge(common, {mode: 'production',plugins: [new CleanWebpackPlugin(),new CopyWebpackPlugin(['public'])]})
    
  • 4,新建 webpack.dev.js ,用来存放 开发模式 配置信息

    配置代码如下(webpack.dev.js):

      const webpack = require('webpack')const merge = require('webpack-merge')const common = require('./webpack.common')module.exports = merge(common, {mode: 'development',devtool: 'cheap-eval-module-source-map',devServer: {hot: true,contentBase: 'public'},plugins: [new webpack.HotModuleReplacementPlugin()]})
    
  • 5,运行时,可以直接使用 --config 参数指定配置文件

      $ yarn webpack --config webpack.prod.js
    
  • 6,或者 在 package.json 中,配置 NPM Scripts

    配置代码如下(package.json):

      {"scripts": {"dev": "webpack --config webpack.dev.js","build": "webpack --config webpack.prod.js"}}
    

配置优化

DefinePlugin

DefinePlugin,为代码注入全局成员。在 production 模式下,默认启用,会为代码中注入一个 process.env.NODE_ENV 的常量。一般通过这个成员去判断当前的运行环境,从而去执行对应的操作。

基本使用

  • 在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      const webpack = require('webpack')module.exports = {// ...plugins: [// DefinePlugin是 webpack 的内置插件// 构造函数接收一个对象,对象中的每一个键值都会被注入到代码中new webpack.DefinePlugin({// 值要求的是一个 JS代码片段API_BASE_URL: JSON.stringify('https://api.example.com')})]}
    

Tree-shaking

Tree-shaking,摇掉 代码中未引用代码(dead-code)。在启动生产模式时,自动检测出未引用的代码,并自动将未引用代码移除,从而减少冗余代码。

Tree-shaking 并不是指某个配置选项,它是一组功能搭配使用后的优化效果,在 production 模式下自动开启。

基本使用

不使用 production 模式,其他模式下开启 Tree-shaking。

  • 1,在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      module.exports = { // 其他代码省略mode: 'none',// 集中配置 webpack 内部的优化功能optimization: {// 模块只导出被使用的成员usedExports: true,// 尽可能将所有模块合并并输出到到一个函数中concatenateModules: true,// 压缩输出结果minimize: true}}
    

    Tree-shaking 前提是 ES Modules,也就是说,由 webpack 打包的代码必须使用 ES Modules。

  • 2,最新的 babel-loader 默认关闭 ESM 转换,可以在 webpack.config.js 中,手动设置关闭

    配置代码如下(webpack.config.js):

      module.exports = {mode: 'none',module: {rules: [{test: /\.js$/,use: {loader: 'babel-loader',options: {presets: [// 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效// ['@babel/preset-env', { modules: 'commonjs' }]// 配置为 false,确保 preset-env 内部不会开启 ES Module 转换的插件// ['@babel/preset-env', { modules: false }]// 使用默认配置:auto,这样 babel-loader 会自动关闭 ESM 转换['@babel/preset-env', { modules: 'auto' }]]}}}]},optimization: {// 模块只导出被使用的成员usedExports: true,// 尽可能合并每一个模块到一个函数中// concatenateModules: true,// 压缩输出结果// minimize: true}}
    

sideEffects

基本介绍

sideEffects,webpack 4 中新增的特性,它允许通过配置的方式去标识代码是否有副作用,从而为 Tree-shaking 提供更大的压缩空间,在 production 模式下自动开启。

副作用,是指模块执行时除了导出成员之外所做的事情。

适用场景

sideEffects,一般用于 npm 包标记是否有副作用。

前提条件

确保你的代码真的没有副作用,否则会误删掉那些有副作用的代码。

基本使用
  • 1,在 webpack.config.js 中,开启sideEffects特性

    配置代码如下(webpack.config.js):

      module.exports = {mode: 'none',optimization: {// 手动开启特性sideEffects: true,// 模块只导出被使用的成员// usedExports: true,// 尽可能合并每一个模块到一个函数中// concatenateModules: true,// 压缩输出结果// minimize: true,}}
    

    检查 package.json 中是否有 sideEffects 的标识,以此来判断这个模块是否有副作用。

  • 2,设置所有模块都没有副作用,模块中没有被用到的代码就不会再被打包,则将会被移除掉。

    配置代码如下(package.json):

      {    "sideEffects": false}
    
  • 3,设置某些模块具有副作用,即 将这些模块会被打包进输出结果,不会被移除掉。

    配置代码如下(package.json):

      {"sideEffects": ["./src/extend.js","*.css"]}
    

Code Splitting

Code Splitting,代码分包/代码分割,在应用中按需加载模块,从而提高应用的响应速度和运行效率。

多入口打包

多入口打包,一般适用于多页应用程序,即 一个页面对应一个打包入口,公共部分单独提取。

基本使用

  • 1,在 webpack.config.js 中,进行配置

    配置代码如下(webpack.config.js):

      const { CleanWebpackPlugin } = require('clean-webpack-plugin')const HtmlWebpackPlugin = require('html-webpack-plugin')module.exports = {mode: 'none',/*** entry 属性值的形式:*       字符串:设置一个打包入口*       数 组: 将多个文件打包进一个文件中*       对 象: 设置多个打包入口,分别生成对应的打包文件*              一个属性对应一个打包入口,*              属性名为入口名称,属性值为入口所对应的文件路径*/ entry: { index: './src/index.js',album: './src/album.js'},output: {// 多个入口,就意味着生成多个打包文件// 使用 [name] 占位符,动态输出文件名// [name] 最终会被替换成 入口的名称filename: '[name].bundle.js'},plugins: [new CleanWebpackPlugin(),new HtmlWebpackPlugin({title: 'Multi Entry',template: './src/index.html',filename: 'index.html',// 指定每个 HTML 文件所使用的 bundle文件// 每个打包入口会形成独立的 chunkschunks: ['index']}),new HtmlWebpackPlugin({title: 'Multi Entry',template: './src/album.html',filename: 'album.html',chunks: ['album']})]}
    
  • 2,在 webpack.config.js 中,配置属性,使其在打包时自动提取公共模块

    配置代码如下(webpack.config.js):

      // 其余部分省略module.exports = {optimization: {splitChunks: {// 自动提取所有公共模块到单独 bundlechunks: 'all'}},}
    
动态导入

按需加载,需要用到某个模块时,再加载这个模块,可以极大的节省带宽和流量。动态导入的模块会被自动提取到对应的 bundle 中,从而实现分包。相对多入口打包,更加灵活。

基本使用

  • 在需要导入的地方,使用 ES Modules 的动态导入

    代码示例如下(index.js):

      if (hash === '#posts') {// 魔法注释:命名 bundle 的名称,相同的名称会打包到一个 bundle 中// /* webpackChunkName: 'components' */' import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => {mainElement.appendChild(posts())})} else if (hash === '#album') {import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => {mainElement.appendChild(album())})}
    

MiniCssExtractPlugin

MiniCssExtractPlugin,将 CSS 代码从打包结果中提取出来的插件,通过这个插件,可以实现 CSS 的按需加载。

基本使用

  • 1,安装插件模块

      $ yarn add mini-css-extract-plugin --dev
    
  • 2,在 webpack.config.js 中,进行导入和配置

    配置代码如下(webpack.config.js):

      // 导入 mini-css-extract-plugin 插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')module.exports = {module: {rules: [{test: /\.css$/,use: ['style-loader', // 将样式通过 style 标签注入到页面中// 实现样式文件通过 link 标签的方式注入MiniCssExtractPlugin.loader,'css-loader']}]},plugins: [// 创建 MiniCssExtractPlugin 插件实例,自动提取 CSS 到单个文件中new MiniCssExtractPlugin()]}
    

使用建议

当样式代码所占内存较小时,不建议生成单个文件,此时减少请求次数,可能效果更好。

OptimizeCssAssetsWebpackPlugin

webpack 内置的压缩插件,只针对 JS 代码,对于其他的资源文件,都要使用对应的压缩插件进行压缩。

OptimizeCssAssetsWebpackPlugin,压缩输出的 CSS 文件。

基本使用

  • 1,安装插件模块

      $ yarn add optimize-css-assets-webpack-plugin --dev # css 压缩插件$ yarn add terser-webpack-plugin --dev      # webpack 内置的 JS 压缩插件
    
  • 2,在 webpack.config.js 中,进行导入和配置

    配置代码如下(webpack.config.js):

      const { CleanWebpackPlugin } = require('clean-webpack-plugin')const HtmlWebpackPlugin = require('html-webpack-plugin')// 导入 mini-css-extract-plugin 插件const MiniCssExtractPlugin = require('mini-css-extract-plugin')// 导入 optimize-css-assets-webpack-pluginconst OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')// 导入 webpack 内置的 JS 压缩插件const TerserWebpackPlugin = require('terser-webpack-plugin')module.exports = {optimization: {// 只有在 minimizer 特性开启时,才会运行其内部的插件// 生产模式时,minimizer 特性自动开启,即下面插件才会工作// 配置 minimizer 时,webpack 内部的压缩插件就会失效,需要手动添加minimizer: [new TerserWebpackPlugin(),new OptimizeCssAssetsWebpackPlugin()]}}
    

substitutions

文件名使用 Hash 的原因?

一旦资源文件发生改变,文件名称也可以跟着改变。对于客户端而言,全新的文件名,就是全新的请求,则将不会存在缓存问题。也就是说,把服务端的缓存策略时间设置的非常长,就不用担心文件更新过后的问题。因此,生产模式下,文件名使用 Hash。

三种 Hash

  • 1,[hash] 属于 项目级别,即项目中的任何一个地方变化,都会导致打包时,Hash值 全部改变

    配置代码如下(webpack.config.js):

      const MiniCssExtractPlugin = require('mini-css-extract-plugin')module.exports = {output: {filename: '[name]-[hash].bundle.js'},plugins: [new MiniCssExtractPlugin({filename: '[name]-[hash].bundle.css'})]}
    
  • 2,[chunkhash] 属于 chunk 级别,即在打包中,只要是同一路的打包,chunkhash 就是相同的。相比于 [hash],控制较精确一点。

    配置代码如下(webpack.config.js):

      const MiniCssExtractPlugin = require('mini-css-extract-plugin')module.exports = {output: {filename: '[name]-[chunkhash].bundle.js'},plugins: [new MiniCssExtractPlugin({filename: '[name]-[chunkhash].bundle.css'})]}
    
  • 3,[contenthash] 属于 文件级别,根据输出文件的内容输出 hash值,即不同的文件就有不同的hash值,最适合解决缓存问题,通过 :number 指定hash的长度。

    配置代码如下(webpack.config.js):

      const MiniCssExtractPlugin = require('mini-css-extract-plugin')module.exports = {output: {filename: '[name]-[contenthash:8].bundle.js'},plugins: [new MiniCssExtractPlugin({filename: '[name]-[contenthash].bundle.css'})]}
    

Rollup

基本介绍

Rollup,可以将项目中散落的代码打包成整块的代码,从而使得这些划分的模块,更好的运行在浏览器环境或者 NodeJs 中。相对于 Webpack 来说,Rollup 更为小巧,它仅仅是一款 ES Modules 的打包器,是一个提供充分利用 ES Modules 各项特性的高效打包器。

适用场景

开发一个框架或者类库,使用 Rollup。

快速上手

Rollup打包时,会默认开启 Tree-shaking。

基本使用

  • 1,安装 Rollup 模块

      $ yarn add rollup--dev
    
  • 2,运行 Rollup 的 cli 命令,查看 rollup 各种参数
      $ yarn rollup
    
  • 3,运行命令,指定打包的入口文件、输出文件的格式,以及输出文件的路径,进行打包
      $ yarn rollup ./src/index.js --format iife --file dist/bundle.js
    

基本使用

配置文件

在项目根目录下,新建 rollup.config.js 配置文件。

基本使用

  • 1,安装 Rollup 模块

      $ yarn add rollup --dev
    
  • 2,新建 rollup.config.js 配置文件,进行打包信息的配置

    配置代码如下(rollup.config.js):

      // 导出一个配置对象export default {    input: 'src/index.js', // 指定打包的入口文件// 指定输出文件的配置信息output: {file: 'dist/bundle.js', // 指定输出文件的文件名        format: 'iife' // 指定输出文件的格式,自执行函数}}
    
  • 3,运行命令,通过 --config 表明使用项目中的配置文件,默认不会读取配置文件,默认读取的配置文件名称:rollup.config.js

      $ yarn rollup --config # [配置文件名称]
    

Plugins

插件是 Rollup 唯一扩展途径。

rollup-plugin-json

rollup-plugin-json,用来在代码中导入 json 文件。

基本使用

  • 1,安装插件模块

      $ yarn add rollup-plugin-json --dev
    
  • 2,在 rollup.config.js 中,进行配置

    配置代码如下(rollup.config.js):

      // 默认导出插件函数import json from 'rollup-plugin-json'export default {// plugins 属性,存放的是插件函数的调用结果plugins: [json()]}
    

rollup-plugin-node-resolve

rollup-plugin-node-resolve,用来加载 NPM 模块。也就是说,可以在代码中,直接使用模块名称导入模块。

基本使用

  • 1,安装插件模块

      $ yarn add rollup-plugin-node-resolve --dev
    
  • 2,在 rollup.config.js 中,进行配置

    配置代码如下(rollup.config.js):

      import resolve from 'rollup-plugin-node-resolve'export default {plugins: [resolve()]}
    

rollup-plugin-commonjs

rollup-plugin-commonjs,用来加载 CommonJS 模块。

基本使用

  • 1,安装插件模块

      $ yarn add rollup-plugin-commonjs --dev
    
  • 2,在 rollup.config.js 中,进行配置

    配置代码如下(rollup.config.js):

      import commonjs from 'rollup-plugin-commonjs'export default {plugins: [commonjs()]}
    

Code Splitting

动态导入

Code Splitting,代码分包/代码分割。由于 Rollup 遵循 ES Modules,因此他可以使用 ES Modules 的动态导入实现。

基本使用

  • 1,在 JS 文件中,使用 动态导入 进行模块的加载

    代码示例如下(index.js):

      // 返回 Promise 对象import('./logger').then(({ log }) => {log('code splitting~')})
    
  • 2,修改 rollup.config.js 配置文件

    配置代码如下(rollup.config.js):

      export default {input: 'src/index.js',output: {// file: 'dist/bundle.js', // 只能指定一个文件// format: 'iife'dir: 'dist',  // 需要输出多个文件// iife 自执行函数,会把所有的模块放到同一个函数中,没有引导代码,无法实现代码拆分。format: 'amd' // 浏览器环境中,遵循 AMD 标准}}
    

多入口打包

类似于 webpack 中的多入口打包,配置多个入口文件,生成多个输出文件,并在打包过程中,自动将公共部分单独提取。

基本使用

  • 1,在 rollup.config.js 中,配置多个入口文件,类似 webpack

    配置代码如下(rollup.config.js):

      export default {// input: ['src/index.js', 'src/album.js'],input: { foo: 'src/index.js',bar: 'src/album.js'},// 多入口打包,会提取公共模块,会实行代码拆分output: {dir: 'dist',format: 'amd'}}
    
  • 2,手动创建 index.html ,在其中使用打包后的 JS 文件

    代码示例如下(index.html):

      <body><!-- AMD 标准格式的输出 bundle 不能直接引用 --><!-- <script src="foo.js"></script> --><!-- 需要 Require.js 这样的库 --><script src="https://unpkg.com/requirejs@2.3.6/require.js" data-main="foo.js"></script></body>
    

优点缺点

优点

  • 1,输出结果更加扁平;
  • 2,自动移除未引用代码;
  • 3,打包结果依然完全可读。

缺点

  • 1,加载非 ESM 的第三方模块比较复杂;
  • 2,模块最终都被打包到一个函数中,无法实现 HMR;
  • 3,浏览器环境中,代码拆分功能依赖 AMD 库。

Parcel

Parcel,是一款完全 零配置 的前端应用打包器。相对于 Webpack 来说,构建速度要快,因为他的内部采用多进程。

基本使用

Parcel 官方建议,使用 HTML 文件作为打包入口。

  • 1,安装 Parcel 打包器

      $ yarn add parcel-bundler --dev
    
  • 2,在 src 目录下,新建 index.html 文件,此文件将作为打包的入口文件

    代码示例如下(index.html):

      <body><script src="main.js"></script></body>
    
  • 3,在 src 目录下,新建 main.js 和 foo.js 文件,作为测试文件

    代码示例如下(main.js):

      import foo from './foo.js'foo.bar()
    

    代码示例如下(foo.js):

      export default {bar: () => {console.log('hello parcel~');}}
    
  • 4,开发环境,打包命令,将 HTML 文件作为打包入口,同时开启 Web 服务器

      $ yarn parcel src/index.html
    
  • 5,生成环境,打包命令

      $ yarn parcel build src/index.html
    

基本功能

在运行打包命令后,不仅会生成对应的打包文件,还会自动开启一个 web 服务器。

启动的web服务器,会自动监听代码的变化,从而实现自动编译,浏览器自动刷新。也就是说,不管做什么,都不用担心配置等,Parcel 会自动去实现。

测试用例

  • 1,HMR(自动热替换),在 main.js 编写 热替换 逻辑

    代码示例如下(main.js):

      if (module.hot) {// accept() 只接收一个回调函数参数,// 作用:当前模块更新,或者其所依赖的所有模块更新过后,会自动执行module.hot.accept(() => {// 热替换逻辑})}
    
  • 2,自动安装依赖

    代码示例如下(main.js):

      import $ from 'jquery'$(document.body).append('<h1>Hello Parcel</h1>')
    

    通过测试,可以知道,在引入 jquery 之后,会自动下载 jquery 的依赖包。

  • 3,动态导入

    代码示例如下(main.js):

      import('jquery').then($ => {$(document.body).append('<h1>Hello Parcel</h1>')})
    

【模块化开发】之 Webpack、Rollup、Parcel相关推荐

  1. 模块化开发工具webpack

    1.模块化开发概述 前端的js代码如何复用,怎样避免多文件之间的命名冲突,前端开发中这之类的问题如何解决,答案是模块化开发.来看看模块化开发的好处吧 创建common公共模块a.js文件 var na ...

  2. Vue学习Day6 插槽slot使用、编译作用域、作用域插槽、模块化开发、webpack介绍、安装

    想利用暑假时间好好学习一下vue,会记录每一天的学习内容. 今天是学习vue的第6天! 起起伏伏乃人生常态,继续加油- 学习内容 1. 插槽slot slot基本使用 具名插槽slot 2. 编译作用 ...

  3. 前端模块化开发中webpack、npm、node、nodejs之间的关系[小白总结]

    https://blog.csdn.net/AngelLover2017/article/details/84801673

  4. html手机模块化,jQuery 移动端拖拽(模块化开发,触摸事件,webpack)

    通过jquery可以很容易实现CP端的拖拽.但是在移动端却不好用了.于是我自己写了一个在移动端的拖拽demo,主要用到的事件是触摸事件(touchstart,touchmove和touchend). ...

  5. 前端模块化开发学习之gulpbrowserify篇

     随着web应用的发展,前端的比重占得越来越多,编写代码从而也越来越复杂.而通常我们需要将不同功能或者不同模块的代码分开写,最后在html中一起加载,这样做是可以的,但是当你需要进行维护或者是二次开发 ...

  6. Vue 模块化开发(构建项目常用工具)

    针对刚接触 JavaScript 模块开发系统的用户 vue官方建议我们参考 Vue CLI 3.只要遵循指示,就能很快地能运行一个带有 .vue 组件.ES2015.webpack 和热重载 (ho ...

  7. js 多个定时器_Node.js系列深入浅出Node模块化开发——CommonJS规范

    前言 本文将为大家透彻的介绍关于Node的模块化--CommonJS的一切. 看完本文可以掌握,以下几个方面: 什么是模块化,以及没有模块化会带来哪些问题,是如何解决的: JavaScript的设计缺 ...

  8. 为什么JavaScript需要模块化开发?

    为什么需要模块化开发? 随着代码复杂程度的提高, 项目也变得越来越难维护, JavaScript模块化 也因此油然而生, 这应该是大家最熟悉的一种加载方式, 但是缺点也比较明显 所有的模块都处于全局作 ...

  9. 什么是前端模块化?为什么要进行模块化开发?前端技术文章分享

    之前文章都分享的Java相关,今天就分享一篇前端技术文章,一起来看看今日前端技术文章吧~希望能够帮助到你! 模块化是一种软件开发的设计模式,它将一个大型的软件系统划分成多个独立的模块,每个模块都有自己 ...

  10. 前端面试题 ~ 有关模块化开发

    1.说说你对前端模块化开发的认识. (1)异步模块定义(AMD)规范是 require. js推广的.对模块定义的规范. (2)通用模块定义(CMD)规范是 SeaJS推广的.对模块定义的规范. (3 ...

最新文章

  1. Linux登录那点事
  2. PHP 选取数组中最大的 键 和 值
  3. oracle adg的特点是什么,Oracle12c ADG新特性
  4. PtQt4标准对话框——QFileDialog
  5. Tailwindcss尤大神都fork了,是未来的趋势?
  6. linux ojvm补丁安装,打补丁PSU
  7. [洪流学堂]Hololens开发入门篇2之Hello World
  8. Python字符串count()
  9. php store快捷键设置,mac 下 phpstorm 快捷键整理
  10. ERP操作手册要不要做?
  11. 【转】程序员这口饭-职业规划解决方案
  12. dejavu歌曲识别介绍
  13. python回归分析
  14. Python 简易实现 base64 编码
  15. 【愚公系列】2022年07月 Go教学课程 004-Go代码注释
  16. 智能机器人JIMI助力用户咨询体验提升
  17. cyusb3014的slavefifo程序的解读
  18. 【python】蓄水池
  19. 第二届“长安杯”电子数据竞赛试题wp
  20. 服务器系统的监测,服务器监测系统

热门文章

  1. 236.二叉树的最近公共祖先
  2. 案例 月工作列表 c# 1614192274
  3. 题库明细 使用C#开发数据库应用系统
  4. django-上传图片-后台上传
  5. python-函数的注释
  6. django-模态框编辑学生
  7. Zabbix监控SQLServer TPS
  8. 关于obs的录制时黑屏问题
  9. Android进阶:自定义视频播放器开发(下)
  10. Java的FTP协议级客户端实现详解