在搞定了CSS工作流之后,前端三剑客还有一个重要的JavaScript也需要工作流。
- 安装Webpack
- 为项目配置Webpack
- export与import
- Webpack自动监听文件改变
- 异常处理
安装Webpack
很久以来,JS都是像脚本一样随写随用,开发比较随意,因为没有很工程化的东西。
现在有了Webpack,可以方便的打包很多东西,也包括之前说的CSS文件。但最重要的,还是Webpack作为JS工程化的重要工具。
继续沿用这个项目,在assets
目录下创建一个scripts
目录用来存放开发中的JS文件,创建一个app/assets/scripts/App.js
文件如下:
function Person(fullName, favColor) {
this.name = fullName;
this.favoriteColor = favColor;
this.greet = function () {
console.log(this.name + " likes " + this.favoriteColor);
}
}
var john = new Person("John Doe", "blue");
john.greet();
很显然,这个文件定义了一个类,然后使用了类。不过一般定义和使用不放在同一个文件里,为了将类单独抽离,就像CSS一样,就需要工程化JS的编写方式。
在scripts
目录下创建modules
目录,然后新建一个Person.js
文件,把类的文件复制进来,然后在App.js
中导入:
var Person = require("./modules/Person");
var john = new Person("John Doe", "blue");
john.greet();
看起来很像之前写Gulp任务文件,但实际上,这么做是无法运行这个文件的,使用Node.js也不行。这就需要前端工程化组件Webpack出场了。
和之前的工具类似,我们安装Webpack,让其监视App.js,然后到里边把依赖的内容拼合成一个文件,重整之后生成最终的JS文件。
先来安装,这里需要全局安装,因为Webpack现在的地位相当于像IDE一样的工具,所有项目都会用到。
npm install webpack -g
npm自动安装的版本是4.31版本。
为项目配置Webpack
为了在项目中使用Webpack,需要在项目根目录下创建一个文件,这个文件的名称必须是webpack.config.js
,告诉Webpack当前目录是一个Webpack项目。
先来在其中写一个JS对象:
var path = require("path");
module.exports = {
entry: "./app/assets/scripts/App.js",
output: {
path: path.resolve(__dirname, "./app/temp/scripts"),
filename: "App.js"
}
};
Webpack需要定义一个起点,类似于一个JS的根文件,或者说像很多编译型程序的main函数,从这个起点出发不断的把所有的依赖解析进来。
之后配置一个输出的路径和文件名,这样就会把各种依赖都打包成为一个文件。Webpack的输出路径必须是绝对路径,这里使用了Node.js的内置库,解析当前目录然后拼合成绝对路径。
至于module.exports
之后再来学习。
然后在项目根目录下直接运行webpack
,webpack会自动检测这个配置文件然后打包JS代码。
注意,Webpack 4.31在使用命令行之前,需要运行npm i webpack webpack-cli --save-dev
在当前目录下安装命令行工具才行。
运行之后有如下输出:
Hash: 6951834551b2fbe8c111
Version: webpack 4.31.0
Time: 369ms
Built at: 2019-05-20 14:16:45
Asset Size Chunks Chunk Names
App.js 984 bytes 0 [emitted] main
Entrypoint main = App.js
[0] ./app/assets/scripts/App.js 102 bytes {0} [built]
[1] ./app/assets/scripts/modules/Person.js 208 bytes {0} [built]
还有一些Warning暂时不用看,因为还没有具体深入配置。通过输出可以看到,构建的文件里包括Person.js。此时看看目标目录,确实生成了一个D:\Coding\frontendworkflow\app\temp\scripts\App.js
,打开看看代码确实看不懂,这是因为经过重新整理。
看不懂没事,运行吧,咦,报错。。。。第一次使用Webpack就坑了么?
export与import
回到这条语句:var Person = require("./modules/Person");
,实际上将Person作为一个包导入,然而要从这个包导入什么内容,没有明确的定义。
Webpack通过export和import来导入内容。导入导出包,由于JS是面向对象的语言,实际导入的都是一个对象。
Node.js执行的时候,如果导入包,会默认到包里寻找一个叫做exports
的对象,require
这个函数就会返回exports
这个对象,通过这个对象来使用包中的代码。
可以先在Person.js里写点代码测试一下:
function Person(fullName, favColor) {
this.name = fullName;
this.favoriteColor = favColor;
this.greet = function () {
console.log(this.name + " likes " + this.favoriteColor);
}
}
exports.exampleProperty = "Super";
exports.exampleFunction = function () {
console.log("THis is demo");
};
然后在App.js里注释掉生成Person对象的代码,加一行:
var Person = require("./modules/Person");
// var john = new Person("John Doe", "blue");
// john.greet();
console.log(Person);
此时再通过Webpack打包,然后加载页面,可以看到浏览器控制台里:
{exampleProperty: "Sunper", exampleFunction: ƒ}
exampleFunction: ƒ ()
exampleProperty: "Sunper"
__proto__: Object
说明为exports
对象设置的属性都被导入了,此时可以在App.js
里添加两行试试:
var Person = require("./modules/Person");
// var john = new Person("John Doe", "blue");
// john.greet();
console.log(Person);
Person.exampleFunction();
console.log(Person.exampleProperty);
再打包后发现正常执行了。
所以,我们在抽出一个JS文件的时候,一定要注意编写exports对象。以让Webpack可以正常打包。
依据这个思路,我们的Person.js
文件只有一个Person类需要让其他文件使用:
function Person(fullName, favColor) {
this.name = fullName;
this.favoriteColor = favColor;
this.greet = function () {
console.log(this.name + " likes " + this.favoriteColor);
}
}
module.exports = Person;
这样导出后,导出的对象就是这个构造函数Person。
然后把App.js
还原成原来的状态,再打包,就一切正常了。
凡是通过NPM安装的包,都为Webpack导入而编写好了对应的代码。比如还可以导入已经安装的jQuery
。
var Person = require("./modules/Person");
var $ = require('jquery');
var john = new Person("John Doe", "blue");
john.greet();
$("h1").remove();
打包并且刷新页面后,所有的H1
元素都不见了。
自动监听文件改变
现在我们每次修改JS文件,都去执行Webpack,还不够自动化,应该像Gulp那样,自动检测所有的JS文件变化并自动打包。之前安装了全局的Webpack,这里还必须安装当前项目的Webpack:
npm install webpack --save-def
我们给Webpack编写一个Gulp任务文件:gulp/tasks/scripts.js
:
var gulp = require('gulp'),
webpack = require('webpack');
gulp.task('scripts', function (done) {
webpack(require('../../webpack.config'),function () {
console.log("Mission Completed!");
});
done();
});
webpack自己就是一个函数,第一个参数是当前JS文件路径去找到配置文件的相对路径,第二个参数是执行成功的回调函数。
之后我们需要在watch里添加监视开发中的所有js文件的功能,然后调用这个任务来打包JS文件:
var gulp = require('gulp'),
watch = require('gulp-watch'),
browserSync = require('browser-sync').create();
gulp.task('watch', function() {
......
watch('./app/assets/scripts/**/*.js',function () {
gulp.start('scriptRefresh');
})
});
gulp.task('scriptRefresh',['scripts'], function () {
browserSync.reload();
});
这里使用任务依赖,更新页面的任务会在重新打包完成后执行。每次监视JS文件有变化的时候,就自动让Webpack进行打包,然后重载页面。实验了发现运行良好,有任何修改都很方便的自动刷新。
异常处理
由于Webpack没有走styles任务的异常处理,所以必须自行编写一个。
异常处理就在scripts
任务的回调函数中,修改如下:
var gulp = require('gulp'),
webpack = require('webpack');
gulp.task('scripts', function (done) {
webpack(require('../../webpack.config'), function (err, stats) {
if (err) {
console.log(err.toString());
}
console.log(stats.toString());
console.log("Mission completed!");
});
done();
});
第一个参数就是错误对象,第二个是执行状态,都可以打印出来看一下。
有了异常处理之后有错误也不会停止watch任务了。