目录javascript
更少的网络请求css
function m1(){ // 模块内容 // 这种方式污染全局变量 }
var module1 = new Object({ _count = 0; m1: function(){ } // 模块成员会被暴露,内部状态能够被外部改写 })
var module1 = (function($){ var _count = 0; var m1 = function(){ // ... } var _$body = $('body'); var foo = function(){ console.log(_$body); } return { m1: m1, foo: foo } // })(jQuery) module1.foo();
若是模块过多且不打包,网络请求多前端
管理模块之间的依赖关系,便于代码的编写和维护java
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="js/require.js" data-main="js/main"></script> </body> </html>
// 主模块(入口文件)的写法 require.config({ paths: { "jquery": 'jquery.min', "underscore": 'underscore.min', "backbone": 'backbone.min' } }); require(['jquery', 'underscore','backbone'], function($, _, Backbone){ console.log(_.min([1,10,2])) }); require(['math'], function (math) { alert(math.min([10, 11])) });
// 定义模块的方法 define(['underscore'], function(_){ var min = function (x, y){ return _.min(x,y); }; return { min: min } });
此外,require.js提供了一个优化工具r.js,能够将模块打包,减小网络请求node
require.js还有一些插件,能够将图片和文本打包jquery
// 引入sea.js而且配置入口文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app">dfslljdslf</div> <script src="../sea-modules/seajs/seajs/2.2.0/sea.js"></script> <script> seajs.config({ base: '../sea-modules/', alias: { 'jquery': 'jquery/jquery/1.10.1/jquery.js' } }) seajs.use('../static/test/main.js'); </script> </body> </html>
// 入口文件写法 define(function (require, exports, module) { var test = require('./test'); test.fadeOut(); });
// 定义模块方法 define(function(require, exports, module){ var $ = require('jquery'); exports.fadeOut = function(){ $('#app').fadeOut(); } })
写法上:require.js依赖前置,sea.js依赖就近,语法更像common.js
加载和执行顺序上:require.js依赖提早执行,sea.js依赖懒执行,打日志能够看出来webpack
// 最终导出的是exports对象 console.log("example.js"); exports.message = "hi"; exports.say = function (){ console.log("hello"); };
// 使用require加载模块 var example = require('./example.js');
ES6 模块的设计思想是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时肯定这些东西。好比,CommonJS 模块就是对象,输入时必须查找对象属性。
// CommonJS模块 let { stat, exists, readFile } = require('fs'); // 等同于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile;
上面代码的实质是总体加载fs模块(即加载fs的全部方法),生成一个对象(_fs),而后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,由于只有运行时才能获得这个对象,致使彻底没办法在编译时作“静态优化”。
ES6 模块不是对象,而是经过export命令显式指定输出的代码,再经过import命令输入。
// ES6模块 import { stat, exists, readFile } from 'fs';
上面代码的实质是从fs模块加载 3 个方法,其余方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 能够在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。固然,这也致使了无法引用 ES6 模块自己,由于它不是对象。
因为 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,好比引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
// export写法 // 1.直接导出 export var firstName = 'Michael'; // 2.先声明后导出 var firstName = 'Michael'; export {firstName} // 3.导出函数或类 export function multiply(x,y){ return x+y; } // 4.export命令规定的是对外的接口,必须与模块内部的变量创建一一对应关系 export 1 // 报错 var m = 1; export m; //报错 export var m = 1; // 不报错 export {m} // 不报错 // import写法 import {firstName, lastName, year} from './profile.js'; import { lastName as surname } from './profile.js'; // js后缀能够省略 import {myMethod} from 'util'; // import命令具备提高效果,会提高到整个模块的头部,首先执行 foo(); import { foo } from 'my_module'; // 因为import是静态执行,因此不能使用表达式和变量,这些只有在运行时才能获得结果的语法结构 import { 'f' + 'oo' } from 'my_module'; // 报错 // import语句会执行所加载的模块,所以能够有下面的写法 import 'lodash'; // 仅仅执行lodash模块,可是不输入任何值 // 模块的总体加载 // circle.js export function area(radius) { return Math.PI * radius * radius; } export function circumference(radius) { return 2 * Math.PI * radius; } import * as circle from './circle'; console.log('圆面积:' + circle.area(4)); console.log('圆周长:' + circle.circumference(14)); // export default用法 // export-default.js export default function () { console.log('foo'); } // import-default.js import customName from './export-default'; // 不用括号 customName(); // 'foo' // 一个模块只能有一个默认输出,所以export default命令只能使用一次
import()方法
在语法上,条件加载就不可能实现。若是import命令要取代 Node 的require方法,这就造成了一个障碍。由于require是运行时加载模块,import命令没法取代require的动态加载功能。
const path = './' + fileName; const myModual = require(path);
上面的语句就是动态加载,require到底加载哪个模块,只有运行时才知道。import命令作不到这一点。
所以,有一个提案,建议引入import()函数,完成动态加载。
import(specifier)
上面代码中,import函数的参数specifier,指定所要加载的模块的位置。import命令可以接受什么参数,import()函数就能接受什么参数,二者区别主要是后者为动态加载。
import()返回一个 Promise 对象。下面是一个例子。
const main = document.querySelector('main'); import(`./section-modules/${someVariable}.js`) .then(module => { module.loadPageInto(main); }) .catch(err => { main.textContent = err.message; });
import()相似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载。
适用场景
button.addEventListener('click', event => { import('./dialogBox.js') .then(dialogBox => { dialogBox.open(); }) .catch(error => { /* Error handling */ }) });
if (condition) { import('moduleA').then(...); } else { import('moduleB').then(...); }
import(f()) // 根据函数f的返回结果,加载不一样的模块 .then(...);
注意点
import()加载模块成功之后,这个模块会做为一个对象,看成then方法的参数。
// 解构赋值 import('./myModule.js') .then(({export1, export2}) => { // ...· }); // 同时加载多个模块 Promise.all([ import('./module1.js'), import('./module2.js'), import('./module3.js'), ]) .then(([module1, module2, module3]) => { ··· });
require.js有相似r.js的打包工具,此外还有一些打包工具
让浏览器加载Nodejs模块
$ browserify main.js -o bundle.js
能够打包各类文件资源,可支持各类模块规范
配置文件自己就是一个模块
var path = require('path'); var webpack = require('webpack'); var devFlagPlugin = new webpack.DefinePlugin({ _DEV_: JSON.stringify(JSON.parse(process.env.DEBUG || 'false')) }); module.exports={ entry: { // 入口文件,String/Array/Object bundle1: 'main1.jsx', bundle2: 'main2.jsx', bundle: 'main.jsx' }, output:{ // 指定输出位置和文件名 filename:'[name].js', path: path.resolve(__dirname, './') }, mode:'development', module: { // loader 让 webpack 可以去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript) rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use:{ loader: 'babel-loader', // 处理ES6 options: { presets: ['es2015', 'react'] } } }, { test: /\.css$/, // 处理css文件 use: [ { loader: 'style-loader', }, { loader: 'css-loader', options: { modules: true, } } ] }, { test: /\.(jpg|png)$/, // 解析图片模块 use: { loader: 'url-loader', options: { limit: 8192 } } } ] }, plugins: [ // loader 被用于转换某些类型的模块,而插件则能够用于执行范围更广的任务,须要经过使用 new 操做符来建立它的一个实例 devFlagPlugin, // 根据node命令行参数设置变量_DEV_,_DEV_能够在任何模块使用 new webpack.optimize.CommonsChunkPlugin({ // code splitting name: 'commons', filename: 'commons.js', }) ] };
// main.js const React = require('react'); const ReactDOM = require('react-dom'); var style = require('./app.css'); // 导入css文件模块(css module) var img1 = new Image(); img1.src = require('./big.png'); // 导入图片资源 document.body.appendChild(img1); if(_DEV_){ document.write(new Date()); } ReactDOM.render( <div> <h1 className={style.h1}>Hello, world!</h1> <h2 className="h2">Hello, webpack</h2> </div>, document.querySelector('#wrapper') );
webpack.config.js还能够导出一个函数或promise对象,能够导出多种规范的模块,能够热替换,只更新修改的模块。
webpack 从命令行或配置文件中定义的一个模块列表开始,处理你的应用程序。 从这些入口起点开始,webpack 递归地构建一个依赖图,这个依赖图包含着应用程序所需的每一个模块,而后将全部这些模块打包为少许的 bundle - 一般只有一个 - 可由浏览器加载。
https://www.webpackjs.com/concepts/
https://github.com/ruanyf/webpack-demos#demo10-code-splitting-source
http://www.ruanyifeng.com/blog/2012/10/javascript_module.html
http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html
http://www.ruanyifeng.com/blog/2012/11/require_js.html