前端修仙之路-四、如何利用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'); //压缩图片--免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上有很多实用的插件。这个就看个人需求了,后面的话,如果有空的话,也是会经常写开发中的日志,遇到的什么难题呀、有比较好用的UI之类的,跟大家分享一下嘛!

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

原文地址:https://www.cnblogs.com/liao123/p/13625886.html