TypeScript自动化工作流程(中)

本文介绍Gulp自动化任务工具的基本使用方法,涵盖安装配置、预处理文件、监听文件变化、自动刷新浏览器等功能,并演示如何利用Gulp进行CSS、JavaScript文件的优化及图片压缩。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自动化任务工具

自动化任务工具是用来自动化地执行开发过程中需要重复进行的任务。这些任务包括编译TS文件、压缩JS文件等等。目前较为流行的两个JavaScript自动化工具分别是Grunt和Gulp。

Grunt的插件数相对较多,在Grunt中,我们使用文件作为任务的输入和输出。

Gulp中,我们使用的 的方式来构建系统。Gulp插件更倾向于使用代码来描述任务,这使得Gulp的可读性更高。

本人选择Gulp作为自动化任务工具,是因为Gulp更加容易上手,配置起来更简洁。
如果你想了解更多Grunt的内容,你可以访问其官网:http://gruntjs.com/ ,或者去慕课网进行相关的课程学习:http://www.imooc.com/view/30


Gulp

安装

全局安装:
使用cnpm全局安装gulp:

cnpm install -g gulp

局部安装
使用npm局部安装gulp:

 npm install gulp --save-dev

gulp设计的时候就已经规定了,必须要全局安装,然后再在项目中在进行局部安装,否则会出现 本地找不到gulp 的错误提示。

安装完成之后,会出现一个node_modules的文件夹,里面包含了gulp的相关依赖。


创建Gulp项目

在项目的目录下执行npm init 命令:
这里写图片描述

目录结构

通用的webapp目录结构:
这里写图片描述

在这个结构中,我们使用app文件夹作为开发目录(所有的源文件都放在这下面),dist文件夹用来存放生产环境下的内容。

创建Gulp任务

在根目录下创建一个gulpfile.js文件,打开文件,输入下面这行代码:

var gulp = require('gulp');

这行命令告知Node去node_modules中查找gulp包,先局部查找,找不到就去全局环境中查找。找到之后就会赋值给gulp变量,然后我们就可以使用它了。

我们来创建一个简单的任务:

gulp.task('task-name', function() {
  // Stuff here
});

task-name 是给你的任务起的名字,稍后在命令行中执行gulp task-name,将运行该任务。

来一个最经典的输出——hello,world!

gulp.task('hello', function() {
  console.log('Hello World!');
});

执行任务

gulp hello

这里写图片描述


Gulp执行预处理

我们使用gulp-sass插件来编译sass。

  • 安装gulp-sass
npm install gulp-sass --save-dev
  • 在gulpfile中引入插件,用变量保存
var gulp = require('gulp');
// Requires the gulp-sass plugin
var sass = require('gulp-sass');
  • 在任务中使用:
gulp.task('sass', function(){
  return gulp.src('source-files')//获取目标文件
    .pipe(sass()) // Using gulp-sass,执行sass,转化成css
    .pipe(gulp.dest('destination'))//输出到目标文件夹中,如果目标文件夹不存在则创建一个新的文件夹
});

我们需要给sass任务提供源文件和输出位置。所以我们先在项目中创建app/scss文件夹,里面有个tank.scss文件。
这个文件将在gulp.src中用到。
sass处理之后,我们希望它生成css文件并产出到app/css目录下,可以这样写:

gulp.task('sass', function(){
  return gulp.src('app/scss/tank.scss')
    .pipe(sass()) // Converts Sass to CSS with gulp-sass
    .pipe(gulp.dest('app/css'))
});

这里写图片描述

Node中的通配符

通常我们不止有一个scss文件。这时候可以使用Node通配符。
使用通配符,计算机检查文件名和路径进行匹配。

大部分时候,我们只需要用到下面4种匹配模式:

  1. *.scss*号匹配当前目录任意文件,所以这里*.scss匹配当前目录下所有scss文件
  2. **/*.scss:匹配当前目录及其子目录下的所有scss文件。
  3. !not-me.scss!号移除匹配的文件,这里将移除not-me.scss
  4. *.+(scss|sass)+号后面会跟着圆括号,里面的元素用|分割,匹配多个选项。这里将匹配scss和sass文件。

根据通配符,我们可以让scss目录下的所有scss转变成css文件:

gulp.task('sass', function() {
  return gulp.src('app/scss/**/*.scss') 
    .pipe(sass())
    .pipe(gulp.dest('app/css'))
})

