大前端学习--使用node、grunt、gulp实现前端项目工程化(含视频讲解)

举报
楚楚冻人玥玥仙女 发表于 2021/11/19 00:26:50 2021/11/19
【摘要】 使用node、grunt、gulp实现前端项目工程化 我把熬夜录制的讲解视频也放在下面了,本宝宝是不是很贴心😄😄😄😄😄😄😄😄😄😄😄😄 1. 概述脚手架实现的过程,并使用Node...

使用node、grunt、gulp实现前端项目工程化

我把熬夜录制的讲解视频也放在下面了,本宝宝是不是很贴心😄😄😄😄😄😄😄😄😄😄😄😄

1. 概述脚手架实现的过程,并使用NodeJS完成一个自定义的小型脚手架工具

脚手架的实现过程就是在启动脚手架之后,自动地去询问一些预设问题,通过回答的结果结合一些模板文件,生成项目的结构。

使用NodeJS开发一个小型的脚手架工具:

  • yarn init初始化一个空文件夹:jal-pro

  • package.json中添加bin属性指定脚手架的命令入口文件为cli.js

    {
    "name": "jal-pro",
    "version": "1.0.0",
    "main": "index.js",
    "bin": "cli.js",
    "license": "MIT",
    "dependencies": {
      "ejs": "^3.1.3",
      "inquirer": "^7.1.0"
    }
    }
    
        
        
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 编写cli.js

    #!/usr/bin/env node
    
    // Node CLI 应用入口文件必须要有这样的文件头
    // 如果Linux 或者 Mac 系统下,还需要修改此文件权限为755: chmod 755 cli.js
    
    // 脚手架工作过程:
    // 1. 通过命令行交互询问用户问题
    // 2. 根据用户回答的结果生成文件
    
    const path = require('path')
    const fs = require('fs')
    const inquirer = require('inquirer') // 发起命令行交互询问
    const ejs = require('ejs') // 模板引擎
    inquirer.prompt([
      {
        type: 'input',
        name: 'name',
        message: 'Project name?'
      }
    ]).then(answer => {
      console.log(answer)
    
      // 模板目录
      const tempDir = path.join(__dirname, 'templates')
      // 目标目录
      const destDir = process.cwd()
    
      // 将模板下的文件全部转换到目标目录
      fs.readdir(tempDir, (err, files) => {
        if (err) throw err
        files.forEach(file => {
          // 通过模板引擎渲染文件
          ejs.renderFile(path.join(tempDir, file), answer, (err, result) => {
            if(err) throw err
            // 将结果写入到目标目录
            fs.writeFileSync(path.join(destDir, file), result)
          })
        })
      })
    })
    
        
        
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 命令行中修改cli.js文件权限:chmod 755 cli.js

  • 模板文件templates/index.html如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title><%= name %></title>
    </head>
    <body>
      
    </body>
    </html>
    
        
        
       
       
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 执行命令将该cli程序link到全局:yarn link

  • 然后再其他文件夹中执行:jal-pro命令,就可以根据模板自动化创建文件了

2. 尝试使用Gulp完成项目的自动化构建

视频演示地址:

gulp讲解

gulpfile.js

// 实现这个项目的构建任务
const {src, dest, parallel, series, watch} = require('gulp')
const del = require('del')
const browserSync = require('browser-sync')
const bs = browserSync.create()
const loadPlugins = require('gulp-load-plugins')
const plugins = loadPlugins()
const {sass, babel, swig, imagemin, ghPages, eslint, sassLint} = plugins
const config = {
  production: false,
  port: 2080,
  open: false
}
const isMini = () => config.production

const calculateConfig = () => {
  const argv = process.argv
  console.log(argv)
  const task = argv[2]
  if(task === 'serve') {
    config.production = false
    config.open = argv.includes('--open')
    config.port = argv.includes('--port') && parseInt(argv[argv.indexOf('--port')+1], 10) || 2080
    config.root = 'temp'
  } else if (task === 'build') {
    config.production = argv.includes('--production') || argv.includes('--prod')
  } else if (task === 'start') {
    config.open = argv.includes('--open')
    config.port = argv.includes('--port') && parseInt(argv[argv.indexOf('--port')+1], 10) || 2080
    config.root = 'dist'
  } else if (task === 'deploy') {
    config.production = true
    config.branch = argv.includes('--branch') && argv[argv.indexOf('--branch')+1] || 'gh-pages'
  }
  console.log('config', config)
}

calculateConfig()

