省略废话。
1.ES6之前两个比较流行的模块机制CommonJS和AMD。CommonJS模块就是对象,加载模块时加载的是拷贝;而ES6加载的是对export的变量的引用。
2.ES6模块不是对象,使用可出现在模块顶层任何位置的export显式指定输出的代码(变量、函数、class):
export:
export var name='microsoft';
---another example 推荐在底部用{}输出需要的代码
var value_x='microsoft';
export { value_x,value_y ,method_y};
如果想给export的变量改名字,可以使用as:
export { value_x,value_y as alias_name ,method_y};
export function fn_name(){};
*export的变量是动态绑定其所在模块的。比如setTimeout后改变value_x的值,接收者得到的value_x也会定时改变。
import:
import { value_x,mothed_y } from strUrl;
value_x,mothed_y的名称必须与 strUr 中export的名称相同。如果想给import的变量改名字,可以使用as:
import { value_x as value_z,mothed_y } from strUrl;
*import具有提升效果。
*import会执行所加载的模块。
*import命令中接收的变量名要在{}中
整体加载:
可以用*指定一个对象,所有import的东西都加载到这对象上:
import * as mod from strUrl;
var x=mod.value_x;
也可以用module命令:
module mod from strUrl;
var x=mod.value_x;
export default:
上面说了import命令中加载的变量名称要和export输出的名称一致,我们也可以在输出模块中用export default指定默认输出:
export default function(){}; --比如一个匿名函数或对象
然后自己在加载的时候给接收的函数/对象命名:
import customName from strUrl;
*import后不用加{}。因为export default在模块中只能使用一次,只输出一个变量/方法/class。
*无论export default输出的是不是匿名,都视为匿名。
*除了默认输出外还想加载其他变量:
import customName,{value_x,method_x} from strUrl;
*export * from strOtherMod 命令,先加载strOtherMod 再将其输出,*会忽略strOtherMod 的默认输出。
-------------------------------------------------------------分割线----------------------------------------------------------------
在实际生产中常用到webpack。webpack有code-spliting功能,允许我们分割代码,实现模块静态/动态加载。
webpack有三种代码分离方法:
1.打包时在entry部署要打包几个chunk。
2.使用CommonsChunkPlugin插件去重和分离chunk。
3.Dynamic Imports:通过模块的内联函数动态导入代码(重点)。
import:
webpack使用import语法来实现模块的动态导入,这里import会生成一个promise对象:import('path/to/module') -> Promise,以下是官方例子:
if ( module.hot ) {
import('lodash').then(_ => {
// Do something with lodash (a.k.a '_')...
})
} --记住,then()中return的变量会作为后续then(param)的参数prama
在这里,import()会被webpack当作一个代码分割点(split point),所要import的文件会被另外打包成一个chunk,等需要时再加载。
使用时报错:
Module build failed: SyntaxError: D:/te/src/js/third.js: 'import' and 'export' may only appear at the top level (251:1)
根据webpack官网案例,案例中能成功,这里的报错有说是webpack并没有按正确的语法来对待import,有网友说需要插件如:syntax-dynamic-import,
也有说babel的presets要加latest和es2015,我的babel配置和presets是没问题的,估计是插件,这里留下这个问题。
lazy-loading(需要时加载):
button.onclick = e => import(/* webpackChunkName: "print" */ './print')
.then(module => {
var print = module.default;
print();
});
官网例子很浅显,触发某个事件的时候再import模块,就像图片廊等用户滚动的时候再加载更多图片一个道理。
CommonJS:
webpack也支持commonjs,commonjs模块主要分为三块:模块引用require、模块定义exports、模块标志module(即模块本身)。
在服务器端,require(strUrl)是同步的,由于服务器在本地硬盘读取文件,速度很快,故而没问题,但浏览器端必须使用异步方法,因而有了AMD/CMD。
webpack实现的commonjs有:
1.require(strUrl); --同步
2.require.resolve(strUrl) --检索(或者说返回)strUrl模块的id。
3.require.cache(module.id)
--模块被require后只执行一次,也只返回一个exports对象。官网表示,在某些极端情况会需要多次require同一个模块,也即重复执行某个require(获取module并执行回调);
因而可以执行那面的代码:
delete require.cache[require.resolve("dependency")];
大意是删除原本加载完的模块的id,让js可以再次加载模块并执行回调。
4.require.ensure([dependencies],callback(require),chunkName_str);
--这是webpack的require api里唯一一个可以实现异步加载的函数。
第一个参数是模块依赖的名称数组(这些依赖是执行callback需要的);
第二个是回调,qi,其形参require允许我们执行进一步的require.ensure;
第三个是chunk名称,require.ensure的代码会被打包到另一个chunk伺机加载,该参数就是这个chunk的名称。同名的chunk会被打包在一起。
//------------------------------------------------------------------------分割线---------------------------------------------------------------------------------------------------------------
使用vue的异步组件:
vue定义全局组件的方式是:
Vue.component('name',optionObj);
文档说,“Vue.js 允许将组件定义为一个工厂函数,动态地解析组件的定义”:
--------------------------------------引用-------------------------------------------
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// Pass the component definition to the resolve callback
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
工厂函数接收一个 resolve 回调,在收到从服务器下载的组件定义时调用。也可以调用 reject(reason) 指示加载失败。
这里 setTimeout 只是为了演示。怎么获取组件完全由你决定。
---------------------------------------------------------------------------------------------
所以确定第一点:vue中异步组件的实现,是把 Vue.component('name',optionObj)中optionObj替换为一个函数,
这个函数执行异步任务,获取组件的定义并执行回调。
第二点是:当需要用到异步组件的时候,往往是模块已经足够大,这时候往往是使用.vue单文件组件,自包含template、css和其他options。
我个人对文档中的方法不太能理解:
后来有人解释说;这总方式是可以的,但省略了一些生产上的具体代码,实际上应该这样写:
<script>
import Vue from 'vue'
const name_ = Vue.component('name', function (resolve) {
require(['./service-search.vue'], resolve)
})
export default{
data(){
return {}
},
methods: {},
components: {
name: name_ //也可以更简便地将function (resolve){}替换这里的name_
}
}
</script>
vue路由:
另外,我也在学习vue路由时使用异步组件,用的是github上同行的写法:
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import Vuex from 'vuex'
const setting = resolve => require.ensure([], () => resolve(require('./components/setting.vue')), 'setting');
Vue.use(VueRouter);
Vue.use(VueResource);
Vue.use(Vuex);
const routes=[
{path:'/setting',component:setting},
];
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes
})
new Vue({
router,
store,
el: '#app',
render: h => h(App)
});
实测可行。今天先到这里。