Webpack工程化解決方案easywebpack

Github:github.com/hubcarl/easy

1.背景

隨著越來越多的項目採用vue, react, weex進行業務開發, 在前端構建方面大多數是用webpack進行構建。但存在以下問題:

  • 各個項目都是自己從零編寫webpack配置,存在很多定製性的配置,無法復用,大多都是複製拷貝。
  • Webpack 配置項多,正式環境,js/css/image壓縮,css extract, css module, sass, less, stylus, postcss, babel, cdn,單頁面,多頁面,熱更新, 前端渲染,伺服器渲染等特性時,配置非常複雜。

在前端工程構建方面迫切需要一套基於webpack的通用且可擴展性強的前端工程化解決方案.

2.我們要解決什麼問題

針對背景裡面提到的一些問題, 基於webpack + egg項目的工程化, 當初想到和後面實踐中遇到問題, 主要有如下問題需要解決:

  • Vue服務端渲染性能如何?
  • webpack 客戶端(browser)運行模式打包支持
  • webpack 服務端(node)運行模式打包支持
  • 如何實現服務端和客戶端代碼修改webpack熱更新功能
  • webpack打包配置太複雜(客戶端,服務端), 如何簡化和多項目復用
  • 開發, 測試, 正式等多環境支持, css/js/image的壓縮和hash, cdn等功能如何配置, 頁面依賴的css和js如何載入
  • 如何快速擴展出基於vue, react前端框架服務端和客戶端渲染的解決方案

3.Webpack工程化設計

我們知道webpack是一個前端打包構建工具, 功能強大, 意味的配置也會複雜. 我們可以通過針對vue, react等前端框架,採用不同的配置構建不同的解決方案. 雖然這樣能實現, 但持續維護的成本大, 多項目使用時就只能採用拷貝的方式, 另外還有一些優化和打包技巧都需要各自處理.

基於以上的一些問題和想法, 我希望基於webpack的前端工程方案大概是這個樣子:

  • webpack太複雜, 項目可重複性和維護性低, 是不是可以把基礎的配置固化, 然後基於基礎的配置擴展出具體的解決方案(vue/react等打包方案).
  • webpack配置支持多環境配置, 根據環境很方便的設置是否開啟source-map, hash, 壓縮等特性.
  • webpack配置的普通做法是寫配置, 是不是可以採用面向對象的方式來編寫配置.
  • 能夠基於基礎配置很簡單的擴展出基於vue, react 服務端渲染的解決方案
  • 針對egg + webpack內存編譯和熱更新功能與框架無關, 可以抽離出來, 做成通用的插件

4.設計實現

4.1. Webpack 基礎配置固化

在使用webpack對不同的前端框架進行打包和處理時, 有些配置是公共的, 有些特性是共性的, 我們把這些抽離出來, 並提供介面進行設置和擴展.

4.2 公共配置

  • option: entry讀取, output, extensions 等基礎配置
  • loader: babel-loader, json-loader, url-loader, style-loader, css-loader, sass-loader, less-loader, postcss-loader, autoprefixer 等
  • plugin: webpack.DefinePlugin(process.env.NODE_ENV), CommonsChunkPlugin等

4.3 公共特性

  • js/css/image 是否hash
  • js/css/image 是否壓縮
  • js/css commonChunk處理

4.4 開發輔助特性

  • 編譯進度條插件 ProgressBarPlugin
  • 資源依賴表 ManifestPlugin
  • 熱更新處理 HotModuleReplacementPlugin
  • ……

以上一些公共特性是初步梳理出來的, 不與具體的前端框架耦合. 針對這些特性可以單獨寫成一個npm組件, 並提供擴展介面進行覆蓋, 刪除和擴展功能.

在具體實現時, 可以根據 env 默認開啟或者關閉一些特性. 比如本地開發時, 關閉 js/css/image 的hash和壓縮,開啟熱更新功能.

5.easywebpack功能介紹

easywebpack 是在 Webpack 上擴展出來的前端項目構建工程化解決方案, 同時支持 Vue,React 服務端端渲染構建,也支持 Weex Nativ 和 Web 構建. 同時內置 Webpack 常用功能和基礎插件,支持插件動態安裝,功能開啟,框架擴展等特性。

