一. 分析
上一章我们使用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',
重新编译结果如下:
我们发现bundle.js文件变大了,下面的编译提示也告诉我们文件过大将影响性能,所以当我们将代码打包发布到生产环境的时候应该关闭掉source-map或者使用其他配置(划重点)。source-map除了我们用到的配置之外还有其他不同选项,如果想要更深入学习就阅读这里:source-map 指南- Devtool。
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.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>
plugins: [ new HtmlWebpackPlugin({ title: '口袋妖怪',template: 'index.tpl.html' }) ]
执行编译npm run build,看到dist/index.html如下:
使用命令行进入dist目录,开启http-server,然后打开对应url:
失败原因是"无法加载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',plugins: [ new CleanWebpackPlugin(['dist']),//创建清空实例 new HtmlWebpackPlugin({ title: '口袋妖怪',template: 'index.tpl.html' }) ],module: { rules: [{ test: /\.html$/,'dist') } };
现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!
至此,我们每次使用npm run build之后都能生成无遗留及冗余文件的、完成代码压缩的index.html 及 bundle.js,也就是可以直接将dist部署到服务器运行我们的网站了(记得关闭或修改source-map)。
2.3 自动编译打包
用过Vue2或者React官方CLI搭建过项目的同学都知道,这些项目都具有实时重载页面功能,即修改文件后自动重新加载网页来展示修改效果。我们引入webpack构建项目的目的自然也是能够这样方便地进行开发。为了解决每次编写完成代码后都要重新输入编译命令的问题,我们查阅webpack文档找到了这个:选择一个开发工具,对比一下官方提供的三种自动编译方式:
- 使用观察者模式需要手动刷新页面直接排除;
- 使用 webpack-dev-server提供了一个简单的web服务器,并且能够实时重新加载,刚刚好能够满足我们的需求;
- 使用 webpack-dev-middleware似乎是webapck-dev-server的内部实现并且能支持更多自定义配置;
按照KISS原则,我们肯定选择一个符合我们需求的最简单的东西,所以接下来我们将使用webpack-dev-server来达到我们的目的,首先安装webpack-dev-server插件:
yarn add webpack-dev-server --save-dev
安装完成之后,在webpack.config.js中加入服务器开启位置配置:
module.exports = { entry: './src/app.js',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...