这是我参与更文挑战的第19天,活动详情查看: 更文挑战
前言
在上一篇文章 gulp 的使用(五):处理图片 里,介绍了如何使用 gulp 处理图片。
在这篇文章里,将介绍在使用 gulp 的同时,如何去使用 webpack 去处理模块化(当然也可以利用 webpack 做其他事情)。
没错,gulp 与 webpack 皆可兼得,我全都要!!!
但是要注意,虽然可以这样做,但我并不建议,如果项目需要模块化,不要用 gulp,请直接使用 webpack !!!
使用 webpack 流
在第一篇文章 gulp 的使用(一):起步 里,我就已经说过了 gulp 自身无法处理模块化,它只是一个基于流的自动化构建工具,如果要处理模块化,它可以利用 webpack 来处理模块化部分。
webpack-stream 其实就是一个 webpack,不过它与普通的 webpack 不同,它是以流的形式存在,与 gulp 集成。
安装 webpack-stream
npm i -D webpack-stream
复制代码
在项目根目录里创建 webpack 配置文件 webpack.config.js,写上以下基础代码。
console.log('利用了 webpack !!!')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
module: {
rules: [
]
},
resolve: {
alias: {
}
},
}
复制代码
在 gulpfile.js 引入 webpack-stream 和 webpack.config.js,并且修改 js 任务。
const webpack = require('webpack-stream')
const webpackConfig = require("./webpack.config.js")
...
function js() {
return src(['src/js/**/*.js'])
.pipe(changed('dist/js/**/'))
.pipe(webpack(webpackConfig))
.pipe(plumber())
.pipe(uglify())
.pipe(dest('dist/js'))
}
复制代码
我们再在 src/js 目录创建 utils.js,写上以下测试代码。
export default {
test() {
console.log('测试模块化成功!!!')
}
}
复制代码
在 index.js 使用 es6 的 import 引入 utils.js。
function print() {
console.log('测试')
}
print()
import utils from './utils'
utils.test()
复制代码
运行 npm run dev
,打开网站,打开控制台。
咦?为什么没有值?不单止是 utils.test() 的内容没有输出来,index.js 自身的 console.log 的内容也没有输出来。
我们再看看终端,webpack 的配置确实是被加载了。
别急,我们再看看 dist/js 目录,可以发现原本应该命名为 index.js 的文件,被打包成了以一串随机字符串为文件名的文件,而我们的 index.html 引用的是以 index.js 为文件名的文件。
而且你会发现无论你的项目里有多少个 js,打包出来的始终只有一个 js,那是因为 webpack 在默认配置里使用的是单页面模式去打包。
熟悉 webpack 的小伙伴可能会说,这是因为我没有配置 webpack 的 entry 和 output,的确我没有进行配置,这也是一种方法,也有另一种方法可以解决,各有优缺点,下面逐一介绍。
如何开发多页面
方法1:配置 webpack 的 entry 和 output
我们只要在 webpack.config.js 配置上 entry 和 output 就可以了。
console.log('利用了 webpack !!!')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: {
index: path.resolve(__dirname, './src/js/index.js'),
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, './dist'),
},
module: {
rules: [
]
},
resolve: {
alias: {
}
},
}
复制代码
不过,你有没有发现如果这样做,以后页面每需要引入一个 js,都需要终端当前的 dev 服务,再在 webpack 里写好新加入的 entry,然后再启动 dev 服务测试。
也许你会想到可以用以下代码进行监听 webpack 配置,每当 webpack 配置被修改,都会去执行 js 任务。
watch('webpack.config.js', series(js))
复制代码
不过经过实际试验,这是行不通的,webpack 配置只有第一次任务执行才会被加载。
那么该怎么办呢?我们可以使用另一种方法
方法2:使用 vinyl-named 插件
vinyl-named 插件可以解决多页面开发的问题。
安装 vinyl-named
npm i -D vinyl-named
复制代码
配置 gulpfile.js,引入 vinyl-named。
const named = require('vinyl-named')
const path = require('path')
...
function js() {
return src(['src/js/**/*.js'])
.pipe(changed('dist/js/**/'))
.pipe(named(function (file) {
return file.relative.slice(0, -path.extname(file.path).length)
}))
.pipe(webpack(webpackConfig))
.pipe(plumber())
.pipe(uglify())
.pipe(dest('dist/js'))
}
复制代码
运行 npm run dev
,当我们在 src/js 里新增 js 时,你会发现 dist/js 目录里会自动生成对应的 js 文件。
注意,不要直接写上 named()
,这样写的话,如果 js 有一个目录 aa,aa 目录里面有一个 aa.js,打包出来的 aa.js 是位于 dist/js/aa.js,而不是在 dist/js/aa/aa.js,少了一个 aa 目录
function js() {
return src(['src/js/**/*.js'])
.pipe(changed('dist/js/**/'))
/*.pipe(named(function (file) {
return file.relative.slice(0, -path.extname(file.path).length)
})) */
.pipe(named()) // 不要这样写
.pipe(webpack(webpackConfig))
.pipe(plumber())
.pipe(uglify())
.pipe(dest('dist/js'))
}
复制代码
当然,这种方法也有自身的缺点,你有没有发现作为模块的 utils.js 也被打包出来了,实际上 utils.js 并不需要打包出来,index.js 已经包含了需要的 utils.js 代码。如果你用的是 webpack 的 entry-output 方法,你会发现 utils.js 是不会打包出来。
请注意此项目将使用 vinyl-named 插件来处理多页面。
结语
webpack-stream 只是其中一种方式,还有 browserify 插件可以令 gulp 项目实现模块化,不过这种方式我没用过,听说有坑。
要弄模块化,我还是比较建议直接使用 webpack,不要使用这种以 gulp 为主,webpack 为辅的方式。因为这种开发方式很不伦不类,核心问题在于 webpack 构建打包出来的东西,gulp 怎么在任务去感知并进行关联。上面的 js 打包问题是因为有 vinyl-named 插件的存在得以解决,但是当你的项目越来越大,需要使用 webpack 的代码分离进行优化的时候,你会遇到很棘手的 html 与 js 的如何关联问题。如果我们都用 webpack 去进行管理 html,进行如何去关联 js,那么为什么我们不一开始就直接使用 webpack 去构建项目呢?
完整代码
目录
gulpfile.js
const { series, parallel, src, dest, watch } = require('gulp')
const Path = require('path')
const htmlmin = require('gulp-htmlmin')
const fileinclude = require('gulp-file-include')
const changed = require('gulp-changed')
const webserver = require('gulp-webserver')
const del = require('del')
const uglify = require('gulp-uglify')
const plumber = require('gulp-plumber')
const cleanCss = require('gulp-clean-css')
const sass = require('gulp-sass')
const fiber = require('fibers') // 变量名命名没 s
const imagemin = require('gulp-imagemin')
const cache = require('gulp-cache')
const webpack = require('webpack-stream')
const webpackConfig = require("./webpack.config.js")
const named = require('vinyl-named')
const path = require('path')
sass.compiler = require('sass')
//sass.compiler = require('node-sass')
function html() {
return src(['src/**/*.html', '!src/include/**.html'])
.pipe(changed('dist'))
.pipe(plumber())
.pipe(fileinclude({
prefix: '@@', //引用符号
basepath: './src/include', //引用文件路径
}))
.pipe(htmlmin({
removeComments: true, //清除HTML注释
collapseWhitespace: true, //压缩HTML
collapseBooleanAttributes: true, //省略布尔属性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes: true, //删除所有空格作属性值 <input id="" /> ==> <input />
removeScriptTypeAttributes: true, //删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: true, //删除<style>和<link>的type="text/css"
minifyJS: true, //压缩页面JS
minifyCSS: true //压缩页面CSS
}))
.pipe(dest('dist'))
}
function js() {
return src(['src/js/**/*.js'])
.pipe(changed('dist/js/**/'))
.pipe(named(function (file) {
return file.relative.slice(0, -path.extname(file.path).length)
}))
.pipe(webpack(webpackConfig))
.pipe(plumber())
.pipe(uglify())
.pipe(dest('dist/js'))
}
function libJs() {
return src(['src/lib/**/*.js'])
.pipe(changed('dist/lib/**/'))
.pipe(plumber())
.pipe(dest('dist/lib'))
}
function css() {
return src(['src/css/**/*.css'])
.pipe(changed('dist/css/**/'))
.pipe(plumber())
.pipe(cleanCss())
.pipe(dest('dist/css'))
}
function scss() {
return src(['src/scss/**/*.scss'])
.pipe(changed('dist/scss/**/'))
.pipe(plumber())
.pipe(sass({ fiber }))
.pipe(cleanCss())
.pipe(dest('dist/scss'))
}
function libCss() {
return src(['src/libCss/**/*.css'])
.pipe(changed('dist/libCss/**/'))
.pipe(plumber())
.pipe(dest('dist/libCss'))
}
function img() {
return src(['src/assets/img/**/*.{png,jpg,gif,jpeg,ico}']) //后缀都用小写,不然不识别
.pipe(
cache(
imagemin({
optimizationLevel: 5, //类型:Number 默认:3 取值范围:0-7(优化等级)
progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片
interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染
multipass: true //类型:Boolean 默认:false 多次优化svg直到完全优化
})
)
)
.pipe(dest('dist/assets/img'))
}
function devServer() {
return src('dist').pipe(webserver({
port: 3000,
livereload: true, // 是否实时加载
// directoryListing: true, // 是否开启浏览目录
// open: true, // 是否自动打开
// proxies: [ // 代理,可以用来解决跨域问题
// {source: '/api', target: 'http://xxxx.com', options: {headers: {"Content-Type": 'application/x-www-form-urlencoded'}}}
// ]
}))
}
function watcher() {
watch('src/**/*.html', series(html)).on('unlink', function (path) {
del('dist/**/*.html' + Path.basename(path))
})
watch('src/js/**/*.js', series(js)).on('unlink', function (path) {
del('dist/js/**/*.js' + Path.basename(path))
})
watch('src/lib/**/*.js', series(libJs)).on('unlink', function (path) {
del('dist/lib/**/*.js' + Path.basename(path))
})
watch('src/css/**/*.css', series(css)).on('unlink', function (path) {
del('dist/css/**/*.css' + Path.basename(path))
})
watch('src/scss/**/*.scss', series(scss)).on('unlink', function (path) {
del('dist/scss/**/*.css' + Path.basename(path))
})
watch('src/libCss/**/*.css', series(libCss)).on('unlink', function (path) {
del('dist/libCss/**/*.css' + Path.basename(path))
})
watch('src/assets/img/**/*.{png,jpg,gif,jpeg,ico}', series(img)).on('unlink', function (path) {
del('dist/assets/img/**/*.{png,jpg,gif,jpeg,ico}' + Path.basename(path))
})
}
function clean() {
return del('dist')
}
exports.default = series(clean, html, libJs, js, css, scss, libCss, img, devServer, watcher)
exports.build = series(clean, html, libJs, js, css, scss, libCss, img)
复制代码
webpack.config.js
console.log('利用了 webpack !!!')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
module: {
rules: [
]
},
resolve: {
alias: {
}
},
}
复制代码
index.js
function print() {
console.log('测试')
}
print()
import utils from './utils'
utils.test()
复制代码
utils.js
export default {
test() {
console.log('测试模块化成功!!!')
}
}
复制代码