场景

某个时刻,某人让你写一个页面。作为一个专业的web前端开发工程师,你毫不犹豫的创建了如下的文档结构。

webapp
|-- css
|   |-- reset.css
|   |-- common.css
|-- js
|   |-- main.js
|-- img
|   |-- bg.png
|-- index.html

过两天需要添加一个about.html、main.html…而且每个页面的风格都大不相同。这时你需要为每个页面都搞一套css,于是在css目录下又多了about.css、main.css。一切都还ok,开发继续。

搞了一个星期,所有需求都ok,用1px的精度打败了挑剔的UE,凭着一手牛逼的兼容性解决方案糊弄过了QA。然后兴致勃勃的开始上线。

然后悲剧了,老大说http链接太多了。每个css都要发起一次http请求,浪费时间。需要优化一下。你一想这不是分分钟的事儿嘛~ cat 一键合并!然后重新上线。

上线不到一分钟,公司就接到用户反馈,页面显示异常!特么一看原来是样式被覆盖了。common.css中的选择器#container被about.css的覆盖了。 想一想,问题的修复并不困难,给每个页面的css都加一个前缀。如:.about-container完美解决问题。

事实是,about这个页面选择器太多!!!给每个选择器前面都加上.about是一项巨大的工程,仔细一想得搞个工具来解决。

less

上网一查发现还有less这种神器,于是赶紧搬到项目中。于是css文件内容变成了这样。

.about-container {
    .about-title {
        font-size: 1.6rem;
        color: #333;
    }
    .about-content {
        padding: 6px;
    }
}

修改完后一键编译:

lessc about.less about.css

一切又回归正常,还有个小问题。就是每次修改less文件都要手动执行一下脚本,对于修改一次就需要编译的强迫症来说,这绝对不能忍! 好吧,继续折腾。

Grunt

用watch监听文件改动,然后自动编译less文件。配置如下:

module.exports = function (grunt) {
    grunt.initConfig({
        less: {
            dev: {
                options: {
                    compress: true,
                    cleancss: true,
                    optimization: 2
                    //sourceMap: true
                },
                files: [{
                    expand: true,
                    cwd: 'css/less/',
                    src: ['*.less'],
                    dest: 'css/',
                    ext: '.css'
                }]
            }
        },
        watch: {
            less: {
                files: ['css/less/*.less'],
                tasks: ['less']
            }
        }
    });
}

然后执行:grunt watch

折腾无止境,虽然到这一步已经很nice了,但是在写less的时候各种css3的前缀也很烦人。对于强迫症来说,最不能接受的是切页面的时候F5已按烂的窘境。。。

最近新技术又火了(React),你想在项目中试用试用,发现jsx也需要每修改一次就编译一次,有了以上的经验,你很轻松的在grunt中配置了一个自动编译jsx的任务~

到目前为止,基本已经解放了前端很大部分的成本!F5的寿命得到了显著提升!

最后

附上所有的Gruntfile配置

module.exports = function(grunt) {
    grunt.initConfig({
        // 监听到文件改动后自动刷新页面
        connect: {
            options: {
                port: 8224,
                hostname: '*',
                base: '.',
                livereload: 8225
            },
            dev: {
                options: {
                    open: true,
                    base: ['.']
                }
            }
        },
        less: {
            dev: {
                options: {
                    compress: true,
                    cleancss: true,
                    optimization: 2
                    //sourceMap: true
                },
                files: [{
                    expand: true,
                    cwd: 'static/css/less/',
                    src: ['*.less'],
                    dest: 'static/css/',
                    ext: '.css'
                }]
            }
        },
        watch: {
            style: {
                files: ['static/css/less/*.less'],
                tasks: ['less', 'autoprefixer'],
                options: {
                    nospawn: true,
                    livereload: '<%= connect.options.livereload %>'
                }
            },
            script: {
                files: ['static/js/tpl/*.js'],
                tasks: ['react'],
                options: {
                    livereload: '<%= connect.options.livereload %>'
                }
            },
            html: {
                files: ['**/*.html'],
                options: {
                    livereload: '<%= connect.options.livereload %>'
                }
            },
            php: {
                files: ['**/*.php'],
                options: {
                    livereload: '<%= connect.options.livereload %>'
                }
            }
        },
        // jsx 编译
        react: {
            dev: {
                files: [{
                    expand: true,
                    cwd: 'static/js/tpl/',
                    src: ['*.js'],
                    dest: 'static/build/',
                    ext: '.js'
                }]
            }
        },
        // 自动添加css3前缀
        autoprefixer: {
            dev: {
                options: {
                    browsers: ['last 2 versions', 'ie 8', 'ie 9']
                },
                src: 'static/css/*.css'
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-react');
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-contrib-connect');

    grunt.registerTask('default', ['connect:dev', 'watch']);
}

你可以在github上看见整个项目。每次创建新应用的时候先clone,然后基于这个模型进行开发~ base_app

以上的配置在日常开发中基本足够,但是在写非纯前端的项目中还有很多不足。特别是基于后端框架的开发过程中,node的server并不能直接解析php。这个功能有待改进,有时间自己搞一个~(可能已经有了这种工具,只是我不知道。。。知道的求告知)