javascript 前端 基于 npm、bower、grunt的标准项目构建

本文不是npm、bower、grunt的初级入门教程,而是希望给一个非常合适团队使用的前端项目构建模板,让不熟悉该部分内容的团队成员可以边开发、边应用、边理解,节约时间的同时还能更专注于具体业务。

前言

就如同还有不少做java的程序猿&攻城狮们居然还没用上maven一样,不少偏重前端开发的程序猿&攻城狮也还没有拥抱npm、bower这样的包管理、构建工具,本文根据自己需要和用途总结的前端标准架构。经常看国外大神们源码的同学一定对这些概念不陌生,具备maven、gradle基础则更容易上手。

开门见山

使用npm、bower、grunt的目的

  • 开发规范化、标准化
  • 提高开发效率,尽可能多的提供自动化辅助功能,比如语法检查、文件复制、压缩、css编译等等

什么是npm、bower、grunt

比较官方的解释:

名称描述
npmNode.js的包管理工具
bower基于github的软件包管理器
gruntGrunt是基于Node.js的项目构建工具

下面给出我自己定义的三者在前端构建中其扮演角色的白话文版本:

名称描述
npmbower和grunt运行的基础环境,如同jvm与java应用程序的关系
bowerSPA项目用到的组件,如jquery,d3等等
grunt可编程的自动化构建工具

上述表述还是不太好理解,下面再简化一次

名称描述
npm当它透明,不存在
bower用什么外部组件就加什么内容,如jquery,easyui等等
grunt自动化完成js语法检查、css编译、js文件压缩、文件复制合并等工作

安装步骤

内容较多,考虑仅补充适合linux的部分,当前暂略

常用命令

暂略,后补

目录结构组成

下面是一个比较标准的基于npm、bower进行构建的目录结构:

名称类型描述
appdirSPA应用程序源码目录
bower_componentsdirbower组件存放目录,比如jquery、bootstrap等等
distdirSPA应用程序打包输出目录
node_modulesdirnpm组件目录,比如grunt等等
.jshintrcfilejs语法检查配置文件
bower.jsonfilebower组件描述文件,bower_components中的内容由本文件决定
Gruntfile.jsfilegrunt构建工具的任务文件
package.jsonfilenpm组件描述文件,node_modules中的内容由本文件决定

项目管理工具配置

上述目录结构中bower_components和node_modules一定需要被git、svn等源码管理工具屏蔽的,因为其内容多而杂,而dist作为编译后的应用程序,是否需要屏蔽可以根据具体需要而定,下面给出git适合一般使用的.gitignore文件配置:

