东方新君 發表於 2019-12-24 11:50:00

Node.js 中使用 ES6 中的 import / export 的方法大全

<h1 class="_1RuRku">Node.js 中使用 ES6 中的 import / export 的方法大全</h1>
<p>转&nbsp;https://www.jianshu.com/p/ce92a09ad6eb</p>
<div>
<div>
<p>Node.js 中使用 ES6 中的 import / export 的方法大全</p>
<p>三种方法。<br>先上图。</p>
<p>源代码文件目录(https://github.com/AK-47-D/nodejs_es6_tutorials):</p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill">&nbsp;</div>
<div class="image-view" data-width="457" data-height="503"><img src="//upload-images.jianshu.io/upload_images/1233356-72c0aa4bd093a99a.png?imageMogr2/auto-orient/strip|imageView2/2/w/457/format/webp"></div>

</div>
<div class="image-caption">image.png</div>

</div>
<p>&nbsp;</p>
<h2>方法1 放弃使用 ES6, 使用 Node中的 module 模块语法</h2>
<p>util_for_node.js</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token keyword">function <span class="token function">log<span class="token punctuation">(<span class="token parameter">o<span class="token punctuation">) <span class="token punctuation">{
    console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(o<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

module<span class="token punctuation">.exports <span class="token operator">= log<span class="token punctuation">;
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>es6_const_let_node_demo.js</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-cpp"><code class="language-cpp"><span class="token comment">// 在 Node 中使用模块的正确姿势:
<span class="token keyword">const log <span class="token operator">= <span class="token function">require<span class="token punctuation">(<span class="token string">"./lib/util_for_node"<span class="token punctuation">)<span class="token punctuation">;
<span class="token comment">// ES5
var a <span class="token operator">= <span class="token number">1<span class="token punctuation">;
a <span class="token operator">= a <span class="token operator">+ <span class="token number">1<span class="token punctuation">;
<span class="token function">log<span class="token punctuation">(a<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// 2

<span class="token comment">// ES6
<span class="token keyword">const b <span class="token operator">= <span class="token number">1<span class="token punctuation">;
<span class="token comment">// b = b + 1; // error : TypeError: Assignment to constant variable.
<span class="token function">log<span class="token punctuation">(b<span class="token punctuation">)<span class="token punctuation">;

<span class="token comment">// ES6
let c <span class="token operator">= <span class="token number">1<span class="token punctuation">;
c <span class="token operator">= c <span class="token operator">+ <span class="token number">1<span class="token punctuation">;
<span class="token function">log<span class="token punctuation">(c<span class="token punctuation">)<span class="token punctuation">;
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>运行测试:</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-ruby"><code class="language-ruby">$ node es6_const_let_node_demo<span class="token punctuation">.js
<span class="token number">2
<span class="token number">1
<span class="token number">2

</span></span></span></span></code></pre>
</div>
<h2>方法2 使用万能变换器:babel</h2>
<p>util_for_babel.js</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token keyword">function <span class="token function">log<span class="token punctuation">(<span class="token parameter">o<span class="token punctuation">) <span class="token punctuation">{
    console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(o<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token keyword">export <span class="token punctuation">{log<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>es6_const_let_babel_demo.js</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-swift"><code class="language-swift"><span class="token keyword">import <span class="token punctuation">{log<span class="token punctuation">} from <span class="token string">"./lib/util_for_babel"<span class="token punctuation">;

<span class="token comment">/**

node: module.exports和require
es6:export和import

nodejs仍未支持import/export语法,需要安装必要的npm包–babel,使用babel将js文件编译成node.js支持的commonjs格式的代码。
因为一些历史原因,虽然 Node.js 已经实现了 99% 的 ES6 新特性,不过截止 2018.8.10,How To Enable ES6 Imports in Node.JS 仍然是老大难问题

借助 Babel
1.下载必须的包

npm install babel-register babel-preset-env --D

命令行执行:

babel-node es6_const_let_babel_demo.js

*
* @type {number}
*/


<span class="token comment">// ES5
<span class="token keyword">var a <span class="token operator">= <span class="token number">1<span class="token punctuation">;
a <span class="token operator">= a <span class="token operator">+ <span class="token number">1<span class="token punctuation">;
<span class="token function">log<span class="token punctuation">(a<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// 2

<span class="token comment">// ES6
const b <span class="token operator">= <span class="token number">1<span class="token punctuation">;
<span class="token comment">// b = b + 1; // error : TypeError: Assignment to constant variable.
<span class="token function">log<span class="token punctuation">(b<span class="token punctuation">)<span class="token punctuation">;

<span class="token comment">// ES6
<span class="token keyword">let c <span class="token operator">= <span class="token number">1<span class="token punctuation">;
c <span class="token operator">= c <span class="token operator">+ <span class="token number">1<span class="token punctuation">;
<span class="token function">log<span class="token punctuation">(c<span class="token punctuation">)<span class="token punctuation">;


</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>上面的代码,直接 node 命令行运行是要报错的:</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-tsx"><code class="language-tsx">$ node es6_const_let_babel_demo<span class="token punctuation">.js
<span class="token operator">/Users<span class="token operator">/jack<span class="token operator">/WebstormProject<span class="token operator">/node<span class="token operator">-tutorials<span class="token operator">/hello<span class="token operator">-node<span class="token operator">/es6_const_let_babel_demo<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">1
<span class="token punctuation">(<span class="token keyword">function <span class="token punctuation">(<span class="token parameter">exports<span class="token punctuation">, <span class="token keyword">require<span class="token punctuation">, <span class="token keyword">module<span class="token punctuation">, __filename<span class="token punctuation">, __dirname<span class="token punctuation">) <span class="token punctuation">{ <span class="token keyword">import <span class="token punctuation">{log<span class="token punctuation">} <span class="token keyword">from <span class="token string">"./lib/util_for_babel"<span class="token punctuation">;
                                                                     <span class="token operator">^

SyntaxError<span class="token punctuation">: Unexpected token <span class="token punctuation">{
    at <span class="token keyword">new <span class="token class-name">Script <span class="token punctuation">(vm<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">79<span class="token punctuation">:<span class="token number">7<span class="token punctuation">)
    at <span class="token function">createScript <span class="token punctuation">(vm<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">251<span class="token punctuation">:<span class="token number">10<span class="token punctuation">)
    at Object<span class="token punctuation">.<span class="token function">runInThisContext <span class="token punctuation">(vm<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">303<span class="token punctuation">:<span class="token number">10<span class="token punctuation">)
    at Module<span class="token punctuation">.<span class="token function">_compile <span class="token punctuation">(internal<span class="token operator">/modules<span class="token operator">/cjs<span class="token operator">/loader<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">656<span class="token punctuation">:<span class="token number">28<span class="token punctuation">)
    at Object<span class="token punctuation">.Module<span class="token punctuation">._extensions<span class="token punctuation">.<span class="token punctuation">.<span class="token function">js <span class="token punctuation">(internal<span class="token operator">/modules<span class="token operator">/cjs<span class="token operator">/loader<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">699<span class="token punctuation">:<span class="token number">10<span class="token punctuation">)
    at Module<span class="token punctuation">.<span class="token function">load <span class="token punctuation">(internal<span class="token operator">/modules<span class="token operator">/cjs<span class="token operator">/loader<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">598<span class="token punctuation">:<span class="token number">32<span class="token punctuation">)
    at <span class="token function">tryModuleLoad <span class="token punctuation">(internal<span class="token operator">/modules<span class="token operator">/cjs<span class="token operator">/loader<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">537<span class="token punctuation">:<span class="token number">12<span class="token punctuation">)
    at <span class="token builtin">Function<span class="token punctuation">.Module<span class="token punctuation">.<span class="token function">_load <span class="token punctuation">(internal<span class="token operator">/modules<span class="token operator">/cjs<span class="token operator">/loader<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">529<span class="token punctuation">:<span class="token number">3<span class="token punctuation">)
    at <span class="token builtin">Function<span class="token punctuation">.Module<span class="token punctuation">.<span class="token function">runMain <span class="token punctuation">(internal<span class="token operator">/modules<span class="token operator">/cjs<span class="token operator">/loader<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">741<span class="token punctuation">:<span class="token number">12<span class="token punctuation">)
    at <span class="token function">startup <span class="token punctuation">(internal<span class="token operator">/bootstrap<span class="token operator">/node<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">285<span class="token punctuation">:<span class="token number">19<span class="token punctuation">)

</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>是的,这个时候,我们需要再加上一层 Babel 的映射逻辑。下面就是 Babel 出场了。</p>
<p>1.安装依赖</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-cpp"><code class="language-cpp">npm install babel<span class="token operator">-<span class="token keyword">register babel<span class="token operator">-preset<span class="token operator">-env <span class="token operator">--D
</span></span></span></span></span></code></pre>
</div>
<p>package.json</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-json"><code class="language-json"><span class="token punctuation">{
<span class="token property">"name"<span class="token operator">: <span class="token string">"hell-node"<span class="token punctuation">,
<span class="token property">"version"<span class="token operator">: <span class="token string">"1.0.0"<span class="token punctuation">,
<span class="token property">"description"<span class="token operator">: <span class="token string">""<span class="token punctuation">,
<span class="token property">"main"<span class="token operator">: <span class="token string">"index.js"<span class="token punctuation">,
<span class="token property">"scripts"<span class="token operator">: <span class="token punctuation">{
    <span class="token property">"test"<span class="token operator">: <span class="token string">"echo \"Error: no test specified\" &amp;&amp; exit 1"
<span class="token punctuation">}<span class="token punctuation">,
<span class="token property">"author"<span class="token operator">: <span class="token string">""<span class="token punctuation">,
<span class="token property">"license"<span class="token operator">: <span class="token string">"ISC"<span class="token punctuation">,
<span class="token property">"devDependencies"<span class="token operator">: <span class="token punctuation">{
    <span class="token property">"babel-preset-env"<span class="token operator">: <span class="token string">"^1.7.0"<span class="token punctuation">,
    <span class="token property">"babel-register"<span class="token operator">: <span class="token string">"^6.26.0"
<span class="token punctuation">}
<span class="token punctuation">}

</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>2.写处理启动脚本</p>
<p>es6_const_let_babel_demo_start.js</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token function">require<span class="token punctuation">(<span class="token string">'babel-register'<span class="token punctuation">) <span class="token punctuation">(<span class="token punctuation">{
    presets<span class="token punctuation">: <span class="token punctuation">[ <span class="token string">'env' <span class="token punctuation">]
<span class="token punctuation">}<span class="token punctuation">)

module<span class="token punctuation">.exports <span class="token operator">= <span class="token function">require<span class="token punctuation">(<span class="token string">'./es6_const_let_babel_demo.js'<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>OK,多费了这么多事,终于可以跑了:</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-ruby"><code class="language-ruby">$ node es6_const_let_babel_demo_start<span class="token punctuation">.js
<span class="token number">2
<span class="token number">1
<span class="token number">2

</span></span></span></span></code></pre>
</div>
<p>跑的时候,你会明显发现,真 TMD 慢了许多!!! 慢就慢在 Babel 这层处理映射逻辑上。</p>
<h2>方法3 使用 Node 中的实验特性:node --experimental-modules</h2>
<p>你看,为了特意区分这是module JavaScript,文件后缀名必须改成 .mjs</p>
<p>util_for_node_exp.mjs</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token comment">/**
* 注意到这里的源码文件的后缀 .mjs
* @param o
*/

<span class="token keyword">function <span class="token function">log<span class="token punctuation">(<span class="token parameter">o<span class="token punctuation">) <span class="token punctuation">{
    console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(o<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}

<span class="token keyword">export <span class="token punctuation">{log<span class="token punctuation">}<span class="token punctuation">;
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>es6_const_let_node_exp_demo.mjs</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-swift"><code class="language-swift"><span class="token keyword">import <span class="token punctuation">{log<span class="token punctuation">} from <span class="token string">"./lib/util_for_node_exp"<span class="token punctuation">;

<span class="token comment">// ES5
<span class="token keyword">var a <span class="token operator">= <span class="token number">1<span class="token punctuation">;
a <span class="token operator">= a <span class="token operator">+ <span class="token number">1<span class="token punctuation">;
<span class="token function">log<span class="token punctuation">(a<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">// 2

<span class="token comment">// ES6
const b <span class="token operator">= <span class="token number">1<span class="token punctuation">;
<span class="token comment">// b = b + 1; // error : TypeError: Assignment to constant variable.
<span class="token function">log<span class="token punctuation">(b<span class="token punctuation">)<span class="token punctuation">;

<span class="token comment">// ES6
<span class="token keyword">let c <span class="token operator">= <span class="token number">1<span class="token punctuation">;
c <span class="token operator">= c <span class="token operator">+ <span class="token number">1<span class="token punctuation">;
<span class="token function">log<span class="token punctuation">(c<span class="token punctuation">)<span class="token punctuation">;

<span class="token comment">/**
* 源码后缀 .mjs
*/

</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>命令行执行:</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-ruby"><code class="language-ruby"> $ node<span class="token operator">--experimental<span class="token operator">-modules es6_const_let_node_exp_demo<span class="token punctuation">.mjs
</span></span></span></code></pre>
</div>
<p>输出:</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-ruby"><code class="language-ruby"> <span class="token punctuation">(node<span class="token punctuation">:<span class="token number">1402<span class="token punctuation">) <span class="token constant">ExperimentalWarning<span class="token punctuation">: <span class="token constant">The <span class="token constant">ESM <span class="token keyword">module loader is experimental<span class="token punctuation">.
<span class="token number">2
<span class="token number">1
<span class="token number">2
</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>OK,上面就是 Node.js 中使用 ES6 中的 import / export 方法。</p>
<hr>
<h1>Node.js 中使用 import / export</h1>
<p>因为一些历史原因,虽然 Node.js 已经实现了 99% 的 ES6 新特性,不过截止 2018.8.10,How To Enable ES6 Imports in Node.JS 仍然是老大难问题</p>
<p>下面我来介绍两种方法可以让我们在 Node.js 中使用 import/export 。</p>
<p>借助 Babel<br>1.下载必须的包</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-cpp"><code class="language-cpp">npm install babel<span class="token operator">-<span class="token keyword">register babel<span class="token operator">-preset<span class="token operator">-env <span class="token operator">--D
</span></span></span></span></span></code></pre>
</div>
<ol start="2">
<li>修改你的 server.js<br>下面是一个 server.js 的例子:</li>

</ol>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token keyword">const Koa <span class="token operator">= <span class="token function">require<span class="token punctuation">(<span class="token string">'koa'<span class="token punctuation">)
<span class="token keyword">const app <span class="token operator">= <span class="token keyword">new <span class="token class-name">Koa<span class="token punctuation">(<span class="token punctuation">)

app<span class="token punctuation">.<span class="token function">listen<span class="token punctuation">(<span class="token number">3000<span class="token punctuation">, console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">"application is start at port 3000"<span class="token punctuation">)<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>用 import 替换 require</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token keyword">import Koa <span class="token keyword">from <span class="token string">'koa'
<span class="token keyword">const app <span class="token operator">= <span class="token keyword">new <span class="token class-name">Koa<span class="token punctuation">(<span class="token punctuation">)

app<span class="token punctuation">.<span class="token function">listen<span class="token punctuation">(<span class="token number">3000<span class="token punctuation">, console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">"application is start at port 3000"<span class="token punctuation">)<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>如果你现在用 node server.js 跑这个文件,你会收到像这样的错误提示:</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-tsx"><code class="language-tsx"><span class="token operator">/Users<span class="token operator">/zyf<span class="token operator">/myStudy<span class="token operator">/demo<span class="token operator">/chatroom<span class="token operator">/server<span class="token operator">/app<span class="token punctuation">.js<span class="token punctuation">:<span class="token number">1
<span class="token punctuation">(<span class="token keyword">function <span class="token punctuation">(<span class="token parameter">exports<span class="token punctuation">, <span class="token keyword">require<span class="token punctuation">, <span class="token keyword">module<span class="token punctuation">, __filename<span class="token punctuation">, __dirname<span class="token punctuation">) <span class="token punctuation">{ <span class="token keyword">import Koa <span class="token keyword">from <span class="token string">'koa'
                                                                     <span class="token operator">^<span class="token operator">^<span class="token operator">^

SyntaxError<span class="token punctuation">: Unexpected identifier
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>下面让我们用 babel 来解决这个问题</p>
<p>3.新增一个 start.js 文件<br>这个文件将成为我们的入口文件,里面是一些 babel 的代码</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token function">require<span class="token punctuation">(<span class="token string">'babel-register'<span class="token punctuation">) <span class="token punctuation">(<span class="token punctuation">{
    presets<span class="token punctuation">: <span class="token punctuation">[ <span class="token string">'env' <span class="token punctuation">]
<span class="token punctuation">}<span class="token punctuation">)

module<span class="token punctuation">.exports <span class="token operator">= <span class="token function">require<span class="token punctuation">(<span class="token string">'./server.js'<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>注意,接下来不是 node server.js,而是用 node start.js 来启动这个文件</p>
<p>来自 Node.js 官方的力量<br>Node 9提供了一个尚处于 Experimental 阶段的模块,让我们可以抛弃 babel 等一类工具的束缚,直接在 Node 环境下使用 import/export。</p>
<p>官方手册:ECMAScript Modules<br>一个不错的教程:Node 9下import/export的丝般顺滑使用<br>有兴趣的可以去了解一下,嫌字多的可以继续往下看看我总结的使用方法</p>
<p>用前须知<br>Node 版本需在 9.0 及以上<br>不加 loader 时候,使用 import/export 的文件后缀名必须为 .mjs<br>举个栗子<br>还是用上面的例子,请将代码回退到 Babel 中第一步的样子</p>
<p>1.改写 server.js</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token keyword">import Koa <span class="token keyword">from <span class="token string">'koa'
<span class="token keyword">const app <span class="token operator">= <span class="token keyword">new <span class="token class-name">Koa<span class="token punctuation">(<span class="token punctuation">)

app<span class="token punctuation">.<span class="token function">listen<span class="token punctuation">(<span class="token number">3000<span class="token punctuation">, console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">"application is start at port 3000"<span class="token punctuation">)<span class="token punctuation">)
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>和前面一样,不过将文件名改一下,从 server.js 改为 server.mjs</p>
<p>2.启动文件<br>执行下面代码,来启动文件</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-undefined"><code class="language-undefined">node --experimental-modules ./server.mjs
</code></pre>
</div>
<p>注意这是引用 koa 第三方模块不用做其他变化,如果要 import 自己的文件,那么那个待引入的文件也要改后缀。</p>
<p>比如</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-jsx"><code class="language-jsx"><span class="token keyword">import example <span class="token keyword">from <span class="token string">'./example'
</span></span></span></code></pre>
</div>
<p>那么原来应该是 example.js 要改为 example.mjs</p>
<p>目前这个模块还处于实验阶段,还是不要放到生产环境,自己拿出来玩玩还是可以的。</p>
<p>原文:https://blog.csdn.net/zwkkkk1/article/details/81564971</p>
<h2>Node 9下import/export的丝般顺滑使用</h2>
<h2>前言</h2>
<p>Node 9最激动人心的是提供了在flag模式下使用<code>ECMAScript Modules</code>,虽然现在还是<code>Stability: 1 - Experimental</code>阶段,但是可以让Noder抛掉babel等工具的束缚,直接在Node环境下愉快地去玩耍<code>import/export</code></p>
<p>如果觉得文字太多,看不下去,可以直接去玩玩demo,地址是https://github.com/chenshenhai/node-modules-demo</p>
<h2>Node 9下import/export使用简单须知</h2>
<ul>
<li>Node 环境必须在 9.0以上</li>
<li>不加loader时候,使用<code>import/export</code>的文件后缀名必须为<code>*.mjs</code>(下面会讲利用Loader Hooks兼容<code>*.js</code>后缀文件)</li>
<li>启动必须加上flag <code>--experimental-modules</code></li>
<li>文件的<code>import</code>和<code>export</code>必须严格按照<code>ECMAScript Modules</code>语法</li>
<li><code>ECMAScript Modules</code>和<code>require()</code>的cache机制不一样</li>
</ul>
<h2>快速使用import/export</h2>
<ul>
<li>新建<code>mod-1.mjs</code>,<code>mod-2.mjs</code>文件</li>
</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">/* ./mod-1.mjs */
export default {
    num: 0,
    increase() {
      this.num++;
    },
    decrease() {
      this.num--;
    }
}
</code></pre>
</div>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">/*./mod-2.mjs */
import Mod1 from './mod-1';

export default {
    increase() {
      Mod1.increase();
    },
    decrease() {
      Mod1.decrease();
    }
}
</code></pre>
</div>
<ul>
<li>建立启动文件 <code>index.mjs</code></li>
</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">import Mod1 from './mod-1';
import Mod2 from './mod-2';

console.log(`Mod1.num = ${Mod1.num}`)
Mod1.increase();
console.log(`Mod1.num = ${Mod1.num}`)
Mod2.increase();
console.log(`Mod1.num = ${Mod1.num}`)
</code></pre>
</div>
<ul>
<li>执行代码</li>
</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-shell"><code class="source-shelllanguage-source-shell">node --experimental-modules ./index.mjs
</code></pre>
</div>
<p>控制台会显示</p>
<div class="image-package">
<div class="image-container">
<div class="image-container-fill">&nbsp;</div>
<div class="image-view" data-width="914" data-height="218"><img src="//upload-images.jianshu.io/upload_images/1233356-1b560641d943fc7e.png?imageMogr2/auto-orient/strip|imageView2/2/w/914/format/webp"></div>
</div>
<div class="image-caption">&nbsp;</div>
</div>
<h2>使用简述</h2>
<p>执行了上述demo后,快速体验了Node的原生<code>import/export</code>能力,那我们来讲讲目前的支持状况,Node 9.x官方文档 https://nodejs.org/dist/latest-v9.x/docs/api/esm.html</p>
<h3>与require()区别</h3>
<table>
<thead>
<tr><th>能力</th><th>描述</th><th>require()</th><th>import</th></tr>
</thead>
<tbody>
<tr>
<td>NODE_PATH</td>
<td>从NODE_PATH加载依赖模块</td>
<td>Y</td>
<td>N</td>
</tr>
<tr>
<td>cache</td>
<td>缓存机制</td>
<td>可以通过require的API操作缓存</td>
<td>自己独立的缓存机制,目前不可访问</td>
</tr>
<tr>
<td>path</td>
<td>引用路径</td>
<td>文件路径</td>
<td>URL格式文件路径,例如<code>import A from './a?v=2017'</code></td>
</tr>
<tr>
<td>extensions</td>
<td>扩展名机制</td>
<td>require.extensions</td>
<td>Loader Hooks</td>
</tr>
<tr>
<td>natives</td>
<td>原生模块引用</td>
<td>直接支持</td>
<td>直接支持</td>
</tr>
<tr>
<td>npm</td>
<td>npm模块引用</td>
<td>直接支持</td>
<td>需要Loader Hooks</td>
</tr>
<tr>
<td>file</td>
<td>文件(引用)</td>
<td><code>*.js</code>,<code>*.json</code>等直接支持</td>
<td>默认只能是<code>*.mjs</code>,通过<code>Loader Hooks</code>可以自定义配置规则支持<code>*.js</code>,<code>*.json</code>等Node原有支持文件</td>
</tr>
</tbody>
</table>
<h2>Loader Hooks模式使用</h2>
<blockquote>
<p>由于历史原因,在ES6的Modules还没确定之前,JavaScript的模块化处理方案都是八仙过海,各显神通,例如前端的AMD、CMD模块方案,Node的CommonJS方案也在这个“乱世”诞生。<br>当到了ES6规范确定后,Node的CommonJS方案已经是JavaScript中比较成熟的模块化方案,但ES6怎么说都是正统的规范,“法理”上是需要兼容的,所以<code>*.mjs</code>这个针对<code>ECMAScript Modules</code>规范的Node文件方案在一片讨论声中应运而生。</p>

</blockquote>
<blockquote>
<p>当然如果<code>import/export</code>只能对<code>*.mjs</code>文件起作用,意味着Node原生模块和npm所有第三方模块都不能。所以这时候Node 9就提供了 <code>Loader Hooks</code>,开发者可自定义配置<code>Resolve Hook</code>规则去利用<code>import/export</code>加载使用Node原生模块,<code>*.js</code>文件,npm模块,C/C++的Node编译模块等Node生态圈的模块。</p>

</blockquote>
<h3>Loader Hooks 使用步骤</h3>
<ul>
<li>自定义loader规则</li>
<li>启动的flag要加载loader规则文件
<ul>
<li>例如:<code>node --experimental-modules --loader ./custom-loader.mjs ./index.js</code></li>

</ul>

</li>

</ul>
<p>如果觉得以下文字太长,可以先去玩玩对应的demo3 https://github.com/chenshenhai/node-modules-demo/tree/master/demo3</p>
<h3>自定义规则快速上手</h3>
<ul>
<li>文件目录</li>

</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-css"><code class="language-css">├── demo3
│   ├── es
│   │   ├── custom-loader.mjs
│   │   ├── index.js
│   │   ├── mod-1.js
│   │   └── mod-2.js
│   └── package.json

</code></pre>
</div>
<ul>
<li>加载自定义loader,执行<code>import/export</code>的<code>*.js</code>文件</li>
</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-shell"><code class="source-shelllanguage-source-shell">node --experimental-modules--loader ./es/custom-loader.mjs ./es/index.js
</code></pre>
</div>
<h3>自定义loader规则解析</h3>
<p>以下是Node 9.2官方文档提供的一个自定义loader文件</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">import url from 'url';
import path from 'path';
import process from 'process';

// 获取所有Node原生模块名称
const builtins = new Set(
Object.keys(process.binding('natives')).filter((str) =&gt;
    /^(?!(?:internal|node|v8)\/)/.test(str))
);

// 配置import/export兼容的文件后缀名
const JS_EXTENSIONS = new Set(['.js', '.mjs']);

// flag执行的resolve规则
export function resolve(specifier, parentModuleURL /*, defaultResolve */) {

// 判断是否为Node原生模块
if (builtins.has(specifier)) {
    return {
      url: specifier,
      format: 'builtin'
    };
}

// 判断是否为*.js, *.mjs文件
// 如果不是则,抛出错误
if (/^\.{0,2}[/]/.test(specifier) !== true &amp;&amp; !specifier.startsWith('file:')) {
    // For node_modules support:
    // return defaultResolve(specifier, parentModuleURL);
    throw new Error(
      `imports must begin with '/', './', or '../'; '${specifier}' does not`);
}
const resolved = new url.URL(specifier, parentModuleURL);
const ext = path.extname(resolved.pathname);
if (!JS_EXTENSIONS.has(ext)) {
    throw new Error(
      `Cannot load file with non-JavaScript file extension ${ext}.`);
}

// 如果是*.js, *.mjs文件,封装成ES6 Modules格式
return {
    url: resolved.href,
    format: 'esm'
};
}
</code></pre>
</div>
<h3>规则总结</h3>
<p>在自定义loader中,export的resolve规则最核心的代码是</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">return {
url: '',
format: ''
}
</code></pre>
</div>
<ul>
<li>url 是模块名称或者文件URL格式路径</li>
<li>format 是模块格式有<code>esm</code>, <code>cjs</code>, <code>json</code>, <code>builtin</code>, <code>addon</code>这四种模块/文件格式.</li>
</ul>
<h2>Koa2 直接使用import/export</h2>
<p>看看demo4,https://github.com/chenshenhai/node-modules-demo/tree/master/demo4</p>
<ul>
<li>文件目录</li>
</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">├── demo4
│   ├── README.md
│   ├── custom-loader.mjs
│   ├── index.js
│   ├── lib
│   │   ├── data.json
│   │   ├── path.js
│   │   └── render.js
│   ├── package-lock.json
│   ├── package.json
│   └── view
│       ├── index.html
│       └── todo.html
</code></pre>
</div>
<p>代码片段太多,不一一贴出来,只显示主文件</p>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">import Koa from 'koa';
import { render } from './lib/render.js';
import data from './lib/data.json';

let app = new Koa();
app.use((ctx, next) =&gt; {
    let view = ctx.url.substr(1);
    let content;
    if ( view === 'data' ) {
      content = data;
    } else {
      content = render(view);
    }
    ctx.body = content;
})
app.listen(3000, ()=&gt;{
    console.log('the modules test server is starting');
});
</code></pre>
</div>
<ul>
<li>执行代码</li>
</ul>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-undefined"><code class="language-undefined">node --experimental-modules--loader ./custom-loader.mjs ./index.js

</code></pre>
</div>
<ul>
<li>访问
<ul>
<li>访问 http://127.0.0.1:3000/index</li>
<li>访问 http://127.0.0.1:3000/data</li>
<li>访问 http://127.0.0.1:3000/todo</li>
</ul>
</li>
</ul>
<h3>自定义loader规则优化</h3>
<p>从上面官方提供的自定义loader例子看出,只是对<code>*.js</code>文件做<code>import/export</code>做loader兼容,然而我们在实际开发中需要对npm模块,<code>*.json</code>文件也使用<code>import/export</code></p>
<h3>loader规则优化解析</h3>
<div class="_2Uzcx_"><button class="VJbwyy" type="button"></button>
<pre class="line-numberslanguage-source-js"><code class="source-jslanguage-source-js">import url from 'url';
import path from 'path';
import process from 'process';
import fs from 'fs';

// 从package.json中
// 的dependencies、devDependencies获取项目所需npm模块信息
const ROOT_PATH = process.cwd();
const PKG_JSON_PATH = path.join( ROOT_PATH, 'package.json' );
const PKG_JSON_STR = fs.readFileSync(PKG_JSON_PATH, 'binary');
const PKG_JSON = JSON.parse(PKG_JSON_STR);
// 项目所需npm模块信息
const allDependencies = {
...PKG_JSON.dependencies || {},
...PKG_JSON.devDependencies || {}
}

//Node原生模信息
const builtins = new Set(
Object.keys(process.binding('natives')).filter((str) =&gt;
    /^(?!(?:internal|node|v8)\/)/.test(str))
);

// 文件引用兼容后缀名
const JS_EXTENSIONS = new Set(['.js', '.mjs']);
const JSON_EXTENSIONS = new Set(['.json']);

export function resolve(specifier, parentModuleURL, defaultResolve) {
// 判断是否为Node原生模块
if (builtins.has(specifier)) {
    return {
      url: specifier,
      format: 'builtin'
    };
}

// 判断是否为npm模块
if ( allDependencies &amp;&amp; typeof allDependencies === 'string' ) {
    return defaultResolve(specifier, parentModuleURL);
}

// 如果是文件引用,判断是否路径格式正确
if (/^\.{0,2}[/]/.test(specifier) !== true &amp;&amp; !specifier.startsWith('file:')) {
    throw new Error(
      `imports must begin with '/', './', or '../'; '${specifier}' does not`);
}

// 判断是否为*.js、*.mjs、*.json文件
const resolved = new url.URL(specifier, parentModuleURL);
const ext = path.extname(resolved.pathname);
if (!JS_EXTENSIONS.has(ext) &amp;&amp; !JSON_EXTENSIONS.has(ext)) {
    throw new Error(
      `Cannot load file with non-JavaScript file extension ${ext}.`);
}

// 如果是*.js、*.mjs文件
if (JS_EXTENSIONS.has(ext)) {
    return {
      url: resolved.href,
      format: 'esm'
    };
}

// 如果是*.json文件
if (JSON_EXTENSIONS.has(ext)) {
    return {
      url: resolved.href,
      format: 'json'
    };
}

}
</code></pre>
</div>
<h2>后记</h2>
<p>目前Node对<code>import/export</code>的支持现在还是<code>Stability: 1 - Experimental</code>阶段,后续的发展还有很多不确定因素,自己练手玩玩还可以,但是在还没去flag使用之前,尽量不要在生产环境中使用。</p>
</div>
<br><br>作者:一个会写诗的程序员<br>链接:https://www.jianshu.com/p/ce92a09ad6eb<br>来源:简书<br>著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</div><br><br>
来源:https://www.cnblogs.com/it-tsz/p/12090723.html
頁: [1]
查看完整版本: Node.js 中使用 ES6 中的 import / export 的方法大全