使用webpack打包ThinkPHP的資原始檔
利用自己的空餘時間一直在維護http://www.wx2share.com這個小網站,全是一個人在弄,由於只租得起虛擬空間,所以後臺採用了簡單方便的ThinkPHP反正主要也是做一些CURD操作ThinkPHP還是挺好用的,幫我提前做好了好多功能。
本人並不擅長前端,但是開始開發這個小網站發現,基本的功能全部要通過前端javascript來實現。一開始的時候所有的javascript程式碼全部寫在html頁面裡。也沒有太大問題,後來為了頁面效能要求把所有javascript和css全部minify一下。我採用的辦法把js和css通過資原始檔引入。然後用線上的minify工具複製過去minify以後,複製回來儲存為xxx.min.js。方法是笨一點,但是一來頁面不多,到也沒有多麻煩。但是漸漸的頁面開始多了以後,就越來越不方便了。
原來方法中的痛點
- 每次minify都要複製來複制去,檔案多了麻煩
程式碼重複利用率不高。
- 辦法1:重複程式碼全部複製到新檔案(傻)
- 辦法2:公用程式碼儲存為單獨檔案分別引入,不利入頁面效能
這時候讓我不得不考慮使用前端自動化構鍵工具
構鍵工具的選擇
我對工具的要求不高,一開始純粹主是我了minify js和css所以一開始我選用了gulp,因為用過所以直接就用了
gulp的方案
打包第三方的javascript類庫
gulp.task('vendor',()=>
gulp.src([
'bower_components/jquery/dist/jquery.js',
'bower_components/bootstrap/dist/js/bootstrap.js',
'src/lib/bootstrap-modal.js',
'src/lib/bootstrap-modalmanager.js',
'bower_components/jquery.cycle2/index.js'
]).pipe(concat('vendor.js'))
.pipe(gulpif(production,uglify({ mangle:true })))
.pipe(gulp.dest('Public/Home/js'))
);
不錯,完美。只要 NODE_ENV=production 時還能自動壓縮和混淆js
minify css並拷貝圖片Public對應目錄。
gulp.task('styles',()=>
gulp.src('src/css/show.css')
.pipe(plumber())
.pipe(autoprefixer())
.pipe(gulpif(production,cssmin()))
.pipe(gulp.dest('Public/Home/css'))
);
gulp.task('img',()=>
gulp.src('src/img/*')
.pipe(gulp.dest('Public/Home/img'))
);
gulp.task('watchcss',()=>
gulp.watch('src/css/**/*.css',['styles'])
);
也很不錯,通過gulp.watch還實現CSS實時更新。
打包自己的js檔案
gulp.task('browserify', () =>{
var bundler = browserify({
//entries:['src/js/pcshowpage.js'],
entries:['src/js/pcshowpage/index.js'],
standalone:'pcshowpage',
cache:{},
packageCache:{},
plugin:[]
})
bundler.transform(babelify,{ presets: ["es2015"]});
bundler.bundle()
.pipe(source('pcshowpage.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(gulpif(production, streamify(uglify({ mangle: true }))))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('Public/Home/js'))}
);
相當以及完全的完美,通過babel的轉譯還能愉快的用ES6來寫程式碼了,什麼新特性都能用來了。程式碼也能模組化了。不同的功能寫成不同的模組,只要import 一下就可以用了。gulp通過browserify幫你轉譯成瀏覽器能識別的ES5寫起程式碼來非常愉快。
通過watchify也能實現程式碼的實時更新
gulp.task('browserify-watch', () =>{
//var bundler = watchify(browserify('src/js/pcshowpage.js', watchify.args));
var bundler = browserify({
//entries:['src/js/pcshowpage.js'],
entries:['src/js/pcshowpage/index.js'],
standalone:'pcshowpage',
cache:{},
packageCache:{},
plugin:[watchify]
})
bundler.transform(babelify,{ presets: ["es2015"]});
bundler.on('update', rebundle);
return rebundle();
function rebundle() {
var start = Date.now();
bundler.bundle()
.on('error', function(err) {
gutil.log(gutil.colors.red(err.toString()));
})
.on('end', function() {
gutil.log(gutil.colors.green('Finished rebundling in', (Date.now() - start) + 'ms.'));
})
.pipe(source('pcshowpage.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('Public/Home/js'));
}
});
看起來非常的美好了,愉快的寫完了這個頁面開始重構別一個頁面的時候,問題來了,我不會用gulp定義多個出口檔案,(就是我不會,沒用過而已,gulp肯定能做到的)我也知道用程式碼來定義多個bundler肯定是做到,但是我滿腦子就想到了webpack定義多個出口檔案是多麼方便。所以我就對gulp始亂終棄了。
webpack的方案
entry: {
css:"./src/css/index.js",
mainpage:"./src/js/mainpage/index.js",
pcshowpage: "./src/js/pcshowpage/index.js",
combpage:"./src/js/combpage/index.js",
tbkpage:"./src/js/tbkpage/index.js",
},
output: {
library: '[name]',
libraryTarget: "umd",
path: path.resolve(__dirname, PublicPath),
filename: 'js/[name].js'
}
就這麼簡單明瞭,每個js檔案生成了單獨的輸出檔案。不同的頁面引入不同的js就好了。接下來通過CommonsChunkPlugin抽取公用的模組到common.js,
new webpack.optimize.CommonsChunkPlugin({
name: "common",
minChunks : 2,
chunks:["mainpage","pcshowpage","combpage","tbkpage"]
}),
這樣一些幾個頁面通用的程式碼都被抽到common.js中了。其它圖片,CSS, Font等內容全部由不同的loader來處理,看起來比gulp明瞭多了。
開發與產生環境不同生成的配置。
開發環境
在目錄下,新建一個dev.js
require('shelljs/global')
var merge = require('webpack-merge')
var path = require('path')
var webpack = require('webpack')
var webpackConfig = require('./webpack.config')
var devcoifng=merge(webpackConfig, {
devtool: '#eval-source-map', //可以理解為繼承通用配置,在dev模式下生成source map 方便除錯。
})
var c=webpack(devcoifng);
c.watch({ // watch options:
aggregateTimeout: 300, // wait so long for more changes
poll: true // use polling instead of native watchers
// pass a number to set the polling interval
}, function(err, stats) {
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n')
});
執行 node dev.js
通過wath()實現了,程式碼的實時更新。修改完js程式碼一儲存,資源目錄下的檔案就可以更新,重新整理一下頁面就可以載入最新的程式碼了。debug的時候由於有source map 就可以看清到底是哪裡的程式碼有問題,還支援單步除錯。
生成環境
由於生成環境還需要配置的東西更多,所以我新建了一個prod.conf.js
var webpack = require('webpack')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.config')
module.exports = merge(baseWebpackConfig, {
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: 'production'
}
}),
new webpack.optimize.UglifyJsPlugin({ //壓縮混淆程式碼
compress: {
warnings: false
}
}),
new webpack.optimize.OccurenceOrderPlugin(),
// extract css into its own file
]
})
再建一個build.js
require('shelljs/global')
env.NODE_ENV = 'production'
var path = require('path')
var webpack = require('webpack')
var webpackConfig = require('./webpack.prod.conf')
webpack(webpackConfig, function (err, stats) {
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n')
})
只要執行 node build.js
就可以生成已經壓縮混淆好的js和css了,可以說相當的方便了
還沒有解決的問題
- 竟然沒有辦法僅僅把程式碼contact在一起的辦法,我的vendor檔案還是用gulp生成的,還好這個檔案基本上不怎麼修改。我過通過定義
var dependencies = [
'./bower_components/jquery/dist/jquery.js',
'./bower_components/bootstrap/dist/js/bootstrap.js',
'./src/lib/bootstrap-modal.js',
'./src/lib/bootstrap-modalmanager.js',
'./bower_components/jquery.cycle2/index.js'
]
........
//然後在 entry 加入
entry:{
vendor:dependencies
........
.......
}
//再配置一個
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
minChunks : Infinity,
}),
.....
這樣生成的vendor檔案裡面所有的類庫全變成AMD JS 規範了再頁面中引入沒有辦法正常使用jquery等類庫,gulp contact的做法就是簡單把幾個檔案合併在一起而已,在頁面中引入不影響使用,
還有一個問題是CDN引起的問題。
由於我的html模版頁面還是自己寫的,而且沒有用webpack處理,因為我用了thinkphp的模版功能,頁面的header footer 我定義在base.html中,其它頁面繼承一下,這樣,好多重複程式碼就不用寫了。但是也沒有辦法用html-webpack-plugin來處理了,為了解決CDN快取問題,我給每個資原始檔加了一個時間戳,每次都要手動去改一下好麻煩
希望大家能提供好的方法來幫我解決這兩個問題。