const data = {
  menus: [
    {
      name: 'Home',
      icon: 'aperture',
      link: 'index.html'
    },
    {
      name: 'Features',
      link: 'features.html'
    },
    {
      name: 'About',
      link: 'about.html'
    },
    {
      name: 'Contact',
      link: '#',
      children: [
        {
          name: 'Twitter',
          link: 'https://twitter.com/w_zce'
        },
        {
          name: 'About',
          link: 'https://weibo.com/zceme'
        },
        {
          name: 'divider'
        },
        {
          name: 'About',
          link: 'https://github.com/zce'
        }
      ]
    }
  ],
  pkg: require('./package.json'),
  date: new Date()
}

// Clean the dist & temp files.
const clean = () => {
  return del(['dist', 'temp'])
}

const myeslint = () => {
  return src(['src/assets/scripts/*.js'])
  .pipe(eslint({
    rules: {
        'my-custom-rule': 1,
        'strict': 2
    },
    globals: [
        'jQuery',
        '$'
    ],
    envs: [
        'browser'
    ]
  }))
  .pipe(eslint.format())
}

const mysasslint = () => {
  return src(['src/assets/styles/*.scss'])
  .pipe(sassLint())
  .pipe(sassLint.format())
  .pipe(sassLint.failOnError())
}

const style = () => {
  return src('src/assets/styles/*.scss', { base: 'src' })
  .pipe(sass({ outputStyle: 'expanded' }))
  .pipe(dest('temp'))
  .pipe(bs.reload({stream: true}))
}

const script = () => {
  return src('src/assets/scripts/*.js', { base: 'src' })
  .pipe(babel({ presets: ['@babel/preset-env'] }))
  .pipe(dest('temp'))
  .pipe(bs.reload({stream: true}))
}

const page = () => {
  return src('src/**/*.html', { base: 'src' })
    .pipe(swig({ data, defaults: { cache: false } })) // 防止模板缓存导致页面不能及时更新
    .pipe(dest('temp'))
    .pipe(bs.reload({ stream: true }))
}

const image = () => {
  return src('src/assets/images/**', {base: 'src'})
  .pipe(imagemin())
  .pipe(dest('dist'))
}

const font = () => {
  return src('src/assets/fonts/**', {base: 'src'})
  .pipe(imagemin())
  .pipe(dest('dist'))
}

const extra = () => {
  return src('public/**', {base: 'public'})
  .pipe(dest('dist'))
}

const browser = () => {
  watch('src/assets/styles/*.scss', style)
  watch('src/assets/scripts/*.js', script)
  watch('src/*.html', page)

  watch([
    'src/assets/images/**',
    'src/assets/fonts/**',
    'public/**'
  ], bs.reload)

  bs.init({
    notify: false,
    port: config.port,
    open: config.open,
    // files: 'temp/**',
    server: {
      baseDir: [config.root, 'src', 'public'], // 按顺序查找
      routes: {
        '/node_modules': 'node_modules'
      }
    }
  })
}

const useref = () => {
  return src('temp/*.html', { base: 'temp' })
  .pipe(plugins.useref({ searchPath: ['temp', '.'] }))
  .pipe(plugins.if(/\.js$/, plugins.uglify()))
  .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
  .pipe(plugins.if(/\.html$/, plugins.htmlmin({
    collapseWhitespace: isMini(),
    minifyCSS: isMini(),
    minifyJS: isMini()
  })))
  .pipe(dest('dist'))
}

const mydeploy = () => {
  return src('dist/**/*')
    .pipe(ghPages([{
      branch: config.branch
    }]))
}

const lint = parallel(myeslint, mysasslint)

const compile = parallel(style, script, page)

const serve = series(compile, browser)

const build = series(
  clean,
  parallel(
    series(compile, useref),
    image,
    font,
    extra
  )
)

const start = series(build, browser)

const deploy = series(build, mydeploy)

module.exports = {
  clean,
  compile,
  build,
  serve,
  start,
  deploy,
  lint
}

/*
演示命令:
yarn clean
yarn lint
yarn compile
yarn serve
yarn serve --port 5210 --open
yarn build
yarn build --production
yarn start --port 5210 --open
yarn deploy --branch gh-pages
*/

  
  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235

package.json的部分内容

{
 "scripts": {
    "clean": "gulp clean",
    "compile": "gulp compile",
    "serve": "gulp serve",
    "build": "gulp build",
    "start": "gulp start",
    "lint": "gulp lint",
    "deploy": "gulp deploy --production"
  },
 "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/preset-env": "^7.10.2",
    "browser-sync": "^2.26.7",
    "del": "^5.1.0",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "gulp-clean-css": "^4.3.0",
    "gulp-eslint": "^6.0.0",
    "gulp-gh-pages": "^0.5.4",
    "gulp-htmlmin": "^5.0.1",
    "gulp-if": "^3.0.0",
    "gulp-imagemin": "^7.1.0",
    "gulp-load-plugins": "^2.0.3",
    "gulp-sass": "^4.1.0",
    "gulp-sass-lint": "^1.4.0",
    "gulp-swig": "^0.9.1",
    "gulp-uglify": "^3.0.2",
    "gulp-useref": "^4.0.1"
  }
}

  
  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

