這一次咱們談談模塊問題。javascript
一般咱們但願這個項目能夠分爲多個獨立的模塊,好比,上一次提升的 hello 函數,若是咱們定義爲一個模塊,其它模塊引用以後,直接調用就行了。在前端怎麼使用模塊呢?這可說來話長了。html
若是咱們把 hello 函數定義在文件 hello.js 中,內容以下:前端
function hello(){ alert("Hello, Webpack!"); }
而後把主入口函數 index.js 的內容寫成下面的內容,你應該會獲得一個錯誤信息。java
require("./hello");
hello();
對話框是彈不出來的。錯誤信息以下:node
Uncaught ReferenceError: hello is not defined, 明明定義了 hello 函數,卻恰恰說找不到。webpack
CommonJs 是目前比較流行,也是出現較早的模塊技術,它誕生於 NodeJs,使用起來其實比較簡單。web
首先,它把一個獨立的文件當作一個模塊,好比上面的 hello.js 文件,就能夠當成一個模塊。模塊的名稱就是文件名稱,可是能夠不用提供擴展名 .js,直接使用文件名就能夠。typescript
在導入一個模塊的時候,使用 require 函數,注意是函數,並非關鍵字,JavaScript 並無提供這個關鍵字。函數的參數就是模塊名稱,不過,要注意模塊分爲兩種,自定義的模塊和系統模塊。npm
自定義的模塊必須使用 . 或者 .. 開頭的相對路徑,若是都在同一個目錄下,也須要使用 . 來表示當前路徑,好比上面用到的 require("./hello")。json
不是使用 . 或者 .. 開始的相對路徑的,都稱爲系統模塊,系統模塊的路徑其實在 node_modules 文件夾中,每一個子文件夾就是一個系統模塊。
require 函數的返回結果就是模塊導出的內容。
咱們的模塊沒有導出任何內容。因此,雖然被 index 引用了,可是在 index 中倒是沒法訪問的。這也說明模塊中定義的函數實際上是局部函數,並非一般意義上的全局函數了。
咱們要在模塊中導出內容怎麼辦呢,CommonJs 提供了 exports 對象。
在 CommonJs 模塊中,但願導出的內容必須經過 exports 對象,這是 CommonJs 系統提供的系統對象,咱們能夠直接使用。導出的內容以鍵值對的形式定義到這個對象上,鍵就是導出的名稱,值就是準備導出的內容。
好比,咱們但願將 hello 函數導出爲名爲函數的函數,好像挺繞的,其實很簡單。
function hello(){ alert("Hello, Webpack!"); } exports.hello = hello;
將 index.js 函數的定義修改成
var hello = require("./hello").hello; hello();
從新執行 webpack 命令,刷新網頁,你會看到函數執行了。
另一個著名的模塊系統稱爲 AMD 模塊,全稱爲 Asynchromous Module Defination,翻譯過來就是異步模塊規範,它是由 RequireJs 推進的。
首先,一個文件也被看做一個模塊,因此,咱們還拿 hello.js 做爲一個模塊吧。
其次,定義模塊使用 define 函數,函數的第一個參數是當前模塊依賴的模塊數組,若是沒有依賴的函數,可使用一個空的數組表示。
define([], function(){ return { hello: function(){ alert("Hello, Webpack!"); } }; });
第二個參數就是模塊的定義函數,這個函數的返回內容就是模塊的導出內容。咱們這裏導出一個對象包含咱們定義的函數的對象。
最後,咱們修改一下 index.js 來導入定義好的 AMD 模塊,並調用 hello 函數。
define(['./hello'], function( helloModule ){ helloModule.hello(); })
此次,咱們的 index 依賴提早定義好的 hello 模塊,注意模塊命名的方式仍是注意自定義模塊和系統模塊的區別,在第二個函數參數中,它的參數就是被依賴模塊導出的內容,因此這裏的 helloModule 就是一個對象,它的 hello 屬性就是咱們定義好的函數。
在 ES2015 中,定義了關鍵字,注意是關鍵字,不是函數了,模塊導出的關鍵字是 export , 導入就是 import 了。
因此,hello 函數能夠這樣定義了。
export default function() { alert("Hello, Webpack!"); };
而 index.js 就能夠這樣定義。
import hello from "./hello";
hello();
可是,須要注意的是,你必須有 ES2015 的運行環境才行,目前不少瀏覽器不支持怎麼辦呢?
咱們能夠將 ES2015 的代碼先翻譯成標準的 JavaScript 代碼,就能夠執行了。
而 TypeScript 做爲 ES2015 的超集,咱們就使用 TypeScript 來實現。
首先,你必須確認你已經安裝了 TypeScript 的編譯器,全局安裝 TypeScript
npm -g install typescript
安裝以後,能夠執行 tsc -v 來檢查版本,目前最新是 2.0.3 版本。
將 hello.js 從新更名爲 hello.ts,將 index.js 更名爲 index.ts. ts 是 TypeScript 文件的擴展名。
你能夠手工執行命令 tsc 分別將兩個 typescript 文件編譯爲 javascript 文件。
生成的 hello.js
"use strict"; function default_1() { alert("Hello, Webpack!"); } exports.__esModule = true; exports["default"] = default_1; ;
生成的 index.js
"use strict"; var hello_1 = require("./hello"); hello_1["default"]();
而後,再次執行 webpack 從新打包,你會發現結果仍然正常執行。
在第三步,咱們能夠手工使用 tsc 將 TypeScript 代碼編譯爲 JavaScript 代碼以後進行打包,可是這樣太麻煩了,使用 ts-loader 咱們可讓 webpack 自動先調用 tsc 將 TypeScript 代碼編譯爲 JavaScript 代碼,而後再自動進行打包工做。
首先,固然要安裝 ts-loader 插件了。
npm install ts-loader --save-dev
目前是 0.9.5 版本。
爲了在項目中使用 typescript,你還須要本地安裝 typescript 一次。
npm install typescript --save-dev
還須要配置一下 TypeScript 如何編譯代碼。在當前目錄中建立名爲 tsconfig.json 的配置文件。
{ "compilerOptions": { "target": "es5", "sourceMap": true }, "exclude": [ "node_modules" ] }
這裏的配置是說,但願生成 es5 標準的 JavaScript 目標文件。
而後,配置 webpack 使用 ts-loader
var HtmlwebpackPlugin = require('html-webpack-plugin'); module.exports = { // 入口 entry: "./index.ts", // 輸出的文件名 output: { filename: 'bundle.js' }, resolve: { // Add `.ts` and `.tsx` as a resolvable extension. extensions: ['', '.ts', '.js'] }, // 添加咱們的插件 會自動生成一個html文件 plugins: [ new HtmlwebpackPlugin({ title: 'Hello Webpack' }) ], module: { loaders: [ // all files with a `.ts` extension will be handled by `ts-loader` { test: /\.ts$/, loader: 'ts-loader' } ] } };
這裏增長了兩個內容,一個是 resolve 配置,這樣咱們只須要使用模塊名稱,webpack 會自動添加後綴來查找模塊文件。另外一個就是 module 中的 loader 了,這裏是說遇到了 .ts 擴展名的文件,須要先使用 ts-loader 處理以後再打包。
如今,從新執行 webpack 命令,你會發現一切都已經處理好了,刷新頁面,你應該看到代碼正確執行了。
webpack 能夠直接支持 CommonJs 和 AMD 模塊,對於 TypeScript ,咱們還須要安裝 TypeScript 和 ts-loader.
須要注意的是 Angular 2 使用 TypeScript 做爲主力語言,你能作到這裏,離 Angular 2 已經很近了。