gulp-tslint和gulp-typescript

为了检查我们的TypeScript代码是否规范,我们可以安装gulp-tslint
gulp-typescript 插件用于编译TS代码。

gulp-tslint
  • 安装
cnpm install gulp-tslint --save-dev
  • 引用和使用
var tslint=require('gulp-tslint');
gulp.task('lint',function(){
    gulp.src([
    './app/ts/**/*.ts'
    ])
        .pipe(tslint())
        .pipe(tslint.report('verbose'))
})

如果你使用的是 visual studio code ,那么你可以直接在扩展中安装TSLint 插件。
这里写图片描述
个人更倾向与这种方法,这样你就不必为每个项目安装这种插件的依赖了。(安装之后记得重新加载一下vscode)

gulp-typescript
  • 安装
cnpm install gulp-typescript --save-dev
  • 引用
var ts = require('gulp-typescript');
  • 添加任务
gulp.task('tsProject', function () {
    return gulp.src('app/**/*.ts')
        .pipe(ts({
            noImplicitAny: true,
            outFile: 'output.js'
        }))
        .pipe(gulp.dest('app/js'));
});

我们可以添加一些参数,来控制TS文件的编译效果

参数作用
outFile (string)生成一个JavaScript和一个定义文件。它仅仅在没有模块系统(module system)时被启用。
outDir (string)将输出移动到不同的(虚拟)目录。请注意,您仍然需要gulp.dest将输出写入磁盘。
noImplicitAny (boolean)默认为false。用隐含的“any”类型警告表达式和声明。
suppressImplicitAnyIndexErrors (boolean)默认为false。压缩 --noImplicitAny 索引缺少索引签名的对象的错误。
noLib (boolean)默认为false。不包括默认的 lib(包含 - Array,Date等的定义)
lib (string[])要包含在编译中的库文件列表。
target (string)指定ECMAScript目标版本:“ES3”(默认),“ES5”或“ES6”。
module (string)指定模板代码生成:”commonjs”,’amd’,’umd’或’system’
jsx (string)指定jsx代码生成:’react’或 ‘preserve’ (TS1.6+).
declaration (boolean)默认为false。生成相应的.d.ts 文件。
removeComments (boolean)默认为false。输出文件将不含注释。
allowJs默认为false。允许JavaScript文件被编译。
rootDir指定输入文件的根目录。只能用来控制outDir 输出文件的目录结构

更多的参数请访问:TypeScript Wiki


监听文件

我们可以手动更新scss文件,也可以监听它,然后让他自动执行命令。

Gulp提供watch方法给我们,语法如下:

gulp.watch(glob [, opts], tasks) 

gulp.watch(glob [, opts, cb])
glob

类型: String or Array

一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。

opts

类型: Object
传给 gaze 的参数。

tasks

类型: Array
需要在文件变动后执行的一个或者多个通过 gulp.task() 创建的 task 的名字,

cb(event)

类型: Function
每次变动需要执行的 callback。

现在添加一个监听任务:

gulp.task('watch', function(){
  gulp.watch('app/scss/**/*.scss', ['sass']);
  // Other watchers
})

这里写图片描述


Browser Sync自动刷新

对于开发者来说,没有最懒最有更懒。

Browser Sync 能让浏览器实时、快速响应文件的更改(html、js、css、sass、less等)并自动刷新页面。更重要的是 Browsersync可以同时在PC、平板、手机等设备下进项调试。

BrowserSync可以同时同步刷新多个浏览器,更神奇的是你在一个浏览器中滚动页面、点击按钮、输入框中输入信息等用户行为也会同步到每个浏览器中。

安装Browser Sync
npm install browser-sync --save-dev

这里没有gulp-前缀,因为browser-sync支持Gulp,所以没有人专门去搞一个给Gulp用。

browserSync服务模式

browserSync有两种服务模式,一种是服务器模式,一种是代理模式。

服务器模式

如果只希望在对某个css、html文件进行修改后会同步到浏览器里。那么只需要运行命令行工具,进入到该项目(目录)下,并运行相应的命令:

// --files 路径是相对于运行该命令的项目(目录) 
browser-sync start --server --files "css/*.css"

如果需要监听多个类型的文件,则需要用逗号隔开。