3. 使用Grunt完成项目的自动化构建

视频演示地址:

grunt自动化构建

gruntfile.js

const sass = require('sass')
const fs = require('fs')
const useref = require('useref')
const loadGruntTasks = require('load-grunt-tasks')
const browserSync = require('browser-sync')
const bs = browserSync.create()

const data = {
  menus: [
    {
      name: 'Home',
      icon: 'aperture',
      link: 'index.html'
    },
    {
      name: 'Features',
      link: 'features.html'
    },
    {
      name: 'About',
      link: 'about.html'
    },
    {
      name: 'Contact',
      link: '#',
      children: [
        {
          name: 'Twitter',
          link: 'https://twitter.com/w_zce'
        },
        {
          name: 'About',
          link: 'https://weibo.com/zceme'
        },
        {
          name: 'divider'
        },
        {
          name: 'About',
          link: 'https://github.com/zce'
        }
      ]
    }
  ],
  pkg: require('./package.json'),
  date: new Date()
}
module.exports = grunt => {
  grunt.initConfig({
    clean: ['dist/**'],

    sass: {
      options: {
        sourceMap: true,
        implementation: sass, // implementation指定在grunt-sass中使用哪个模块对sass进行编译,我们使用npm中的sass
      },
      main: {
        files: {
          'dist/assets/styles/main.css': 'src/assets/styles/main.scss'
        }
      }
    },

    babel: {
      options: {
        presets: ['@babel/preset-env'],
        sourceMap: true
      },
      main: {
        files: {
          'dist/assets/scripts/main.js': 'src/assets/scripts/main.js'
        }
      }
    },
    web_swig: {
      options: {
        swigOptions: {
          cache: false
        },
        getData: function (tpl) {
          return data;
        }
      },
      main: {
        expand: true,
        cwd: 'src/',
        src: "**/*.html",
        dest: "dist/"
      },
    },

    uglify: {
      production: {
        files: [{
          expand: true,
          cwd: 'dist/',
          src: ['assets/scripts/*.js'],
          dest: 'dist/',
        }]
      },
      dev: {}
    },
    cssmin: {
      production: {
        files: [{
          expand: true,
          cwd: 'dist/',
          src: ['assets/styles/*.css'],
          dest: 'dist/',
        }]
      },
      dev: {}
    },
    htmlmin: {
      production: {
        options: {
          removeComments: true,
          collapseWhitespace: true
        },
        files: [{
          expand: true,
          cwd: 'dist/',
          src: ['**/*.html'],
          dest: 'dist/'
        }]
      },
      dev: {}
    },
    image: {
      production: {
        options: {
          optipng: false,
          pngquant: true,
          zopflipng: true,
          jpegRecompress: false,
          mozjpeg: true,
          gifsicle: true,
          svgo: true
        },
        files: [{
          expand: true,
          cwd: 'dist/',
          src: ['assets/fonts/*', 'assets/images/*'],
          dest: 'dist/'
        }]
      },
      dev: {}
    },
    eslint: {
      options: {
        rulePaths: ['src/assets/scripts/']
      },
      target: ['src/assets/scripts/main.js']
    },
    sasslint: {
      main: {
        options: {
          configFile: 'config/.sass-lint.yml',
          rulePaths: ['src/assets/scripts/']
        },
        target: ['src/assets/styles/main.scss']
      }
    },
    copy: {
      main: {
        files: [{
          expand: true,
          cwd: 'public/',
          src: ['**'],
          dest: 'dist/'
        },
        {
          expand: true,
          cwd: 'src',
          src: ['assets/fonts/*'],
          dest: 'dist/'
        },
        {
          expand: true,
          cwd: 'src',
          src: ['assets/images/*'],
          dest: 'dist/'
        }
      ]}
    },
    watch: {
      js: {
        files: ['src/js/*.js'],
        tasks: ['babel', 'bs-reload']
      },
      css: {
        files: ['src/scss/*.scss'],
        tasks: ['sass', 'bs-reload']
      },
      html: {
        files: ['src/**/*.html'],
        tasks: ['web_swig', 'bs-reload']
      }
    },
    
    ghDeploy: {
      options: {
        repository: 'https://github.com/2604150210/pages-boilerplate-grunt.git',
        deployPath: 'dist',
       	branch: grunt.option('branch') || 'gh-pages',
    	  message: 'Auto deplyment ' + grunt.template.today()
    },
    }
  })

  grunt.registerTask("jal-useref", function () {
    const done = this.async()
    const cwd = 'dist/'
    const htmls = ['index.html', 'about.html']
    htmls.forEach((html, index) => {
      const inputHtml = fs.readFileSync(cwd + html, "utf8")
      const [code, result] = useref(inputHtml)
      for (let type in result) {
        const dests = Object.keys(result[type])
        dests.forEach(dest => {
          const src = result[type][dest].assets
          let read
          const files = src.map(file => {
            read = cwd + file
            if(file[0] === '/') {
              read = file.substr(1)
            }
            return fs.readFileSync(read)
          })
          fs.writeFile(cwd + dest, files.join(''), (err) => {
            if (err) {
                return console.error(err);
            }
            console.log(`${cwd + dest}数据写入${read}成功!`);
          })
        })
      }
      fs.writeFile(cwd + html, code, (err) => {
        if (err) {
          return console.error(err);
        }
        console.log(`${cwd + html}重写成功!`);
        if(index === htmls.length - 1) {
          done()
        }
      })
    })
  });

  // grunt.loadNpmTasks('grunt-sass')


  // 启动browserSync
  grunt.registerTask("bs", function () {
    const done = this.async();
    bs.init({
      notify: false,
      port: grunt.option('port') || 2080,
      open: grunt.option('open'),
      // files: 'temp/**',
      server: {
        baseDir: ['dist', 'src', 'public'], // 按顺序查找
        routes: {
          '/node_modules': 'node_modules'
        }
      }
    }, function (err, bs) {
      done();
    });
  });
  grunt.registerTask("bs-reload", function () {
    bs.reload()
  });

  // 获取命令行参数是否含有production或者prod,判断是开发模式还是生产模式
  const mode = (grunt.option('production') || grunt.option('prod')) ? 'production': 'development'

  loadGruntTasks(grunt) // 自动加载所有的grunt插件中的任务

  // 根据命令行参数判断是否需要压缩
  grunt.registerTask('mini:production', ['image', 'uglify', 'cssmin', 'htmlmin'])
  grunt.registerTask('mini:development', [])

  grunt.registerTask('lint', ['sasslint', 'eslint'])

  grunt.registerTask('compile', ['sass', 'babel', 'web_swig'])

  grunt.registerTask('serve', ['compile', 'bs', 'watch'])

  grunt.registerTask('build', ['clean', 'compile', 'copy', 'jal-useref', `mini:${mode}`])

  grunt.registerTask('start', ['clean', 'compile', 'copy', 'jal-useref', 'mini:production', 'bs', 'watch'])

  grunt.registerTask('deploy', ['clean', 'compile', 'copy', 'jal-useref', 'mini:production', 'ghDeploy'])

}

