Webpack 如何让浏览器来支持模块呢

455 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

在早期,我们使用类似于像 browserify 以及 requirejs 这样的打包工具,来编写能够在我们浏览器中运行 commonjs 的模块的代码。

我们使用 requirejs 来给大家演示一下。在当前目录下面,我们创建一个 requirejs 文件夹。在这个文件夹中新建 add.js 和 minus.js 这两个文件,可见我们把刚才的 commonjs 规范中的 math.js 给拆成两个模块。

// add.js
const add=(x,y)=>{
    return x+y;
}


define([], function(){
    return add
})


//minus.js
const minus=(x,y)=>{
    return x-y;
}


define([], function(){
    return minus
})

 

add 和 munis 这两方法该如何暴露呢?我们 requirejs 给我们提供了一个 define 的方法。这个方法需要传入两个参数:

  • 第一个参数是个数组,定义这个模块依赖其他模块,如果没有去依赖其他的模块,定义成空数组即可;

  • 第二个是一个回调函数,这个回调函数才是我们真正的模块对外暴露的接口,在这个函数题中返回一个值,返回的值是方法的引用。

我们接下来看如何的去实现加载这两个模块呢?首先得需要在我们当前的目录下面创建一个新的 index-6.html 文件。

在 index-6.html 文件通过 script 标签去加载我们定义模块,但是需要在该 HTML 文件中引入一个第三方 require.js 库。

<script src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js" data-main="./require/main.js"></script>

加载 requirejs 又该如何加载我们刚才定义好的那两个模块呢?其实 requirejs 给我们提供了一个规范,我们在 script 标签里面除了使用 src 属性以外,它还给我们提供了一个 data-main 属性,它表示我们去加载一个入口的 JS 文件。

因此,我们得需要再 requirejs 文件夹里 新建一个 mian.js 文件,注意该文件不一定非得是 main.js,取决于你自己的定义以及引用。

这个入口的文件我们定义好之后,如何在 main.js 文件中如何去加载刚才我们定义好的那两个文件。requirejs 给我们提供了一个 require 方法,就可以是直接的去引用我们定义那两个模块。

require 方法提供两个参数:

  • 第一个参数就是依赖模块的数组,引入模块的数组应该是当前文件夹或者是上一层文件夹里面的一些文件。但是,这里有个问题,我们究竟能不能直接使用当前这个目录去访问我们这个文件呢?其实上 main.js 访问的目标或者是访问的参照的目录点是以引用它文件的目录为参照点,因此,我们目录不能写成当前模块路径,比如不能直接写 ./add.js,而是写 ./requirejs/add.js。

  • 第二个参数呢是一个回调函数,在这个回调函数的参数里边,我们可以去拿到这里边载入的方法,比如 add 和 minus 这两个方法。当然了,这两个形参也是我们随便定义的。它俩究竟指向了什么呢?其实指向了我们在定义模块里面的第二个函数。

既然 add 和 minus 指向了我们在定义的时候的两个回调函数,我们就可以直接调用的 add 和 minus 方法了。方法一旦调用,它其实相当于调用了我们在 add.js 和 minus.js 里面定义的 add 和 minus 回调函数,它就返回了 add 和 minus 的方法。然后,我们就可以给这两方法传入相关的参数。

require(['./requirejs/add.js','./requirejs/minus.js'],function(add,minus){
    console.log(add(4,5));
    console.log(minus(6,2))
})

大家可以看到,在浏览器控制台输入:9 和 4。

image.png

早前期,我们就是通过这种方式来去实现模块的拆分和加载。

其实当前还有一个选择,这个就是来自于我们 Web 项目的消息,模块正在成为我们 ECMAScript 的一个官方的标准。

下面我们想使用 ECMAScript 定义和导出模块,那如何定义模块呢?在当前的目录下面创建一个 esm 文件夹,在该文件夹中新建两个模块:add.js 和 minus.js。

//add.js
const add=(x,y)=>{
    return x+y;
}
export default add


//minus.js
const minus=(x,y)=>{
    return x-y;
}
export default minus

在 add.js 和 minus.js 文件中分别定义 add 和 minus 方法,并且接受两参数,add 方法返回两个数求和,minus 方法返回两个数的差。add 和 minus 这两个方法该如何暴露呢?我们的 ECMAScript module 给我们提供了 export default 语法,我们来去暴露 add 和 minus 方法。

两个模块定义好,我们在 HTML 文件如何去载入自定义模块呢?方法很简单,我们只需要使用 script 标签,并且使用 import 关键即可导入我们自定义的 add.js 和 minus.js 模块。

  <script>


        import Add from './esm/add.js'
        console.log(Add(4,5))


        import Minus from './esm/minus.js'
        console.log(Minus(6,2))
    </script>

在浏览器方法,可以看到在控制台输出“Cannot use import statement outside a module ”错误信息,   image.png

说明不能够在一个模块的外部去使用 import 关键字。什么叫模块的外部呢?是因为我们这里面少了 type 属性。

 <script type="module">


      import Add from './esm/add.js'
      console.log(Add(4,5))


      import Minus from './esm/minus.js'
      console.log(Minus(6,2))
  </script>

刷新浏览器,发现刚才的错误信息没了,又产生一个新的错误,错误的信息是不让我们访问脚本。

image.png

它违背了跨域的一个请求的一个安全策略,这就告诉我们通过当前的 file 方式去访问文件是有问题的。

在本地临时搭建一个 http server 环境,我们可以使用 npx 从我们的线上去拉一个 http-server,npx 可以实现当我某一个模块在本地不存在的时候,可以从互联网上帮助我们去安装一个模块。

image.png

等待片刻以后,它会帮助我们启动一个服务,服务地址是 127.0.0.1,端口号是 8080。

 

在浏览器访问 127.0.0.1:8080,可以看到显示我们之前开发所有的目录。

  image.png

我们点击 index-07.html,可以看到在浏览器控制台输出 9 和 6。说明我们使用了 ECMAScript 模块化实现了定义和载入模块。

  image.png

学习到这里,发现 ECMAScript 非常的不错,不用着急使用它,目前浏览器对它的支持还不是很完整,版本迭代的速度也不够快,那我们该怎么办呢?是否有一种好的方式,不仅可以让我们编写模块,而且还可以支持任何的模块格式?

现在就要请出我们的主角 Weboack,它可以帮助我们打包 JavaScript 的应用程序,并且同时支持 JS 的模块化以及 commonjs,它还可以扩展支持很多的静态资源打包,比如像图片、字体文件、样式文件等等,这就是我们需要 Webpack 的原因。