省略廢話。
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)
});
實測可行。今天先到這里。