通过前面三篇内容我们已经学习如何利用gulp插件和gulp脚手架来搭建一个完整的web前端框架,这篇就主要讲解将框架中的一些代码和文件独立,让框架项目结构更加清晰,也便于在日常开发中使用。
主要从下面四个步骤出发:
1.利用npm来运行框架。
2.代码整合
3.文件整合
4.如何配置和使用gitignore文件
首先我们先把第一步实现一下:用npm来执行脚本命令。
看一下npm的强大功能之一,npm允许在package.json文件里面,使用scripts定义脚本命令。就像我们创建的package.json文件中,可以看到下面这一段:
scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
上面代码是package.json
文件的一个片段,里面的scripts
字段是一个对象。它的每一个属性,对应一段脚本。比如,test
命令对应的脚本是输出一串报错终止脚本。
我们在控制台中运行一下,输入:
npm run test
从上面截图可以看到,虽然报了个错,但是说明还是执行了那个test名称的脚本。
那么为什么要学习用npm来执行命令呢?当然有作用,因为我们接下来将用npm来运行gulpfile.js这个文件,不用gulp脚手架了。
那么我们改一下脚本运行命令test的执行:
"scripts": {
"test": "gulp dev --env development"
},
改成gulp执行开发环境的命令,然后在控制台中用npm再执行一下上面的命令,
可以看到项目已经在跑起来了。
通过上面的方法,我们省去了安装gulp脚手架的麻烦。那么将打包生产环境的脚本命令也添加到package.json文件中。
"scripts": {
"dev": "gulp dev --env development",
"build": "gulp dev --env production"
},
可以通过在控制台运行“npm run dev” 和 “npm run build”就可以将项目运行起来和将项目打包。
上面已经把第一步完成了,下面接着讲第二、三步:代码和文件的整合。
我们需要在框架根目录下创建一main个文件夹,然后把框架根目录下的gulpfile.js、utils.js、config.js移入到main文件夹中并需要修改一下gulpfile.js中的代码:
const gulp = require('gulp');
const stylus = require('gulp-stylus');
const uglify = require('gulp-uglify');
const cleanCss = require('gulp-clean-css');
const postcss = require('gulp-postcss');
const postcssrc = require('postcss-load-config');
const connect = require('gulp-connect');
const plumber = require('gulp-plumber'); // 避免出错task终止
const minimist = require('minimist'); // 用于命令行传参数
const gulpif = require('gulp-if'); // 用于命令行传参
const cleanCSS = require('gulp-clean-css'); // 缩小css文件
const changed = require('gulp-newer'); // 增量更新
const babel = require('gulp-babel');
const tinypng_nokey = require('gulp-tinypng-nokey'); //压缩图片--免key
const opn = require('opn'); // 开启浏览器
const del = require('del'); // 删除dist文件夹
const { getPort } = require('./utils');
const Config = require('./config');
// 命令行传参数
const knownOptions = {
string: 'env',
default: { env: process.env.NODE_ENV || 'production' }
};
const options = minimist(process.argv.slice(2), knownOptions);
// 检查端口冲突
const checkPort = async () => {
Config.connect.port = await getPort();
};
// 开启文件服务器
const openServer = async () => await connect.server(Config.connect);
// 自动打开浏览器
const openBrowser = async () => {
console.log('打开浏览器');
const { host, port } = Config.connect;
const url = `http://${host}:${port}/view`;
opn(url);
};
// HTML文件
const htmls = () => {
return gulp
.src('src/view/**/*.html')
.pipe(changed('src/view/**/*.html'))
.pipe(plumber())
.pipe(gulp.dest('dist/view/'))
.pipe(connect.reload());
};
// CSS文件
const styles = () => {
return gulp
.src('src/css/**/*.styl')
.pipe(changed('src/css/**/*.styl'))
.pipe(plumber())
.pipe(stylus())
.pipe(gulpif(options.env === 'production', cleanCSS()))
.pipe(gulp.dest('dist/css/'))
.pipe(connect.reload());
};
// 图片文件
const images = () => {
return gulp
.src('src/images/**/*')
.pipe(changed('src/images/**/*'))
.pipe(plumber())
.pipe(gulp.dest('dist/images/'))
.pipe(connect.reload());
};
// 图片压缩
const imgTiny = () => {
return gulp
.src('src/images/**/*')
.pipe(changed('src/images/**/*'))
.pipe(tinypng_nokey())
.pipe(gulp.dest('dist/images/'))
.pipe(connect.reload());
}
// JS文件
const scripts = () => {
return gulp
.src('src/js/**/*.js')
.pipe(changed('src/js/**/*.js'))
.pipe(plumber())
.pipe(babel()) //ES6转换
.pipe(gulpif(options.env === 'production', uglify()))
.pipe(gulp.dest('dist/js/'))
.pipe(connect.reload());
};
//检测文件是否有更新
const watchFiles = () => {
gulp.watch('src/view/**/*.html', htmls); //检测到html文件更新,更新版本号
gulp.watch('src/css/**/*.styl', styles);
gulp.watch('src/images/*', images);
gulp.watch('src/js/**/*.js', scripts);
};
// 删除dist目录
const clean = () => {
return del(['dist']);
};
// 开发环境-不压缩JS、css和图片
const devbuild = gulp.series(
checkPort,
gulp.parallel(openServer, htmls, styles, images, scripts),
openBrowser,
watchFiles
);
// 生产环境-不运行服务、不开启浏览器、代码压缩和图片压缩
const prodbuild = gulp.series(
clean,
gulp.parallel(htmls, styles, imgTiny, scripts),
);
const build = options.env === 'production' ? prodbuild : devbuild; //判断是开发环境还是生产环境
options.env === 'production' ? null : watchFiles();
module.exports = build;
View Code
上面在gulpfile.js文件中做了导出模块,我们在根目录下再创建个gulpfile.js文件(这个文件名用其他的不行,必须要用gulpfile,gulp命令必须需要找到gulpfile这个文件名才能正常运行,其他不行,诶,没办法了),写入下面代码:
const gulp = require('gulp');
const build = require('./main/gulpfile.js');
gulp.task('dev', build);
如果大家有点强迫症的话,我们只能修改main文件夹下的gulpfile.js文件名,就把它改成main.js,相应的再改一下根目录下gulpfile.js中的导入路径:
const gulp = require('gulp');
const build = require('./main/main.js');
gulp.task('dev', build);
接下来,在配置config.js文件中,封装项目资源的路径:
const { host } = require('./utils');
const folder = {
src: 'src/', // 源文件目录
dist: 'dist/' // 文件处理目录
};
const distFiles = folder.dist + '**'; // 目标路径下的所有文件
const Config = {
connect: {
root: 'dist',
livereload: true,
port: 1234,
host
},
src: folder.src,
dist: folder.dist,
distFiles: distFiles,
html: {
src: folder.src + 'view/**/*.html',
dist: folder.dist + 'view/'
},
css: {
src: folder.src + 'css/**/*.styl',
avoid: '!' + folder.src + 'css/stylus/*',
dist: folder.dist + 'css/'
},
js: {
src: folder.src + 'js/**/*.js',
dist: folder.dist + 'js/'
},
images: {
src: folder.src + 'images/**/*',
dist: folder.dist + 'images/'
},
libs: {
src: folder.src + 'common/**/*',
dist: folder.dist + 'common/'
}
};
module.exports = Config;
再来修改main.js代码中的静态资源路径为实参:
const gulp = require('gulp');
const stylus = require('gulp-stylus');
const uglify = require('gulp-uglify');
const cleanCss = require('gulp-clean-css');
const postcss = require('gulp-postcss');
const postcssrc = require('postcss-load-config');
const connect = require('gulp-connect');
const plumber = require('gulp-plumber'); // 避免出错task终止
const minimist = require('minimist'); // 用于命令行传参数
const gulpif = require('gulp-if'); // 用于命令行传参
const cleanCSS = require('gulp-clean-css'); // 缩小css文件
const changed = require('gulp-newer'); // 增量更新
const babel = require('gulp-babel');
const tinypng_nokey = require('gulp-tinypng-nokey'); //压缩图片--免key
const opn = require('opn'); // 开启浏览器
const del = require('del'); // 删除dist文件夹
const { getPort } = require('./utils');
const Config = require('./config');
// 命令行传参数
const knownOptions = {
string: 'env',
default: { env: process.env.NODE_ENV || 'production' }
};
const options = minimist(process.argv.slice(2), knownOptions);
// 检查端口冲突
const checkPort = async () => {
Config.connect.port = await getPort();
};
// 开启文件服务器
const openServer = async () => await connect.server(Config.connect);
// 自动打开浏览器
const openBrowser = async () => {
console.log('打开浏览器');
const { host, port } = Config.connect;
const url = `http://${host}:${port}/view`;
opn(url);
};
// HTML文件
const htmls = () => {
return gulp
.src(Config.html.src)
.pipe(changed(Config.html.src))
.pipe(plumber())
.pipe(gulp.dest(Config.html.dist))
.pipe(connect.reload());
};
// CSS文件
const styles = async () => {
const config = await postcss();
return gulp
.src([Config.css.src, Config.css.avoid])
.pipe(changed(Config.css.src))
.pipe(plumber())
.pipe(stylus())
.pipe(postcss(config.plugins, config.options))
.pipe(gulpif(options.env === 'production', cleanCSS()))
.pipe(gulp.dest(Config.css.dist))
.pipe(connect.reload());
};
// 图片文件
const images = () => {
return gulp
.src(Config.images.src)
.pipe(changed(Config.images.src))
.pipe(plumber())
.pipe(gulp.dest(Config.images.dist))
.pipe(connect.reload());
};
// 图片压缩
const imgTiny = () => {
return gulp
.src(Config.images.src)
.pipe(tinypng_nokey())
.pipe(gulp.dest(Config.images.dist))
.pipe(connect.reload());
}
// JS文件
const scripts = () => {
return gulp
.src(Config.js.src)
.pipe(changed(Config.js.src))
.pipe(plumber())
.pipe(babel()) //ES6转换
.pipe(gulpif(options.env === 'production', uglify()))
.pipe(gulp.dest(Config.js.dist))
.pipe(connect.reload());
};
//检测文件是否有更新
const watchFiles = () => {
gulp.watch(Config.html.src, htmls); //检测到html文件更新,更新版本号
gulp.watch(Config.css.src, styles);
gulp.watch(Config.images.src, images);
gulp.watch(Config.js.src, scripts);
};
// 删除dist目录
const clean = () => {
return del(['dist']);
};
// 开发环境-不压缩JS、css和图片
const devbuild = gulp.series(
checkPort,
gulp.parallel(openServer, htmls, styles, images, scripts),
openBrowser,
watchFiles
);
// 生产环境-不运行服务、不开启浏览器、代码压缩和图片压缩
const prodbuild = gulp.series(
clean,
gulp.parallel(htmls, styles, imgTiny, scripts),
);
const build = options.env === 'production' ? prodbuild : devbuild; //判断是开发环境还是生产环境
options.env === 'production' ? null : watchFiles();
module.exports = build;
View Code
现在我们接着讲第四步:如何配置和使用gitignore文件。
我们需要了解一下gitignore文件是个啥东西呢?首先要强调一点,这个文件的完整文件名就是".gitignore",注意最前面有个“.”一般来说每个Git项目中都需要一个“.gitignore”文件,这个文件的作用就是告诉Git哪些文件不需要添加到版本管理中。日常开发项目的时候,为了自己方便和同事方便,也一般会把项目提交都Git上,这样的话,这个gitignore文件可就是个好东西了,首先就我们现在创建的框架来说,先看一下现在的整体目录吧:
上图中里面圈出的目录就是现在的框架整体目录,如果我们需要将这个整体提交到git上,node_modules和dist这两个文件夹及其里面的内容是不用提交的。node_modules可以根据packa.json来安装,dist可以运行“npm run dev”或者“npm run build”来创建。
所以完全是可以忽略掉这两个的。
那应该怎么做呢?我们同样在项目根路径下创建一个“.gitignore”文件,然后写入:
node_modules/
dist/
“/”代表该文件夹下的所有。这样在每次push的时候,会自动忽略掉这两个文件夹里面的文件的。
写在最后:好久没有试过写这么长的教程了,记得写这么长的时候还是第一次写博客的时候,当时是去年还是去去年了,当时是做毕业设计的时候,当时还是Java出身的小白,现在已经转身走上了web前端的修仙之路了,有时候命运还是挺会捉弄人的哈。不想感慨太多,这个长篇教程可能有很多地方写的不是很好,但敬请大家见谅就好了。其实这个框架不是我做的,是我公司的前辈们做的,现在只不过是引用并写下心得教程啦~当然我在入职这个公司之前也还是有过gulp构建框架的基础,不过那个时候是用gulp和bower搭建过一套不是很完整的框架,入职这个公司之后,看到这个框架,才知道什么叫做大佬吧,虽然这里的功能不是很多,但够用和实用确实是够了的。当然,这个框架现在也算是开源了吧,哈哈~大家有需要的什么功能,比如:手机端自适应插件、版本hash值添加这些功能也是可以自行添加的,gulp上有很多实用的插件