標籤:樣本 第三方 目標 install public end 今天 fun 調用
現代前端開發離不開打包工具,以Webpack為代表的打包工具已經成為日常開發必備的工具,以React技術棧為例,我們ES6形式的原始碼,需要經過Webpack和Babel處理,才能產生發布版檔案,在瀏覽器中運行。今天就結合React來梳理一下Webpack打包時模組的組織圖,先給定下面一個簡單的應用樣本:
import React from ‘react‘;import ReactDOM from ‘react-dom‘;import {greet} from ‘./utils‘;const App = <h1>{greet(‘scott‘)}</h1>;ReactDOM.render(App, document.getElementById(‘root‘));
代碼中的utils
模組如下:
export function greet(name) { return `hello ${name}`;}
如果編譯該範例程式碼,由於要將第三方庫一起打包,最終產生的目標代碼會比較多,所以我們先在webpack.config.prod.js
中將React
和ReactDOM
配置為externals
,不將它們編譯到目標代碼中,而是在運行時直接從外部取值。配置如下:
let appConfig = { entry: ‘./src/index.js‘, externals: { ‘react‘: ‘React‘, ‘react-dom‘: ‘ReactDOM‘, }, output: { path: ‘./dist‘, filename: ‘index.js‘ }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: ‘babel-loader‘ } ] },};
運行webpack,產生的目標代碼如下:
(function (modules) { // 存放已載入的模組 var installedModules = {}; // 載入函數 function __webpack_require__(moduleId) { // 如果該模組已被載入 直接返回module.exports if (installedModules[moduleId]) { return installedModules[moduleId].exports; } var module = installedModules[moduleId] = { exports: {}, id: moduleId, loaded: false }; // 調用模組函數 載入相應的模組 // 參數是(module, exports[, __webpack_require__]) modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 標記該模組已被載入 module.loaded = true; // 最後也返回exports return module.exports; } // 暴露modules和installedModules對象 __webpack_require__.m = modules; __webpack_require__.c = installedModules; // __webpack_public_path__ __webpack_require__.p = ""; // 載入主模組 return __webpack_require__(0);})/* 以下是模組列表 作為參數被載入 */([ /* 0 主模組 */ (function (module, exports, __webpack_require__) { ‘use strict‘; // 取索引為1的React模組 var _react = __webpack_require__(1); var _react2 = _interopRequireDefault(_react); // 取索引為2的ReactDOM模組 var _reactDom = __webpack_require__(2); var _reactDom2 = _interopRequireDefault(_reactDom); // 取索引為3的utils模組 var _utils = __webpack_require__(3); // 模組取值 不包含__esModule:true的是預設匯出 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // 開始渲染視圖 var App = _react2.default.createElement( ‘h1‘, null, (0, _utils.greet)(‘scott‘) ); _reactDom2.default.render(App, document.getElementById(‘root‘)); }), /* 1 React模組 */ (function (module, exports) { module.exports = React; }), /* 2 ReactDOM模組 */ (function (module, exports) { module.exports = ReactDOM; }), /* 3 utils模組 */ (function (module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.greet = greet; function greet(name) { return "hello " + name; } })]);
上面就是基本的模組載入機制。其實,有了externals
配置,我們也可以不在代碼中引入React
和React-DOM
,下面稍微修改一下代碼:
import {greet} from ‘./utils‘;const App = <h1>{greet(‘scott‘)}</h1>;ReactDOM.render(App, document.getElementById(‘root‘));
轉譯後的代碼如下:
(function (modules) { // ...})/* 以下是模組列表 作為參數被載入 */([ /* 0 主模組 */ (function (module, exports, __webpack_require__) { ‘use strict‘; // 取索引為1的utils模組 var _utils = __webpack_require__(1); // 開始渲染視圖 var App = React.createElement( ‘h1‘, null, (0, _utils.greet)(‘scott‘) ); ReactDOM.render(App, document.getElementById(‘root‘)); }), /* 1 utils模組 */ (function (module, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.greet = greet; function greet(name) { return "hello " + name; } })]);
可以看到,代碼清晰了不少,主模組中直接調用React.createElement()
來建立虛擬DOM對象,最後調用ReactDOM.render()
方法來渲染真實的DOM結點。訪問下面的入口檔案,我們就可以看到程式啟動並執行結果:
<!DOCTYPE html><html> <body> <div id="root"></div> <script crossorigin src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script> <script src="index.js"></script> </body></html>
JavaScript系列文章:React總結之Webpack模組組織