logs/*
!.gitkeep
node_modules/
app/bower_components/
bower_components/
tmp
.tmp
.DS_Store
.idea
dist

注:上述配置中dist是被屏蔽的

文件内容:

bower.json

{
  "name": "myapp",
  "description": "A starter project for backbone",
  "version": "0.3.0",
  "license": "MIT",
  "private": true,
  "dependencies": {
    "requirejs-text": "2.0.x",
    "requirejs": "2.1.x",
    "bootstrap": "~3.3.5",
    "backbone": "~1.2.3",
    "handlebars": "~4.0.5",
    "requirejs-domready": "~2.0.1",
    "highcharts": "~4.1.9",
    "font-awesome": "~4.5.0",
    "d3": "~3.5.11"
  },
  "devDependencies": {},
  "appPath": "app", 
  "moduleName": "myApp"
}

重要节点解释:

名称描述
dependencies本应用程序依赖的外部组件,分为名称和版本号,该信息都来自于github,版本号前缀字符有‘~’、‘>=’等等,暂时将其统一理解为必须满足该版本号的要求即可
devDependencies内容格式与dependencies完全一样,但只用于开发阶段
appPath指定应用程序存放的路径,本例中指向了当前目录下的app,与目录结构中的app是一致的

.jshintrc

.jshintrc是grunt的jshint插件的配置文件,该插件能够对js文件进行语法检查,具体配置项非常多,下面是我一直使用的内容,大体意思是打开严格检查、结尾需要‘;‘等等,一般情况下一个团队改一次就够了。需要修改时建议查阅官方文档。

{ 
    "globalstrict": true,
    "boss":false,
    "sub":true, 
    "strict": true, 
    "undef": true, 
    "eqeqeq": true, 
    "eqnull": true, 
    "laxbreak":true, 
    "laxcomma":true, 
    "regexp":true, 
    "plusplus":true,
    "asi": false, 
    "newcap": true, 
    "bitwise": true, 
    "curly": false, 
    "expr":true, 
    "browser": true, 
    "node": true, 
    "latedef": false,   
    "noarg": true,      
    "-W032":false,
    //"es5": true, 
    //"esnext": true,       
    //"proto": true,    
    "smarttabs": true,
    "jasmine": true,
    "predef": [
        "require",
        "define",
        "notify"
    ]
}

Gruntfile.js

'use strict';
module.exports=function(grunt){
    var appConfig = {
        app: require('./bower.json').appPath || 'app',//重点:对应目录结构中的app目录
        dist: 'dist' //重点:对应目录结构中的dist目录
    };
    //任务配置,所有插件的配置信息
    grunt.initConfig({

        // Project settings
        yeoman: appConfig,

        //获取package.json的信息
        pkg:grunt.file.readJSON('package.json'),

        //jshint语法检查配置,Gruntfile.js就是被该插件调用
        jshint:{
            build:['Gruntfile.js','<%= yeoman.app %>/*.js'],
            options:{
                jshintrc:'.jshintrc'
            }
        },      
        bower: {   //由grunt调用bower时需要的参数
            install: { 
                options: {
                        "targetDir": "<%= yeoman.dist %>/lib",
                        "layout": "byComponent",
                        "install": true,
                        "verbose": false,
                        "cleanTargetDir": true
                      }
                  }
        },
        // 重点:清理dist目录
        clean: {
          dist: {
            files: [{
              dot: true,
              src: [
                '.tmp',
                '<%= yeoman.dist %>/{,*/}*',
                '!<%= yeoman.dist %>/.git{,*/}*'
              ]
            }]
          },
          server: '.tmp'
        },      
        // 重点:复制文件到dist目录下,这个部分经常会根据项目的需要进行修改,但总体上还是固定的
        copy: {
          dist: {
            files: [{
              expand: true,
              dot: true,
              cwd: '<%= yeoman.app %>',
              dest: '<%= yeoman.dist %>',
              src: [
                '*.{ico,png,txt}',
                '.htaccess',
                '*.js',
                //'*.html',
                'images/{,*/}*.{webp}',
                'styles/fonts/{,*/}*.*'
              ]
            }, {
              expand: true,
              cwd: '.tmp/images',
              dest: '<%= yeoman.dist %>/images',
              src: ['generated/*']
            },{
              expand: true,
              cwd: 'bower_components/bootstrap/dist',
              src: 'fonts/*',
              dest: '<%= yeoman.dist %>'
            },{
              expand: true,
              cwd: 'bower_components/bootstrap/dist/css',
              src: '*.min.css',
              dest: '<%= yeoman.dist %>/styles'
            },{
                expand: true,
                cwd: 'bower_components/font-awesome',
                src: 'fonts/*',
                dest: '<%= yeoman.dist %>'
            },{
                expand: true,
                cwd: 'bower_components/font-awesome/css',
                src: '*.min.css',
                dest: '<%= yeoman.dist %>/styles'
            }
            ]
          },
          styles: {
            expand: true,
            cwd: '<%= yeoman.app %>/styles',
            dest: '<%= yeoman.dist %>/styles/',
            src: '{,*/}*.css'
          },
          scripts:{
            expand: true,
            cwd: '<%= yeoman.app %>/scripts',
            dest: '<%= yeoman.dist %>/scripts/',
            src: '{,*/}*.js'
          },
          views:{
            expand: true,
            cwd: '<%= yeoman.app %>/views',
            dest: '<%= yeoman.dist %>/views/',
            src: '{,*/}*.*'
          },
          routers:{
            expand:true,
            cwd: '<%= yeoman.app %>/routers',
            dest: '<%= yeoman.dist %>/routers/',
            src: '{,*/}*.*'
          }
        },      
        targethtml: {
            dist: {
                src: 'app/index.html',
                dest: 'dist/index.html'
            }
        },              
        replace: {//重点:将源码中的字符进行替换,可以使得支持开发中调试所见即所得
          dist: {
            options: {
              patterns: [
                {
                  match: /\.\.\/bower_components/g,
                  replacement: 'lib'
                }
              ]
            },
            files: [
              {expand: true, flatten: true, src: ['app/require-config.js'], dest: 'dist/'}
            ]
          }
        },
        watch:{
            scripts: {
                files: ['src/*.js'],
                tasks: ['jshint']
            }
        }
    });

