"手動是不可能手動的了勾笆,這輩子都不可能手動的了。"
一接谨、目標
上一章我們借助ngRoute,完成了口袋妖怪SPA系統(tǒng)的多模塊導(dǎo)航開發(fā)塘匣,但是現(xiàn)在引用的東西越來越多脓豪,項目文件目錄開始變得混亂不堪:
現(xiàn)在先對當(dāng)前項目文件列表進行整理,將五大模塊移入src文件夾中:
然后修改index.html中的js引用路徑:
<script src="src/pokemon/pokemon.js"></script>
<script src="src/skill/skill.js"></script>
<script src="src/hagberry/hagberry.js"></script>
<script src="src/prop/prop.js"></script>
<script src="src/game/game.js"></script>
<script src="src/app.js"></script>
再分別修改每個模塊js文件中的templateUrl忌卤,使其指向文件位置:
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/pokemons', {
templateUrl: 'src/pokemon/pm-list.html',
controller: 'PMListController'
})
.when ('/pokemon/:no', {
templateUrl: 'src/pokemon/pm-detail.html',
controller: 'PMDetailController'
})
}])
簡單修改一下之后項目瞬間整潔了許多扫夜,但是如果以后工程變得更大,將會有越來越多的第三方庫驰徊、環(huán)境工具包笤闯、樣式庫及圖片庫等,這樣項目整理維護難度肯定會隨著項目的擴大而不斷加大辣垒,所以我們需要一個編譯打包工具來協(xié)助我們對項目文件進行管理打包望侈,以方便開發(fā)的推進。
二勋桶、分析
參考網(wǎng)上文章對grunt脱衙、gulp、webpack三種打包工具的分析比對例驹,grunt的配置較為復(fù)雜且效率不高所以先舍棄捐韩,gulp的配置簡單和流式工作比較吸引,但是顯然webpack的模塊化特性與AngularJS的機制具有更好的相性鹃锈,于是本項目選中Webpack作為項目的打包工具荤胁。
三、開發(fā)
3.1 安裝
參考Webpack指南屎债,安裝webpack最新版本(當(dāng)前版本4.6.0):
yarn add webpack --save-dev
3.2 起步
A. 簡化工程仅政,完成初始打包
現(xiàn)在準備開始使用Webpack4.6.0打包工程,但是由于是初學(xué)Webpack盆驹,所以先將工程簡化為最簡版本圆丹,即先不加入多模塊,只留下index.html和src/app.js躯喇,index.html代碼如下:
<!DOCTYPE html>
<html lang="en" ng-app="pokemon-app">
<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>口袋妖怪</title>
<!-- <script src="libs/angular.js"></script>
<script src="libs/angular-route.js"></script>
<script src="src/pokemon/pokemon.js"></script>
<script src="src/skill/skill.js"></script>
<script src="src/hagberry/hagberry.js"></script>
<script src="src/prop/prop.js"></script>
<script src="src/game/game.js"></script>
<script src="src/app.js"></script> -->
<script src="dist/bundle.js"></script>
<link rel="stylesheet" href="app.css"/>
</head>
<body ng-controller="AppController">
<h1>口袋妖怪管理系統(tǒng)</h1>
<!-- <div>
<h2>快速導(dǎo)航:</h2>
<a href="/#!/pokemons">口袋妖怪</a>
<a href="/#!/skills">技能</a>
<a href="/#!/hagberrys">樹果</a>
<a href="/#!/props">道具</a>
<a href="/#!/games">游戲</a>
</div>
<div ng-view></div> -->
<p>{{test}}</p>
</body>
</html>
index.html中我們做了兩個處理:
1. 屏蔽所有的js引用辫封,加入一條對bundle.js(即編譯之后的js)的引用;
2. 屏蔽掉導(dǎo)航div和ngView的div硝枉,加入<p>{{test}}</p>
;
經(jīng)過處理之后,index.html 不再需要引用angular.js文件倦微,只需要直接引用最終打包好bundle.js妻味,angular加載的任務(wù)交給了app.js:
import angular from 'angular'; // 引入angular模塊
(function () {
'use strict';
angular.module('pokemon-app', [
// 'ngRoute',
// 'pokemon-app.pokemon',
// 'pokemon-app.skill',
// 'pokemon-app.hagberry',
// 'pokemon-app.prop',
// 'pokemon-app.game'
])
// .config (['$routeProvider', function ($routeProvider) {
// $routeProvider
// .otherwise({
// redirectTo: '/pokemons' // 初始化直接跳轉(zhuǎn)到pokemon模塊
// });
// }])
.controller('AppController', AppController);
AppController.$inject = ['$scope'];
function AppController ($scope) {
$scope.test = "webpack success!"; // 加入測試文字以供顯示
}
})();
app.js中我們做了三個處理:
1. 開頭用import語法引入angular模塊;
2. 屏蔽掉所有模塊的引用和路由配置;
3. 在AppController中加入$scope.test = "webpack success!";;
經(jīng)過處理,app.js就能直接引入依賴庫欣福,這樣的引用方式更加簡潔明了责球,而且編譯打包過程中將能夠發(fā)現(xiàn)缺少依賴的問題,由此避免了因依賴不存在或者順序錯誤問題導(dǎo)致的腳本執(zhí)行錯誤劣欢。
現(xiàn)在依舊看看跟著官網(wǎng)-起步-創(chuàng)建一個 bundle 文件走棕诵,完成了上面對index.html & src/app.js的修改之后,我們開始嘗試編譯打包凿将,我的Node.js版本是8.9.3,所以直接執(zhí)行官網(wǎng)的命令:
npx webpack src/app.js --output dist/bundle.js
運行完提示請求安裝webpack-cli价脾,看到cli后綴就知道大概是封裝了命令行輸入之類的東西吧牧抵,直接跟著安裝試試就行,輸入yes然后發(fā)現(xiàn)居然還沒動靜了
于是我中斷安裝然后自己輸入安裝webpack-cli的命令:
yarn add webpack-cli --save-dev
果然安裝成功了侨把,馬上再次運行npx的編譯指令~咦又報錯了,直接拉到最后看Error信息:
Error信息指出缺少angular模塊(編譯階段就可以檢測是否缺少依賴犀变,優(yōu)越性體現(xiàn)達成√),由于我們沒有配置libs的路徑所以webpack很明顯讀不到秋柄,寫webpack配置文件也暫時沒學(xué)過呢获枝,回到官網(wǎng)就近找找解決方法,很快就能看到官方項目對loadsh的引用方式:
沒有經(jīng)過其他配置就能直接編譯成功骇笔,說明了編譯過程中會自動在node_modules找對應(yīng)的庫文件省店,同樣地,我們也為項目添加一個angular模塊:
yarn add angular --save
再用npx命令編譯試試:
果然成功了笨触,warning先記下來不去深究懦傍,我們先就當(dāng)前的工程http-server一下,看看運行結(jié)果:
哎喲不錯哦芦劣,Webpack入門也沒傳說中那么難嘛粗俱。不過問題來了,現(xiàn)在文件少虚吟,輸入一句命令就可以編譯打包寸认,但如果文件在開發(fā)中數(shù)量劇增,輸入一長串命令編譯的方式不止像土撥鼠一般愚蠢串慰,同時也難以協(xié)助開發(fā)偏塞,那怎樣的方式才能讓打包變得便捷呢?
B. 添加配置文件
在Webpack4中,雖無需任何配置亦可編譯打包模庐,但是使用一個配置文件來代替大量手動輸入的命令仍然是一個高效優(yōu)雅的選擇烛愧,故為工程在根目錄添加新文件:webpack.config.js,編輯文件:
const path = require('path');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
之后有兩種webpack和npm兩種打包輸入方式可供選擇:
- Webpack腳本
現(xiàn)在按照配置文件執(zhí)行打包操作,只需在命令行輸入:
webpack --config webpack.config.js
稍等片刻即可完成打包操作怜姿,如果想要輸入更少更簡單慎冤,在保持配置文件名為"webpack.config.js"的情況下,可輸入:
webpack // 省略默認參數(shù)沧卢,執(zhí)行效果與上條命令相同
- NPM腳本
為解決使用npx腳本運行本地webpack的不方便蚁堤,我們可以在package.json添加一個npm腳本命令,可以像npx一樣通過模塊名引用本地安裝的包但狭,故在package.json中加入:
"scripts": {
"build": "webpack"
}
試運行:
npm run build
可在"不安裝webpack-cli的情況下",達到相同的效果披诗。
C. 打包其他模塊
我們用pockmon模塊作為操作樣例吧,先將index.html中的導(dǎo)航欄和ngView引用恢復(fù)立磁,并去除測試元素:
<div>
<h2>快速導(dǎo)航:</h2>
<a href="/#!/pokemons">口袋妖怪</a>
<a href="/#!/skills">技能</a>
<a href="/#!/hagberrys">樹果</a>
<a href="/#!/props">道具</a>
<a href="/#!/games">游戲</a>
</div>
<div ng-view></div>
由于打包中用到的import和export需要在js文件的最頂成呈队,所以先去掉pokemon.js的use strict外圍,然后在angular.module前面加上export default唱歧,在末端加上.name宪摧,即將該模塊以其名稱('pokemon-app.pokemon')暴露出去,代碼如下:
import angular from 'angular';
import ngRoute from 'angular-route';
export default angular.module('pokemon-app.pokemon', ['ngRoute'])
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/pokemons', {
templateUrl: 'src/pokemon/pm-list.html',
controller: 'PMListController'
})
.when ('/pokemon/:no', {
templateUrl: 'src/pokemon/pm-detail.html',
controller: 'PMDetailController'
})
}])
.controller('PMListController', PMListController)
.controller('PMDetailController', PMDetailController)
.name; // 暴露模塊名稱
var pokemons = [
{ no:'001', name:'妙蛙種子', count: 1, weight: 6.9, property: '草/毒', type: '種子寶可夢',
character: { common: '茂盛', conceal: '葉綠素'},
img: 'https://s1.52poke.wiki/wiki/thumb/2/21/001Bulbasaur.png/300px-001Bulbasaur.png'
},
{ no:'002', name:'妙蛙草', count: 1, weight: 13.0, property: '草/毒', type: '種子寶可夢',
character: { common: '茂盛', conceal: '葉綠素'},
img: 'https://s1.52poke.wiki/wiki/thumb/7/73/002Ivysaur.png/300px-002Ivysaur.png'
},
{ no:'003', name:'妙蛙花', count: 1, weight: 100, property: '草/毒', type: '種子寶可夢',
character: { common: '茂盛', conceal: '葉綠素'},
img: 'https://s1.52poke.wiki/wiki/thumb/a/ae/003Venusaur.png/300px-003Venusaur.png'
},
{ no:'004', name:'小火龍', count: 1, weight: 8.5, property: '火', type: '蜥蜴寶可夢',
character: { common: '猛火', conceal: '太陽之力'},
img: 'https://s1.52poke.wiki/wiki/thumb/7/73/004Charmander.png/300px-004Charmander.png'
},
{ no:'025', name:'皮卡丘', count: 1, weight: 6, property: '電', type: '鼠寶可夢',
character: { common: '靜電', conceal: '避雷針'},
img: 'http://s1.52poke.wiki/wiki/thumb/0/0d/025Pikachu.png/260px-025Pikachu.png',
forms: [
{ name: '偶像皮卡丘', src: 'http://s1.52poke.wiki/wiki/thumb/e/e8/025Pikachu-Pop_Star.png/260px-025Pikachu-Pop_Star.png'},
{ name: '博士皮卡丘', src: 'http://s1.52poke.wiki/wiki/thumb/2/2f/025Pikachu-PhD.png/260px-025Pikachu-PhD.png'},
{ name: '面罩摔角手皮卡丘', src: 'http://s1.52poke.wiki/wiki/thumb/e/e7/025Pikachu-Libre.png/260px-025Pikachu-Libre.png'},
{ name: '貴婦皮卡丘', src: 'http://s1.52poke.wiki/wiki/thumb/f/f0/025Pikachu-Belle.png/260px-025Pikachu-Belle.png'},
{ name: '重搖滾皮卡丘', src: 'http://s1.52poke.wiki/wiki/thumb/4/4f/025Pikachu-Rock_Star.png/260px-025Pikachu-Rock_Star.png'},
] }
];
PMListController.$inject = ['$scope'];
function PMListController ($scope) {
$scope.pokemons = pokemons;
$scope.remove = function (index) {
$scope.pokemons.splice(index, 1);
}
}
PMDetailController.$inject = ['$scope', '$routeParams'];
function PMDetailController ($scope, $routeParams) {
console.log('$routeParams:', $routeParams);
angular.forEach(pokemons, function (element) {
if (element.no === $routeParams.no) {
$scope.pokemon = element;
console.log('the match pokemon:', $scope.pokemon);
}
});
}
頂部兩行import引用其實不重要颅崩,但是再次import可以讓依賴關(guān)系更加清晰几于,而且如果其他應(yīng)用中需要用到它,我們只需要簡單的復(fù)制粘貼就搞定了沿后,不用擔(dān)心依賴出錯沿彭。參考自用ES6和webpack開發(fā)angular1.x項目(譯)
再安裝路由插件angular-route到node_modules中:
yarn add angular-route --save
最后在app.js中加入對angular-route & pokemon模塊的引用,完成依賴加載并將重新打開路由配置中:
import route from 'angular-route';
import pokemon from './pokemon/pokemon';
angular.module('pokemon-app', [
route,
pokemon
// 'ngRoute',
// 'pokemon-app.pokemon', // 添加依賴
// 'pokemon-app.skill',
// 'pokemon-app.hagberry',
// 'pokemon-app.prop',
// 'pokemon-app.game'
])
.config (['$routeProvider', function ($routeProvider) {
$routeProvider
.otherwise({
redirectTo: '/pokemons' // 初始化直接跳轉(zhuǎn)到pokemon模塊
});
}])
.controller('AppController', AppController);
至此pokemon模塊的引入已經(jīng)完成尖滚,其他模塊的修改和引入也是依照這種方式直接加入app.js喉刘,無需在index.html中應(yīng)用。app.js通過聲明模塊所需的依賴熔掺,webpack 能夠利用這些信息去構(gòu)建依賴圖饱搏,然后使用圖生成一個優(yōu)化過的,會以正確順序執(zhí)行的 bundle置逻。
四推沸、源碼
口袋妖怪SPA系統(tǒng)源碼地址:https://github.com/Nodreame/pokemon-website
本章基本功能提交:build(webpack): finish base webpack of project
本章文檔補充說明提交:doc(Readme): add cmd for build && add dev note
五、總結(jié)
本章借助Webpack編譯打包了工程券坞,現(xiàn)在似乎也完成了所有模塊的打包鬓催,但是現(xiàn)在的打包結(jié)果好像并不是那么實用,打包成一個壓縮文件應(yīng)該對調(diào)試會造成影響吧恨锚,還有每次修改代碼難道都要手動輸入npm run build
嗎這也太麻煩了宇驾,還有現(xiàn)在的dist文件夾部署到服務(wù)器應(yīng)該是毫無作用的吧。猴伶。课舍。還存在這么多問題塌西,看來我們項目的工程化之路還要再走一程呢。關(guān)注項目推進動態(tài)筝尾,請看下章~
To be continue...