// --files 路径是相对于运行该命令的项目(目录) 
browser-sync start --server --files "css/*.css, *.html"
// 如果你的文件层级比较深,您可以考虑使用 **(表示任意目录)匹配,任意目录下任意.css 或 .html文件。 
browser-sync start --server --files "**/*.css, **/*.html"
代理模式

如果我们已经有其他本地服务器环境PHP或类似的,则需要使用代理模式。 BrowserSync会通过代理URL(localhost:3000)来查看指定的网站。

// 主机名可以是ip或域名
browser-sync start --proxy "主机名" "css/*.css"
Browsersync和Gulp

gulpfile.js 中引入browsersync。
我们不推荐这样引用:

var browserSync = require('browser-sync')

我们推荐这样引用:

var browserSync = require('browser-sync').create();

前者我们仍然可以用,但是我们更推荐调用.create(),因为这意味着你将获得一个唯一的引用,同时还能允许你创建多个服务器或者代理。
关于create 的更多操作信息请点这里API-create。

// 如果你是静态服务器
gulp.task('browser-sync', function() {
//启动browsersnyc静态服务器
    browserSync.init({
        server: {
            baseDir: "./"
        }
    });
});

// 如果你是代理服务器
gulp.task('browser-sync', function() {
//启动browsersnyc代理服务器
    browserSync.init({
        proxy: "你的域名或IP"
    });
});

通过流的方式创建任务流程,我们可以在任务完成之后调用 reload (重载),这样所有的浏览器都会被告知变化并实时更新。但是 Browersync 仅仅关心编译完成的CSS,这就需要我们在gulp.dest 后面调用.steam()

根据上述内容,我们修改gulpfile.js之后的样子:

var gulp=require('gulp');
var sass=require('gulp-sass');
var browserSync = require('browser-sync').create();

// 静态服务器 + 监听 scss/html 文件
gulp.task('serve',['sass'],function(){
    browserSync.init({
        server:"./app"
    });

    gulp.watch('app/scss/*.scss',['sass']);//监听scss文件,发生变动启动任务sass
    gulp.watch('app/*.html').on("change",browserSync.reload);//监听html文件,发生变动触发重载
})
//编译sass为css,自动注入到浏览器中
gulp.task('sass',function(){
    return gulp.src('app/scss/*.scss')
        .pipe(sass())
        .pipe(gulp.dest('app/css'))
        .pipe(browserSync.reload({
            stream: true
          })))
})
gulp.task('default', ['serve']);

这样当我们修改 tank.scss ,将会在浏览器中得到最直观的展示。
这里写图片描述

保存了三次,均及时检测到,同时完成编译、浏览器重载。
这里写图片描述

当然,我们还可以添加对js(ts)文件的监听:

gulp.task('tsproject',()=>{
        return gulp.src('app/ts/*.ts')
            .pipe(ts({
                noImplicitAny: true,
                outFile: 'test3.js'
            }))
            .pipe(gulp.dest('app/js'))
            .pipe(browserSync.reload({
                stream: true
              }))
})

// 静态服务器 + 监听 scss/html/jsts) 文件
gulp.task('serve',['sass'],function(){
    browserSync.init({
        server:"./app"
    });
    gulp.watch('app/scss/*.scss',['sass']);//监听scss文件,发生变动启动任务sass
    gulp.watch('app/ts/**/*.ts',['tsproject']);//监听ts文件,发生变动启动任务tsproject
    gulp.watch('app/*.html').on("change",browserSync.reload);//监听html文件,发生变动触发重载
    gulp.watch('app/js/*/*.js').on('change',browserSync.reload)//监听js文件,发生变化触发重载
})

目前我们已经通过gulp做到了:

  • 预处理文件(sass)
  • 监听文件变化(html、scss/css、js)
  • 自动刷新浏览器(方便查看修改效果)

开发的流程已经讲完,接下来我们来看看如何通过gulp优化项目。

优化CSS和JavaScript文件

目前优化分成两步,第一步是合并文件,第二步是压缩文件。

合并——gulp-useref

通过合并,能够减少CSS和JavaScript的文件数量,从而减少HTTP请求次数,达到优化的作用。

gulp-useref 能将HTML页面中的js、css引用进行合并。即便是js文件的路径不同,它一样可以进行合并。