目前 easywebpack 已支持 Webpack3(easywebpack 3.x.x) 和 Webpack2(easywebpack 1.x.x),很多新特性可以馬上嘗鮮了。另外支持 Mac 和 Window 系統(被 Windows 坑了好久,如果可以,儘可能用 Mac)。

  • 首先我們看看 easywebpack 具備的基礎功能:

  • 針對上面梳理的公共基礎配置, 可以把webpack配置分離成三部分: option, loader, plugin
  • 針對客戶端和服務端打包的差異性, 設計成三個類 WebpackBaseBuilder, WebpackClientBuilder, WebpackServerBuilder

最終形成Webpack構建解決方案:easywebpack

5.1 基礎功能

  • 支持服務端渲染, 前端渲染, 靜態頁面渲染三種構建方式
  • 支持單頁面, 多頁面服務端渲染構建模式
  • 默認支持 dev,test, prod 環境配置
  • 集成 webpack-hot-middleware 熱更新實現
  • 支持 entry 原生配置和目錄遍歷自動構造 entry 功能
  • 支持自動根據後綴名構建 entry 文件,比如 .vue 和 .jsx 文件為入口文件
  • 支持 es6 class 繼承方式編寫 Webpack 配置
  • 支持 js/css/image 壓縮, 內置支持 CDN 特性
  • 支持 css/sass/less/stylus, 支持css module 和 css extract 特性
  • 支持 loader 是否啟用,合併,覆蓋配置
  • 支持 plugin 是否啟用,合併,覆蓋配置
  • 支持 loader 和 plugin npm module 是否啟用,按需安裝
  • 支持 eslint, postcss 等特性
  • 提供 easywebpack-cli 和 webpack-tool 輔助工具。

5.2 內置loader

  • babel-loader
  • eslint-loader
  • style-loader
  • css-loader
  • postcss-loader
  • sass-loader
  • less-loader
  • stylus-loader
  • url-loader

5.3 內置plugin

  • extract-text-webpack-plugin
  • npm-install-webpack-plugin
  • webpack.optimize.ModuleConcatenationPlugin
  • webpack.NoEmitOnErrorsPlugin
  • webpack.ProvidePlugin
  • webpack.DefinePlugin
  • webpack.optimize.CommonsChunkPlugin
  • webpack.optimize.UglifyJsPlugin
  • webpack.HotModuleReplacementPlugin
  • progress-bar-webpack-plugin
  • imagemin-webpack-plugin
  • directory-named-webpack-plugin
  • webpack.NormalModuleReplacementPlugin
  • webpack.IgnorePlugin
  • html-webpack-plugin

6.已有解決方案

基於 easywebpack 基礎骨架,目前已擴展 Vue React Weex 三種解決方案,其中 easywebpack-vue 和 easywebpack-react 支持純前端構建和Node端構建模式,easywebpack-weex 支持 Native 和 Web 構建模式。

  • easywebpack-vue
  • easywebpack-react
  • easywebpack-weex

如果你需要基於 easywebpack 擴展其他解決方案也很簡單, 只需要繼承 easywebpack 的 WebpackClientBuilder(前端渲染構建模式) 和 WebpackServerBuilder(服務端渲染構建模式) 即可, 你只需要把框架相關的擴展進來即可。 大概實現如下:

7.解決方案擴展實現

7.1 前端渲染構建模式

