概述

Parcel是一款完全零配置的前端打包器,那他提供了近乎傻瓜式的使用体验,我们只需要了解他所提供的几个简单的命令就可以直接使用他去构建我们前端应用程序了。

那下面我们就来直接去看具体如何去使用Parcel。

使用

首先我们有一个空的项目,我们先通过yarn init去初始化一个项目中的package.json文件。

完成以后我们就可以去安装Parcel所对应的模块,不过这里注意,Parcel的npm模块叫做parcel-bundler,我们这里同样应该将它安装到项目的开发依赖当中。

yarn add parcel-bundler --dev

安装完成过后呢,parcel-bundler这个模块在node_modules的bin目录里面就为我们提供了parcel的cli程序,那后续我们就可以使用这个cli去执行对我们整个应用的打包。

那既然是打包我们的应用代码,那这里我们就得先有代码,我们在项目的跟目录下新建一个src目录,用于存放开发阶段所编写的源代码。同时我们创建一个/src/index.html文件。这个文件会是我们parcel打包的入口文件。

虽然parcel和webpack一样都支持以任意类型的文件作为打包入口,不过parcel的官方建议我们使用html文件作为打包入口,官方所给出的理由是因为html是应用运行在浏览器端时的入口。所以我们应该使用html文件作为打包入口。

在这个入口文件当中我们可以正常像平常一样去编写,也可以在这去引用一些资源文件。那在这里被引用的资源最终都会被parcel打包到一起。最终输出到输出目录。

我们这里先引入一个main.js的脚本文件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Parcel Tutorials</title>
</head>
<body>
    <script src="main.js"></script>
</body>
</html>

然后我们新建一个/src/main.js文件,除此之外我们再新建一个/src/foo.js文件。

然后我们再foo.js当中以ES Module的方式去默认导出一个对象,而且我们在这个对象中去定义一个bar方法。

export default {
    bar: () => {
        console.log('hello parcel~')
    }
}

然后我们在main.js当中通过import去导入刚刚这个foo模块,并且我们使用它导出对象当中的bar方法。

import foo from './foo';

foo.bar();

那parcel同样支持对ES Module模块的打包,完成以后我们再命令行中执行打包。

这个命令需要我们传入打包入口的路径,在我们这里就是src/index.html文件,

yarn parcel src/index.html

那此时如果我们执行这个命令,parcel就会根据我们这所传入的参数,先去找到index.html文件,然后根据index.html当中的script标签去找到他所引入的main.js文件,最后再顺着import语句找到foo模块,从而去完成整体项目的打包。

执行之后我们可以发现,parcel这个命令他不仅仅帮我们打包的应用而且他同时还开启了一个开发服务器。

那这个开发服务器就跟webpack当中的dev-server一样,我们可以打开这里所提供的地址。

我们在浏览器当中打开开发人员工具(控制台F12),那此时我们就可以使用自动刷新的功能了。

这里我们可以尝试修改一下源代码当中console.log中内容。

export default {
    bar: () => {
        // console.log('hello parcel~');
        console.log('hello parcel11~');
    }
}

保存过后你就会发现, 浏览器会自动刷新一下,从而去执行最新的打包结果。

那如果说我们需要的是模块热替换的体验,那parcel当中同样也支持,我们可以回到main.js当中,这里我们同样需要使用hmr所提供的api。

我们这里先去判断一下module.hot对象是否存在,如果存在这个对象的话就证明当前这个环境可以使用hmr的api。然后我们就可以使用module.hot.accept方法去处理模块热替换的逻辑了。

不过这里的accept和webpack提供的api有一点不太一样,webpack当中的api他支持接收两个参数,用来去处理我们指定模块更新过后的逻辑。

而这里parcel提供的accept他只可以接收一个参数也就是这个回调函数,作用呢就是当我们当前这个模块更新或者是当前这个模块所依赖的模块更新过后他会自动执行。

我们可以尝试先在这里打印HMR

import foo from './foo';

foo.bar();

if (module.hot) {
    module.hot.accept(() => {
        console.log('hmr');
    })
}

然后我们回到foo.js当中,尝试再次修改源代码。那此时我们就可以看到我们代码会自动被热替换到浏览器当中自动执行了。

那除了热替换,parcel还支持一个非常友好的功能,就是自动安装依赖。

那试想一下你正在开发一个应用的过程中,那突然间你想要去使用某个第三方的模块,那你此时就需要先停止正在运行的dev-server然后去安装这个模块,安装完成过后再去重新启动dev-server。

那有了自动安装依赖这个功能过后我们就不必要再这样麻烦了,我们可以回到main.js当中。

假设我们这里想要去使用一下jQuery, 虽然我们这里并没有安装这个模块,但是因为有了自动安装依赖这样的功能的缘故。我们这里只管正常导入就可以了。

那导入完成之后我们使用一下jQuery提供的api, 在文件保存过后, parcel会自动取安装我们刚刚所导入的这个模块。极大程度避免了我们额外的一些手动操作。

import $ from 'jquery';
import foo from './foo';

foo.bar();

