对于现在的前端开发来说, 比起之前jQuery时期真心方便很多, 自从ES6的更新后, 更是进一步加快了前端开发的脚步, 并有统一天下的趋势, 随之而出的React / React-Native, Vue / Weex (Vue-Native)之流的框架兴起更是降低了前端开发的门槛, 以至于开发者们对大前端时代趋之若鹜.
对于现在的前端开发已经不想以前仅靠切图加上HTML+CSS+JS三件套就能够轻松搞定了, 现在三方提供的工具层出不穷, jQuery, Angular, BootStrap到现在的Grunt, Gulp, Webpack, Babel, Node.js掌握这些只能算是刚刚入了前端的大门. 相比起iOS这样的移动端有条不紊的技术栈, 略显松散的前端技术对初学者并不是那么的友好.
对于现在的前端开发来说, React和Vue可能已经是必须要掌握的技能了, 通过官方对应的脚手架工具就能够快速的搭建项目框架了. 其实搭建项目框架并不是一个很神奇的事情, 使用grunt或者gulp就能够轻松搞定. 使用哪种工具并不重要关键是要了解其中的原理.
本文我们就通过搭建前端项目框架来进行理解一些容易被忽视的知识点.
npm
npm 是Node Package Manage的缩写, 是Node.js的官方包管理器, 目前已经是世界上最大的包管理器了, 如果你会iOS, 就理解成cocoapods就好了.
要使用npm, 我们就必须要配置Node环境, 好在现在只需要在Node官网上下载就可以了. 如果速度慢的话可以切换淘宝镜像
Express
$ npm install express-generator -g
$ express -e .
使用npm 先进行全局安装, 并进行server的脚手架创建.
package.json
package.json就是npm的包配置文件了, 通过终端npm init进行生成, 和cocoapods的Pods.profile是一个类型的东西.
我们先把需要配置的东西配置好:
{
"name": "gulp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel": "^6.23.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-es2015": "^6.24.1",
"connect-livereload": "^0.6.0",
"del": "^3.0.0",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.1",
"gulp-if": "^2.0.2",
"gulp-live-server": "0.0.31",
"gulp-livereload": "^3.8.1",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-sequence": "^0.4.6",
"gulp-uglify": "^3.0.0",
"gulp-util": "^3.0.8",
"require-dir": "^0.3.2",
"vinyl-named": "^1.1.0",
"webpack": "^3.5.5",
"webpack-stream": "^4.0.0",
"yargs": "^8.0.2"
}
}
配好的项目依赖可以通过 npm install进行加载, 或则手动 npm xxx –save-dev进行配置.
gulpfile.babel.js
使用gulp必须创建这样一个文件, 对于使用的Demo可以从Google的github/web-starter-kit上查看.
对于gulp的API也可以在官方文档上查找 –> 官网文档
我们要做的就是读取文件夹下的配置文件默认读取default.js
import requireDir from 'require-dir';
requireDir('./tasks');
default.js
import gulp from 'gulp';
gulp.task('default', ['build']);
gulp.task(name[, deps], fn)
定义一个使用 Orchestrator 实现的任务(task)
name
任务的名字,如果你需要在命令行中运行你的某些任务,那么,请不要在名字中使用空格。
deps
类型: Array
一个包含任务列表的数组,这些任务会在你当前任务运行之前完成。
注意: 默认的,task 将以最大的并发数执行,也就是说,gulp 会一次性运行所有的 task 并且不做任何等待。如果你想要创建一个序列化的 task 队列,并以特定的顺序执行,你需要做两件事:
给出一个提示,来告知 task 什么时候执行完毕, 并且再给出一个提示,来告知一个 task 依赖另一个 task 的完成。
build.js
import gulp from 'gulp';
import gulpSequence from 'gulp-sequence';
gulp.task('build', gulpSequence('clean', 'css', 'pages', 'scripts', ['browser', 'serve']));
通过gulp-sequence进行任务的排序, 按顺序执行.
args.js
yargs是一个命令行工具集, 我们可以配置以下参数:
import yargs from 'yargs';
const args = yargs
.option('production', {
boolean: true,
default: false,
describe: 'min all scripts'
})
.option('watch', {
boolean: true,
default: false,
describe: 'watch all files'
})
.option('verbose', {
boolean: true,
default: false,
describe: 'log'
})
.option('sourcemaps', {
describe: 'force the creation of sroucemaps'
})
.option('port', {
string: true,
default: 8080,
describe: 'server port'
})
.argv
export default args;
clean.js
import gulp from 'gulp';
import del from 'del';
import args from './util/args';
gulp.task('clean', () => {
return del(['server/public', 'server/views']);
})
通过 del模块进行制定路径的清空操作.
scripts.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import concat from 'gulp-concat';
import webpack from 'webpack';
import gulpWebpack from 'webpack-stream';
import named from 'vinyl-named';
import livereload from 'gulp-livereload';
import plumber from 'gulp-plumber';
import rename from 'gulp-rename';
import uglify from 'gulp-uglify';
import {log,colors} from 'gulp-util';
import args from './util/args';
gulp.task('scripts',()=>{ //执行scripts任务
return gulp.src(['app/js/index.js']) //读取资源文件
.pipe(plumber({ //处理错误
errorHandle:function(){
}
}))
.pipe(named())
.pipe(gulpWebpack({ //Webpack配置
module:{
loaders:[{
test:/\.js$/,
loader:'babel-loader' //Babel
}]
}
}),null,(err,stats)=>{
log(`Finished '${colors.cyan('scripts')}'`,stats.toString({
chunks:false
}))
})
.pipe(gulp.dest('server/public/js')) //重置备份
.pipe(rename({ //重命名
basename:'cp',
extname:'.min.js'
}))
.pipe(uglify({compress: //压缩{properties:false},output:{'quote_keys':true}}))
.pipe(gulp.dest('server/public/js')) //重置备份
.pipe(gulpif(args.watch,livereload())) //热更新
})
gulp.src(globs[, options])
输出(Emits)符合所提供的匹配模式(glob)或者匹配模式的数组(array of globs)的文件。 将返回一个 Vinyl files 的 stream 它可以被 piped 到别的插件中。
gulp.dest(path[, options])
能被 pipe 进来,并且将会写文件。并且重新输出(emits)所有数据,因此你可以将它 pipe 到多个文件夹。如果某文件夹不存在,将会自动创建它。
具体请参考文档.
browser.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import gutil from 'gulp-util';
import args from './util/args';
gulp.task('browser', (cb) => {
if (!args.watch) return cb();
gulp.watch('app/**/*.js', ['scripts']);
gulp.watch('app/**/*.ejs', ['pages']);
gulp.watch('app/**/*.css', ['css']);
});
gulp.watch(glob [, opts], tasks)
或 gulp.watch(glob [, opts, cb])
监视文件,并且可以在文件发生改动时候做一些事情。它总会返回一个 EventEmitter 来发射(emit) change 事件。
注意点: 路径中的/**/*
代表任意路径.
server.js
import gulp from 'gulp';
import gulpif from 'gulp-if';
import liveserver from 'gulp-live-server';
import args from './util/args';
gulp.task('serve', (cb) => {
if (!args.watch) return cb();
var server = liveserver.new(['--harmony', 'server/bin/www']);
server.start();
gulp.watch(['server/public/**/*.js', 'server/views/**/*.ejs'], function(file) {
server.notify.apply(server, [file]);
})
gulp.watch(['server/routes/**/*.js', 'server/app.js'], function() {
server.start.bind(server)();
})
})
开启服务器, 当路径文件监听到有改变时, 告知服务器进行自动刷新页面.
.babelrc
为了能让文件跑起来我们先简陋的配置如下:
{
"presets" : ["es2015"]
}
Babel 是一个 JavaScript 编译器,没什么复杂的 可以直接官网学习
Terminal
这样我们的架构就算是搭建成功了, 最后我们看着终端的代码来总结一下流程:
- 1 执行了build任务,
- 2 执行了clean任务, 清空了指定路径的文件,
- 3 执行了css & pages & scripts任务, 将在书写路径的文件保存至服务器路径中, 并在scripts任务中使用webpack进行babel-loader及压缩, 热更新等操作.
- 4 执行了browser任务, 对相关路径进行监听,
- 5 执行了serve任务, 开启服务器并监测是否需要热更新及重启.
由于流程过于简单就不上传github, 需要的可以私信要源码哦.