Webpack + Angular的組件化實踐

jopen 9年前發布 | 39K 次閱讀 Angular 前端技術

原文 http://segmentfault.com/a/1190000003915443


最近寫復旦二手平臺的時候開始嘗試用一直推崇了很久的組件化。經過一番抉擇之后選擇了 webpack + angular 的組合。所以在這里分享一下具體的實踐流程。

Webpack

Webpack是目前比較流行的前端打包工具,它同時支持AMD、CMD兩種模塊寫法,也原生支持npm或者bower安裝的模塊。它還能給css、scss、less、coffeescript、es6、圖片、html以及諸如jade、ejs的模板打包。

所以有什么卵用呢?

簡單地說就是,

1、原來你需要在<script>中引入angular或者其他的npm模塊(有些npm模塊甚至沒有提供可以直接在瀏覽器端引用的js文件),現在只需要:

npm install angular

然后在app.js中:

angular = require('angular');
var app = angular.module('myApp',[]);

然后執行

webpack app.js bundle.js

webpack會自動分析依賴,然后編譯,這樣bundle.js就是你想要的東西了。

2、組件化的時候你要在頁面中引入一大堆東西,比如這樣:

<!--index.html-->
<script type="text/javascript" src="header.js">
<script type="text/javascript" src="tab.js">
<script type="text/javascript" src="waterfoo.js">
<link rel="stylesheet" type="text/css" href="header.css">
<link rel="stylesheet" type="text/css" href="tab.css">
<link rel="stylesheet" type="text/css" href="waterfoo.css">

當然你可能不會用如此傻的方式引入組件,但如果用了webpack之后只需要這樣:

//index-bundle.js
require('header.js');
require('header.scss');
require('tab.js');
require('tab.scss');
require('waterfoo.js');
require('waterfoo.scss');

然后在index.html中引入打包好的js即可(沒錯連scss都給你打包好了/w\ 它甚至還能把圖片打包成base64,然后替換所有url):

<!--index.html-->
<script type="text/javascript" src="bundle.js">

