1

一. 分析

上一章我们使用webpack对项目进行了工程化改造,实现了从简单系统到多模块系统的打包升级,你可能以为现在就完成了webpack的配置,但是实际上现在项目还是处于webpack入门级配置的程度。调试一下代码就会发现,除了比原先项目变得稍微整洁以及完成了app.js模块引入之外,仍然有很多不足:

1. 程序被打包到一个文件中,导致调试日志全都指向了bundle.js文件,无法追踪错误和警告;
2. 打包之后的dist目录应包含项目所有功能,现在缺少index.html;
3. 每次修改完代码想要运行的时候都需要输入*npm run build*重新打包项目;

应该明确一点,打包项目是为了方便我们的开发调试而不是增加开发复杂度,所以为了使让我们的开发变得更加舒服,这些问题需要马上解决。

二、问题处理

2.1 日志追踪

为了更容易地追踪错误和警告,应将编译后的代码bundle.js映射回原始代码。打包工具的开发者肯定早就考虑到了打包导致的日志追踪问题,所以我们在webpack的文档中很容易就找到这个:使用source map,也就是使用source-map功能实现编译后代码到源代码的映射。于是在项目的webpack.config.js文件中加入:

devtool: 'inline-source-map',

重新编译结果如下:
source-map 编译结果

我们发现bundle.js文件变大了,下面的编译提示也告诉我们文件过大将影响性能,所以当我们将代码打包发布到生产环境的时候应该关闭掉source-map或者使用其他配置(划重点)。source-map除了我们用到的配置之外还有其他不同选项,如果想要更深入学习就阅读这里:source-map 指南- Devtool

修改之后重新运行代码,日志打印结果如下:

加入source-map 后的日志

ok现在可以成功映射源代码输出日志了。

2.2 将index.html加入dist

dist中现在只有bundle.js文件,index.html仍在根目录,我们想要成功运行程序,则每次编译都应清除dist目录下文件,再把最新的index.html复制到dist中,与bundle.js配合运行。

2.2.1 index.html生成

首先考虑如何往dist加入html文件,参考文档设定 HtmlWebpackPlugin,阅读之后发现大概意思是:在webpack.config.js中加入插件htmlwebpackplugin,然后就可以自动生成一个全新的index.html。

那我们原来的html咋办呢,里面有meta、title、对js和css的引用,还有一些其他代码,难道只能抛弃他们吗?当然是不可能的,htmlwebpackplugin作为一个插件,是为让我们在编译打包过程中获得更好的体验,特别是自动为index.html添加生成后文件引用而不用手动修改这一功能。想要更好地使用它就先阅读一下htmlwebpackplugin的Readme,看完大概知道通过在webpack.config.js配置htmlwebpackplugin,使其在目标目录生成一个原来index.html相似的html,话有点多现在马上开始,先安装html-webpack-plugin:

yarn add html-webpack-plugin --save-dev

然后模仿设定 HtmlWebpackPlugin,在webpack.config.js中完成基础index.html生成配置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/app.js',
  devtool: 'inline-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      title: '口袋妖怪'
    })
  ],
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

上面配置中我们做了两件事:

1. 加入html-webpack-plugin插件的引用;
2. 在plugins数组中创建了HtmlWebpackPlugin的对象(最终webpack将根据该插件对象的配置形成我们的新index.html);

npm run build一下,现在我们看看生成的index.html和原本的index.html有什么不一样:
插件生成(左)和原本index(右)的对比

看来还有点差距,我们还需要对新index.html的<body>插入 ng-controller="AppController",然后再在<body>中加入导航&ngView的html。官方没说怎么配置,所以我们参考了Github-html-webpack-plugin的Readme,在Options中找到一个简单的配置参数template,并了解到我们只需要创建并引用一个html作为模板,即可以轻松生成想要的index.html,故在根目录创建index.tpl.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><%= htmlWebpackPlugin.options.title %></title>
  </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>
  </body>
</html>

再修改webpack.config.js中的插件:

plugins: [
    new HtmlWebpackPlugin({
        title: '口袋妖怪',
        template: 'index.tpl.html'
    })
]

执行编译npm run build,看到dist/index.html如下:
基于index.tpl.html生成新index.html

使用命令行进入dist目录,开启http-server,然后打开对应url:
新生成index.html后运行失败.png

失败原因是"无法加载pm-list.html",这里暴露了我们打包过程的一个问题,即当前项目只是打包了所有js,但是对于每个模块的html模板文件(list & detail)却仍然是用相对路径引用的。正式上线的话只会将dist文件夹放到服务器上,而list & detail是读不到的,所以我们需要使用webpack将这两个html也一起打包进bundle.js中。

这时很明显的,我们不能再用相对路径引用html模板了,应该把list & detail的html当作字符串加载进bundle.js,于是修改pokemon.js如下:

import angular from 'angular';
import ngRoute from 'angular-route';
import pmlist from './pm-list.html';
import pmdetail from './pm-detail.html';

