了解Browserify

时间:2021-08-19 21:37:26

Browserify是一个Javascript的库,可以用来把多个Module打包到一个文件中,并且能很好地应对Modules之间的依赖关系。而Module是封装了属性和功能的单元,是一个Javascript对象,Modules之间可以相互依赖。某种程度上来说,Browserify模仿了Node.js加载Module的方式。一个js文件包含一个Module。所以,Browserify通过读取文件来加载该文件内的Module。

【module的写法】

'use strict';
exports.save = function(tasks){};
exports.load = function(){};
exports.clear = function(){};

还可以这么写:

'use strict';
module.exports = {
save: function(tasks){},
load: function(){},
clear: function(){}
};

【module的缓存】

1、单例模式缓存

module a

exports.value = "original";

module b

var a = require('./a');
a.value = "changed";
console.log(a.value);//changed

module c

var a = require('./a');
console.log(a.value);//original

module c中的a.value值之所以是original,是因为module c对module a有依赖,而且依赖的是缓存。所以,在默认情况下,module是有缓存的,也可以理解成单例模式。

2、实例模式缓存

还可以通过构造函数来创建一个module。

module a

module.exports = function(){
this.value = "original value";
};

module b

var A = require('./a');
var a = new A();
a.value = "changed";
console.log(a.value);//changed

module c

var A = require('./a');
var a = new A();
console.log(a.value);//original

【准备工作】

  • 安装Node.js
  • 安装Browserify:npm install -g browserify

【明确目标】

了解Browserify

  • app这个module包含了和视图交互的逻辑,是整个程序的entry point
  • app这个module依赖tasks这个module,tasks这个module用来管理task list
  • taskRender这个module用来渲染页面,taskData这个module用来保存加载有关task list的数据

【文件结构】

.....css/

..........tasks.css

.....js/

..........data/

...............taskData.js

..........renderers/

...............taskRenderer.js

..........tasks.js

..........app.js

.....index.html

【代码实现】

文件结构有了,module的写法也搞清楚了,接下来就实现一遍。

① taskData.js 是用来处理数据的一个module

'use strict';

var STORE_NAME = "tasks";

exports.save = function(tasks){
localStorage.setItem(STORE_NAME, JSON.stringify(tasks));
}; exports.load = function(){
var storedTasks = localStorage.getItem(STORE_NAME);
if(storedTasks){
return JSON.parse(storedTasks);
}
return [];
}; exports.clear = function(){
localStorage.removeItem(STORE_NAME);
};

② taskRenderer.js 是用来处理页面相关的一个module

'use strict';

var $ = require('jquery');
var taskTemplate = '<li class="task"><input class="complete" type="checkbox" /><input class="description" type="text" /></li>'; //返回一段带值的html
//task是传入的一个object对象
function _renderTask(task){
var $task = $(taskTemplate);
if(task.complete){
$task.find(".complete").attr("checked", "checked");
}
$task.find(".description").val(task.description);
return $task;
} exports.renderTasks = function(tasks){ //遍历任务获得带值html的数组
var elementArray = $.map(tasks, _renderTask); $("#task-list")
.empty()
.append(elementArray);
}; exports.renderNew = function(){
var $taskList = $("#task-list");
$taskList.prepend(_renderTask({}));
}

③ tasks.js 用到了以上2个module

'use strict';

var $ = require('jquery');
var taskData = require('./data/taskData');
var taskRenderer = require('./renderers/taskRenderer'); exports.add = function () {
taskRenderer.renderNew();
}; exports.remove = function (clickEvent) {
var taskElement = clickEvent.target;
$(taskElement).closest(".task").remove();
}; exports.clear = function(){
taskData.clear();
exports.render();
}; exports.save = function(){
var tasks=[];
$("#task-list .task").each(function(index, task){
var $task = $(task);
tasks.push({
complete: $task.find(".complete").prop('checked'),
description: $task.find(".description").val()
});
});
taskData.save(tasks);
}; exports.cancel = function(){
exports.render();
}; exports.render = function(){
taskRenderer.renderTasks(taskData.load());
};

④ app.js 只需要和tasks.js打交道就可以

'use strict';

var $ = require('jquery');
var tasks =require('./tasks'); function _addTask(){
tasks.add();
} function _deleteAllTasks(){
tasks.clear();
} function _saveChanges(){
tasks.save();
} function _cancelChanges(){
tasks.cancel();
} function _deleteTask(clientEvent){
tasks.remove(clientEvent);
} function _registerEventHandlers(){
$('#new-task-button').on("click", _addTask);
$('#delete-all-button').on("click", _deleteAllTasks);
$('#save-button').on("click",_saveChanges);
$('#cancel-button').on("click", _cancelChanges);
$('#task-list').on("click", ".delete-button", _deleteTask);
} _registerEventHandlers();
tasks.render();

⑤ 使用browserify把所有module捆绑到一个js文件中去:

browserify src\js\app.js -o src\js\app.bundle.js

⑥ index.html 只需要引用src\js\app.bundle.js就可以

<!DOCTYPE html>
<html>
<head>
<title>Task List</title>
<link rel="stylesheet" href="css/tasks.css">
</head>
<body>
<header>
<h1>TaskList</h1>
</header> <div class="toolbar">
<button id="new-task-button">New Task</button>
<button id="delete-all-button">Delete All</button>
</div> <div id="content">
<ul id="task-list"> </ul> <ul id="log-list"> </ul>
</div> <div class="toolbar">
<button id="save-button">Save</button>
<button id="cancel-button">Cancel</button>
</div> <script src="js/app.bundle.js"></script>
</body>
</html>

