近年來,js開發涌現出了諸多模塊化解決方案,例如以在瀏覽器環境以外(服務端)構建 JavaScript
生態系統爲目標而產生的CommonJS
規範,從CommonJS
社區中獨立出來的AMD
規範(異步模塊定義),還有國人制定的CMD
規範等。隨着遵循AMD
規範的RequireJS
的流行,AMD
規範在前端界已被普遍認同。後來,隨着npm社區的逐漸壯大,CommonJS
也愈來愈受歡迎,因而產生了統一這兩種規範的需求,即但願提供一個先後端跨平臺的解決方案,也所以產生了UMD
(通用模塊定義)規範。javascript
CommonJS
定義的是模塊的同步加載,主要用於Node端;而遵循AMD規範的RequireJS
則是異步加載,適用於瀏覽器端。requirejs
是一種在線」編譯」 模塊的方案,至關於在頁面上加載一個AMD 解釋器,以便於覽器可以識別 define、exports、module
,而這些東西就是用於模塊化的關鍵。css
1. CommonJS
同步式的require
html
Node端的模塊加載遵循 CommonJS
規範,該規範的核心思想是容許模塊經過 require
方法來加載。前端
該規範首先加載所要依賴的其餘模塊,而後經過 exports
或 module.exports
來導出須要暴露的接口。但它的缺點也是顯而易見的,即一個文件一個文件的加載很容易發生阻塞。java
require("module");//find from node_modules require("../file.js"); exports.something = function() {}; module.exports = something;
2. 使你的模塊兼容AMD規範node
//web.js (function (root, factory) { //判斷define是否存在 if (typeof define === 'function' && define.amd) { // 存在則使用AMD方式加載模塊 define(['b'], factory); } else { // 不存在則使用瀏覽器全局變量暴露模塊 root.web = factory(root.b); } }(this, function (b) { //use b in some fashion. // Just return a value to define the module export. // This example returns an object, but the module // can return a function as the exported value. return {}; }));
定義一個叫web.js
的模塊,依賴於另外一個叫b
的模塊。若是你不想支持瀏覽器全局路徑,那麼你能夠移除root
並傳遞this
參數在函數頂部。react
3. define.amd
屬性jquery
爲了清晰的標識全局函數(爲瀏覽器加載script必須的)聽從AMD編程接口,任何全局函數應該有一個"amd"的屬性,它的值爲一個對象。webpack
關於RequireJS的使用不在本文範圍以內,所以不展開講解,有興趣的請移步個人另外一篇文章:git
詳解JavaScript模塊化開發:http://www.javashuo.com/article/p-tvzesnwv-ku.html
由於CommonJS
阻塞式的缺點,因此並不適合前端。因而有了AMD異步加載模塊的規範。
Asynchronous Module Definition
規範其實只有一個主要接口 define(id?, dependencies?, factory)
,它要在聲明模塊的時候指定全部的依賴 dependencies
,而且還要當作形參傳到 factory
中,對於依賴的模塊提早執行,依賴前置。
由於瀏覽器端的需求和同步require的問題,因此社區引進了異步模塊加載的規範,即AMD規範。
define("module", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; }); require(["module", "../file"], function(module, file) { /* ... */ });
使你的模塊兼容於UMD規範:
//UMD,兼容AMD和CommonJS規範 (function (root, factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('b')); } else if (typeof define === 'function' && define.amd) { // AMD define(['b'], function (b) { return (root.returnExportsGlobal = factory(b)); }); } else { // 瀏覽器全局變量,root即window root.returnExportsGlobal = factory(root.b); } }(this, function (b) { // 你的實際模塊 return {}; }));
UMD規範實現的思路:
首先判斷是否支持Node.js
模塊格式,即exports
對象是否存在。
而後判斷是否支持AMD格式(require是否存在),存在則使用AMD
方式加載
若前兩個都不存在,則將模塊暴露到全局,Node
即global
,瀏覽器即window。
例如,建立一個兼容UMD規範的jQuery插件:
// Uses CommonJS, AMD or browser globals to create a jQuery plugin. (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function( root, jQuery ) { if ( jQuery === undefined ) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if ( typeof window !== 'undefined' ) { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } factory(jQuery); return jQuery; }; } else { // Browser globals factory(jQuery); } }(function ($) { $.fn.jqueryPlugin = function () { return true; };
Common Module Definition
規範和 AMD 很類似,儘可能保持簡單,並與 CommonJS
和 Node.js
的 Modules
規範保持了很大的兼容性。
define(function(require, exports, module) { var $ = require('jquery'); var Spinning = require('./spinning'); exports.doSomething = ... module.exports = ... })
CMD規範地址:https://github.com/seajs/seajs/issues/242
ES6 module
ECMAScript6
內建的用法:
import "jquery"; export function doStuff() {} module "localModule" {}
JavaScript
文件?爲何模塊化系統只幫助開發者處理JavaScript
?然而還有其餘靜態資源須要被處理,好比:
stylesheets images webfonts html for templating 其餘..
coffeescript ➞ javascript less stylesheet ➞ css jade ➞ html i18n ➞ something require("./style.css"); require("./style.less"); require("./template.jade"); require("./image.png");
由於上面這些動機,因此有了webpack
。
webpack
是一款模塊封裝工具(module bundler,是打包工具,也是模塊加載工具,各類資源均可以當成模塊來處理),webpack
會將模塊與其餘相關聯的模塊,函數庫,其餘須要預編譯的文件等整合,編譯輸出此模塊的靜態資源文件。
// webpack.config.js `like=>` gulpfile.js/gruntfile.js module.exports = { entry: "./entry.js", output: { path: __dirname, filename: "bundle.js" }, //module 對象用於添加loaders module: { //一個用於加載loader的數組 loaders: [ { test: /\.css$/, loader: "style!css" } ] } };
module具備以下屬性:
test: 須要知足的條件A condition that must be met
exclude: 不須要知足的條件A condition that must not be met
include: 須要知足的條件A condition that must be met
loader: !
用於分隔loaders
loaders: 一個loaders數組An array of loaders as string
"include" 一般被用於匹配目錄:
include: [ path.resolve(__dirname, "app/src"), path.resolve(__dirname, "app/test") ],
簡單的說,webpack會把咱們經常使用的 .less
, .scss
, .jade
, .jsx
等等文件編譯成純 js + 圖片
(圖片有時也能夠被編譯成 base64
格式的 dataUrl
)。
webpack的優點和特色:
將依賴項分塊,按需加載
儘量減小初始化載入的時間
使每個靜態資源都可以做爲組件使用
有能力整合其餘第三方函數庫爲模塊
高度可配置化
適合大型項目
webpack擁有更聰明的解析工具能夠處理幾乎全部的第三方函數庫。甚至容許在相依性設定上使用表達式,例如: require("./templates/" + name + ".jade")
,這幾乎能處理大部分的模塊化標準(CommonJS, AMD)。
webpack 最基本的啓動webpack命令 webpack -w 提供watch方法,實時進行打包更新 webpack -p 對打包後的文件進行壓縮 webpack -d 提供SourceMaps,方便調試 webpack --colors 輸出結果帶彩色,好比:會用紅色顯示耗時較長的步驟 webpack --profile 輸出性能數據,能夠看到每一步的耗時 webpack --display-modules 默認狀況下 node_modules 下的模塊會被隱藏,加上這個參數能夠顯示這些被隱藏的模塊
webpack
首先在項目根目錄新建一個package.json
或者經過$ npm init
指令來產生:
接着經過npm
指令安裝webpack
:
$ npm install webpack --save-dev or => $ cnpm i webpack -g
單純的編譯指令
$ webpack <entry> <output>
context:用來指明entry選項的基礎目錄(絕對路徑)。默認值爲process.cmd()
,即webpack.config.js
文件所在路徑
對象entry
定義了打包後的入口文件,能夠是數組(全部文件打包生成一個filename文件),對象或者字符串
{ entry: { page1: "./page1", page2: ["./entry1", "./entry2"] }, output: { // 在 output.filename 中使用 [name]或者[id],當使用多個entry points時 filename: "[name].bundle.js", path: "dist/js/page", chunkFilename: "[id].bundle.js" //chunkFilename是非主入口的文件名 } }
該段代碼最終會生成一個page1.bundle.js
和 page2.bundle.js
,並存放到 ./dist/js/page
文件夾下
chunkFilename
是非主入口的文件名,當按需異步加載模塊的時候,這時生成的文件名是以chunkname
配置的
output
:該參數是個對象,定義了輸出文件的位置及名字:
path
: 打包文件存放的絕對路徑
publicPath
: 網站運行時的訪問路徑URL
filename
:打包後的文件名
你可使用<name>=<filename>
的格式來替代 entry point
創建一個別名:
// webpack.config.js module.exports = { output: { filename: "[name].bundle.js" } }
接着執行以下命令:
$ webpack index=./entry.js >> Output a file that is named index.bundle.js
對象output
表示欲輸出的路徑,其會被映射到設定檔中的 output.path
以及 output.filename
output.filename
:指定每個在磁盤上輸出的文件名,不容許指定絕對路徑
output.path
:輸出絕對路徑目錄(必須)
output.publicPath
:指定在瀏覽器端引用的文件公開URL地址
對象resolve
webpack在構建包的時候會按目錄進行文件的查找,resolve
屬性中的extensions
數組可用於配置程序能夠自行補全哪些文件後綴。extensions
第一個是空字符串,對應不須要後綴的狀況。好比,爲了查找CoffeeScript
文件,你的數組應當包含字符串".coffee"
。使用extensions
,在引入模塊的時候就不須要寫後綴,會自動補全
resolve: { extensions: ['', '.js', '.jsx','.es6','css','scss','png','jpg'] },
resolve.alias
定義別名
alias:{ 'react-dom':path.join(nodeModulesPath,'/dist/react-dom'), 'redux': path.join(nodeModulesPath,'dist/redux') },
對象externals
當咱們想在項目中require
一些其餘的類庫或者API
,而又不想讓這些類庫的源碼被構建到運行時文件中,
這在實際開發中頗有必要。此時咱們就能夠經過配置externals
參數來解決這個問題:
externals: { "jquery": "jQuery" }
這樣咱們就能夠放心的在項目中使用這些API了:
var $ = require(「jquery」);
配置項詳情:https://webpack.github.io/docs/configuration.html
你能夠在你的js文件裏引入css文件和圖片,例如:
require('./bootstrap.css'); require('./style.less'); require('../../main.scss'); var img = document.createElement('img'); img.src = require('./myImg.png');
當你require
了CSS(less或者其餘)文件,webpack
會在頁面中插入一個內聯的<style>
標籤去引入樣式。當你require
圖片的時候,bundle文件會包含圖片的url
,並經過require()
返回圖片的url
。
固然,你須要在webpack.config.js
裏作相應的配置:
// webpack.config.js module.exports = { entry: './entry.js',//入口文件 output: { path: './build', // 輸出js和圖片的目錄 publicPath: 'http://mycdn.com/', // 用來生成圖片的地址 filename: 'bundle.js' }, module: { loaders: [ { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // 用!去鏈式調用loader { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.scss$/,loaders: ["style", "css", "sass"]} {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // 內聯的base64的圖片地址,圖片要小於8k,直接的url的地址則不解析 ] } };
固然,你須要先經過npm包來安裝這些loader,webpack
會經過test
查找匹配文件,而後加載相應的loader
。好比經過npm安裝sass loader
:
$ npm install sass-loader node-sass webpack --save-dev
新建一個content.js
// content.js module.exports = "It works from content.js";
而後編輯 entry.js
加入 require
// File: entry.js document.write(require("./content.js"));
打開瀏覽器,看到屏幕輸出:"It works from content.js";
Webpack 會給每一個模塊一個惟一的 ID
而後經過 ID
存取這些模塊,這些模塊都會被整合到 bundle.js
裏面。
1. CommonsChunkPlugin
合併公共代碼
它用於提取多個入口文件的公共腳本部分,而後生成一個 公共文件來方便多頁面之間進行復用。如下是該插件的使用方法和webpack的具體用法介紹:
var webpack = require('webpack'); var path = require('path'); var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); var config = { //Webpack 自己內置了一些經常使用的插件,還能夠經過 npm 安裝第三方插件。 plugins: [commonsPlugin], //頁面入口文件配置 entry: { index : './index.js' }, //入口文件輸出配置 output: { path: 'dist/js/page', filename: '[name].js' }, //module 的做用是添加loaders module: { //加載器配置 loaders: [ //.css 文件使用 style-loader 和 css-loader 來處理 { test: /\.css$/, loader: 'style-loader!css-loader' },//test屬性匹配css文件 //.js 文件使用 jsx-loader 來編譯處理 { test: /\.js$/, loader: 'jsx-loader?harmony' }, //.scss 文件使用 style-loader、css-loader 和 sass-loader 來編譯處理 { test: /\.scss$/, loader: 'style!css!sass?sourceMap'}, //圖片文件使用 url-loader 來處理,小於8kb的直接轉爲base64 { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}, {//處理字體 test: /\.(woff2?|otf|eot|svg|ttf)$/i, }, loader: 'style!css'//加載style和css loader ] }, //其它解決方案配置 resolve: { root: '/Users/trigkit4/webpack', //絕對路徑 extensions: ['', '.js', '.json', '.scss'],//文件擴展名 //模塊別名定義,方便後續直接引用別名 alias: { a : './assets/a.js', // require(「a」)便可引用該模塊 b : './assets/b.js', c : './assets/c.js' } } }; module.exports = config;
Webpack
中將打包後的文件都稱之爲Chunk
。這個插件能夠將多個打包後的資源中的公共部分打包成單獨的文件。這裏指定公共文件輸出爲common.js
Webpack
自己只能處理 JavaScript
模塊,若是要處理其餘類型的文件,就須要使用loader
進行轉換。
不一樣模塊的加載是經過模塊加載器(webpack-loader
)來統一管理的。
!
用來定義loader
的串聯關係,」-loader」
是能夠省略不寫的,多個loader
之間用「!」
鏈接起來,但全部的加載器都須要經過npm
來加載。
2. UglifyJsPlugin
壓縮js文件
//webpack.config.js //...other webpack settings plugins: [ new Webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ]
3. Extract Text Plugin
你們都知道在 webpack
中 CSS
是能夠被 require()
的,webpack
會自動生成一個 <style>
標籤並加入到 html
的 <head>
標籤 中。可是在發佈時,咱們可能只但願有一個被打包事後 css 文件。這時 Extract Text Plugin
就能幫助咱們完成這項任務。
//webpack.config.js var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { loaders: [ { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") } ] }, plugins: [ new ExtractTextPlugin("app.bundle.css") ] }
以上設置會輸出一個 app.bundle.css
的文件。
4. html-webpack-plugin
webpack中生成HTML的插件。
var HtmlWebpackPlugin = require('html-webpack-plugin') var webpackConfig = { entry: 'index.js', output: { path: 'dist', filename: 'index_bundle.js' }, plugins: [ new HtmlWebpackPlugin({ //根據模板插入css/js等生成最終HTML favicon: './src/img/favicon.ico', //favicon路徑,經過webpack引入同時能夠生成hash值 filename: './view/about.html', //生成的html存放路徑,相對於path template: './src/view/about.html', //html模板路徑 inject: true, //js插入的位置,true/'head'/'body'/false hash: true, //爲靜態資源生成hash值 chunks: ['vendors', 'about'],//須要引入的chunk,不配置就會引入全部頁面的資源 minify: { //壓縮HTML文件 removeComments: true, //移除HTML中的註釋 collapseWhitespace: false //刪除空白符與換行符 } }), ] }
詳情:https://www.npmjs.com/package/html-webpack-plugin
5. ProvidePlugin
plugins: [ new webpack.ProvidePlugin({ React: 'react', ReactDOM: 'react-dom' }) ]
ProvidePlugin
插件能夠定義一個共用的插件入口,之後的文件就不須要require('react')
也能使用React了。
6. babel loader
Babel-loader
可以將JSX/ES6
文件轉爲js文件
$ npm install babel-loader babel-core babel-preset-es2015 babel-preset-react --save-dev
配置:
module.exports = { entry: {}, //文件入口 output: { }, //輸出出口 module: { loaders: [ { test:/\.js[x]?$/, loader: 'babel-loader', exclude:/node_modules/, query:{presets: ['es2015','react']} }, ] }, plugins: [ ],//編譯的時候所執行的插件數組 devtool : "source-map" //調試模式 };
插件列表:http://webpack.github.io/docs/list-of-plugins.html
Webpack
開發服務器須要單獨安裝,一樣是經過npm
進行:
$ sudo npm install -g webpack-dev-server
可使用webpack-dev-server
直接啓動,也能夠增長參數來獲取更多的功能,
具體配置能夠參見官方文檔。在終端輸入:
$ webpack-dev-server
而後打開:http://localhost:8080/webpack-dev-server/index.html
在webpack.config.js
裏配置:
//使用webpack-dev-server,提升開發效率 module.exports={ devServer: { contentBase: './', host: 'localhost', port: 9090, //默認8080 inline: true, //能夠監控js變化 hot: true, //熱啓動 } }
詳情:https://webpack.github.io/docs/webpack-dev-server.html
browser-sync
實時刷新頁面若是每次更改代碼都要從新執行webpack
命令難免太過麻煩,因此這裏推薦使用browser-sync-webpack-plugin
,能夠監聽代碼變化,實時刷新頁面。
安裝browser-sync-webpack-plugin
:
$ npm install --save-dev browser-sync-webpack-plugin $ webpack --watch
webpack.config.js
配置:
var BrowserSyncPlugin = require('browser-sync-webpack-plugin'); module.exports = { // ... plugins: [ new BrowserSyncPlugin({ // browse to http://localhost:3000/ during development, // ./public directory is being served host: 'localhost', port: 3000, server: { baseDir: ['public'] } //根目錄就填'./' }) ] }
react+webpack+es6
開發模式