$(document.body).append('<h1>Hello Parcel</h1>');

if (module.hot) {
    module.hot.accept(() => {
        console.log('hmr');
    })
}

除此之外,parcel同样支持加载其他类型的资源模块,而且相比于其他的模块打包器,在parcel当中去加载任意类型的资源模块同样还是零配置的。

例如我们这里添加一个/src/style.css的样式文件。然后我们在这个文件当中添加一些简单的样式。

body {
    background: red;
}

回到main.js当中通过import去导入这个样式文件,保存过后这个样式就可以立即生效。了。

import $ from 'jquery';
import foo from './foo';
import './style.css';

foo.bar();

$(document.body).append('<h1>Hello Parcel</h1>');

if (module.hot) {
    module.hot.accept(() => {
        console.log('hmr');
    })
}

整个这个过程我们并没有安装额外的插件,我们还可以随意添加一个图片,到我们的项目当中,然后我们回到代码当中导入这张图片,最后我们再通过jQuery将它显示到我们的页面当中。同样也是可以的。

import $ from 'jquery';
import foo from './foo';
import './style.css';
import logo from './icon.png';

foo.bar();

$(document.body).append('<h1>Hello Parcel</h1>');

$(document.body).append(`<img src=${logo} />`);

if (module.hot) {
    module.hot.accept(() => {
        console.log('hmr');
    })
}

那总之呢,parcel他希望给开发者的体验就是,你想要做什么你就只管去做,额外的事情呢就由工具去负责处理。

另外parcel同样支持使用动态导入,那内部如果你使用了动态导入的话,他也会自动拆分我们的代码,我们也一起来尝试一下。

这里我们先将静态导入的jQuery注释掉,然后我们使用动态导入的方式去导入jQuery模块,那在这个import所返回的promise对象的then方法当中,我们就可以拿到所导入的jquery对象了。

然后我们把使用了jquery的代码移到then方法里面。

// import $ from 'jquery';
import foo from './foo';
import './style.css';
import logo from './icon.png';

foo.bar();

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

    $(document.body).append(`<img src=${logo} />`);
})

if (module.hot) {
    module.hot.accept(() => {
        console.log('hmr');
    })
}

我们回到浏览器,刷新一下页面,然后再network面板,在这个面板当中我们就能看到刚刚所拆分出来的jquery所对应的bundle文件请求。

那以上基本上就是Parcel当中最常用的一些特性了。在使用上Parcel几乎没有任何难度,从头到尾我们只是执行了一个Parcel命令。所有事情都是Parcel内部自动帮我们完成的。

那我们再回到命令行,这里我们结束掉Parcel命令。

部署生产

然后我们再看一下Parcel如何以生产模式去运行打包。

我们需要执行parcel这个cli所提供的build命令,然后跟上我们打包入口文件的路径就可以以生产模式运行打包了。

parcel build src/index.html

那这里额外补充一点,对于相同体量的项目打包,Parcel的构建速度会比Webpack快很多。

因为在Parcel的内部他使用的是多进程同时去工作,充分发挥了多核CPU的性能,那Webpack当中也可以使用一个叫做happypack的插件来去实现这一点。

那我们这里看一下输出的结果,那这里我们所输出的一些文件都会被压缩,而且样式代码也都单独提取到单个单间当中了,那这就是Parcel的一个体验。

总结

那整体我们体验下来就是一个感觉,舒服。

因为他在使用上真的太简单了,设想一下我们之前用的Webpack我们都需要做很多额外的配置,安装很多的插件,在Parcel当中其实也有这样的插件,只不过他是自动帮我们去安装的。

我们整体是不需要关心这些东西的,所以他在使用上就给了一种非常舒服的感觉。

那Parcel是2017年发布的,那出现的原因呢也就是因为当时Webpack在使用上过于繁琐,而且官网的文档也不是很清晰明了。

所以说Parcel他一经推出就迅速被推上了风口浪尖,那其核心特点呢就是真正意义上做到了完全零配置。那对我们的项目没有任何的侵入。

而且整个过程我们会有自动安装依赖的这样一个体验,让我们开发过程可以更加专注编码。

除此之外呢还有一个就是Parcel他一开始提供的这种构建速度就非常快,因为他内部使用了多进程同时工作,所以相比于Webpack的打包,他的速度要更快一些,但是这个刚刚我们也说了,Webpack也可以借助于插件去解决这样的问题。

那Parcel的优点固然很明显,但是目前实际上你去观察使用情况你会发现,绝大多数的项目打包还是会选择使用Webpack,那我个人认为原因可能有两点。

第一点呢就是Webpack他的生态可能会更好一些,扩展也就会更丰富,而且出现问题我们也可以很容易去解决。

第二点就是随着这两年的发展,Webpack也越来越好用,开发者随着不断去使用他也越来越熟悉,所以要是我个人选择的话我可能也会选择Webpack。

那Parcel这样的工具对于开发者而言,我们去了解他其实也就是为了保持对新鲜技术和工具的敏感度。从而更好的把握技术的趋势和走向,仅此而已。