//重点:插件注册区,上述所有插件都在这里完成注册,当需要使用其他插件是,这里需要将其引入
    //grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    //grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-bower-task');
    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.loadNpmTasks("grunt-targethtml");
    grunt.loadNpmTasks("grunt-replace");

    //注册任务,使支持命令行调用
    //default任务只进行js代码验证
    grunt.registerTask('default',['jshint']);
    //build任务完成整个构建(本例中没有加入test部分),其实质为按顺序调用上述插件,如果一个插件执行出现异常,则整个任务失败
    grunt.registerTask('build',['jshint','clean','bower','copy','targethtml','replace']);
};

package.json

{
  "name": "angular-requirejs-seed",
  "private": true,
  "version": "0.3.0",
  "description": "AngularJS seed project modified for RequireJS support.",
  "repository": "https://github.com/tnajdek/angular-requirejs-seed",
  "license": "MIT",
  "devDependencies": {
    "bower": "^1.3.1",
    "grunt-bower-task": "^0.4.0",
    "grunt-contrib-clean": "^0.6.0",
    "grunt-contrib-copy": "^0.8.0",
    "grunt-contrib-jshint": "^0.11.2",
    "grunt-contrib-requirejs": "^0.4.4",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-replace": "^0.9.2",
    "grunt-string-replace": "^1.2.0",
    "grunt-targethtml": "^0.2.6",
    "grunt-usemin": "^3.0.0",
    "grunt-wiredep": "^2.0.0",
    "http-server": "^0.6.1",
    "jasmine-core": "^2.3.0",
    "protractor": "^1.1.1",
    "requirejs": "^2.1.15",
    "shelljs": "^0.2.6",
    "grunt": "^0.4.5"
  },
  "scripts": {
    "postinstall": "bower install",
    "prestart": "npm install",
    "start": "http-server -a localhost -p 8000 -c-1 app",
    "pretest": "npm install"
  }
}

在package.json中预定义了很多指令,如start、test等,其实质为npm run 命令的简写,执行的化只需要输入npm [指令名称]即可,比如npm start就会启动http server并在localhost:8000这个位置提供http服务,具体使用时还需要查阅npm官方手册

常用命令

在项目所在目录依次执行下述命令即可快速初始化和项目构建工作。

初始化:
npm update          //根据package.json生成node_modules目录
bower update        //根据bower.json生成bower_components目录
项目构建
grunt build    //根据Gruntfile.js中的配置进行项目架构
安装移除组件
bower install jquery --save //加入jquery组件,其会保存在bower_components中
bower uninstall jquery --save   //移除jquery组件

日常工作中执行最多的是grunt命令,用其完成项目构建,而npm update和bower update经常是在需要初始化组件或者更新组件时才需要执行。

总结

前端的构建与java的maven、gradle其实是同样的思路,而且该项工作可以被团队、项目标准化。本文最重要的目的是给出一个适合的SPA应用项目构建结构,形成内部规范。从而使团队合作更加协调,更容易聚焦于用户体验、业务逻辑等具体工作中。本文中给出的方案没有给出文件合并、压缩等指令,而scss等自动生成我将其放在了开发工具中(比如webstorm),这个读者可以自行实验下。合适的工具做合适的事,最终达到思路清晰、事半功倍的效果。但文中还缺少一个非常重要的grunt部分:test!前端test编写也是非常重要的部分,考虑单独写一篇来描述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值