const EasyWebpack = require(easywebpack);nclass WebpackClientBuilder extends EasyWebpack.WebpackClientBuilder {nconstructor(config) {nsuper(config);n// call below api custom client buildern}n}nmodule.exports = WebpackClientBuilder;n

7.2 服務端渲染構建模式

const EasyWebpack = require(easywebpack);nclass WebpackServerBuilder extends EasyWebpack.WebpackServerBuilder {nconstructor(config) {nsuper(config);n// call below api custom server buildern}n}nmodule.exports = WebpackServerBuilder;n

具體實現請參考 easyebpack-vue`,`easyebpack-react, easyebpack-weex

8.命令行工具

8.1 easywebpack-cli 命令行工具

  • Vue, React, Weex 骨架項目初始化工具, 支持純前端項目和Egg項目
  • 提供命令行 easywebpack 或 easy 命令編譯項目和啟動靜態功能
  • 依賴 webpack-tool 工具構建

8.1.1 特性

  • 支持Vue,React, Weex Webpack 編譯和Server功能
  • 支持Vue,React, Weex easywepback-cli 配置初始化easywebpack-cli-template
  • 支持Vue,React, Weex webpack config build 配置初始化easywebpack-cli-template
  • 支持Vue,React, Weex client render boilerplate 項目初始化easywebpack-cli-template
  • 支持Vue,React server side boilerplate 項目初始化egg-vue-webpack-boilerplate, egg-react-webpack-boilerplate

8.1.2 安裝

$ npm i easywebpack-cli -gn

8.1.3 運行

easywebapck -hn

Usage: easywebpack [command] [options]

Options:

-V, --version output the version number

-f, --filename [path] webpack config file name, default webpack.config.js

-w, --watch webpack watch and hot-update

-m, --hash webpack md5 hash js/css/image

-c, --compress webpack compress js/css/image

-b, --build [option] w(watch), m(hash) , c(compress), ex: wm/wc/mc/wmc

-h, --help output usage information

Commands:

init [options] init webpack config or boilerplate for Vue/React/Weex

install npm install

print [env] [options] print webpack config, support print by env or config node key

build [env] webpack building

server [env] webpack building and start server

8.1.4. 命令介紹

1. 配置模板和Boilerplate初始化

  • easywebpack init

step one:

step two:

2. 編譯舉例

  • easywebpack build
  • easywebpack build -f build/webpack.config.js
  • easywebpack build -c
  • easywebpack build dev
  • easywebpack build test
  • easywebpack build prod
  • easywebpack build -b wmc

默認讀取項目根目錄下的 webpack.config.js 配置

3. 編譯和啟動服務舉例

  • easywebpack server
  • easywebpack server -f build/webpack.config.js
  • easywebpack server dev
  • easywebpack server test
  • easywebpack server prod
  • easywebpack server -b wmc

默認讀取項目根目錄下的 webpack.config.js 配置

4. 列印配置

easywebpack print -hn

Usage: print [env] [options]

print webpack config, support print by env or config node key

Options:

-n, --node [key] print webpack config info by config node key, example: [module/module.rules/plugins] and so on

-h, --help output usage information

  • easywebpack print -n module
  • easywebpack print dev -n entry
  • easywebpack print test -n module.rules
  • easywebpack print prod -n module.rules[0]
  • easywebpack print -n plugins
  • easywebpack print -n plugins[0]
  • easywebpack print -n output
  • easywebpack print -n resolve

默認讀取項目根目錄下的 webpack.config.js 配置

GitHub:github.com/hubcarl/easy

8.2 webpack-tool 命令行工具

webpack-tool 是一個純粹的 Webpack 構建工具, 不依賴任何框架, 支持以下特性:

  • 提供 Webpack 配置編譯功能
  • 提供 Webpack 編譯結果文件UI視圖導航和訪問功能

使用

//build/index.jsnconst WebpackTool = require(webpack-tool);nconst weexNativeConfig = require(./weex/native);nconst weexWebConfig = require(./weex/web);nconst NODE_ENV = process.env.VIEW;nconst webpackConfig = [weexNativeConfig, weexWebConfig];nconst webpackTool = new WebpackTool();nif (NODE_ENV === development) {n // start webpack build and show build result ui viewn webpackTool.server(webpackConfig);n} else {n webpackTool.build(webpackConfig);n}n

9.項目構建方案配置案例

9.1 純前端項目配置和構建

假如要實現基於 Vue 或者 React 實現一個純前端渲染項目改如何配置webpack.config.js.

9.1.1 項目結果要求如下:

  • 支持單頁面和多頁面entry配置
  • 支持根據 .vue 或者 .react 構建入口文件
  • 支持根據目錄遍歷, 項目page根目錄為 page
  • 支持熱更新,支持css extract, 支持 css和sass(默認支持),支持構建預覽
  • 支持cdn配置,支持構建完成回調用於編寫自定義邏輯
  • 支持公共文件抽取,抽取文件默認為 vendor.js
  • 支持 es6 編寫
  • 支持js/css/image壓縮和hash
  • 支持eslint,babel, postcss
  • 支持dev(不壓縮,無hash,支持熱更新)和 prod(壓縮hash,css exteract) 配置

9.1.2 基於 easywebpack-vue 和 easywebpack-cli

上面的要求 easywebpack-vue 都支持,其中 支持eslint,babel, postcss 和 autoprefixer是默認開啟

  • 按照上面的截圖新建好項目,或者可以通過 easywebpack-ci 工具初始化完成
  • 項目安裝 easywebpack-vue 解決方案依賴

npm install easywebpack-vue --save-devn

  • 編寫 ${project}/webpack.config.js配置

const BUILD_ENV = process.env.BUILD_ENV;nconst cdn = BUILD_ENV === prod ? { url: http://your.cdn.com} : ,nmodule.exports = {n type: client, // 指定只構建前端渲染n framework: vue, // 支持 react/weexn entry: {n include: page,n exclude: [page/test],n template: view/layout.htmln }n alias: {n asset: asset,n component: component,n framework: framework,n store: storen },n cdn,n done(){ // 編譯完成n // 這裡可以做你想做的事情喲,比如 打包上傳 CDNntif(cdn && cdn.url){nt}n }n}n

只需要配上面這麼多, 就可以 Running 了,因為 easywebpack-vue 把 babel,postcss,sass都默認支持了,當然你可以擴展。

  • 命令行編譯

首先請安裝 easywebpack-ci 工具, 然後就可以用 easywebpack 或 easy 命令

npm install easywebpack-ci -g

  • 命令行啟動運行

easywebpack server 或 easywebpack server devneasywebpack server prodn

  • 命令行編譯,默認開發模式

easywebpack build 或 easywebpack build devneasywebpack build prodn

  • 獲取 Webpack 配置結果

const EasyWebpack = require(easywebpack-vue);nconst webpackConfigList = EasyWebpack.getWebpackConfig()n

9.1.3 基於 easywebpack-react 和 easywebpack-cli

如果要用react實現類似功能, 請把上面 framework 改為 react

9.2 Vue/React Server Side Render 配置

如果要基於上面的要求實現一個 Vue /React 服務端渲染的構建配置, 該如何配置,非常簡單。

9.2.1 構建配置

  • copy 一份上面的配置
  • 去掉 type:client 配置
  • 如果 react,framework配置改為 framework: react, 另外安裝 easywebpack-react 依賴
  • 添加 jsx file loader template, 請查看配置舉例。

loader: {n client: framework/vue/entry/client-loader.js,n server: framework/vue/entry/server-loader.jsn}n

完整結構如下:

const BUILD_ENV = process.env.BUILD_ENV;nconst cdn = BUILD_ENV === prod ? { url: http://your.cdn.com} : ,nmodule.exports = {n framework: vue, // 支持 react/weexn entry: {n include: page,n exclude: [page/test],n template: view/layout.html,n loader: {n client: framework/vue/entry/client-loader.js,n server: framework/vue/entry/server-loader.jsn }n }n alias: {n asset: asset,n component: component,n framework: framework,n store: storen },n cdn,n done(){ // 編譯完成n // 這裡可以做你想做的事情喲,比如 打包上傳 CDNn if(cdn && cdn.url){n }n }n}n

9.2.2 結合 Vue vue-server-renderer 做服務端渲染, 核心代碼如下:

const renderer = require(vue-server-renderer);n// filepath 為 Webpack 構建的服務端代碼nconst bundleRenderer = renderer.createBundleRenderer(filepath, renderOptions);n// data 為 Node端獲取到的數據nconst context = { state: data };nreturn new Promise((resolve, reject) => {n bundleRenderer.renderToString(context, (err, html) => {n if (err) {n reject(err);n } else {n resolve(html);n }n});n

請參考 egg-view-vue 和 egg-view-vue-ssr 實現: egg-view-vue 和 egg-view-vue-ssr

拿到服務端渲染的 html 後,可以根據 manifest 資源依賴注入 css,js 等依賴,實際項目這裡要考慮緩存。完整的基於 koa, express 項目請參考下面要介紹的 Egg + Vue 服務端渲染實現。

9.2.3 結合 React react-dom/server 做服務端渲染, 核心代碼如下:

const React = require(react);nconst ReactDOMServer = require(react-dom/server);nconst reactClass = require(name);nreturn ReactDOMServer.renderToString(React.createElement(reactClass, locals))請參考 egg-view-react egg-view-react-ssr 實現 egg-view-react egg-view-react-ssrn

拿到服務端渲染的 html 後,可以根據 manifest 資源依賴注入 css,js 等依賴,實際項目這裡要考慮緩存。完整的基於 koa, express 項目請參考下面要介紹的 Egg + React 服務端渲染實現。

9.3 Egg + Vue 服務端渲染(Server Side Render)配置

如果要基於上面的要求實現一個 Egg + Vue 服務端渲染的構建配置, 我們服務端渲染的配置基礎上面增加 egg: true 配置即可。

9.3.1 完整配置結構如下:

const BUILD_ENV = process.env.BUILD_ENV;nconst cdn = BUILD_ENV === prod ? { url: http://your.cdn.com} : ,nnmodule.exports = {n egg: true,n framework: vue, // 支持 react/weexn entry: {n include: page,n exclude: [page/test],n template: view/layout.html,n loader: {n client: framework/vue/entry/client-loader.js,n server: framework/vue/entry/server-loader.jsn }n }n alias: {ntasset: asset,ntcomponent: component,ntframework: framework,ntstore: storen },n cdn,n done(){ // 編譯完成nt// 這裡可以做你想做的事情喲,比如 打包上傳 CDNntif(cdn && cdn.url){nt}n }n}n

9.3.2 安裝相關配置插件

  • easywebpack-vue Webpack building solution for Vue
  • egg-view-vue egg view plugin for vue.
  • egg-view-vue-ssr vue server side render solution for egg-view-vue.
  • egg-webpack webpack dev server plugin for egg, support read file in memory and hot reload.
  • egg-webpack-vue egg webpack building solution for vue.

項目骨架: egg-vue-webpack-boilerplate

9.4 Egg + React 服務端渲染(Server Side Render)配置

如果要基於上面的要求實現一個 Egg + React 服務端渲染的構建配置,我們服務端渲染的配置基礎上面增加 egg: true即可。

完整結構如下:

const BUILD_ENV = process.env.BUILD_ENV;nconst cdn = BUILD_ENV === prod ? { url: http://your.cdn.com} : ,nmodule.exports = {n egg: true,n framework: react, // 支持 react/weexn entry: {n include: page,n exclude: [page/test],n template: view/layout.html,n loader: {ntclient: framework/vue/entry/client-loader.js,ntserver: framework/vue/entry/server-loader.jsn }n },n alias: {n asset: asset,n component: component,n framework: framework,n store: storen },n cdn,n done(){ // 編譯完成n // 這裡可以做你想做的事情喲,比如 打包上傳 CDNn if(cdn && cdn.url){n }n }n}n

骨架項目請見: egg-react-webpack-boilerplate

9.5 Weex Native 和 Web 雙端模式構建

假如要實現基於 Weex + Vue 構建配置項目該如何配置 webpack.config.js. 除了上面的要求外,還需要支持 Native 和 Web 構建。 基於 easywbpack-weex 配置也非常簡單, 只需要 把 framework 配置為 weex 即可。

  • 首先安裝 easywbpack-weex

npm i easywbpack-weex --save-devn

  • webpack.config.js 配置如下:

module.exports = {n egg: true,n framework: weex,n entry: {ntinclude: page,ntexclude: [page/test],nttemplate: view/layout.htmln }tn alias: {ntasset: asset,ntcomponent: component,ntframework: framework,ntstore: storen },n done(){ // 編譯完成n // 這裡可以做你想做的事情喲n }n}n

  • 獲取 Webpack Config 配置

const EasyWebpack = require(easywebpack-weex);nconst webpackConfigList = EasyWebpack.getWebpackConfig()n

  • 開發運行

easy server devneasy server testneasy server prodn

  • 編譯

easy build devneasy build testneasy build prodn

骨架項目請見: easywebpack-weex-boilerplate

文檔: hubcarl.github.io/easyw

推薦閱讀:

你的Tree-Shaking並沒什麼卵用
Webpack 之 Loader 的使用
基於 Webpack 3 的 Vue.js 工程項目腳手架
阿里雲前端工程化方案dawn
webpack真的適合SPA么?

TAG:webpack | 前端工程化 | vue |