學習流程
參考文檔:
一. 簡單使用webpack打包node模塊并且調(diào)用步驟
安裝好node.js和全局的npm
新建文件夾webpack, 打開cmd轉(zhuǎn)到此處
轉(zhuǎn)到E盤:
E:
轉(zhuǎn)到路徑:
cd WorkSpace\webpack
-
初始化npm說明文件package.json:
npm init
- 注意: 此處需要輸入一些包的信息, 在name的時候不能用默認,不能用字符 否則后面安裝webpack的時候會報錯, 可以隨便給個名字, 如: 1
-
在本地安裝Webpack作為依賴包:
npm install --save-dev webpack
- 會生成一個node_modules的文件夾, 里面放了各種node的模塊包,webpack也是其中一個, 然后它本身依賴的包也會一并加入進來
創(chuàng)建需要的文件夾:
md app
和md public
進入app文件夾
cd app
, 創(chuàng)建需要的文件type nul>Greeter.js
和type nul>main.js
進入Public文件夾
cd ..
,cd public
, 創(chuàng)建頁面文件type nul>Index.html
-
創(chuàng)建好的文件結(jié)構(gòu)如下圖:
-
在Index.html寫入下面文本,加載打包的bundle.js文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Webpack Sample Project</title> </head> <body> <div id="root"></div> <script src="bundle.js"></script> </body> </html>
-
在Greeter.js文件中寫一個node.js的模塊來打個招呼
// Greeter.js module.exports = function () { var greet = document.createElement('div'); greet.textContent = '你好呢'; return greet; };
注意: module.exports是聲明了一個node的模塊焰手,為了解決直接用<script>來引用沒有命名空間的尷尬, 詳細參考: node.js module初步理解
-
在main.js里獲取Greeter模塊插入頁面,實際上就是調(diào)用了greeter的函數(shù)
// main.js var greeter = require('./Greeter.js'); document.getElementById('root').appendChild(greeter());
-
開始用webpack指定入口文件, 并打包所有依賴的js文件到指定文件
E:/workspace/webpack/node_modules/.bin/webpack app/main.js public/bundle.js
注意, 如果不是全局安裝的webpack, 這里需要用絕對路徑來使用webpack命令拿穴, 第一個參數(shù)(app/main.js)就是用來指定入口文件, 而第二個參數(shù)(public/bundle.js)則是用來把依賴的所有js文件打包到這個bundle.js文件
-
我們來調(diào)試下壓縮的bundle.js, 看下執(zhí)行順序以及怎么執(zhí)行依賴模塊的
/******/ (function(modules) { // webpackBootstrap ---------------------------------- 第一步 ----------- /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache ------------------------------- 第四步 -------------- /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) -------------------- 第二步 ------------- /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 1); //-------------- 第三步 --------------- /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { // Greeter.js --------------------------------------------------------------- 第六步 ------------------ module.exports = function () { var greet = document.createElement('div'); greet.textContent = '你好呢'; return greet; }; /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { // main.js ------------------------------------------------------------------ 第五步 ------------------ var greeter = __webpack_require__(0); document.getElementById('root').appendChild(greeter()); /***/ }) /******/ ]);
-
==第一步==:進來可以看到modules就是一個數(shù)組纷宇,里面是放的就是兩個模塊函數(shù)的引用
- ==第二步==: 聲明了一個
__webpack_require__
的函數(shù)對象孔飒, 用于按照id來調(diào)用并且返回模塊寄悯,在里面存放modules這個全局模塊容器萤衰,以后方便調(diào)用, 還放了些功能函數(shù)方便調(diào)用 - ==第三步==: 重要的一步猜旬,開始調(diào)用
__webpack_require__
函數(shù)了脆栋, 注意這里的1, 上面我們看到modules[1]就是main.js里面函數(shù)的引用洒擦, 所以這里是從主要的模塊開始調(diào)用 - ==第四步==: 先看是否有緩存椿争,沒有的話根據(jù)ID用call直接開始調(diào)用真正的main.js里面的模塊函數(shù)了
- ==第五步==: 調(diào)用到main模塊, 可以看到把
__webpack_require__
函數(shù)的引用也傳進來了熟嫩, 方便進一步調(diào)用依賴的模塊 - ==第六步==: 果然它開始調(diào)用0模塊了秦踪, 就是Greeter.js, 這個模塊沒有依賴項了, 所有就沒有把函數(shù)的引用傳進來掸茅,然后返回了一個div, 里面寫著你好呢椅邓, 這樣一層一層返回就執(zhí)行完所有的模塊了,該做的事做完了昧狮, 自然頁面就渲染出來了
-
二. 通過配置文件來使用Webpack
通過配置文件來配置webpack包, 不容易出錯而且易于部署, 用來代替上面指定入口文件, 壓縮文件路徑等, 當然還有跟多新功能, 新建配置文件
type nul>webpack.config.js
, 這也是一個JS的模塊-
簡單定義下入口文件, 和輸出路徑
module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 } }
注:“__dirname”是node.js中的一個全局變量景馁,它指向當前執(zhí)行腳本所在的目錄。
如果現(xiàn)在你要打包就直接輸入
E:/workspace/webpack/node_modules/.bin/webpack
就可以了, 這條命令會自動參考配置文件來打包項目有沒有發(fā)現(xiàn)上面還要輸入路徑很煩的, 不過npm可以通過配置來引導任務執(zhí)行, 相當于規(guī)定好npm的配置文件來指定執(zhí)行任務, 用統(tǒng)一的
npm start
就可以去執(zhí)行了, 不用知道具體細節(jié), 這些都在配置文件中寫清楚了-
更改package.json文件, 讓
npm start
來執(zhí)行webpack工作{ "name": "1", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack" //配置的地方就是這里啦逗鸣,相當于把npm的start命令指向webpack命令 }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^2.6.0" } }
Source Map, 生成方便調(diào)試的代碼合住,在webpack.config.js中加入這一句配置:
devtool: "eval-source-map"
-
熱更新服務器(webpack-dev-server):Webpack有一個很實用的功能叫做熱替換, 開發(fā)過程中都不需要刷新瀏覽器,任何前端代碼的更改都會實時的在瀏覽器中表現(xiàn)出來撒璧,首先需要安裝
Webpack-dev-server
到本地工作目錄,一個輕量的node.js express服務器:- 安裝命令:
npm install webpack-dev-server --save-dev
- 看下版本:
E:\workspace\webpack\node_modules\.bin\webpack-dev-server -v
, 顯示出來的是2.4.5, ==注意了==:網(wǎng)上一堆教你各種寫配置文件的, 但是他們沒有說是基于什么版本透葛,這是十分坑爹的,看到版本后沪悲,一定要去官方手冊去查怎么配置获洲,因為1.0和2.0版本相差很大,一不小心就錯了殿如,比如本文參考的文章中可以用colors
這個屬性,但是在自己的電腦上會報錯最爬,一定切記I婺佟!爱致!webpack-dev-server 2.4.5 webpack 2.6.0
- 配置webpack.config.js如下
module.exports = { devtool: "eval-source-map", entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 }, devServer: { contentBase: "./public",//本地服務器所加載的頁面所在的目錄 //colors: true,//終端中輸出結(jié)果為彩色, 這句不能要烤送,應該是兼容性問題 historyApiFallback: true,//不跳轉(zhuǎn) inline: true//實時刷新 } }
- 啟動服務器:
E:\workspace\webpack\node_modules\.bin\webpack-dev-server
, 如下服務器就算是被啟動了, 他提示你可以通過http://localhost:8080/
訪問,并且你動態(tài)更改js文件代碼糠悯,就可以實時更新帮坚,注意:更改html是不能更新的妻往,感覺沒什么鳥用啊, 刷新下有多大不了的事呀.
小技巧: 服務器啟動后要停止用
Ctrl+C
, 再按提示輸入Y
就可以了
- 安裝命令:
-
Loader:Webpack 本身只能處理 JavaScript 模塊试和,如果要處理其他類型的文件讯泣,就需要使用 loader 進行轉(zhuǎn)換。Loader 可以理解為是模塊和資源的轉(zhuǎn)換器阅悍,它本身是一個函數(shù)好渠,接受源文件作為參數(shù),返回轉(zhuǎn)換的結(jié)果节视。這樣拳锚,我們就可以通過 require 來加載任何類型的模塊或文件,比如 CoffeeScript寻行、 JSX霍掺、 LESS 或圖片。下面就用loader加載一個json文件拌蜘。
- 安裝可以轉(zhuǎn)換JSON的loader:
npm install --save-dev json-loader
- 在webpack.config.js中配置如下
module.exports = { devtool: "eval-source-map", entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 }, devServer: { contentBase: "./public",//本地服務器所加載的頁面所在的目錄 historyApiFallback: true,//不跳轉(zhuǎn) inline: true//實時刷新 }杆烁, module: { loaders: [ { test: /\.json$/, //一個匹配loaders所處理的文件的拓展名的正則表達式(匹配以.json結(jié)尾的的字符串,$表示結(jié)尾位置) loader: "json-loader" //loader的名稱(必須) } ] } }
- 在app目錄下:
cd app
創(chuàng)建config.json文件:type nul>config.json
, 并寫入招呼語{ "greetText":"你好呀拦坠,我來自JSON!" }
- 在Greeter.js中引用JSON
var config = require('./config.json'); module.exports = function () { var greet = document.createElement('div'); greet.textContent = config.greetText; return greet; };
- 用
npm start
重新壓縮下, 打開Index.html即可看見
- 安裝可以轉(zhuǎn)換JSON的loader:
-
Babel: babel其實是一個編譯javascript的符合node的模塊包连躏,可以通過編譯ES6, ES7這些下一代沒有被當前瀏覽器支持的標準編譯為當前支持的js代碼贞滨, 或者將react的JSX擴展語言轉(zhuǎn)為標準的js, 下面使用babel的6版本來示例
-
下載babel核心包入热,loader編譯包, es6的轉(zhuǎn)碼規(guī)則包,react的JSX轉(zhuǎn)碼規(guī)則包晓铆, 中間用空格分開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
-
配置babel-loader和babel自己的配置文件, 這樣webpack會自動讀.babelrc配置結(jié)合loader允許寫ES6的語法和JSX了勺良,webpack會自動轉(zhuǎn)換為當前js標準
webpack.config.js
module.exports = { devtool: "eval-source-map", entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 }, devServer: { contentBase: "./public",//本地服務器所加載的頁面所在的目錄 historyApiFallback: true,//不跳轉(zhuǎn) inline: true//實時刷新 }, module: { loaders: [ { test: /\.json$/, loader: "json-loader" }, { test: /\.js$/, exclude: /node_modules/, // 這個模塊下的js文件不通過babel轉(zhuǎn)換 loader: "babel-loader" } ] } }
創(chuàng)建.babelrc文件
type nul>.babelrc
骄噪,并且引入規(guī)則如下 (這個奇怪的名字來源于linux系統(tǒng)習慣尚困,用rc結(jié)尾的文件代表運行時自動加載的配置等文件){ "presets": [ "react", "es2015" ] }
-
我們來用React來試一下, 先裝react和react-dom
npm install --save-dev react react-dom
-
使用ES6語法和react的JSX語法來改寫下Greeter.js和main.js如下,
import React, {Component} from 'react' import config from './config.json'; class Greeter extends Component{ render() { return ( <div> {config.greetText} </div> ); } } export default Greeter
import React from 'react'; import {render} from 'react-dom'; import Greeter from './Greeter'; render(<Greeter/>, document.getElementById('root'));
先
npm start
重新編譯链蕊, 再打開服務器E:\workspace\webpack\node_modules\.bin\webpack-dev-server
就可以熱更新事甜!
-
-
CSS處理: webpack提供兩個工具處理樣式表,
css-loader
和style-loader
滔韵,二者處理的任務不同逻谦,css-loader
使你能夠使用類似@import 和 url(...)的方法實現(xiàn) require()的功能,style-loader
將所有的計算后的樣式加入頁面中,二者組合在一起使你能夠把樣式表嵌入webpack打包后的JS文件中陪蜻。-
安裝樣式的包
npm install --save-dev style-loader css-loader
-
在webpack.config.js配置文件中增加loader配置
module.exports = { devtool: "eval-source-map", entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 }, devServer: { contentBase: "./public",//本地服務器所加載的頁面所在的目錄 historyApiFallback: true,//不跳轉(zhuǎn) inline: true//實時刷新 }, module: { loaders: [ { test: /\.json$/, loader: "json-loader" }, { test: /\.js$/, exclude: /node_modules/, // 這個模塊下的js文件不通過babel轉(zhuǎn)換 loader: "babel-loader" }, { test: /\.css$/, loader: 'style-loader!css-loader?modules'//添加對樣式表的處理, 并且分開為css模塊 } ] } }
注:
(style!css?modules)
:感嘆號(!)的作用在于使同一文件能夠使用不同類型的loader,而?modules
則是把css打包為單獨的模塊, 單獨打包為一個css文件, 不要這句則會和js打包到一起(05/26注: 調(diào)試發(fā)現(xiàn)并沒有打包為單獨的css文件, 而且不用?modules
css都不起作用, 應該是版本問題, 加了這句才能正常顯示樣式, 而且后來了解到module里面用loaders是1.版本的問題, 2.后都用rules了) -
在app文件夾里新建main.css文件:
cd app
>>type nul>main.cs
, 并且設置寫樣式如下html { box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } *, *:before, *:after { box-sizing: inherit; } body { margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; } h1, h2, h3, h4, h5, h6, p, ul { margin: 0; padding: 0; }
-
webpack只有單一的入口邦马,其它的模塊需要通過 import, require, url等導入相關位置,為了讓webpack能找到”main.css“文件,我們把它導入單一入口”main.js “中, main.js配置如下
import React from 'react'; import { render } from 'react-dom'; import Greeter from './Greeter'; import './main.css'; //使用require導入css文件 render(<Greeter />, document.getElementById('root'));
-
再單獨為Greeter.js模塊增加Greeter.css文件增加樣式并且引入Greeter.js文件
type nul>Greeter.css
#root { color: red; background-color: #eee; padding: 10px; border: 3px solid #ccc; }
import React, { Component } from 'react' import config from './config.json'; import styles from './Greeter.css'; //導入 class Greeter extends Component { render() { return ( <div className={styles.root}> // 增加css類名 {config.greetText} </div> ); } } export default Greeter
-
-
插件(Plugins): 用來擴展webpack功能的組件, 會在整個構(gòu)建過程中生效, 執(zhí)行相關的任務, loaders和Plugins是完全不同的東西, loaders是在打包構(gòu)建過程中處理各種源文件(JSX, Sass, Less)等轉(zhuǎn)化為當前瀏覽器支持的格式的一種模塊, 而插件是整個構(gòu)建過程都起作用的
- HtmlWebpackPlugin插件:這個插件的作用是依據(jù)一個自定義的簡單的模板滋将,幫你生成一個最終的Html5文件邻悬,這個文件中自動引用了你用wbpack打包后的JS文件, 相當于一個基本頁面, 每次編譯都在文件名中插入一個不同的哈希值, 防止引用緩存的外部文件問題. 用法如下:
- 安裝這個插件
npm install --save-dev html-webpack-plugin
- 刪除目前文件結(jié)構(gòu)中的public文件夾, 里面的bundle.js由webpack打包自動生成, 而Index.html頁面則可以通過這個插件自動生成(本例子中不管這個文件夾也是可以的)
- 在app目錄下創(chuàng)建一個用于插件的Html的模版, 這是個基本的html5默認頁面. 注意此刻不用引用例如bundle.js等js或者css文件, 等到插件自動生成的時候會自動引用進來, 本例中命名模版名稱為:
index.templ.html
, 代碼如下<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id='root'> </div> </body> </html>
- 更改webpack配置文件, 新建一個build文件夾來存放最終輸出的文件(為什么叫build, 這是個工程習慣)
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { devtool: "eval-source-map", entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/build",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 }, devServer: { contentBase: "./build",//本地服務器所加載的頁面所在的目錄 historyApiFallback: true,//不跳轉(zhuǎn) inline: true//實時刷新 }, module: { loaders: [ { test: /\.json$/,loader: "json-loader"}, { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader"}, { test: /\.css$/, loader: "style-loader!css-loader?modules"} ] }, plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html"http://new 一個這個插件的實例,并傳入相關的參數(shù) }) ], }
- 運行
npm start
后看到自動生成Public文件夾和bundle.js, Index.html文件
- 安裝這個插件
- HtmlWebpackPlugin插件:這個插件的作用是依據(jù)一個自定義的簡單的模板滋将,幫你生成一個最終的Html5文件邻悬,這個文件中自動引用了你用wbpack打包后的JS文件, 相當于一個基本頁面, 每次編譯都在文件名中插入一個不同的哈希值, 防止引用緩存的外部文件問題. 用法如下:
-
使用
extract-text-webpack-plugin
插件分離css文件:-
安裝extract-text-webpack-plugin
npm install --save-dev extract-text-webpack-plugin
-
配置webpack.config.js
var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); const ExtractTextPlugin = require("extract-text-webpack-plugin"); // 引入插件 module.exports = { devtool: "eval-source-map", entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/build",//打包后的文件存放的地方 filename: "bundle.js"http://打包后輸出文件的文件名 }, devServer: { //contentBase: "./build",//本地服務器所加載的頁面所在的目錄 historyApiFallback: true,//不跳轉(zhuǎn) inline: true//實時刷新 }, module: { loaders: [ { test: /\.json$/, loader: "json-loader" }, { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }, // { test: /\.css$/, loader: "style-loader!css-loader?modules" }, { test: /\.css$/, loader: ExtractTextPlugin.extract({ // 分離配置 fallback: "style-loader", use: "css-loader" }) } ] }, plugins: [ new HtmlWebpackPlugin({ template: __dirname + "/app/index.tmpl.html"http://new 一個這個插件的實例随闽,并傳入相關的參數(shù) }), new ExtractTextPlugin("styles.css"), // 新建style.css文件打包所有css ], }
重新壓縮文件
npm start
, 這樣就會在build文件夾下面自動生成style.css文件存放所有的css樣式, 同樣Index.html文件也會自動引入此文件, 實現(xiàn)css文件從bundle.js中分開
-