原文地址:前端快速開發模版
之前一直在開發移動端的單頁面應用,而移動web對性能和效果要求是比較苛刻的,所以用的都是輕量級的框架體系。基本上是Zepto加自己開發的單頁面框架,再加上若干簡單的庫。這樣前端要加載的文件很小,修改起來也非常方便,同時這樣的輕量級的體系使用gulp進行自動化管理也是非常合適的。
自從開發react項目后,對它的工程化和開發模式也是開了眼界,標准的框架體系就是重量級的react+redux+webpack。開發大型項目和后台管理系統用react,vue確實是不錯的。但是開發一些小項目,比如前端h5之類的又有殺雞用牛刀的感覺。
於是借鑒webpack工程化思想動手寫了個前端快速開發模版,用於開發快速簡潔的項目,比如之前的單頁面應用。而這個項目模版就是類似前端開發的腳手架了,基本的思路就是自動化和前端編譯 ,主要達到以下目的:
- 使用less預編譯css,使用autoprefixer進行樣式兼容
- js代碼用babel編譯,因此可基於es6,es7編寫代碼
- 類庫文件使用npm包,js文件使用browserify組織和打包,不再使用sea,require之類的加載器
- html代碼使用swig模版引擎進行組織和預編譯
- 靜態資源版本使用rev管理,當css或js文件內容有變化,打包時自動更新版本
- 前端開發基礎樣式,框架的整合。
工具鏈
- 構建工具gulp
- 編譯打包工具browserify
- 代碼編譯babel
- css兼容autoprefixer
- css預編譯less
- html代碼預編譯模版swig
- 前端代碼版本更新rev
公共類庫
- jQuery
- Zepto
- 自己開發的簡潔單頁面框架app.js
- 圖標fontello
- 字體reboto
- 基礎樣式base.css
文件目錄結構
- css: css文件
- less: less文件
- js: js文件
- dist: 打包生成的項目文件
- layout: html母板
- lib: 公共類庫
- template: html頁面
項目自動化
沒有使用重量級的webpack,而是使用輕量級的gulp和browserify
公共類庫
公共類庫的文件,主要有基礎樣式base.css,圖標樣式reboto,字體樣式fontello,單頁面框架app,直接拷貝到dist文件就ok。其他npm包跟nodejs項目一樣在node_modules中,不需要處理。
//復制公共庫目錄下的所有內容
gulp.task('copy',function(){
return gulp.src('./lib/**')
.pipe(gulp.dest('./dist/lib/'));
});
css文件
首先將less文件轉為css,用autoprefixer添加兼容性前綴,合並壓縮,放到dist目錄,最后用rev插件生成版本信息,這個后面用於替換更新鏈接用。
//編譯css,添加兼容后綴,壓縮
gulp.task('css', function() {
return gulp.src('./less/*.less')
.pipe(less())
// .pipe(concatCss("index.css"))
.pipe(postcss([ autoprefixer({
"browsers": ["last 2 version", "> 0.5%", "ie 6-8","Firefox < 20"]
// "browsers": ["last 2 version", "> 0.1%"]
})
]))
.pipe(cleanCSS())
// .pipe(rename({suffix: '.min'}))
// .pipe(gulp.dest('./dist/css'));
.pipe(rev())
.pipe(gulp.dest('./dist/css'))
.pipe(rev.manifest())
.pipe(gulp.dest('./rev/css'));
});
js文件
我這里有兩個入口文件,可以隨時將新入口文件添加到scripts數組中,這里做的就是使用babel轉換代碼,然后將外部文件,npm包打包進來,生成sourcemap,輸出到dist文件夾,最后一樣用rev插件生成版本信息。
var scripts=['app','index'];
scripts.map(name=>{
gulp.task(name,function(){
return browserify({
entries:'./js/'+name+'.js', //entries file name
debug:true // set true so the bundle file can generate sourcemap
})
.transform(babelify,{
plugins: ["transform-runtime"],
presets: [
'es2015', //convert to es5
'stage-0' //es7
]
})
.bundle() //merge
.pipe(source(name+'.js'))
.pipe(buffer())
// .pipe(uglify())
.pipe(sourcemaps.init({loadMaps: true})) //External sourcemap file
.pipe(sourcemaps.write('./'))
.pipe(rev())
.pipe(gulp.dest('./dist/js/'))
.pipe(rev.manifest(name+'.json'))
.pipe(gulp.dest('./rev/js/'));
});
});
html文件
html編譯我使用的是模版引擎是swig,這里用的是gulp-swig插件,當然也可以用ejs或jade的引擎。但我個人比較習慣用swig,因為它更靈活,支持各種文件格式,可以直接使用html文件,也支持ejs不支持的layout等。gulp-swig插件的使用也非常簡單。
//swig編譯輸出html
gulp.task('html', function() {
return gulp.src('./template/*.html')
.pipe(swig({
defaults: {cache: false }
}))
.pipe(gulp.dest('./'))
});
文件版本控制
之前編譯css和js的時候已經調用rev生成了記錄了版本信息的json文件,如果內容有變化它會生成新的md5文件名。這里就是用rev-collector替換html文件中除md5部分外相同文件名的鏈接,這樣就就實現了版本更新功能,用這個插件可以更新css,js,圖片等的版本。
//更新css和js版本,同時替換html中的鏈接標簽
gulp.task('rev', scripts.concat(["css","html"]),function () {
return gulp.src(['./rev/**/*.json', './*.html'])//add md5 suffix to js and css file, replace the link of html as well
.pipe( revCollector({
replaceReved: true,
dirReplacements: {
'/css': '/css',
'/js': '/js'
}
}))
.pipe( gulp.dest('./dist') );
});
html里面替換后的鏈接格式如下
<link rel="stylesheet" href="./css/app-d333f590b0.css">
<script src="./js/app-62bad8e549.js"></script>
項目模版
項目自動化配置完后,接着就是配置項目模版了,這里分兩種類型的模版:1.移動端優先的單頁面;2.pc端優先的普通頁面。
swig標簽語法
- extends 繼承父模板,必須在文件最前
- block 定義一個塊,使之可以被繼承的模板重寫,或者重寫父模板的同名塊
- parent 將父模板中同名塊注入當前塊中
- include 包含一個模板到當前位置
移動端單頁面模版
在layout文件夾新建移動端的單頁面模版app-layout.html,然后再建立子目錄partial,用於存放公共代碼塊。
我們知道加載頁面時再去加載外部文件是需要耗費加載時間,而頁面直出可以大大提高加載速度,同時預先加載部分樣式也可以避免加載時的白屏問題。這也是我們這里將公共部分的js和css以代碼塊的形式嵌入到html文件中,達到頁面直出的效果。
swig支持將html文件嵌入,同時也可以將css文件嵌入,我這里就是把單頁面樣式文件app.css和基礎樣式文件app-base.css的內容直接輸出到style標簽中。主要的代碼塊有:
- meta.html: 頭部meta標簽
- header-script.html: 主要是動態設置fontsize大小的腳本
- footer-script.html: 主要是頁面加載過程的動畫腳本
當然了圖標樣式,好看的字體文件,單頁面的css,基礎的樣式css都有, 同時母版還挖出了幾個block給繼承的頁面進行重寫,比如block title,block css,block js,內容block content。css塊和js塊既可以寫外部鏈接也可以直接寫代碼。 我們可以根據自己的需求進行修改和配置文件,具體內容看如下代碼。
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
{% include './partial/meta.html' %}
<!-- 頁面內加載的頭部js -->
{% include './partial/header-script.html' %}
<!-- 頁面內加載的css -->
<style>{% include '../lib/app/app.css' %}{% include '../lib/app-base.css' %}</style>
<!-- 圖標 -->
<link rel="stylesheet" href="./lib/fontello/fontello.css">
<!-- 字體 -->
<link rel="stylesheet" href="./lib/reboto/roboto.css">
<!-- 項目css -->
<link rel="stylesheet" href="./css/app.css">
<!-- 外部css塊 -->
{% block css %}{% endblock %}
</head>
<body>
<div id="app" class="view-wrap"></div>
<div id="loading" class="loading-wrap">
<div class="loading">
<div class="round"></div>
<div class="txt">0%</div>
</div>
</div>
<div id="freshing" class="fresh-wrap">
<div class="loading">
<div class="round"></div>
<p class="txt">loading...</p>
</div>
</div>
<!-- 內容填充塊 -->
{% autoescape false %}
{% block content %}{% endblock %}
{% endautoescape %}
<!-- 頁面內加載的尾部js -->
{% include './partial/footer-script.html' %}
<!-- 外部js塊 -->
{% block js %}{% endblock %}
</body>
</html>
PC端優先模版
接着就是建立PC端優先的模版layout.html,這個和單頁面模版相似,主要區別就是基礎樣式,同時不用嵌入移動端的代碼,頁面模版請看如下代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<title>{% block title %}{% endblock %}</title>
{% include './partial/meta.html' %}
<link rel="stylesheet" href="./lib/fontello/fontello.css">
<link rel="stylesheet" href="./lib/reboto/roboto.css">
<style>{% include '../lib/base.css' %}</style>
<link rel="stylesheet" href="./css/index.css">
{% block css %}{% endblock %}
</head>
<body>
{% autoescape false %}
{% block content %}{% endblock %}
{% endautoescape %}
{% include './partial/copyright.html' %}
{% block js %}{% endblock %}
</body>
</html>
使用方式
項目模板完成了,我以建立單頁面項目為例演示使用的步驟
-
首先從github clone模版到文件夾webapp-project,安裝相關npm包。
git clone https://github.com/edwardzhong/project-template.git webapp-project npm install -
在less文件夾內建立app.less,並編寫代碼
@lightBlue:hsl(198, 73%, 53%); // base html,body{ font-family: "Roboto", "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif; font-size: 0.25rem; color: #555; width: 100%; height: 100%; } body,p,h1,h2,h3,h4,h5,h6{margin:0;} ul,ol{list-type:none; margin:0; padding:0;} // todo more ... -
在js文件夾建立app.js並編寫代碼, 使用es6寫代碼就是舒服😊
require('../lib/zepto.js');// zepto沒有使用CommonJs規范,修改后使用require引入 import { webApp } from '../lib/app/app.js' var App=webApp({ container:'app', animate:{ effects:['slideInRight', 'slideOutLeft'],delay:600}, preLoad:function(){ }, aftLoad:function(){ loadPercent(100); } }) .other('/index',{temId:'tempIndex',create:createIndex}) .when('/button',{temId:'tempButton'}) .when('/form',{temId:'tempForm'}) .when('/dialog',{temId:'tempDialog',create:createDialog}) .init(); // todo: more ... -
在template文件夾建立app.html,引用單頁面模板app-layout.html,並編寫內容,我這里把單頁面的各個視圖代碼也以代碼塊的方式引入。
{% extends '../layout/app-layout.html' %} {% block title %}Web App{% endblock %} {% block js %} <script src="./js/app.js"></script> {% endblock %} {% block content %} {% include './partial/index.html' %} {% include './partial/button.html' %} {% include './partial/dialog.html' %} {% include './partial/form.html' %} {% endblock %} -
運行自動化命令,進行編譯,合並,壓縮,打包
npm run build -
最后項目文件都輸出到dist文件夾,找到app.html運行即可。