【如果有很多文件,调试时出错】

当有很多文件的时候,调试出错,使用Source Map可以方便找到出错的文件和出错的地方。

现在,有了app.bundle.js文件,以及有了app.js, tasks.js, taskRenderer.js, taskData.js文件们,我们可以在app.bundle.js和其它js文件中创建一个Souce Map.

browserify src\js\app.js -o src\js\app.bundle.js --debug

这样,会在app.bundle.js文件最后面追加上类似//# sourceMappingURL=data:application/json;,这样在调试的时候会很容易找到出错的文件和出错的位置。

【修改文件】

如果此时修改某个js文件呢?我们还需要使用browserify把所有的module依赖关系捆绑到一个文件中,执行如下的命令:

browserify src\js\app.js -o src\js\app.bundle.js

解决思路:Watchify为此而生,当发现有文件变化,自动运行Browserify。

全局安装Watchify:npm install -g watchify

在命令行窗口运行Watchify命令:watchify src\js\app.js -o src\js\app.bundle.js --debug -v

此时保持命令窗口打开着。

修改某个文件,并保存,发现命令窗口会自动运行:watchify src\js\app.js -o src\js\app.bundle.js --debug -v

【Grunt Browserify】

Grunt是Javascript Task Runner,也是运行在Node.js之上。

如何安装Grunt?

  1. npm install -g grunt-cli
  2. npm install grunt --save-dev

检测版本?

grunt --version

在哪个文件中配置?

一般在根目录下的Gruntfile.js

Grunt与Browserify的结合?

npm install grunt-browserify --save-dev

在根目录下的Gruntfile.js文件:

module.exports=function(grunt){

    //配置
grunt.initConfig({
browserify: {
app: {
src: 'templates/src/js/app.js',
dest: 'templates/src/js/app.bundle.js',
options: {
browserifyOptions:{
debug: true
}
}
}
}
}); //加载其它module/plugins
grunt.loadNpmTasks('grunt-browserify'); //定义task
grunt.registerTask('default',['browserify']);
}

【Grunt Watch】

使用了Grunt以及用Gruntfile.js进行配置之后,每次有文件变化,我们需要在命令行窗口输入gulp命令。能不能自动为我们运行gulp命令呢?

Grunt Watch出场。

如何安装Grunt Watch?

npm install grunt-contrib-watch --save-dev

修改Gruntfile.js文件

module.exports=function(grunt){

    //配置
grunt.initConfig({
browserify: {
app: {
src: 'templates/src/js/app.js',
dest: 'templates/src/js/app.bundle.js',
options: {
browserifyOptions:{
debug: true
}
}
}
},
watch: {
app: {
files: ['templates/src/js/*/*.js'],
tasks: ['browserify']
}
}
}); //加载其它module/plugins
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch'); //定义task
grunt.registerTask('default',['browserify']);
}

在命令行窗口输入:grunt watch

现在,修改templates/src/js中的任何js文件,会自动运行browserify命令。

【Grunt Connect】

Grunt Connect可以让我们搭建一个web server。

如何安装?

npm install grunt-contrib-connect --save-dev

修改Gruntfile.js文件

module.exports=function(grunt){

    //配置
grunt.initConfig({
browserify: {
app: {
src: 'templates/src/js/app.js',
dest: 'templates/src/js/app.bundle.js',
options: {
browserifyOptions:{
debug: true
}
}
}
},
watch: {
app: {
files: ['templates/src/js/*/*.js'],
tasks: ['browserify']
}
},
connect: {
app: {
options: {
port: 9001,
base: 'templates/src'
}
}
}
}); //加载其它module/plugins
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect'); //定义task
grunt.registerTask('default',['browserify']);
grunt.registerTask('serve',['browserify:app', 'connect:app', 'watch:app']);
}

在命令行窗口输入:grunt serve

在浏览器窗口输入:localhost:9001

【Connect Live Reload】

现在,可以在浏览器中输入localhost:9001浏览到网页内容,此时,如果某个文件有变化,我们需要重新刷新浏览器。web server可以有自动刷新的功能吗?

Connect Live Reload就是解决这个问题的。

如何安装?

npm install connect-livereload --save-dev

修改Gruntfile.js文件

module.exports=function(grunt){

    //配置
grunt.initConfig({
browserify: {
app: {
src: 'templates/src/js/app.js',
dest: 'templates/src/js/app.bundle.js',
options: {
browserifyOptions:{
debug: true
}
}
}
},
watch: {
app: {
files: ['templates/src/js/*/*.js'],
tasks: ['browserify'],
options: { //为保持web server 的自动刷新而设置
livereload: true
}
}
},
connect: {
app: {
options: {
port: 9001,
base: 'templates/src',
middleware: function(connect, options, middlewares){//为保持web server 的自动刷新而设置
middlewares.unshift(require('connect-livereload')());
return middlewares;
}
}
}
}
}); //加载其它module/plugins
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect'); //定义task
grunt.registerTask('default',['browserify']);
grunt.registerTask('serve',['browserify:app', 'connect:app', 'watch:app']);
}

在命令行窗口输入:grunt serve

在浏览器中打开:http://localhost:9001/

修改某个文件,浏览器中自动有变化。