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

【明确目标】

  • 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/

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

了解Browserify的更多相关文章

  1. Vue.js——60分钟browserify项目模板快速入门

    概述 在之前的一系列vue.js文章,我们都是用传统模式引用vue.js以及其他的js文件的,这在开发时会产生一些问题. 首先,这限定了我们的开发模式是基于页面的,而不是基于组件的,组件的所有代码都直 ...

  2. 前端构建工具的用法—grunt、gulp、browserify、webpack

    随着前端项目的飞速发展,项目越来越大.文件越来越多,前端工程化的工具也越来越多.下面介绍目前最流行的四种构建工具——grunt.gulp.browserify.webpack 所有的构建工具都是基于N ...

  3. gulp启动一个小型web服务器配置&browserify(require)

    var gulp = require('gulp'), connect = require('gulp-connect'), // 运行live reload服务器 browserify = requ ...

  4. browserify压缩合并源码反编译

    最近在学习钉钉(一个协作应用)桌面应用的前端源码时候,发现其js源码是用browserify做模块开发.于是想还原其源码的原本的目录结构,学习它的目录分类以及业务划分. 前言 用过browserify ...

  5. browserify学习总结

    前言 在未接触browserify,虽然我知道它是一个前端构建工具,但还是有几个疑问: 1. browserify出现的日期? 2. 能构建哪些文件? 3. 附加的browserify代码体积是多大? ...

  6. gulp/grunt和browserify/webpack的区别

    Gulp应该和Grunt比较,他们的区别我就不说了,说说用处吧.Gulp / Grunt 是一种工具,能够优化前端工作流程.比如自动刷新页面.combo.压缩css.js.编译less等等.简单来说, ...

  7. angular项目总结——angular + browserify + gulp + bower + less 架构分享

    一眨眼,快三个月没有写博客了.一直在为自己没有写博客而懊恼,忙过这段时间,好好总结一下. 新项目主要是自己一个人在写,先搭建了一个初步的架构,用了我并不熟悉的angular,这个过程中,慢慢也熟悉了a ...

  8. browserify使用手册

    简介 这篇文档用以说明如何使用browserify来构建模块化应用 browserify是一个编译工具,通过它可以在浏览器环境下像nodejs一样使用遵循commonjs规范的模块化编程. 你可以使用 ...

  9. 使用Gulp和Browserify来搭建React应用程序

    对React有一定了解之后,我们知道,需要把JSX文件转换成JS文件,组件需要导入导出.本篇就体验使用Gulp把JSX文件转换成JS文件,使用Browserify来把组件捆绑到一个文件并理顺组件之间的 ...

  10. 使用Gulp和Browserify创建多个绑定文件

    Browserify是一个Javascript的绑定工具,帮助我们理顺module之间的依赖关系.Gulp用来优化workflow.两者的共同点都是使用流,但在使用流方面也有不同之处: Browser ...

随机推荐

  1. Ubuntu遇到Please ensure that adb is correctly located at '...adb.exe' and can be executed 问题解决方法

    上次我们在SDK更新的到最新的Android L版本之后,我发现我的ADT和android指定的版本不对应,我的ADT是22版本的,android L需要23版本以上的,版本不对应的话就无法加载这个S ...

  2. android studio小技巧

    1. 为了防止乱码,请将 android studio 右下角编码设置成 UTF-8 2. Ctri + Q 查看api 3 Ctri + Shift +U 大小写互换 4 Ctrl + Alt + ...

  3. JDBC常用接口详解

    JDBC中常用接口详解 ***DriverManager 第一.注册驱动 第一种方式:DriverManager.registerDriver(new com.mysql.jdbc.Driver()) ...

  4. ubuntu 14.04 编译内核出现unable to locate package ncurses-devel 问题的解决

    http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece7631046893b4c4380146d96864968d4e414c422461614 ...

  5. 关于在xml文件中的 error: invalid symbol: 'switch' 错误

    在xml布局文件中使用Switch控件时,出现error: invalid symbol: 'switch'报错,代码如下: <Switch android:id="@+id/swit ...

  6. networkx的绘图中文显示方块问题

    修改matplotlibrc文件 font.family : sans-serif #打开该选项 font.sans-serif : Microsoft YaHei , Bitstream Vera ...

  7. 调用数据库函数CallableStatement

  8. 在VLFEAT中mat类型图片转换成constant float* 来进行vl_dsift_process

    How to convert an OpenCV cv::Mat into a float* that can be fed into Vlfeat vl_dsift_process: Mat mat ...

  9. [ASE][Daily Scrum]11.17

    这两天感冒了没有第一时间更新blog和tfs,给大家抱歉了! 上周五我们已经将服务器搭建完成并成功通讯,周六周日大家非常给力的完成了很多内容! View Shilin Liu 处理来自服务器的数据 显 ...

  10. SQL Server 利用游标解决Tempdb究极竞争-DBA-程序员需知

    SQL Server tempdb分配竞争算是DBA老生常谈的问题了,几乎现在所有的DBA都知道多建几个文件来解决/缓解问题.但是深层次的的竞争依旧不可避免.这里给大家剖析下游标在tempdb中的特点 ...