安装gulp-useref
cnpm install --save-dev gulp-useref

现在我们要对index.html 中的引用文件进行合并。
合并之前:
这里写图片描述

配置HTML文件
在引用和添加任务之前我们需要配置一下HTML文件。
配置的语法如下:

<!-- build:<type>(alternate search path) <path> <parameters> -->
... HTML Markup, list of script / link tags.
<!-- endbuild -->
参数作用
type合并文件的类型,包括jscss或者removeremove类型意味着gulp不会生成文件。
alternate search path默认情况下,输入文件的路径是相对于处理的文件的。当然我们也可以用搜索路径来替代输入文件的路径。
path输出目标,即优化之后的文件输出的位置
parameters可以被添加的额外的标签。如果类型是css,则默认情况下会添加rel='stylesheet'。我们可以在js类型的文件中添加属性async,这样生成的js文件会自带异步属性。


按照上述语法进行配置:

 <!-- build:css  css/main.css -->
     <link rel="stylesheet" type="text/css" href="css/tank.css">
     <!-- endbuild -->
   <!-- build:js js/main.js -->
    <script src='js/test1.js'></script>
    <script src='js/test2.js'></script>
    <script src='js/test3.js'></script>
    <!-- endbuild -->

引用和添加任务

var useref = require('gulp-useref');
gulp.task('useref',function(){
    return gulp.src('app/*.html')
            .pipe(useref())
            .pipe(gulp.dest('dist'))
})

dist目录代表着生产目录,所以我们通过 gulp.dest('dist') 将合并之后的文件输出到dist目录中。

启动任务

gulp useref

合并之后
这里写图片描述


压缩——gulp-uglify、gulp-clean-css

gulp-uglify 可以将js文件进行压缩,
gulp-clean-css 可以压缩css文件。

压缩js文件
  • 安装gulp-uglify
cnpm install --save-dev gulp-uglify
  • 引用 gulp-uglify
var uglify=require('gulp-uglify');
  • 添加任务
gulp.task('jsmin', function () {
    gulp.src(['app/js/*.js'])
        .pipe(uglify({
            mangle: true,//类型:Boolean 默认:true 是否修改变量名
            compress: true,//类型:Boolean 默认:true 是否完全压缩
        }))
        .pipe(gulp.dest('dist/js'));
});

uglify可以添加一些参数,以获得不同的压缩效果或提示。

参数作用
warings默认为false。设置为true 将返回result.warnings的压缩警告。使用verbose 值可以获得更多警告细节。
parse默认为{}。选择解析方式。如果希望指定其他parse options,请传递一个对象。
compress默认为{}。选择压缩方式。如果输入false,则跳过完全压缩。更多细节操作,请访问:compress options
mangle默认为true。是否压缩变量名。如果输入false,则跳过压缩变量名。更多细节操作,请访问:mangle options
output默认是为null。选择输出方式。默认设置为最佳压缩。更多细节操作,请访问:output options
sourceMap默认为false。什么是sourceMap? 更多细节操作,请访问:source map options
toplevel默认为false。是否启用顶级压缩。如果你希望启用最顶级的变量和函数名的压缩,同时删除未使用的变量和函数,请设置为true
nameCache默认为null。如果你希望通过minify() 的多个调用来缓存压缩的变量和函数,则传递一个空对象{}或以前使用的nameCache对象。注意:这是一个读/写属性。minify()将读取此对象的名称缓存状态并在压缩期间进行更新,以便它可以被重用或由用户外部保留。
ie8默认为false。是否支持IE8。设置为true将支持IE8。
keep_fnames默认为false。是否改变函数名。设置为true将会防止丢弃或改变函数名。该方法依赖Function.prototype.name


如果你对压缩没有什么具体要求,那么使用默认模式即可。

gulp.task('jsmin', function () {
    gulp.src(['dist/js/*.js'])
        .pipe(uglify())
        .pipe(gulp.dest('dist/js'));
});


压缩css

gulp-minify-css 用于压缩css,但是gulp目前更推荐使用 gulp-clean-css 插件来压缩css。
这里写图片描述

  • 安装gulp-clean-css
cnpm install gulp-clean-css --save-dev
  • 引用gulp-clean-css
var cleancss=require('gulp-clean-css')
  • 添加任务
