從零開始搭建口袋妖怪管理系統(tǒng)(4)-借助webpack4.6工程化項目(上)

"手動是不可能手動的了勾笆,這輩子都不可能手動的了。"

一接谨、目標

上一章我們借助ngRoute,完成了口袋妖怪SPA系統(tǒng)的多模塊導(dǎo)航開發(fā)塘匣,但是現(xiàn)在引用的東西越來越多脓豪,項目文件目錄開始變得混亂不堪:

項目文件列表.png

現(xiàn)在先對當(dāng)前項目文件列表進行整理,將五大模塊移入src文件夾中:


項目文件列表2.png

然后修改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)居然還沒動靜了


npx命令運行結(jié)果.png

于是我中斷安裝然后自己輸入安裝webpack-cli的命令:

yarn add webpack-cli --save-dev

果然安裝成功了侨把,馬上再次運行npx的編譯指令~咦又報錯了,直接拉到最后看Error信息:


npx編譯失敗.png

Error信息指出缺少angular模塊(編譯階段就可以檢測是否缺少依賴犀变,優(yōu)越性體現(xiàn)達成√),由于我們沒有配置libs的路徑所以webpack很明顯讀不到秋柄,寫webpack配置文件也暫時沒學(xué)過呢获枝,回到官網(wǎng)就近找找解決方法,很快就能看到官方項目對loadsh的引用方式:


官網(wǎng)引入lodash.png

沒有經(jīng)過其他配置就能直接編譯成功骇笔,說明了編譯過程中會自動在node_modules找對應(yīng)的庫文件省店,同樣地,我們也為項目添加一個angular模塊:

yarn add angular --save

再用npx命令編譯試試:


npx編譯成功.png

果然成功了笨触,warning先記下來不去深究懦傍,我們先就當(dāng)前的工程http-server一下,看看運行結(jié)果:


簡化工程打包后運行成功.png

哎喲不錯哦芦劣,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...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捡需,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子筹淫,更是在濱河造成了極大的恐慌站辉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件损姜,死亡現(xiàn)場離奇詭異赡译,居然都是意外死亡贬媒,警方通過查閱死者的電腦和手機歧蕉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門委煤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棒卷,你說我怎么就攤上這事古沥。” “怎么了娇跟?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長太颤。 經(jīng)常有香客問我苞俘,道長,這世上最難降的妖魔是什么龄章? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任吃谣,我火速辦了婚禮,結(jié)果婚禮上做裙,老公的妹妹穿的比我還像新娘岗憋。我一直安慰自己,他們只是感情好锚贱,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布仔戈。 她就那樣靜靜地躺著,像睡著了一般拧廊。 火紅的嫁衣襯著肌膚如雪监徘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天吧碾,我揣著相機與錄音凰盔,去河邊找鬼。 笑死倦春,一個胖子當(dāng)著我的面吹牛户敬,可吹牛的內(nèi)容都是我干的落剪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼尿庐,長吁一口氣:“原來是場噩夢啊……” “哼忠怖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起屁倔,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤脑又,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后锐借,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體问麸,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年钞翔,在試婚紗的時候發(fā)現(xiàn)自己被綠了严卖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡布轿,死狀恐怖哮笆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汰扭,我是刑警寧澤稠肘,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站萝毛,受9級特大地震影響项阴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笆包,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一环揽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庵佣,春花似錦歉胶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至验毡,卻和暖如春衡创,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晶通。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工璃氢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狮辽。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓一也,卻偏偏與公主長得像巢寡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子椰苟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容