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

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

1、目標

上一章咱們藉助ngRoute,完成了口袋妖怪SPA系統的多模塊導航開發,可是如今引用的東西愈來愈多,項目文件目錄開始變得混亂不堪:
項目文件列表.pngcss

如今先對當前項目文件列表進行整理,將五大模塊移入src文件夾中:
項目文件列表2.pnghtml

而後修改index.html中的js引用路徑:node

<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,使其指向文件位置:webpack

.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'
      })
  }])

簡單修改一下以後項目瞬間整潔了許多,可是若是之後工程變得更大,將會有愈來愈多的第三方庫、環境工具包、樣式庫及圖片庫加入項目,這樣項目整理維護難度確定會隨着項目的擴大而不斷加大,因此咱們須要一個編譯打包工具來協助咱們對項目文件進行管理打包,以方便開發的推動。git

2、分析

參考網上文章對grunt、gulp、webpack三種打包工具的分析比對,grunt的配置較爲複雜且效率不高因此先捨棄,gulp的配置簡單和流式工做比較吸引,可是顯然webpack的模塊化特性與AngularJS的機制具備更好的相性,因而本項目選中Webpack做爲項目的打包工具。github

3、開發

3.1 安裝

參考webpack指南,安裝webpack最新版本(當前版本4.6.0):web

yarn add webpack --save-dev

3.2 起步

A. 簡化工程,完成初始打包

如今準備開始使用Webpack4.6.0打包工程,可是因爲是初學Webpack,因此先將工程簡化爲最簡版本,即先不加入多模塊,只留下index.html和src/app.js,index.html代碼以下:npm

<!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>口袋妖怪管理系統</h1>
    <!-- <div>
      <h2>快速導航:</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中咱們作了兩個處理:json

1. 屏蔽全部的js引用,加入一條對bundle.js(即編譯以後的js)的引用;
2. 屏蔽掉導航div和ngView的div,加入```<p>{{test}}</p>```;

通過處理以後,index.html 再也不須要引用angular.js文件,只須要直接引用最終打包好bundle.js,angular加載的任務交給了app.js:gulp

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'     // 初始化直接跳轉到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!";*;

通過處理,app.js就能直接引入依賴庫,這樣的引用方式更加簡潔明瞭,並且編譯打包過程當中將可以發現缺乏依賴的問題,由此避免了因依賴不存在或者順序錯誤問題致使的腳本執行錯誤。

如今依舊看看跟着官網-起步-建立一個 bundle 文件走,完成了上面對index.html & src/app.js的修改以後,咱們開始嘗試編譯打包,個人Node.js版本是8.9.3,因此直接執行官網的命令:

npx webpack src/app.js --output dist/bundle.js

運行完提示請求安裝webpack-cli,看到cli後綴就知道大概是封裝了命令行輸入之類的東西吧,直接跟着安裝試試就行,輸入yes而後發現竟然還沒動靜了
npx命令運行結果.png

因而我中斷安裝而後本身輸入安裝webpack-cli的命令:

yarn add webpack-cli --save-dev

果真安裝成功了,立刻再次運行npx的編譯指令~咦又報錯了,直接拉到最後看Error信息:
npx編譯失敗.png

Error信息指出缺乏angular模塊(編譯階段就能夠檢測是否缺乏依賴,優越性體現達成√),因爲咱們沒有配置libs的路徑因此webpack很明顯讀不到,寫webpack配置文件也暫時沒學過呢,回到官網就近找找解決方法,很快就能看到官方項目對loadsh的引用方式:
官網引入lodash.png

沒有通過其餘配置就能直接編譯成功,說明了編譯過程當中會自動在node_modules找對應的庫文件,一樣地,咱們也爲項目添加一個angular模塊:

yarn add angular --save

再用npx命令編譯試試:
npx編譯成功.png

果真成功了,warning先記下來不去深究,咱們先就當前的工程http-server一下,看看運行結果:
簡化工程打包後運行成功.png

哎喲不錯哦,Webpack入門也沒傳說中那麼難嘛。不過問題來了,如今文件少,輸入一句命令就能夠編譯打包,但若是文件在開發中數量劇增,輸入一長串命令編譯的方式不止像土撥鼠通常愚蠢,同時也難以協助開發,那怎樣的方式才能讓打包變得便捷呢?

B. 添加配置文件

在Webpack4中,雖無需任何配置亦可編譯打包,可是使用一個配置文件來代替大量手動輸入的命令仍然是一個高效優雅的選擇,故爲工程在根目錄添加新文件:webpack.config.js,編輯文件:

const path = require('path');

module.exports = {
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

如今按照配置文件執行打包操做,只需在命令行輸入:

webpack  --config webpack.config.js

稍等片刻便可完成打包操做,若是想要輸入更少更簡單,在保持配置文件名爲"webpack.config.js"的狀況下,可輸入:

webpack    // 省略默認參數,執行效果與上條命令相同

咱們能夠在package.json添加一個npm腳本命令,能夠像npx同樣經過模塊名引用本地安裝的包,故在package.json中加入:

"scripts": {
    "build": "webpack"
  }

試運行:

npm run build

使用npm腳本命令,可在"不安裝webpack-cli的狀況下",達到相同的效果。

C. 打包其餘模塊

咱們用pockmon模塊做爲操做樣例吧,先將index.html中的導航欄和ngView引用恢復,並去除測試元素:

<div>
      <h2>快速導航:</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可讓依賴關係更加清晰,並且若是其餘應用中須要用到它,咱們只須要簡單的複製粘貼就搞定了,不用擔憂依賴出錯。參考自用ES6和webpack開發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'     // 初始化直接跳轉到pokemon模塊
      });
  }])
  .controller('AppController', AppController);

至此pokemon模塊的引入已經完成,其餘模塊的修改和引入也是依照這種方式直接加入app.js,無需在index.html中應用。app.js經過聲明模塊所需的依賴,webpack 可以利用這些信息去構建依賴圖,而後使用圖生成一個優化過的,會以正確順序執行的 bundle。

4、源碼

口袋妖怪SPA系統源碼地址:https://github.com/Nodreame/p...

本章基本功能提交:build(webpack): finish base webpack of project

本章文檔補充說明提交:doc(Readme): add cmd for build && add dev note

5、總結

本章藉助Webpack編譯打包了工程,如今彷佛也完成了全部模塊的打包,可是如今的打包結果好像並非那麼實用,打包成一個壓縮文件應該對調試會形成影響吧,還有每次修改代碼難道都要手動輸入npm run build嗎這也太麻煩了,還有如今的dist文件夾部署到服務器應該是毫無做用的吧。。。還存在這麼多問題,看來咱們項目的工程化之路還要再走一程呢。關注項目推動動態,請看下章~

系列文章

從零開始搭建口袋妖怪管理系統(1)-從Angular1.x開始

從零開始搭建口袋妖怪管理系統(2)-藉助ngRoute實現詳情頁面跳轉

從零開始搭建口袋妖怪管理系統(3)-實現一個簡單的SPA管理系統

[從零開始搭建口袋妖怪管理系統(5)-藉助webpack4.6工程化項目(下)]
(https://segmentfault.com/a/11...

To be continue...

相關文章
相關標籤/搜索