/*
演示命令:
yarn clean
yarn lint
yarn compile
yarn serve
yarn serve --port=5210 --open
yarn build
yarn build --production
yarn start --port=5210 --open
yarn deploy --branch=gh-pages
*/

  
  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309

package.json的部分内容

{
 "scripts": {
    "clean": "grunt clean",
    "compile": "grunt compile",
    "lint": "grunt lint",
    "serve": "grunt serve",
    "build": "grunt build",
    "start": "grunt start",
    "deploy": "grunt deploy --production"
  },
 "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/preset-env": "^7.10.2",
    "browser-sync": "^2.26.7",
    "concat": "^1.0.3",
    "grunt": "^1.1.0",
    "grunt-babel": "^8.0.0",
    "grunt-browser-sync": "^2.2.0",
    "grunt-contrib-clean": "^2.0.0",
    "grunt-contrib-concat": "^1.0.1",
    "grunt-contrib-copy": "^1.0.0",
    "grunt-contrib-csslint": "^2.0.0",
    "grunt-contrib-cssmin": "^3.0.0",
    "grunt-contrib-htmlmin": "^3.1.0",
    "grunt-contrib-jshint": "^2.1.0",
    "grunt-contrib-uglify": "^4.0.1",
    "grunt-contrib-watch": "^1.1.0",
    "grunt-eslint": "^23.0.0",
    "grunt-gh-deploy": "^0.1.3",
    "grunt-html-build": "^0.7.1",
    "grunt-html-template": "^0.1.6",
    "grunt-image": "^6.3.0",
    "grunt-sass": "^3.1.0",
    "grunt-sass-lint": "^0.2.4",
    "grunt-scss-lint": "^0.5.0",
    "grunt-web-swig": "^0.3.1",
    "load-grunt-tasks": "^5.1.0",
    "sass": "^1.26.8",
    "useref": "^1.4.3"
  }
}

  
  
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

文章来源: blog.csdn.net,作者:爱玲姐姐,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jal517486222/article/details/106948869

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。