vlambda博客
学习文章列表

前端进阶之路-四、如何利用gulp创建web前端开发框架(终)

大家好,我是“廖某某前端日志”,今天为大家讲讲前端的进阶技能。

通过前面三篇内容我们已经学习如何利用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'); //压缩图片--免keyconst 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;
上面在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'); //压缩图片--免keyconst 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;
现在我们接着讲第四步:如何配置和使用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上有很多实用的插件。这个就看个人需求了,后面的话,如果有空的话,也是会经常写开发中的日志,遇到的什么难题呀、有比较好用的UI之类的,跟大家分享一下嘛!

格言:人生没有彩排,每天都是现场直播!加油!