上面只是最基礎的用法,所以有些人可能要吐槽這不是browserify也能干么/w\ 可以看看這篇文章[《用webpack來取代browserify》

]( http://segmentfault.com/a/1190000002490637) ,以及webpack還有很多酷炫的功能這里就不再贅述。

</div>

項目實踐流程

回到正題吧,復旦二手平臺這個項目更像是多頁面的網站而不是單頁面的web應用,所以我決定嘗試用node去寫一個渲染層(個人感覺Koajs目前還不太成熟,所以選用了Express4.0),這樣后臺寫JAVA的同學就只需要給我提供數據API就行了,把數據庫接口化。

渲染引擎用的是jade,而angular的角色更像是一個頁面的“控制器”,控制由jade生成的頁面,而不是去自行加載html自行渲染(說白了就是放棄了用angular渲染頁面)。

Webpack和Angular的結合

Angular自帶了Module以及Directive機制,但Angular1.x版本下,我覺得這些機制不太適合做這種多頁面網站的組件化,而且也違背了選用jade渲染的初衷。

Angular自己有自己獨特的依賴注入以及模塊聲明方式,看起來似乎和Webpack是水火不容的,但事實上他們完全可以融合。只需要多幾行代碼:

主文件app.js大概長這樣:

var angular = require('angular');
var starkAPP = angular.module('starkAPP', [
]);
module.exports = starkAPP;

注意到我們在這里把starkAPP作為模塊的接口暴露出去,然后我們就可以這樣寫controller:

//someController.js
var starkAPP = require('./app.js');
starkAPP.controller('someController', ['$scope', function($scope) {
    //...
}])

運行一下webpack someController.js bundle.js即生成了一個可以使用的bundle.js。

當然如果你有一堆controller、directive、service,最好用個main.js全部聲明一下依賴:

//main.js
require('./Controller1');
require('./Controller2');
require('./Controller3');
require('./Service');
require('./Directive');

目錄結構設計

這里我只放了瀏覽器端的文件結構,整個的項目結構可以看 這里

|package.json 存放npm相關的配置
|gulpfile.js gulp的配置文件
|webpack.config.js 存放webpack相關的配置
|build 存放構建完畢的資源文件
|node_modules 不解釋了= =
|src 源代碼
    └── components 組件
    ├── angular angular組件,比如各種directive、service
    ├── base 需要全站引入的組件,比如reset.css
    └── header 頭部組件
         ├── header.jade
        ├── header.scss
        └── header.js
     └── pages 頁面定義文件
    └──  index 首頁配置文件
         ├── index.js
         └── index.scss
     └── template 提供給node渲染的jade模板
     └── index.jade 首頁模板

看文件結構絕對是云里霧里的,下面詳細說明:

1、首先這是首頁的模板index.jade

html(ng-app="starkAPP")
    head
        link(rel='stylesheet',href='/static/css/index.css')
        script(type='text/javascript',src='/static/js/index.bundle.js')
    body(ng-view)
        include ../components/header/header.jade

注意到我們引入了header的jade,以及兩個文件index.css和index.bundle.js

2、index.css是啥?

它是pages/index/里面的index.scss編譯成的:

// pages/index/index.scss
@import '../../components/header/header';
@import '../../components/base/base';

注意到我們在這里引入了header.scss

3、index.bundle.js是啥?

它是pages/index/里面的index.js經過webpack打包成的東西

// pages/index/index/js
require('../../components/angular/app.js');
require('../../components/header/header.js');

我們在這里引入了angular以及header.js

總之,pages下面放的就是各個頁面的組件依賴文件

比如我的首頁index依賴了一個組件header,那么我需要在index.js和index.scss中聲明我依賴了header.js以及header.scss

其實用webpack打包的話,只需要一個定義文件就可以同時打包js和scss,但我還不太確定webpack打包scss這種方法是否成熟。

項目構建

自動構建工具我選擇了gulp全家桶,簡單地說就是讀取src/pages/[page-name]下面所有的js、scss文件,把他們編譯到對應的build/[page-name]下面,并且監聽文件變化以便熱替換。

這是我的gulpfile.js:

var gulp = require('gulp');
var sass = require('gulp-sass'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    uglify = require('gulp-uglify'),
    clean = require('gulp-clean'),
    webpack = require('gulp-webpack');
var webpackConfig = require('./webpack.config');
gulp.task('default', ['clean', 'watch', 'sass:watch', 'sass', 'webpack']);
gulp.task('sass:watch', function() {
    gulp.watch('src/pages/*/*.scss', ['sass']);
    gulp.watch('src/components/*/*.scss', ['sass']);
});
gulp.task('sass', function() {
    gulp.src(['src/pages/**/*.scss'])
        .pipe(sass.sync().on('error', sass.logError))
        .pipe(minifycss())
        .pipe(gulp.dest('build/pages'));
});
gulp.task('clean', function() {
    return gulp.src(['build/pages'], {
            read: false
        })
        .pipe(clean());
});
gulp.task("webpack", function() {
    return gulp
        .src('./')
        .pipe(webpack(webpackConfig))
        //.pipe(uglify())
        .pipe(gulp.dest('./build/pages'));
});
gulp.task('watch', function() {
    gulp.watch('src/components/*/*.js', ['webpack']);
    gulp.watch('src/pages/*/*.js', ['webpack']);
});

以及簡化后的webpack.config.js:

module.exports = {
    entry: {
        'index/index': './src/pages/index/index.js'
    },
    output: {
        filename: '[name].bundle.js'
    }
};

怎么寫一個組件?

比如現在我們要寫一個waterfoo(瀑布流)組件

首先我們在src/components/下面新建一個文件夾waterfoo,然后建立

  • waterfoo.jade

    </li>

  • waterfoo.scss

    </li>

  • waterfoo.js

    </li> </ul>

    分別對應waterfoo組件的模板、樣式、以及行為,當然waterfoo組件完全可以依賴其他更低層級組件,只需要在相應的文件中聲明依賴即可。

    怎么在頁面中加入組件?

    比如現在我們要把waterfoo(瀑布流)組件加到首頁index中

    首先在src/template/index.jade中引入模板:

    include ../components/waterfoo/waterfoo.jade

    然后在src/pages/index/下面的index.js、index.scss配置依賴:

    //index.js
    require('../../components/waterfoo/waterfoo');
    //index.scss
    @import '../../components/waterfoo/waterfoo';

    有更優雅更傻瓜的方法嗎?

    當然有,未來的期望是用webpack把js和scss一起打包,并且把template和pages文件夾合并(具體配置express渲染路徑的方法我還在探索),大概就是這樣的效果:

    src/pages/index/下面放著首頁的配置文件:

    • index.jade

      </li>

    • index-config.js

      </li>

    • index.scss (一些非組件的樣式)

      </li> </ul>

      index.jade是模板

      html(ng-app="starkAPP")
         head
             script(type='text/javascript',src='/static/js/index.bundle.js')
         body(ng-view)
             include ../components/header/header.jade

      index-config.js聲明依賴的組件

      require('../../components/angular/app.js');
      require('../../components/header/header.js');
      require('../../components/header/header.scss');
      require('../../components/waterfoo/waterfoo.js');
      require('../../components/waterfoo/waterfoo.scss');
      require('./index.scss');

      -------

      最后

      這個項目的源碼在 Github · starkwang/FDSHM ,FDSHM是Fudan Secondhand Market的縮寫/w\,現在還在慢慢地寫組件中,爭取這學期上線吧……(缺人啊QAQ)

      </div>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!