gulp.task('minify-css', () => {
  return gulp.src('app/css/*.css')
    .pipe(cleancss({compatibility: 'ie8'}))
    .pipe(gulp.dest('dist/css'));
});

我们可以在cleancss中加上不同的参数,以获得不同的压缩效果。

参数作用
compatibility控制使用兼容模式。默认是ie10+,效果等同于compatibility: '*',兼容Internet Explorer 10+模式。你还可以使用ie9ie8ie7这几种模式。
fetch控制处理远程请求的功能。更多操作信息,请访问fetch option
format控制输出的css格式。默认为false。默认情况下输出CSS的格式不带任何空格。更多操作细节,请访问format
inline控制@import内联规则; 默认为local;更多操作细节,请访问inline
level控制优化等级。默认为1。该参数选项包括012三种。0 意味着不优化,1级优化选项通常是安全的,而2级优化对大多数用户来说应该是安全的。更多细节操作,请访问Optimization levels

这里只是罗列部分参数,更多参数请访问:https://github.com/jakubpawlowicz/clean-css#constructor-options

我们可以一个个启动任务,也可以让它识别不同文件(css和js文件)自动进行合并、压缩。

  • 安装gulp-if
cnpm install gulp-if --save-dev
  • 引用gulp-if
var gulpif=require("gulp-if")
  • 使用语法:
gulpif(condition, stream [, elseStream, [, minimatchOptions]]);
//例如gulpif(condition, uglify(), beautify())
  • 之前的合并、压缩任务
gulp.task('useref',function(){
    return gulp.src('app/*.html')
            .pipe(useref())
            .pipe(gulp.dest('dist'))
})
gulp.task('jsmin', function () {
    gulp.src(['dist/js/*.js'])
        .pipe(uglify({
            mangle: true,//类型:Boolean 默认:true 是否修改变量名
            compress: true,//类型:Boolean 默认:true 是否完全压缩
        }))
        .pipe(gulp.dest('dist/js'));
});
gulp.task('minify-css',()=>{
    gulp.src(['dist/css/*.css'])
    .pipe(cleancss({compatibility: 'ie8'}))
    .pipe(gulp.dest('dist/css'));
})
  • 优化之后的合并、压缩任务
gulp.task('minify',()=>{
    return gulp.src('app/*.html')
            .pipe(useref())
            .pipe(gulpif("*.js",uglify({
                mangle: true,
                compress: true,
            })))
            .pipe(gulpif("*.css",cleancss({
                compatibility: 'ie8'
            })))
            .pipe(gulp.dest('dist'));
})

图片优化

除了css、js的文件合并、压缩之外,自然还有图片优化。
图片优化使用gulp-imagemin

  • 安装 gulp-imagemin
cnpm install --save-dev gulp-imagemin
  • 引用gulp-imagemin
var imagemin=required('gulp-imagemin');
  • 添加任务
gulp.task('imagemin', () =>
    gulp.src('app/images/*')
    .pipe(imagemin())
    .pipe(gulp.dest('dist/images'))
);

我们可以添加一些参数,从而控制图片压缩效果。
参数包括图片类型参数

  • 图片类型
参数作用
gifsicle控制 GIF 图片的压缩效果。
jpegtran控制 JPEG 图片的压缩效果。
optipng控制 PNG 图片的压缩效果。
svgo控制 SVG 图片的压缩效果。


  • 图片类型的参数
图片类型gifsiclejpegtranoptipngsvgo
参数
interlaced:
默认为false。是否采用渐进渲染。
图片渲染分成两种,一种是线性渲染,另一种是交错渲染(又称渐进渲染)。
关于图片渲染的异同,你可以在张鑫旭老师的文章中体验一下。
progressive:
默认为false。是否采用渐进渲染。
bitDepthReduction:
默认为true。应用bit深度减少。
SVG Optimizer简写为SVGO,是一个基于node.js的插件。它可以删除SVG文件中冗余和无用的信息,同时不影响SVG渲染效果。具体的参数类型,请访问:svgo
optimizationLevel:
默认为1。选择压缩等级,共有三个等级,13。优化级别决定完成多少优化,更高的优化级别将会花费更多的时间,但却能获得更好的优化结果。
arithmetic:
默认为false。是否采用算数编码
optimizationLevel:
默认为3。选择压缩等级,共有八个等级,070级优化最少,图像的深度、颜色类型都不会发生改变,也不会对现有的IDAT数据流进行重新压缩。从1级别开始对单个IDAT进行压缩,级别越高,IDAT压缩越多。
color:
Type:number。减少每个输出的GIF图片中的颜色数。颜色数必须在2到256之间。
colorTypeReduction:
默认为true。应用颜色类型减少。
paletteReduction:
默认为true。减少调色板数据块。


