创建模版的方法有plop.js、yeoman及node cli方式,本文将以node cli为示例进行讲解:
一、前置知识
1、chalk
可以使用它来实现各种命令行字体的颜色;
const chalk = require('chalk');
console.log(chalk.green('success'));
console.log(chalk.red('error'));
2、inquirer
可以使用它在命令行实现与用户的一些交互如让用户选择你预定的选项或者让用户输入文字;
const inquirer = require('inquirer');
inquirer
.prompt([
// 一些交互式的问题
])
.then(answers => {
// 回调函数,answers 就是用户输入的内容,是个对象
});
3、ora
在命令行实现一个好看的loading效果;
const ora = require('ora')
let spinner = ora('downloading template ...')
spinner.start()
4、download-git-repo
用它可以下载或者clone远程仓库
const download = require('download-git-repo')
download(repository, destination, options, callback)
// repository 表示远程仓库地址
// destination 表示存放下载的文件路径
// options 一些选项 如 { clone:boolean } 表示是clone 还是http下载
5、commander
用来编写指令和处理命令行的
const program = require("commander");
// 定义指令
program
.version('0.0.1')
.command('init', 'Generate a new project from a template')
.action(() => {
// 回调函数
})
// 解析命令行参数
program.parse(process.argv);
6、handlebars
根据指定版本用来生成html的
(ps:除此之外,还有Mustach/ejs等)
7、figlet
制作欢迎页的
8、runjs
运行脚本(新tasksfile)
9、其他
清屏 https://www.npmjs.com/package/clear
打开文件 https://www.npmjs.com/package/open
进度 https://www.npmjs.com/package/ora
任务列表 https://www.npmjs.com/package/listr
二、创建步骤
1、npm init
2、在 package.json里写入如下依赖并安装
"dependencies": {
"chalk": "^2.4.2",
"commander": "^2.19.0",
"download-git-repo": "^1.1.0",
"inquirer": "^6.2.2",
"ora": "^3.2.0",
"handlebars": "~4.7.6",
}
3、在项目根目录下新建 bin文件夹,并在里面新建无后缀名的文件init
#!/usr/bin/env node
const Handlebars = require('handlebars')
// 交互式命令行
const inquirer = require('inquirer')
// 修改控制台字符串的样式
const chalk = require('chalk')
// node 内置文件模块
const fs = require('fs')
const { resolve } = require('path')
const path = require('path')
// 读取根目录下的 template.json
var htmlTemplet='';
var packagejsonTemplet='';
var cssTemplet='';
var jsTemplet='';
// console.log('resolve',resolve('./'))
// console.log('cwd : ' + process.cwd())
// console.log('__dirname : ' + __dirname)
// 读取文件操作
fs.readFile(`${__dirname}/../template/doNotChangeIt.html`, null, function(error, data) {
if(error){
console.log('读取doNotChangeIt.html文件出错了'+error)
}else {
htmlTemplet=data.toString()
// console.log('成功读取index.html'+htmlTemplet)
}
})
fs.readFile(`${__dirname}/../template/package.json`, function(error, data) {
if(error){
console.log('读取package.json文件出错了'+error)
}else {
packagejsonTemplet=data.toString()
}
})
fs.readFile(`${__dirname}/../template/doNotChangeIt.css`, null, function(error, data) {
// console.log('读取doNotChangeIt.css __dirname:'+__dirname)
if(error){
console.log('读取doNotChangeIt.css文件出错了'+error)
}else {
cssTemplet=data.toString()
// console.log('成功读取index.html'+htmlTemplet)
// console.log('读取doNotChangeIt.css==cssTemplet:'+cssTemplet)
}
})
fs.readFile(`${__dirname}/../template/doNotChangeIt.js`, null, function(error, data) {
if(error){
console.log('读取doNotChangeIt.js文件出错了'+error)
}else {
jsTemplet=data.toString()
// console.log('成功读取index.html'+htmlTemplet)
}
})
// 拷贝文件
// function copyFile(src, dist) {
// fs.writeFileSync(dist, fs.readFileSync(src));
// }
function copyFile(src, dist) {
fs.createReadStream(src).pipe(fs.createWriteStream(dist));
}
// 检测目录并创建目录
// checkDirAndMkDir=(folderpath)=>{
// const pathArr=folderpath.split('/');
// let _path='';
// for(let i=0;i {
fs.stat(path, (err, stats) => {
if(err){
resolve(false);
}else{
resolve(stats);
}
})
})
}
/**
* 创建路径
* @param {string} dir 路径
*/
function mkdir(dir){
return new Promise((resolve, reject) => {
fs.mkdir(dir, err => {
if(err){
resolve(false);
}else{
resolve(true);
}
})
})
}
/**
* 路径是否存在,不存在则创建
* @param {string} dir 路径
*/
async function dirExists(dir){
let isExists = await getStat(dir);
//如果该路径且不是文件,返回true
if(isExists && isExists.isDirectory()){
return true;
}else if(isExists){ //如果该路径存在但是文件,返回false
return false;
}
//如果该路径不存在
let tempDir = path.parse(dir).dir; //拿到上级路径
//递归判断,如果上级目录也不存在,则会代码会在此处继续循环执行,直到目录存在
let status = await dirExists(tempDir);
let mkdirStatus;
if(status){
mkdirStatus = await mkdir(dir);
}
return mkdirStatus;
}
// var data = { "name": "Alan",
// "hometown": "Somewhere, TX",
// "kids": [{"name": "Jimmy", "age": "12"}, {"name": "Sally", "age": "4"}]
// };
//
// var result = htmlHandlebarsComp(data);
// 自定义交互式命令行的问题及简单的校验
let question = [
{
name: "projectName",
type: 'input',
message: "请输入项目名称(要求英文),方便错误监控识别",
validate (val) {
if (val === '') return '项目名称为必填项!'
if (/\W/.test(val)) return '项目名称要求为英文!'
return true
}
},
{
name: "fileName",
type: 'input',
message: "请输入html/css/js的文件名",
validate (val) {
if (val === '') return 'html/css/js文件名为必填项!'
return true
}
},
{
name: "title",
type: 'input',
message: "请输入页面标题title",
validate (val) {
if (val === '') return '页面标题title为必填项!'
return true
}
},
{
name: "apikey",
type: 'input',
message: "请输入您的fundebug的apikey",
validate (apikey) {
if (apikey === '') {
return 'apikey为必填项!'
}else if (apikey.length<64){
return 'apikey位数小于64!'
}else {
return true
}
}
},
{
name: "isInjectJQ",
type: 'confirm',
message: "是否引入jQuery?"
// validate (apikey) {
// if (apikey === '') {
// return 'apikey为必填项!'
// }else if (apikey.length<64){
// return 'apikey位数小于64!'
// }else {
// return true
// }
// }
}
]
inquirer
.prompt(question).then(answers => {
// answers 就是用户输入的内容,是个对象
let {projectName,fileName,title,apikey,isInjectJQ } = answers;
var htmlHandlebarsComp = Handlebars.compile(htmlTemplet);
var packagejsonHandlebarsComp = Handlebars.compile(packagejsonTemplet);
var htmlResult = htmlHandlebarsComp(answers);
var packagejsonResult = packagejsonHandlebarsComp(answers);
// console.log('answers====',answers)
// console.log('htmlTemplet====',htmlTemplet)
// // 过滤 unicode 字符
// htmlTemplet[name] = url.replace(/[\u0000-\u0019]/g, '')
// // 把模板信息写入 template.json 文件中
// fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(htmlTemplet), 'utf-8', err => {
// if (err) console.log(err)
// console.log('\n')
// console.log(chalk.green('Added successfully!\n'))
// console.log(chalk.grey('The latest template list is: \n'))
// console.log(htmlTemplet)
// console.log('\n')
// })
async function creatHTMLCssJs(){
// 生成html文件
async function creatHtml(){
await dirExists(`${resolve('./')}/workspace`);
fs.writeFile(`${resolve('./')}/workspace/${fileName}.html`, htmlResult, 'utf8', err => {
if (err) console.log(err)
console.log('\n')
console.log(chalk.green(`在${resolve('./')}/workspace下成功新建了${fileName}.html文件\n`))
// console.log(chalk.grey('请不要移动项目文件哦\n'))
// console.log('\n')
})
}
await creatHtml()
// 生成package.json文件
async function creatPackagejson(){
fs.access(`${resolve('./')}/package.json`, fs.constants.F_OK, async (err) => {
if(err && err.code==='ENOENT'){
fs.writeFile(`${resolve('./')}/package.json`, packagejsonResult, 'utf8', err => {
if (err) console.log('创建package.json出错了',err)
console.log('\n')
console.log(chalk.green(`在${resolve('./')}下成功新建了package.json文件\n`))
})
}else if(err){
if (err) console.log('访问package.json出错了',err)
}
});
}
await creatPackagejson()
// 生成css文件
async function creatCss(){
await dirExists(`${resolve('./')}/workspace/css`);
fs.writeFile(`${resolve('./')}/workspace/css/${fileName}.css`,cssTemplet , 'utf8', err => {
if (err) console.log(err)
console.log('\n')
console.log(chalk.green(`在${resolve('./')}/workspace/css下成功新建了${fileName}.css文件\n`))
// console.log('写入cssTemplet中的__dirname',__dirname)
// console.log('写入cssTemplet',cssTemplet)
// console.log(chalk.grey('请不要移动项目文件哦\n'))
// console.log('htmlResult')
// console.log('\n')
})
}
await creatCss()
// 生成js文件
async function creatJS(){
await dirExists(`${resolve('./')}/workspace/js`);
fs.writeFile(`${resolve('./')}/workspace/js/${fileName}.js`,jsTemplet , 'utf8', err => {
if (err) console.log(err)
console.log('\n')
console.log(chalk.green(`在${resolve('./')}/workspace/js下成功新建了${fileName}.js文件\n`))
console.log(chalk.red(`所有文件均在${resolve('./')}下创建完毕,请执行 npm install`))
// console.log(chalk.grey('请不要移动项目文件哦\n'))
// console.log('htmlResult')
// console.log('\n')
})
}
await creatJS()
// 具备检测是否存在的拷贝文件功能
async function copyFileBeforeCheck(fromFileSrc,toFileSrc,toFileSrcSubDir){
fs.access(toFileSrc, fs.constants.F_OK, async (err) => {
if(err && err.code==='ENOENT'){
// copyFile(`${__dirname}/../gulpfile.js`,`${resolve('./')}/gulpfile.js`)
if(toFileSrcSubDir){
await dirExists(toFileSrcSubDir);
}
copyFile(fromFileSrc,toFileSrc)
}else if (err) {
console.log('拷贝'+fromFileSrc+'出错了:',err)
}
});
}
copyFileBeforeCheck(`${__dirname}/../template/jQuery.v1.12.4.js`,`${resolve('./')}/workspace/js/notchange/jQuery.v1.12.4.js`,`${resolve('./')}/workspace/js/notchange/`)
copyFileBeforeCheck(`${__dirname}/../gulpfile.js`,`${resolve('./')}/gulpfile.js`)
// copyFileBeforeCheck(`${__dirname}/../template/package.json`,`${resolve('./')}/package.json`)
// async function copyJQFile(){
// fs.access(`${resolve('./')}/workspace/js/notchange/jQuery.v1.12.4.js`, fs.constants.F_OK, async (err) => {
// console.log('拷贝jQuery出错了:',err)
// if(err.code==='ENOENT'){
// // copyFile(`${__dirname}/../gulpfile.js`,`${resolve('./')}/gulpfile.js`)
// await dirExists(`${resolve('./')}/workspace/js/notchange/`);
// copyFile(`${__dirname}/../template/jQuery.v1.12.4.js`,`${resolve('./')}/workspace/js/notchange/jQuery.v1.12.4.js`)
// }
// });
//
//
//
// }
// copyJQFile()
// async function copyGulpFile(){
// // copyFile(`${__dirname}/../gulpfile.js`,`${resolve('./')}/gulpfile.js`)
// // 检查文件是否存在于当前目录中。
// fs.access(`${resolve('./')}/gulpfile.js`, fs.constants.F_OK, (err) => {
// // console.log('拷贝gulp出错了:',err)
// if(err.code==='ENOENT'){
// copyFile(`${__dirname}/../gulpfile.js`,`${resolve('./')}/gulpfile.js`)
// }
// });
//
// }
// copyGulpFile()
}
creatHTMLCssJs()
// // 写入文件
// var writerStream = fs.createWriteStream(`${fileName}.html`);
// writerStream.write(htmlResult.toString(), 'UTF8');
// writerStream.end();
})
// 输入fundebug apikey
// 输入项目名称
// 输入文件名名称
// 输入网页标题
4、在package.json里写入如下信息,以便使用npm link
后再运行 init 即可代替运行node ./bin/xr
指令;
"bin": {
"init": "bin/init"
}
或者使用 npm install -g
也是一样的效果
5、新建如下目录或文件
6、doNotChangeIt.html 如下:
{{title}}
欢迎使用模板
{{#if isInjectJQ}}
{{/if}}
7、package.json文件如下
{
"name": "{{projectName}}",
"version": "1.0.0",
"main": "gulpfile.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "gulp"
},
"repository": {
"type": "git",
"url": ""
},
"author": "",
"license": "ISC",
"dependencies": {
"@babel/runtime": "~7.4.5",
"babel-core": "~6.26.3",
"babel-plugin-transform-runtime": "~6.23.0",
"babel-polyfill": "~6.26.0",
"babel-preset-env": "~1.7.0",
"babel-preset-es2015": "~6.24.1",
"browser-sync": "~2.26.7",
"fix-orientation": "0.0.2",
"gulp": "~3.9.1",
"gulp-htmlmin": "~5.0.1",
"gulp-jshint": "~2.0.2",
"gulp-livereload": "~3.8.1",
"gulp-minify-css": "~1.2.4",
"gulp-notify": "~2.2.0",
"gulp-open": "~3.0.1",
"gulp-postcss": "~7.0.1",
"gulp-rename": "~1.2.2",
"gulp-sourcemaps": "~2.6.5",
"gulp-uglify": "~3.0.1",
"postcss-px2rem": "~0.3.0",
"pump": "^3.0.0"
},
"devDependencies": {
"@babel/core": "~7.4.5",
"@babel/plugin-transform-runtime": "~7.4.4",
"@babel/preset-env": "~7.4.5",
"gulp-asset-rev": "0.0.15",
"gulp-autoprefixer": "~5.0.0",
"gulp-babel": "~8.0.0",
"gulp-clean": "~0.3.2",
"gulp-clean-css": "~2.0.13",
"gulp-concat": "~2.6.0",
"gulp-connect": "~5.7.0",
"gulp-ejs": "~3.2.0",
"gulp-file": "~0.4.0",
"gulp-file-include": "~2.0.1",
"gulp-imagemin": "~4.1.0",
"gulp-inject": "~5.0.2",
"gulp-jshint": "~2.0.2",
"gulp-livereload": "~3.8.1",
"gulp-minify-css": "~1.2.4",
"gulp-notify": "~2.2.0",
"gulp-plumber": "~1.2.1",
"gulp-rename": "~1.2.2",
"gulp-rev": "~8.1.0",
"gulp-rev-collector": "~1.2.3",
"gulp-watch": "~5.0.1",
"jshint": "~2.9.4",
"run-sequence": "~2.2.0"
},
"description": ""
}
8、gulpfile文件如下
var gulp = require('gulp');
var plumber = require('gulp-plumber'); // 实时更新错误不会导致终端gulp运行开启的服务断开
var connect = require('gulp-connect'); // 在本地开启一个websocket服务,使用liveReload实现实时更新
var watch = require('gulp-watch'); // 监听文件的变化,配合connect实现服务自动刷新
var gulpOpen = require('gulp-open');//用指定软件打开文件
var browserSync = require('browser-sync').create();
var postcss = require('gulp-postcss');
var px2rem = require('postcss-px2rem');
var autoprefixer = require('gulp-autoprefixer');
var fileinclude = require('gulp-file-include');
var ejs = require('gulp-ejs');
var os = require('os');
// var path = require('path');
var ipv4IpLocal = function getLoaclIP() {
var ipObj = os.networkInterfaces();
var IPv4 = [];
Object.keys(ipObj).forEach(function (ele) {
ipObj[ele].forEach(function (ip) {
if (ip.family === 'IPv4') {
IPv4.push(ip.address);
}
});
});
return IPv4[0];
};
// 实现静态资源版本更新与缓存==================================================
var runSequence = require('run-sequence'),
rev = require('gulp-rev'),
revCollector = require('gulp-rev-collector');
// assetRev = require('gulp-asset-rev');
// 压缩相关插件 ========================================
var cleanCSS = require('gulp-clean-css'),//css压缩
uglify = require('gulp-uglify'),//js压缩
concat = require("gulp-concat"), //合并文件
htmlmin = require('gulp-htmlmin'),//html压缩
// jshint=require("gulp-jshint"), //代码规范检查
imagemin = require("gulp-imagemin"), //图片压缩
rename = require("gulp-rename"),//重命名
clean = require("gulp-clean");//清空文件夹
var pump = require('pump');
var babel = require("gulp-babel");
var sourcemaps = require('gulp-sourcemaps');
var file = require('gulp-file');
var inject = require('gulp-inject');
var workSpaceDir='./workspace';
var distDir='./workspace/dist';
// function getDateAndTime(dateObj) {
// var nowDate = dateObj;
// var y = (nowDate.getFullYear()).toString().slice(2,4);
// var m = nowDate.getMonth() + 1;
// m = m < 10 ? ('0'+m) : m;
// var d = nowDate.getDate();
// d = d < 10 ? ('0'+d) : d;
// var h = nowDate.getHours();
// h = h < 10 ? ('0'+h) : h;
// var min = nowDate.getMinutes();
// min = min < 10 ? ('0'+min) : min;
// var s = nowDate.getSeconds();
// s = s < 10 ? ('0'+s) : s;
// return y+m+d+h+min+s
// // return `${y}${m}${d}${h}${min}${s}`;
//
// }
// var curDateAndTime=getDateAndTime( new Date());
gulp.task('cssREM', function () {
var processors = [px2rem({remUnit: 90.4})];
var stream = gulp.src(workSpaceDir+'/css/*.css')
.pipe(postcss(processors))
.on('error', function (err) {
console.error(' css rem处理出错了'+err)
}).pipe(gulp.dest(workSpaceDir+'/css/rem'));
console.log(' css rem在执行中 stream ' + stream)
return stream;
});
gulp.task('fileinclude', function () {
// 适配page中所有文件夹下的所有html,排除page下的include文件夹中html
gulp.src(['page/**/*.html', '!page/include/**.html'])
.pipe(fileinclude({
prefix: '@@',
basepath: '@file'
}))
.pipe(gulp.dest('dist0'));
});
gulp.task('ejs', function () {
gulp.src('ejs/**.ejs')
.pipe(ejs())
.pipe(gulp.dest('dist_ejs'));
});
// gulp.task('html', function () {
// return gulp.src("./workspace/*.html")
// .pipe(htmlmin({collapseWhitespace: true}))
// .pipe(gulp.dest("./dist/"));
// });
//
// gulp.task('cssjr', function () {
// console.log('开始执行css兼容程序')
// // https://www.gulpjs.com.cn/docs/api/#gulp.task
// // cssjr 是css 任务的依赖 因此它要通知css它什么时候执行完了
// // 有三种方式 通知它执行完了 1、回调 2、返回数据流stream 3、返回promise
// var stream = gulp.src('./workspace/css/*.css')
// .pipe(autoprefixer({
// browsers: ['since 2010'],
// cascade: false
// }))
// // .pipe(rename({
// // suffix: ".jr"
// // }))
// .pipe(gulp.dest('./dist/css/jianrong'));
// console.log(' css兼容程序在执行中 stream ' + stream)
// return stream;
//
//
// });
//
// gulp.task('css', ['cssjr'], function () {
// // 执行该css任务的前提是 执行完了cssjr (cssjr 是css 的依赖)
// //
// console.log('开始执行css压缩')
// return gulp.src("./dist/css/jianrong/*.css")
// .pipe(cleanCSS({compatibility: 'ie8'}))
// // .pipe(rename({
// // suffix: ".min."+curDateAndTime
// // }))
// .pipe(gulp.dest('./dist/css/'));
// });
//
//
// gulp.task('js', function (cb) {
// console.log('开始压缩js');
//
// pump([
// gulp.src('./workspace/js/*.js'),
// uglify(),
// // rename({suffix: '.min.'+curDateAndTime}),
// gulp.dest('./dist/js')
// ],
// cb
// );
// });
//
// gulp.task('img', function () {
// console.log('开始压缩图片');
// return gulp.src('./workspace/img/**/*')
// .pipe(imagemin())
// .pipe(gulp.dest('./dist/img'))
// }
// );
//
//
// gulp.task("clean", function(){
// console.log('开始清空dist文件夹 --不清空img ');
// return gulp.src(['./dist/*.html','./dist/css/*','./dist/js/*.js'])
// .pipe(clean());
// });
//
// gulp.task("cleanIncludeImg", function(){
// console.log('开始清空dist文件夹 --清空img');
// return gulp.src(['./dist/*.html','./dist/css/*','./dist/js/*.js','./dist/img/*'])
// .pipe(clean());
// });
//
//
// // 压缩(不压缩图片)但不清空
// gulp.task('jrmin', ['html', 'css', 'js']);
//
// // 清空并压缩 (不含图片)
// gulp.task('cleanAndMin', ['clean'],function(){
// gulp.start('html', 'css', 'js')
// });
//
//
// // 清空并压缩 (含图片)
// gulp.task('min', ['cleanIncludeImg'],function(){
// gulp.start('html', 'css', 'js','img')
// });
// //定义css、js源文件路径
// var cssSrc = './dist/css/*.css', //dist下的所有css文件
// jsSrc = './dist/js/*.js'; //dist下的所有js文件
//
//
// //为css中引入的图片/字体等添加hash编码
// // gulp.task('assetRev', function(){
// // return gulp.src(cssSrc) //该任务针对的文件
// // .pipe(assetRev()) //该任务调用的模块
// // .pipe(gulp.dest('src/css')); //编译后的路径
// // });
//
// //CSS生成文件hash编码并生成 rev-manifest.json文件名对照映射
// gulp.task('revCss', function(){
// console.log('revCss执行了')
// return gulp.src(cssSrc)
// .pipe(rev())
// .pipe(gulp.dest('revDist/css'))
// .pipe(rev.manifest())
// .pipe(gulp.dest('revDist/css'));
//
// });
//
// //js生成文件hash编码并生成 rev-manifest.json文件名对照映射
// gulp.task('revJs', function(){
// return gulp.src(jsSrc)
// .pipe(rev())
// .pipe(gulp.dest('revDist/js'))
// .pipe(rev.manifest())
// .pipe(gulp.dest('revDist/js'));
// });
//
// //Html替换css、js文件版本
// gulp.task('revHtml', function () {
// return gulp.src(['revDist/**/*.json', 'dist/*.html'])
// .pipe(revCollector())
// .pipe(gulp.dest('revDist'));
// });
//
// //开发构建 代码压缩后 加hash 注意img文件夹和js/notchange 文件夹中的文件均不会进行更新!!!
// gulp.task('cacheCtr', function (done) {
// condition = false;
// runSequence(
// // ['assetRev'],
// ['revCss'],
// ['revJs'],
// ['revHtml'],
// done);
// });
// gulp gulp1 执行前应该先执行 default
// gulp.task('gulp1', ['cacheCtr']);
// // E6语法转换相关代码
//
// var babel = require("gulp-babel");
// var sourcemaps = require('gulp-sourcemaps');
//
// gulp.task('babelTest', function () {
// // 1、插入polyfill文件到html并测试 2、 实现类似于 browser-sync 功能 todo
// // var polyfill = './node_modules/babel-polyfill/dist/polyfill.js';
// // return gulp.src([polyfill,'es6/*.js'])
// // .pipe(sourcemaps.init())
// return gulp.src('es6/*.js')
// .pipe(sourcemaps.init())
// // .pipe(babel({presets: ['es2015']}))
// // .pipe(concat('all.min.js'))
// // .pipe(babel())
// .on('error', function (err) {
// console.error('sourcemaps相关程序出错了'+err)
// })
// // .pipe(babel())
// .pipe(babel({
// presets: ['@babel/env']
// // ,plugins: ['@babel/transform-runtime'] // 如果启用这个 则会在js中 采用require语法引用的方式 因此不适合
// // 另外 babel-runtime 可以部分取代babel-polyfill功能 但是像 [1,2,3].includes(1)实例方法 则不可替代
// }))
// .on('error', function (err) {
// console.error('babel({\n' +
// ' presets: [\'@babel/env\']\n' +
// ' })出错了'+err)
// })
// .pipe(sourcemaps.write('.'))
// // .pipe(uglify())
// .pipe(gulp.dest('es6/dist/'))
// .on('error', function (err) {
// console.error('babelTest出错了'+err)
// })
// });
// 一、gulp 启动服务并监测资源变化并自动刷新
// (一)、方法一:不使用browserSync的方法 缺点 不能启用两个
// var workSpaceDir='./es6';
// gulp.task('connectAllFile', function(){
// gulp.src(workSpaceDir+'/**/*')
// .pipe(plumber({
// errorHandler: function(error) {
// this.error('end');
// console.error('监听所有文件出错了'+error)
// }
// }))
// // .pipe(gulp.dest('dist/js'))
// .pipe(connect.reload())
// console.log('connectAllFile')
//
// })
// // gulp.task('ant_html', function(){
// // gulp.src('index.html')
// // .pipe(rev())
// // .pipe(gulp.dest('dist/html'))
// // .pipe(connect.reload())
// // })
// //选取谷歌浏览器
// var browser = os.platform() === 'linux' ? 'google-chrome' : (
// os.platform() === 'darwin' ? 'google chrome' : (
// os.platform() === 'win32' ? 'chrome' : 'firefox'));
//
// gulp.task('startServerAndOpenBrower', function() {
// connect.server({
// livereload: true,
// port: 8888,
// host:ipv4IpLocal()
// })
// gulp.src(__filename)
// .pipe(gulpOpen({
// uri: ipv4IpLocal()+':8888/'+workSpaceDir.slice(2),
// app: browser
// }));
// console.log('startServerAndOpenBrower执行了')
// })
// gulp.task('watchAllFile', function() {
// gulp.watch(workSpaceDir+'/**/*', ['connectAllFile'])
// // gulp.watch('src/js/*.js', ['js'])
// console.log('watchAllFile执行了')
//
// })
//
// gulp.task('default', [ 'connectAllFile', 'watchAllFile', 'startServerAndOpenBrower'])
// (二)、方法二:使用browserSync的方式
gulp.task('default', function() {
browserSync.init({
server: {
baseDir: workSpaceDir
},
open:'external',
files:[workSpaceDir+'/*.html',workSpaceDir+'/css/*.css',workSpaceDir+'/js/*.js'],
ghostMode: false // 禁止 点击,滚动和表单在任何设备上输入将被镜像到所有设备里
});
});
// 二、gulp build 依次实现 1、babel语法转换 2、加hash 3、压缩混淆 4、启动build后服务
// js sourcemap和babel及hash值
gulp.task('sourcemapBabelHashJs', function () {
console.log('开始压缩js');
// uglify 暂时不加
return gulp.src(workSpaceDir+'/js/*.js')
.pipe(sourcemaps.init())
.on('error', function (err) {
console.error('sourcemaps相关程序出错了'+err)
})
.pipe(babel({
presets: ['@babel/env']
// ,plugins: ['@babel/transform-runtime'] // 如果启用这个 则会在js中 采用require语法引用的方式 因此不适合
// 另外 babel-runtime 可以部分取代babel-polyfill功能 但是像 [1,2,3].includes(1)实例方法 则不可替代
}))
.on('error', function (err) {
console.error('babel({\n' +
' presets: [\'@babel/env\']\n' +
' })出错了'+err)
})
.pipe(sourcemaps.write('.'))
.on('error', function (err) {
console.error('sourcemaps.write出错了'+err)
})
.pipe(rev())
.on('error', function (err) {
console.error(' js加hash值出错了 pipe(rev())'+err)
})
.pipe(gulp.dest(distDir+'/js'))
.pipe(rev.manifest())
.on('error', function (err) {
console.error(' rev.manifest出错了'+err)
})
// .pipe(uglify())
// .on('error', function (err) {
// console.error(' js 压缩混淆出错了 pipe(uglify())'+err)
// })
.pipe(gulp.dest(distDir+'/js'));
// pump([
// gulp.src(workSpaceDir+'/js/*.js'),
// sourcemaps.init(),
// uglify(),
// // rename({suffix: '.min.'+curDateAndTime}),
// gulp.dest('./dist/js')
// ],
// cb
// );
});
// js 压缩
// 其执行的前提是sourcemapBabelHashJs先执行完了
gulp.task('sourcemapBabelHashJsThenUglifyJs', ['sourcemapBabelHashJs'], function () {
console.log('开始执行js压缩,不过执行前 要先执行sourcemapBabelHashJs')
return gulp.src(distDir+"/js/*.js")
.pipe(uglify())
.on('error', function (err) {
console.error(' js 压缩混淆出错了 pipe(uglify())'+err)
})
.pipe(gulp.dest(distDir+'/js/'));
})
gulp.task('remThenJrAndHashCss', ['cssREM'], function () {
console.log('开始执行css兼容程序')
// https://www.gulpjs.com.cn/docs/api/#gulp.task
// cssjr 是css 任务的依赖 因此它要通知css它什么时候执行完了
// 有三种方式 通知它执行完了 1、回调 2、返回数据流stream 3、返回promise
var stream = gulp.src(workSpaceDir+'/css/rem/*.css')
.pipe(autoprefixer({
browsers: ['since 2010'],
cascade: false
}))
.on('error', function (err) {
console.error(' css兼容处理出错了 autoprefixer'+err)
})
.pipe(rev())
.on('error', function (err) {
console.error(' css hash处理出错了 .pipe(rev())'+err)
})
.pipe(gulp.dest(distDir+'/css/jianrong'))
.pipe(rev.manifest())
.on('error', function (err) {
console.error(' css hash manifest.json处理出错了 rev.manifest'+err)
})
// .pipe(rename({
// suffix: ".jr"
// }))
.pipe(gulp.dest(distDir+'/css/'));
console.log(' css兼容程序在执行中 stream ' + stream)
return stream;
})
// css 加前缀及hash
gulp.task('jrAndHashCss', function () {
console.log('开始执行css兼容程序')
// https://www.gulpjs.com.cn/docs/api/#gulp.task
// cssjr 是css 任务的依赖 因此它要通知css它什么时候执行完了
// 有三种方式 通知它执行完了 1、回调 2、返回数据流stream 3、返回promise
var stream = gulp.src(workSpaceDir+'/css/*.css')
.pipe(autoprefixer({
browsers: ['since 2010'],
cascade: false
}))
.on('error', function (err) {
console.error(' css兼容处理出错了 autoprefixer'+err)
})
.pipe(rev())
.on('error', function (err) {
console.error(' css hash处理出错了 .pipe(rev())'+err)
})
.pipe(gulp.dest(distDir+'/css/jianrong'))
.pipe(rev.manifest())
.on('error', function (err) {
console.error(' css hash manifest.json处理出错了 rev.manifest'+err)
})
// .pipe(rename({
// suffix: ".jr"
// }))
.pipe(gulp.dest(distDir+'/css/'));
console.log(' css兼容程序在执行中 stream ' + stream)
return stream;
});
// css 压缩
gulp.task('jrAndHashCssThenCompressionCss', ['jrAndHashCss'], function () {
// 执行该css任务的前提是 执行完了jrAndHashCss (jrAndHashCss是css 的依赖)
//
console.log('开始执行css压缩')
return gulp.src(distDir+"/css/jianrong/*.css")
.pipe(cleanCSS({compatibility: 'ie8'}))
.on('error', function (err) {
console.error(' css压缩处理出错了 cleanCSS'+err)
})
// .pipe(rename({
// suffix: ".min."+curDateAndTime
// }))
.pipe(gulp.dest(distDir+'/css/'));
});
gulp.task('jrAndHashCssThenCompressionCssREM', ['remThenJrAndHashCss'], function () {
// 执行该css任务的前提是 执行完了jrAndHashCss (jrAndHashCss是css 的依赖)
//
console.log('开始执行css压缩')
return gulp.src(distDir+"/css/jianrong/*.css")
.pipe(cleanCSS({compatibility: 'ie8'}))
.on('error', function (err) {
console.error(' css压缩处理出错了 cleanCSS'+err)
})
// .pipe(rename({
// suffix: ".min."+curDateAndTime
// }))
.pipe(gulp.dest(distDir+'/css/'));
});
// html hash
gulp.task('compressionHtmlThenHashHtml',['compressionHtml'], function () {
return gulp.src([ distDir+'/**/*.json', distDir+'/*.html'])
.pipe(revCollector())
.pipe(gulp.dest(distDir));
});
// html 压缩
gulp.task('compressionHtml', function () {
return gulp.src(workSpaceDir+"/*.html")
.pipe(htmlmin({collapseWhitespace: true}))
.pipe(gulp.dest(distDir));
});
// 清空文件夹
gulp.task("cleanHTMLCssJsImg", function(){
console.log('开始清空dist文件夹');
return gulp.src([distDir+'/*.html',distDir+'/css/*',distDir+'/js/*',distDir+'/img/*'])
.pipe(clean());
});
// 在'))
// .on('error', function (err) {
// console.error('injectJS相关程序出错了--file(distDir+\'/index.html\''+err)
// })
.pipe(inject(gulp.src([distDir+'/js/notchange/polyfill.min.js']), {
starttag: '',
endtag: '
补充
模版管理
一、和脚手架耦合(@umijs/create-umi-app)
二、基于github或者gitlab(cra、vue-cliinit)
三、基于npm(cra、react-native、tuyaworks)
模版解析
一、metalsmith、handlebars、consolidate实现
二、node-plob小而美的脚手架工具
三、基于handlebars、recursive-readdir和glob自己实现
参考资料:
1、仿vue-cli搭建属于自己的脚手架的方法步骤
2、前端工程化之plop的使用
3、https://my.oschina.net/u/3347851/blog/5146718