export default angular.module('pokemon-app.pokemon', [ngRoute])
    .config(['$routeProvider', function ($routeProvider) {
      $routeProvider
        .when('/pokemons', {
          template: pmlist,
          controller: 'PMListController'
        })
        .when ('/pokemon/:no', {
          template: pmdetail,
          controller: 'PMDetailController'
        })
    }])
    .controller('PMListController', PMListController)
    .controller('PMDetailController', PMDetailController)
    .name;

上面我们做了两件事:

1. 用import引入两个html模板;
2. 将原来的templateUrl改为template并赋予其引用的html模板文件;

经过修改,模块html的内容文本应该也会被打包到bundle.js中。完成编辑之后运行npm run build,发现:
解析失败

如上图高亮处所示,list文件和detail文件的错误都是"解析失败,需要对应类型的loader"。查阅Loader文档可知,webpack使用loader来预处理各种文件,那我们现在需要解析html内容,自然是使用用来加载文件原始内容的raw-loader,参考文档中的使用方法,先安装:

yarn add raw-loader --save-dev

再在webpack.config.js的module.exports={}的大括号中加入:

  module: {
    rules: [{
      test: /\.html$/,
      loader: 'raw-loader'
    }]
  },

完成之后运行npm run build,发现编译打包成功,再尝试运行http-server dist,发现管理系统的口袋妖怪部分已经能够正常使用了!快速将其他模块按照pokemon的修改方式进行修改,完成编译之后,该项目的dist文件应该是能够直接部署在服务器上运行的了,也就是说我们已经成功实现了项目打包工作的基础功能了~

2.2.2 清除dist目录文件

完成了上一小结的实践之后项目的打包上线应该是没什么问题了,不过如果项目在上线前出现了重大修改或者回撤,dist目录下可能残留有一些冗余文件。官方文档说每次编译打包前先清除dist目录文件是比较推荐的做法。那么现在就来考虑下如何每次构建都清除dist中的文件了,参考文档清理 /dist 文件夹,先安装clean-webpack-plugin插件:

yarn add clean-webpack-plugin --save-dev

再在webpack.config.js插入引用并创建清空实例:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');    //插入引用

module.exports = {
  entry: './src/app.js',
  devtool: 'inline-source-map',
  plugins: [
    new CleanWebpackPlugin(['dist']),        //创建清空实例
    new HtmlWebpackPlugin({
      title: '口袋妖怪',
      template: 'index.tpl.html'
    })
  ],
  module: {
    rules: [{
      test: /\.html$/,
      loader: 'raw-loader'
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!

至此,我们每次使用npm run build之后都能生成无遗留及冗余文件的、完成代码压缩的index.html 及 bundle.js,也就是可以直接将dist部署到服务器运行我们的网站了(记得关闭或修改source-map)。

2.3 自动编译打包

用过Vue2或者React官方CLI搭建过项目的同学都知道,这些项目都具有实时重载页面功能,即修改文件后自动重新加载网页来展示修改效果。我们引入webpack构建项目的目的自然也是能够这样方便地进行开发。为了解决每次编写完成代码后都要重新输入编译命令的问题,我们查阅webpack文档找到了这个:选择一个开发工具,对比一下官方提供的三种自动编译方式:

按照KISS原则,我们肯定选择一个符合我们需求的最简单的东西,所以接下来我们将使用webpack-dev-server来达到我们的目的,首先安装webpack-dev-server插件:

yarn add webpack-dev-server --save-dev

安装完成之后,在webpack.config.js中加入服务器开启位置配置:

module.exports = {
  entry: './src/app.js',
  devtool: 'inline-source-map',
  devServer: {                        // 服务器开启位置配置
      contentBase: './dist'
  },
  ...,
  ...
};

完成配置后,在webpack-dev-server运行过程中,该文件夹将作为可访问文件以供访问。接下来我们在package.json文件中加入一行'start'脚本以运行webpack-dev-server:

"scripts": {
    "start": "webpack-dev-server --open",            // 新增
    "build": "webpack"
}

现在只需要简单运行npm start,就会看到浏览器自动加载页面。如果我们修改代码并保存,页面将会自动重载。

至此我们已经完成了项目的实时重载功能。在开发之前运行npm start,等待初始化打包完成之后,每次我们完成代码修改并保存,页面都会自动重载以展示新页面。

三、源码

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

本章基本功能提交:build(webpack): add source-map & auto bulid & finish dist & merge Readme

四、总结

至此,我们系统已经基本完成了项目的工程化,项目当前支持自动实时重载、日志映射,以及支持将编译打包后的dist文件夹部署到服务器直接运行。既然已经完成开发环境的构建那么接下来自然要继续开发项目啦!继续设计接下来的功能和样式,让它愈加丰富起来吧!请看下章~

系列文章

从零开始搭建口袋妖怪管理系统(1)-从Angular1.x开始

从零开始搭建口袋妖怪管理系统(2)-借助ngRoute实现详情页面跳转

从零开始搭建口袋妖怪管理系统(3)-实现一个简单的SPA管理系统

从零开始搭建口袋妖怪管理系统(4)-借助webpack4.6工程化项目(上)

To be continue...


Nodreame
155 声望32 粉丝

伪全栈|前端|前软粉