根据图片类型和参数,我们来修改压缩效果

gulp.task('imagemin', () =>{
  return  gulp.src('app/images/*')
    .pipe(imagemin([
        imagemin.gifsicle({interlaced: true,optimizationLevel:3}),
        imagemin.jpegtran({progressive: true}),
        imagemin.optipng({optimizationLevel: 5}),
        imagemin.svgo({
            plugins: [
                {removeViewBox: true},
                {cleanupIDs: false}
            ]
        })
    ]))
    .pipe(gulp.dest('dist/images'))
});

注:一般来说压缩图片都比较慢,所以为了防止重复压缩图片,我们可以使用 gulp-cached 插件。


清除多余文件

在开发、压缩文件之后,是清理多余文件。
del 可以帮我们完成这项工作。

  • 安装del
cnpm install del --save-dev
  • 引用del
var del=require('del');

假如我们想要删除dist目录下的文件,那么可以这样添加任务

gulp.task('clean', function() {
  del('dist');
});

但是我们又不想图片被删除,我们可以添加新任务

gulp.task('clean:dist', function(){
   return del(['dist/**/*', '!dist/images', '!dist/images/**/*']);
  });

你可以根据自己实际情况来进行文件的删除。


总结

gulp自动化任务分成两条路线

  • 第一条是开发过程路线,预处理sass、ts文件,监听文件,自动刷新浏览器。

  • 第二条是优化,优化css、压缩js和图片等等,同时删除不必要的文件。

如果我们想让gulp.task一条条运行,我们需要可以使用 run-sequence 插件。
正常情况下,如果我们这样写:

gulp.task('build', [`clean`, `sass`, `minify`,`imagemin`], function (){
  console.log('Building files');
})

Gulp会同时触发[]的事件,这不是我们想要的结果。来试试run-sequence 的效果。

  • 安装run-sequence
cnpm install run-sequence --save-dev
  • 使用方法
var runSequence = require('run-sequence');
    gulp.task('task-name', function(callback) {
      runSequence('task-one', 'task-two', 'task-three', callback);
    });

执行task-name时,Gulp会按照顺序执行task-one,task-two,task-three。
如果你要同时执行多个任务,则可以这样

gulp.task('task-name', function(callback) {
      runSequence('task-one', ['task-two', 'task-three'], callback);
    });

Gulp就会先执行task-one,然后异步执行task-twotask-three

  • 合并之后的代码(以第二条为例子)
gulp.task("optimize",()=>{
    runSequence('clean:dist',['minify','imagemin'])
})

注意:如果你没有在gulp.task中正确使用return,那么任务将会终止。
例如你在clean:dist中没有使用return

gulp.task('clean:dist', function(callback){
   del(['dist/**/*', '!dist/images', '!dist/images/**/*']);
  });

那么 runSequence在运行完clean:dist任务之后将会终止,不会继续运行接下来的任务。

其他开发插件
  • 使用 Autoprefixer,你不再需要写CSS浏览器内核前缀
  • 增加 Sourcemaps,让你更方便的调试Sass,coffeescript
  • 使用 sprity创建精灵图
  • gulp-changed 只允许通过修改的文件
  • BabelTraceur 写ES6
  • Browserify , webpack , jspm 模块化JavaScript
  • Handlebars ,Swing 模块化Html
  • require-dir 分割gulpfile成多个文件
  • gulp-moderinizr 自动生成Modernizr脚本
  • unCSS 移除多余的CSS
  • CSSO 更深入地优化CSS
  • Critical 生成行内CSS

参考文档:
Gulp新手入门教程
gulp-typescript
Gulp系列教程:使用BrowserSync浏览及相关配置
Browsersync中文官网
Browsersync-api
gulp-useref-npm
gulp-useref-git
UglifyJS
clean-css
gulp-if
gulp教程之gulp-imagemin
gulp-imagemin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值