gulp+webpack多页应用开发,webpack仅处理打包js
项目背景:一个综合网站,开发模式为后端嵌套数据,前端开发静态页面和部分组件。
问题:gulp任务处理自动刷新、sass编译等都是极好的。但是对于js的处理并不是很好,尤其是项目需要开发组件时候,如评论组件,需要有模版、css、js[各个模块]。这时候选择用gulp感觉并不合适,当然可以选择require.js or seajs等AMD/CMD规范来开发,但是想想项目中的组件应该是独立于项目之外的,不依赖于任何第三方js,因此选择去折腾webpack+gulp来搞。几番折腾、百度之后,配置如下:
package.js
{ "name": "work", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "autoprefixer": "^6.3.6", "autoprefixer-core": "^6.0.1", "babel-core": "^6.24.1", "babel-loader": "^7.0.0", "babel-preset-es2015": "^6.24.1", "browser-sync": "^2.13.0", "cssgrace": "^3.0.0", "cssnext": "^1.8.4", "del": "^2.2.1", "extract-text-webpack-plugin": "^2.1.0", "fs": "^0.0.2", "gulp": "^3.9.1", "gulp-babel": "^6.1.2", "gulp-concat": "^2.6.0", "gulp-htmlmin": "^2.0.0", "gulp-imagemin": "^3.0.1", "gulp-jshint": "^2.0.1", "gulp-less": "^3.1.0", "gulp-livereload": "^3.8.1", "gulp-minify-css": "^1.2.4", "gulp-notify": "^2.2.0", "gulp-plumber": "^1.1.0", "gulp-postcss": "^6.1.1", "gulp-rename": "^1.2.2", "gulp-sass": "^2.3.2", "gulp-uglify": "^1.5.3", "gulp-util": "^3.0.7", "gulp-watch": "^4.3.8", "gulp-webpack": "^1.5.0", "imagemin-jpegtran": "^5.0.2", "imagemin-pngcrush": "^5.0.0", "jshint": "^2.9.2", "json-loader": "^0.5.4", "node-sass": "^4.5.2", "path": "^0.12.7", "run-sequence": "^1.2.1", "through2": "^2.0.1", "webpack": "^2.4.1" } }
package中有部分插件并未使用,自行选择安装。
gulp配置[gulpfile.js],关于gulp使用请参考这篇文章
1 var gulp = require("gulp") 2 , gutil = require("gulp-util") 3 4 , del = require("del") 5 , sass = require("gulp-sass") 6 , uglify = require('gulp-uglify') 7 , rename = require("gulp-rename") 8 9 , browserSync = require("browser-sync").create() 10 , reload = browserSync.reload 11 12 , sequence = require("run-sequence") 13 , plumber = require("gulp-plumber") 14 , watch = require("gulp-watch") 15 16 , through2 = require("through2") 17 , path = require("path") 18 , fs = require("fs") 19 , minifycss = require('gulp-minify-css') 20 , postcss = require('gulp-postcss') 21 , autoprefixer = require('autoprefixer') // Autoprefixer 为CSS补全浏览器前缀 22 , cssnext = require('cssnext') // CSSNext 用下一代CSS书写方式兼容现在浏览器 23 , cssgrace = require('cssgrace') // CSS Grace 让CSS兼容旧版IE 24 , webpack = require('webpack'); 25 26 // ############################################# 27 // # init params 28 // 收集参数 29 var cwd = process.cwd(); 30 var cmdargs = process.argv.slice(2); 31 var cmdname = cmdargs.shift(); 32 var cmdopts = {}; 33 var srcpath = "./src"; 34 var distpath = "./dist"; 35 36 while (cmdargs.length) { 37 var key = cmdargs.shift().slice(2); 38 var val = cmdargs.shift(); 39 cmdopts[key] = key === "src" || key === "dist" ? normalizePath(val) : val; 40 } 41 42 // 参数配置 43 var release = cmdname === "release"; 44 var reloadTimer = null; 45 var devport = 5678; 46 var paths = { 47 src: path.join(__dirname, srcpath), 48 dist: path.join(__dirname, distpath) 49 } 50 51 function normalizePath(url) { 52 if (url.charAt(0) === "/" || url.indexOf(":") > -1) { 53 return path.normalize(url); 54 } 55 return path.normalize(path.join(cwd, url)); 56 } 57 58 function setOptions(cmd, cmdopts) { 59 if (cmd === "start") { 60 paths.src = cmdopts.src ? path.join(cmdopts.src, srcpath) : paths.src; 61 } else if (cmd === "release") { 62 paths.src = cmdopts.src ? path.join(cmdopts.src, srcpath) : paths.src; 63 paths.dist = cmdopts.dist ? cmdopts.dist : path.normalize(paths.src + "/../" + distpath); 64 } 65 } 66 67 function showUsage() { 68 console.log("Usage:\n"); 69 console.log(" gulp 显示帮助"); 70 console.log(" gulp help 显示帮助"); 71 console.log(" gulp start --src src 在--src目录下自动化开发调试环境"); 72 console.log(" gulp release --src src --dist dist 构建--src线上版本到--dist目录\n"); 73 console.log(" gulp start --src src --proxy localhost 使用gulp代理localhost请求,并且实时监听src文件修改"); 74 } 75 76 // ############################################# 77 // # default tasks 78 79 // # clean path 80 gulp.task("clean:dist", function() { 81 return del([paths.dist], { 82 force: true 83 }); 84 }); 85 86 // # 编译css 87 gulp.task("sass", function() { 88 var base = paths.src; 89 var dest = base; 90 var processors = [ 91 autoprefixer({ 92 browsers: ['last 3 version', '> 5%', 'Android >= 4.0', 'iOS >= 7'], 93 cascade: false, //是否美化属性值 默认:true 像这样: 94 remove: true //是否去掉不必要的前缀 默认:true 95 }), 96 // cssnext(), 97 // cssgrace 98 ]; 99 return gulp.src(base + "/**/*.scss", { 100 base: base 101 }) 102 .pipe(plumber()) 103 .pipe(sass({ 104 precision: 2, 105 outputStyle: release ? "compressed" : "expanded" 106 //sourceComments: release ? false : true 107 }) 108 .on("error", sass.logError)) 109 .pipe(postcss(processors)) 110 .pipe(gulp.dest(dest)); 111 }); 112 // 编译单个css 113 function parseSingleFile(file) { 114 var base = paths.src; 115 var dest = base; 116 var processors = [ 117 autoprefixer({ 118 browsers: ['last 3 version', '> 5%', 'Android >= 4.0', 'iOS >= 7'], 119 cascade: false, //是否美化属性值 默认:true 像这样: 120 remove: true //是否去掉不必要的前缀 默认:true 121 }), 122 // cssnext(), 123 // cssgrace 124 ]; 125 return gulp.src(file, { 126 base: base 127 }) 128 .pipe(plumber()) 129 .pipe(sass({ 130 precision: 2, 131 outputStyle: release ? "compressed" : "expanded" 132 //sourceComments: release ? false : true 133 }) 134 .on("error", sass.logError)) 135 .pipe(postcss(processors)) 136 .pipe(gulp.dest(dest)); 137 } 138 139 140 // # 压缩js 141 gulp.task("uglify", function() { 142 var base = paths.src; 143 var dest = paths.dist; 144 return gulp.src([ 145 base + "/**/*.js", 146 "!" + base +"/**/*-component/**", 147 "!" + base + "/**/*min.js" // 排除压缩min.js文件 148 ], { 149 base: base 150 }) 151 .pipe(plumber()) 152 .pipe(uglify()) 153 .pipe(gulp.dest(dest)); 154 }); 155 156 // # 调用webpack处理component 157 function parseComponentToWebpack(path){ 158 var webpackConfig = require('./webpack.config.js'); 159 // 灵活处理output位置,将js文件生成在component同级目录 160 // 将**/*-component/xx.js路径替换为** 161 // var outputPath = path.replace(/\/[^\/]+\/[^\/]+$/,''); 162 var outputPath = path.replace(/\/([^\/]+)-component\/[^\/]+$/,''); 163 var filename = RegExp.$1; 164 webpackConfig.entry = {}; 165 webpackConfig.entry[filename] = path+'/../index.js'; 166 // windows上webpack不认识D:/xxx路径,替换为D:\\xxx路径 167 outputPath = outputPath.replace('/','\\\\'); 168 webpackConfig.output.path = outputPath; 169 return webpack(webpackConfig, function(err, stats) { 170 console.log(stats.toString()); 171 }); 172 } 173 174 175 // # 压缩css 176 gulp.task("mincss", function() { 177 var base = paths.src; 178 var dest = paths.dist; 179 gulp.src(dest + '/**/*.css') 180 .pipe(minifycss()) 181 .on('error', function(e) { 182 console.log(e) 183 }) 184 .pipe(gulp.dest(dest)); 185 }); 186 187 // # 复制静态资源 188 gulp.task("copy:dist", function() { 189 var base = paths.src; 190 var dest = paths.dist; 191 // 复制min.js文件 192 gulp.src([ 193 base + "/**/*min.js" 194 ], { 195 base: base 196 }) 197 .pipe(gulp.dest(dest)); 198 return gulp.src([ 199 base + "/**/*", 200 "!" + base +"/**/*-component", 201 "!" + base +"/**/*-component/**", 202 "!" + base + "/**/*.js", 203 "!" + base + "/**/*.scss" 204 ], { 205 base: base 206 }) 207 .pipe(gulp.dest(dest)); 208 }); 209 210 211 // # serv & watch 212 gulp.task("server", function() { 213 // start server 214 browserSync.init({ 215 ui: false, 216 notify: false, 217 port: devport, 218 // 设置代理请求 219 proxy: cmdopts.proxy, 220 server: !cmdopts.proxy ? { 221 baseDir: paths.src 222 } : false 223 }); 224 225 // # watch src资源, 调用相关任务预处理 226 // # 刷新浏览器 227 // # 限制浏览器刷新频率 228 watch(paths.src + "/**/*", function(obj) { 229 var url = obj.path.replace(/\\/g, "/"); 230 var absurl = url; 231 url = path.relative(paths.src, url).replace(/\\/g, "/"); 232 console.log("[KS] " + absurl); 233 234 // skip scss & css 235 if (!/\.scss$/.test(url) && !/\.css$/.test(url)) { 236 if (/.+-component\/.+\.js$/.test(url)) { 237 // 评论组件,调用webpack 238 console.log("[webpack] "+absurl); 239 parseComponentToWebpack(absurl); 240 return; 241 } 242 if (reloadTimer) { 243 clearTimeout(reloadTimer); 244 } 245 reloadTimer = setTimeout(reload, 1000); 246 }else if(/\.scss$/.test(url)){ 247 // sass任务 248 parseSingleFile(absurl) 249 // 无刷新加载css 250 .pipe(reload({ 251 stream: true 252 })); 253 // sequence("sass"); 254 } 255 }); 256 }); 257 258 259 // ############################################# 260 // # public task 261 262 gulp.task("default", showUsage); 263 gulp.task("help", showUsage); 264 265 gulp.task("start", function(cb) { 266 release = false; 267 setOptions("start", cmdopts); 268 sequence("sass", "server", cb); 269 }); 270 271 gulp.task("release", function(cb) { 272 release = true; 273 setOptions("release", cmdopts); 274 sequence("clean:dist", "copy:dist", ["mincss", "uglify"], cb); 275 });
webpack配置(webpack.config.js),webpack配置参考
1 var webpack = require('webpack'); 2 3 module.exports = { 4 // devtool: 'eval-source-map', 5 devtool: false, 6 entry: { 7 "component-comment": __dirname + "/src/assets/comment/component/index.js" 8 }, 9 output: { 10 // path: __dirname + "/src/assets/comment", 11 path: __dirname + "/dist/component", 12 filename: "[name].js" 13 }, 14 15 module: { 16 rules: [{ 17 test: /\.json$/, 18 loader: "json-loader" 19 }, 20 { 21 test: /\.js$/, 22 exclude: /node_modules/, 23 loader: 'babel-loader', //在webpack的module部分的loaders里进行配置即可 24 /*query: { 25 presets: ['es2015'] 26 }*/ 27 query: { 28 presets: [ 29 ['es2015', { 30 'modules': false //babel不编译es6的模块加载,让webpack支持Tree-shaking 31 }] 32 ] 33 } 34 }, 35 ] 36 }, 37 38 }
以上配置webpack将单独处理component目录中的js文件,并在component同级目录生成文件夹相同名字的js文件(如:'./component-comment-component/' 文件夹对应的js文件为 './component-comment.js')。
以下是我的文件结构
1 src 2 app 3 *.html //html目录 4 assets //静态资源目录 5 component-comment-component //评论组件 6 images //评论组件所用图片 7 component //组件模块 8 common.js //公用方法,如ajax、jsonp、extend等方法 9 defaultConfig.js //默认配置 10 emoticon.js //表情模块,如获取服务端表情资源,渲染表情等 11 index.js //主模块,如获取评论、发表评论、点赞、回复、举报等 12 selector.js //选择器方法,模拟jQuery封装 13 template.js //模版 14 component-comment.js //webpack编译component目录的js 15 component-comment.scss //评论样式表 16 component-comment.css //编译的sass文件 17 css //项目其他sass|css资源 18 images //项目图片文件 19 js //项目的其他js
以上只是一个简单的使用案例,针对不同结构需要作出不同调整。