本文将介绍如何将基于WP的项目,结合前端工作流tmt-workflow,实现开发、体验、部署上线流程,流程简单,自定义程度高。
tmt-workflow
是一个基于 Gulp(v4.0)、高效、跨平台(macOS & Win)、可定制的前端工作流程。
介绍文章:tmt-workflow前端工作流
themes/ │ ├─── _tasks/ // tmt-workflow自带 Gulp 任务目录 │ ├── TaskBuildDev.js // gulp build_dev │ ├── TaskBuildDist.js // gulp build_dist │ │ ├──── xuanfeng-v3.0 // pc版主题 │ ├── _tasks/ │ │ ├── TaskBuildDev.js // BuildDevAll 本地开发 │ │ ├── TaskBuildDist.js // BuildDistAll 打包构建 │ │ └── TaskBuildPub.js // BuildPubAll 上线发布 │ ├── dev/ // dev 本地构建目录 │ ├── dist/ // dist 构建最终目录 │ ├── src/ // 源代码目录 │ │ ├── css/ // less源文件 │ │ ├── html/ // html静态页面 │ │ ├── js/ // js源文件 │ │ ├── img/ // 图片目录 │ │ ├── slice/ // 雪碧图目录 │ │ ├── fonts/ // 代码高亮字体目录 │ │ ├── media/ // 钢琴导航声音文件 │ │ ├── index.php // php源文件,修改后同步至外层 │ │ ├── xxx.php │ │ │ ├── gulpfile.js // gulp配置文件 │ ├── index.php // src目录生成的php文件 │ ├── xxx.php // php文件使用于主题 │ │ ├──── xuanfeng-v3.0-m // 移动端主题,目录结构同上 │ │──── node_modules/ // node包文件,不同主题共用 ├──── package.json // tmt package配置 └──── server.json // 服务器信息配置
每个项目下都有对应的_tasks目录,用于自定义构建,因为tmt-workflow只能满足基本需求,如果情况需要特殊处理:
./css
、./img
、./js
var del = require('del'); var ejs = require('gulp-ejs'); var less = require('gulp-less'); var gulpif = require('gulp-if'); var util = require('../../_tasks/lib/util'); var ejshelper = require('tmt-ejs-helper'); var bs = require('browser-sync').create(); // 自动刷新浏览器 var lazyImageCSS = require('gulp-lazyimagecss'); // 自动为图片样式添加 宽/高/background-size 属性 var postcss = require('gulp-postcss'); // CSS 预处理 var postcssPxtorem = require('postcss-pxtorem'); // CSS 转换 `px` 为 `rem` var posthtml = require('gulp-posthtml'); // HTML 预处理 var posthtmlPx2rem = require('posthtml-px2rem'); // HTML 内联 CSS 转换 `px` 为 `rem` var replace = require('gulp-replace'); var localhost = getLocalHost(); var paths = { src: { dir: './src', img: './src/img/**/*.{JPG,jpg,png,gif}', slice: './src/slice/**/*.png', js: './src/js/**/*.js', media: './src/media/**/*', fonts: './src/fonts/**/*', less: './src/css/style-*.less', lessAll: './src/css/**/*.less', html: ['./src/html/**/*.html', '!./src/html/_*/**.html', '!./src/html/_*/**/**.html'], htmlAll: './src/html/**/*.html', php: './src/*.php', phpAll: './src/*.php' }, dev: { dir: './dev', css: './dev/css', html: './dev/html', php: './' } }; function getLocalHost(){ var interfaces = require('os').networkInterfaces(); for(var devName in interfaces){ var iface = interfaces[devName]; for(var i=0;i<iface.length;i++){ var alias = iface[i]; if(alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal){ return 'http://' + alias.address + ':8080'; } } } } module.exports = function (gulp, config) { var lazyDir = config.lazyDir || ['../slice']; // 复制操作 var copyHandler = function (type, file) { file = file || paths['src'][type]; return gulp.src(file, {base: paths.src.dir}) .pipe(gulp.dest(paths.dev.dir)) .on('end', reloadHandler); }; // 自动刷新 var reloadHandler = function(){ config.livereload && bs.reload(); }; // 清除目标目录 function delDev() { return del([paths.dev.dir]); } function deletePhp() { return del(['./*.php', './*.html']); } // 复制操作 start function copyImg() { return copyHandler('img'); } function copySlice() { return copyHandler('slice'); } function copyJs() { return copyHandler('js'); } function copyMedia() { return copyHandler('media'); } function copyFonts() { return copyHandler('fonts'); } function copyPhp(file) { return gulp.src(file) .pipe(gulp.dest(paths.dev.php)) .on('end', reloadHandler); } // 复制操作 end // 编译 less function compileLess() { return gulp.src(paths.src.less) .pipe(less()) .on('error', function (error) { console.log(error.message); }) .pipe(gulpif( config.supportREM, postcss([ postcssPxtorem({ root_value: '20', // 基准值 html{ font-size: 20px; } prop_white_list: [], // 对所有 px 值生效 minPixelValue: 2 // 忽略 1px 值 }) ]) )) .pipe(lazyImageCSS({imagePath: lazyDir})) .pipe(gulp.dest(paths.dev.css)) .on('data', function () { }) .on('end', reloadHandler) } // 编译 html function compileHtml() { return gulp.src(paths.src.html) .pipe(ejs(ejshelper()).on('error', function (error) { console.log(error.message); })) .pipe(gulpif( config.supportREM, posthtml( posthtmlPx2rem({ rootValue: 20, minPixelValue: 2 }) )) ) .pipe(gulp.dest(paths.dev.html)) .on('data', function () { }) .on('end', reloadHandler) } // 编译 php function compilePhp() { return gulp.src(paths.src.phpAll) .on('error', function (error) { console.log(error.message); }) .pipe(replace(/\.\/(css|js|img)/g, "<?php bloginfo('template_url'); ?>/dev/$1")) .pipe(gulp.dest(paths.dev.php)) .on('data', function () { }) .on('end', reloadHandler) } // 启动 livereload function startServer() { bs.init({ server: paths.dev.dir, port: config['livereload']['port'] || 8080, startPath: config['livereload']['startPath'] || '/html', reloadDelay: 0, notify: { //自定制livereload 提醒条 styles: [ "margin: 0", "padding: 5px", "position: fixed", "font-size: 10px", "z-index: 9999", "bottom: 0px", "right: 0px", "border-radius: 0", "border-top-left-radius: 5px", "background-color: rgba(60,197,31,0.5)", "color: white", "text-align: center" ] } }); } var watchHandler = function (type, file) { file = file.replace(/\\/g,'/'); var target = ''; if(file.match(/^src[\/|\\](.*?)[\/|\\]/)){ target = file.match(/^src[\/|\\](.*?)[\/|\\]/)[1]; }else if(/\.php/.test(file)){ target = 'php'; } switch (target) { case 'img': if (type === 'removed') { var tmp = file.replace('src/', 'dev/'); del([tmp]); } else { copyHandler('img', file); } break; case 'slice': if (type === 'removed') { var tmp = file.replace('src/', 'dev/'); del([tmp]); } else { copyHandler('slice', file); } break; case 'js': if (type === 'removed') { var tmp = file.replace('src/', 'dev/'); del([tmp]); } else { copyHandler('js', file); } break; case 'media': if (type === 'removed') { var tmp = file.replace('src/', 'dev/'); del([tmp]); } else { copyHandler('media', file); } break; case 'css': if (type === 'removed') { var tmp = file.replace('src/', 'dev/').replace('.less', '.css'); del([tmp]); } else { compileLess(); } break; case 'html': if (type === 'removed') { var tmp = file.replace('src/', 'dev/'); del([tmp]).then(function () { util.loadPlugin('BuildDev'); }); } else { compileHtml(); } if (type === 'add') { setTimeout(function () { util.loadPlugin('BuildDev'); }, 500); } break; case 'php': if (type === 'removed') { var tmp = file.replace('src/', './'); del([tmp]).then(function () { util.loadPlugin('BuildDev'); }); } else if (type === 'add') { copyPhp(file); } else { compilePhp(); } break; } }; // 监听文件 function watch(cb) { var watcher = gulp.watch([ paths.src.img, paths.src.slice, paths.src.js, paths.src.media, paths.src.lessAll, paths.src.htmlAll, paths.src.phpAll ], {ignored: /[\/\\]\./} ); watcher .on('change', function (file) { util.log(file + ' has been changed'); watchHandler('changed', file); }) .on('add', function (file) { util.log(file + ' has been added'); watchHandler('add', file); }) .on('unlink', function (file) { util.log(file + ' is deleted'); watchHandler('removed', file); }); cb(); } // 加载插件 function loadPlugin(cb) { util.loadPlugin('build_dev'); cb(); } // 注册 build_dev 任务 gulp.task('BuildDevAll', gulp.series( delDev, deletePhp, gulp.parallel( copyImg, copySlice, copyJs, copyMedia, copyFonts, compileLess, compileHtml, compilePhp ), gulp.parallel( watch, loadPlugin ), startServer )); };
var webpack = require('webpack-stream'); var del = require('del'); var watch = require('gulp-watch'); var fs = require('fs'); var path = require('path'); var RevAll = require('gulp-rev-all'); var revDel = require('gulp-rev-delete-original'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var replace = require('gulp-replace'); var minifyHTML = require('gulp-minify-html'); var wrap = require("gulp-wrap"); // 目录结构 var paths = { src: { dir: './src', html: './src/*.html', js: ['./src/js/**/*.js'], php: './src/*.*', fonts: './src/fonts/**', indexJs: ['./src/js/lib/lazyload.js', './src/js/lib/template.js', './src/js/lib/sns_share.js', './src/js/piano-play.js', './src/js/piano.js', './src/js/public.js'], singleJs: ['./src/js/lib/jquery.nav.js', './src/js/lib/animatescroll.js'] }, dist: { dir: './dist/', js : './dist/js/', css: './dist/css/', html: './dist/html/', php: './dist/*.*' }, publish:{ js: "<?php bloginfo(\'template_url\'); ?>/dist/js/", css: "<?php bloginfo(\'template_url\'); ?>/dist/css/", img: "<?php bloginfo(\'template_url\'); ?>/dist/img/", login: '" . get_bloginfo("template_url") . "/dist/css/' } }; // 文件版本号处理 var revAll = new RevAll({ debug: false, dontRenameFile: ['.html', '.php', '.mp3', '.ogg'], replacer: function(fragment, replaceRegExp, newReference, referencedFile) { if(regStr.indexOf('.js') > -1){ newReference = newReference.replace(/(.\/)*js\//,''); var _content = '$1' + paths.publish.js + newReference + '$3$4'; fragment.contents = fragment.contents.replace(replaceRegExp, _content); } else if(regStr.indexOf('.css') > -1){ newReference = newReference.replace(/(.\/)*css\//,''); // 登录页面样式文件处理 if(regStr.indexOf('style\\-login') > -1){ fragment.contents = fragment.contents.replace(replaceRegExp, '$1' + paths.publish.login + newReference + '$3$4'); }else{ fragment.contents = fragment.contents.replace(replaceRegExp, '$1' + paths.publish.css + newReference + '$3$4'); } } else if((regStr.indexOf('.jpg') > -1 || regStr.indexOf('.png') > -1) && !/background/gi.test(fragment.contents)){ newReference = newReference.replace(/(.\/)*img\//,''); fragment.contents = fragment.contents.replace(replaceRegExp, '$1' + paths.publish.img + newReference + '$3$4'); } fragment.contents = fragment.contents.replace(replaceRegExp, '$1' + newReference + '$3$4'); }, hashLength: 8, }); module.exports = function (gulp, config) { function delDist(){ return del([paths.dist.dir]); } // 复制php等到dev目录 function copyPhp(){ return gulp.src(paths.src.php) // .pipe(minifyHTML({ // // quotes: true, // // conditionals: true, // IE // comments: false, // // empty: true, // // spare: true, // })) .pipe(gulp.dest(paths.dist.dir)); } // 复制终php至主题目录 function copyPhpDist(){ return gulp.src('./dist/*.*') .pipe(gulp.dest('./')); } function copyFonts() { return gulp.src(paths.src.fonts) .pipe(gulp.dest('./dist/fonts/')); } // 合并压缩js function concatIndexJs(cb){ gulp.src(paths.src.indexJs) .pipe(concat('index.js')) .pipe(uglify()) .pipe(gulp.dest(paths.dist.js)) .on('end', function(){ cb(); }); } function concatSingleJs(cb){ gulp.src(paths.src.singleJs) .pipe(concat('single.js')) .pipe(uglify()) .pipe(gulp.dest(paths.dist.js)) .on('end', function(){ cb(); }); } // 删除重复的PHP文件 function delPhpDist(){ return del([paths.dist.php]); } //注册 BuildDistAll 任务 gulp.task('BuildDistAll',gulp.series( delDist, gulp.parallel( 'build_dist', copyPhp, copyFonts, concatIndexJs, concatSingleJs ), function(cb){ gulp.src(['./dist/**/*', '!**/*.html']) .pipe(revAll.revision()) .pipe(gulp.dest('./dist')) .pipe(revDel({ exclude: /(.html|.htm|.php)$/ })) .pipe(gulp.dest('./dist')) .pipe(revAll.manifestFile()) .pipe(gulp.dest('./dist')) .on('end', function () { cb(); }); }, copyPhpDist, delPhpDist )); }
var GulpSSH = require('gulp-ssh'); var replace = require('gulp-replace'); var gutil = require('gulp-util'); var ftpConfig = require('../../server.json').publish; var vftp = require('vinyl-ftp'); // 目录结构 var paths = { dist: { dir: './dist/', php : './*.*', src: './src/**', dist: './dist/**' }, publish:{ dir: '/domains/xuanfengge.com/public_html/wp-content/themes/xuanfeng-3.0/', php: '/domains/xuanfengge.com/public_html/wp-content/themes/xuanfeng-3.0/', src: '/domains/xuanfengge.com/public_html/wp-content/themes/xuanfeng-3.0/src/', dist: '/domains/xuanfengge.com/public_html/wp-content/themes/xuanfeng-3.0/dist/' } }; // ftp配置 var deploy = vftp.create({ host: ftpConfig.host, user: ftpConfig.user, password: ftpConfig.password, port: 21, parallel: 10, log: gutil.log, }); // 发布资源 module.exports = function (gulp, config) { // 复制dist资源目录 function copyDist(){ return gulp .src(paths.dist.dist) .pipe(deploy.newer(paths.publish.dist)) .pipe(deploy.dest(paths.publish.dist)); } // 复制php function copyPhp(){ return gulp.src(paths.dist.php) .pipe(deploy.newer(paths.publish.php)) .pipe(deploy.dest(paths.publish.php)); } // 复制src目录 function copySrc(){ return gulp .src(paths.dist.src) .pipe(deploy.newer(paths.publish.src)) .pipe(deploy.dest(paths.publish.src)); } // 强制更新版本号 function copyVersion(){ return gulp .src(['./footer.php', './header.php']) .pipe(deploy.dest(paths.publish.php)); } //注册 BuildPublishAll 任务 gulp.task('BuildPubAll', gulp.series( 'BuildDistAll', copyDist, copyPhp, copySrc, copyVersion )); }
增量发布使用vinyl-ftp
提供的newer
方法判断文件是否已更新,是否进行上传替换。