package.json你真的懂么

137 阅读4分钟

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

我们在做vue开发的时候,对import vue from 'vue'肯定是不陌生的

但是有没有人具体思考过这个vue到底是什么,从哪里来

以及我们使用vue报错的时候为什么指向的文件是vue.runtime.esm.js文件

在这里的话,我就做一个简单的分享(菜鸟一只,如果不对,欢迎指正)

  • 首先,import vue from 'vue' 这个过程我们来看一下发生了什么
  • 首先import Vue from 'vue' 解析为 const Vue = require('vue')。
  • require 判断 vue 是否未 nodejs 核心包,如我们常用的:path,fs 等,是那么就直接导入,否则继续往下走
  • vue 如果不是 nodejs 的核心包,再来判断 vue 是不是以 '/' 根目录开头,显然不是,所以继续往下走
  • 再判断vue 是否为 './'、'/' 或者 '../' 开头,显然也不是,再继续往下走
  • 以上条件都不符合,读取项目目录下 node_modules 包里的包
  • 所以最后会在node_modules中找到这个vue
  • node_modules中vue的结构如下所示:

image.png 将dist打开之后 目录如下图:

image.png

我们会发现node_modules/vue/dist底下有很多的文件,我们并不知道当前的这个vue到底是谁

其实我们从平时vue代码出错时候的报错可以发现,事实上我们执行的文件好像是vue.runtime.esm.js,如下图所示

image.png

所以我们能不能说我们的import vue from vue中的这个vue就是这个文件呢,这里先画个问号

接着我们再来看看node_modules/vue/package.json文件,内容如下:

image.png

在这个文件中我们可以看到main和module两个字段,那么我们都知道 main 代表的是入口文件,那是不是代表 dist/vue.runtime.common.js才是真正的vue所代表的文件呢

要弄清楚这个问题,我们就要先搞懂package.json中的这个module字段是什么意思

最早的 npm 包都是基于 CommonJS 规范的,当 require('package1') 的时候,就会根据 main 字段去查找入口文件。 而 es2016 后,js 也拥有了 模块 的概念, 并且js种的 模块 在使用上更加的标准和优雅,且打包的时候就可以利用 js模块的很多特性,从而提高打包的性能,其中提升一个便是 tree shaking

我们来简单的了解一下什么是tree shaking

简单的举个小例子,比如我现在有两个文件:

// main.js
exports.test1 = function (data) {
    return data++;
}
 
exports.test2 = function (data) {
    return data++;
}
// app.js
import {
    test1
} from './main';
 
test1(100);

app.js 文件通过 import 引入了 math.js 中的 test1 方法

我们通过 webpack 命令打包:

webpack --entry ./app.js --output-filename app.bunble.js

在生成的 app.bundle.js 文件中我们可以看到以下内容:

image.png

这里我们可以看到虽然我们只用到了 main.js 文件中的 test1 方法,但是在最终生成的 bundle 文件中却包含了 test1 和 test2 两个方法

这是因为在 CommonJS 规范(math.js 文件的模块格式即为 CommonJS)中,模块只能通过 exports 对象向外暴露属性。所有要暴露的方法、变量等都只能作为 exports 对象的一个属性出现

所以打包工具并不知道我们代码中最终会用到模块中的哪些方法, 为了安全起见,整个模块的代码都被包含在了最终生成的 bundle 中

ES6 定义了一套基于 import、export 操作符的模块规范。它与 CommonJS 规范最大的区别在 ES6 中的 import 和 export 都是静态的。静态意味着一个模块要暴露或引入的所有方法在编译阶段就全部确定了,之后不能再改变。这样做的好处就是打包工具在打包阶段就可以分析出代码中用到了某个模块中的哪几个方法。其它没有用到的方法就可以从最终的 bundle 文件中剔除掉。这样既可以减少 bundle 文件的大小,又可以提高脚本的执行速度。这个机制就叫做 Tree Shaking

然后把我们的main.js种的文件改成:

// main.js
export function test1 (data) {
    return data++;
}
 
export function test2 (data) {
    return data++;
}

我们会发现,在执行webpack之后的bundle文件中并没有找到相应导出的test1和test2函数

关于 Tree Shaking 我们已经说得差不多了,也看到了它的有点,接着我们继续往下走

CommonJS 规范的包都是以 main 字段表示入口文件了,如果使用 ES模块 的也用 main 字段,就会对使用者造成困扰,所以又引入了另外一个字段 module

打包工具遇到 package.json 的时候,如果存在 module 字段,会优先使用,如果没找到对应的文件,则会使用 main 字段,并按照 CommonJS 规范打包。

所以综上所述,我们再来回顾一下,我们的package.json文件

image.png

我们能够清楚的看到存在module字段,,所以肯定是优先采用module了,也就是dist/vue.runtime.esm.js才是我们真正的入口文件~

这也就解释了为什么vue报错的时候,提示dist/vue.runtime.esm.js 错误啦

最后再来看看我们的package.json中的其他字段代表的含义吧:

image.png