基於 Webpack 3 的 Vue.js 工程項目腳手架

基於 Webpack 3 的 Vue.js 工程項目腳手架從屬於筆者的 Web 前端入門與工程實踐,是筆者基於兄弟項目 React 腳手架改造而來,二者在 Webpack 配置層面差異不大。更多關於 Vue.js 或者前端開發相關的資料鏈接可以參考Vue.js 學習與實踐資料索引,Vue.js 與前端工程化實踐系列文章以及 Webpack 學習與資料索引,對於其中淺薄的工程化的思考可以參考 2016-我的前端之路:工具化與工程化。

基於 Webpack 3 的 Vue.js 工程項目腳手架

create-webpack-app 是筆者對於日常工作中的基於 React/Vue.js 技術棧與實踐的沉澱,dev-config/* 與 package.json 構成了基礎的腳手架,支持最新的開發流程與默認的生產環境優化;模板項目包含特性如下:

  • 技術棧支持:使用 ES6/ES7 語法、允許使用 CSS Modules、SCSS、Less 並且使用 PostCSS 進行自動 Polyfill、支持使用 styled-component 進行 CSS-in-JS 樣式控制、使用 Flow 作為靜態類型檢測工具、使用 Jest 作為默認的測試框架

  • 開發環境:使用 WebpackDevServer 部署開發伺服器、使用 React Hot Loader 進行組件熱載入、使用 Babel 進行代碼轉換、使用 ESLint 進行代碼檢測、使用 DllPlugin 作為開發環境下公共代碼提取工具以優化編譯速度

  • 生產環境:使用 CommonChunksPlugin 作為生產環境下公共代碼提取工具、使用 Prepack & prepack-webpack-plugin 進行代碼優化、使用 offline-plugin 添加簡單的 PWA 特性增強

  • 部署方式:支持獨立部署(Hash 方式切換路由)、支持服務端部署、支持服務端渲染

本部分即是針對 Vue.js 項目的腳手架,我們可以直接拷貝該項目來展示部分開發模式或者作為模板項目使用:

# 下載本項目ngit clone https://github.com/wxyyxc1992/create-webpack-appnn# 可以使用 yarn install & npm start 直接運行本項目n# 僅保留 dev-config、package.json、src/client.js、src/ssr_server.jsnmkdir /path/to/your/projectnn# 拷貝必須的啟動文件ncp -r vue/dev-config/ /path/to/your/projectncp vue/package.json /path/to/your/projectncp vue/src/client.js /path/to/your/project/src/nn# 安裝運行依賴ncd /path/to/your/projectnnyarn install / npm installnn# 有時候需要安裝 better-npm-runnnpm install better-npm-run -gnn# 啟動項目nnpm startnn# 編譯為純客戶端部署模式,即單個 HTML 頁面nnpm run buildnn# 進行依賴升級檢查nnpm run updaten

  • 開發環境下的操作面板:

  • 編譯之後的包體分析:

本模板相較於官方的 webpack-simple 功能稍微複雜,並且引入了完整的 Flow、Jest 等技術棧配置,同時優化了 Webpack 的構建性能。

!Important! 項目尚未支持 SSR

基礎配置

create-webpack-app 默認的應用配置位於 dev-config/apps.config.js 文件中,該文件也是 dev-config/ 文件夾下唯一與應用業務相關的文件;該文件定義了不同應用中的需要配置的應用相關信息。create-webpack-app 定位為單項目多應用的模板,因此我們可以在apps 鍵下配置項目設計的應用入口;在打包時會自動將多個應用並行編譯並且提取出所有公共的代碼。每個應用需要提供唯一編號、入口文件地址、模板頁面、是否編譯等信息;接下來 devServer 則是定義了當前正在開發的應用入口,ssrServer 定義了打包時需要使用的渲染伺服器入口,其會在執行 npm run build:ssr 時調用,proxy 與 api 則定義了後端伺服器信息,開發者可以根據業務需求自行使用。典型的 apps.config.js 文件配置如下:

module.exports = {n //基本的應用配置信息n apps: [n //HelloWorldn {n id: "pwa",n src: "./pwa/client.js",n indexPage: defaultIndexPage,n compiled: truen }n ],nn //開發入口配置n devServer: {n appEntrySrc: "./pwa/client.js", //當前待調試的APP的入口文件n port: 3000 //監聽的Server埠n },nn //用於服務端渲染的Server路徑n ssrServer: {n serverEntrySrc: "./pwa/ssr_server.js"n },nn //依賴項配置n proxy: {n //後端伺服器地址 http://your.backend/n "/api/*": "http://localhost:3001"n },nn //後端 api 配置,這樣配置可以避免將測試伺服器埠暴露出去n api: {n dev: {},n prod: {}n }n};n

應用入口文件則遵循官方的單文件組件範式:

import Vue from vue;nimport App from ./application/App.vue;nnnew Vue({n el: #root,n render: h => h(App)n});n

腳本編譯與熱載入

在 dev-config/webpack/loaders.js 文件中定義了模板所需要的載入器,默認支持 js、jsx、vue、ts、tsx、css、scss、less、json 以及各種資源文件等常見格式。當我們執行 npm start 命令時,會自動啟動dev-config/server/devServer.js 文件中定義的 Webpack 開發伺服器,該伺服器會使用 dev-config/webpack.config.js 文件進行配置項生成。值得一提的是,WebpackDevServer 中的 contentBase 設置為了 path.join(__dirname, "../../public"),也就是將 /public 目錄作為開發伺服器的默認根目錄。熱載入配置包括以下步驟:

  • 開發時應用入口設置:

entry = [n `webpack-dev-server/client?http://0.0.0.0:${appsConfig.devServer.port}`,n "webpack/hot/only-dev-server",n require("./apps.config.js").devServer.appEntrySrcn ];n

  • Babel 配置,默認的 Babel 文件位於 dev-config/tool/.babelrc:

...n "plugins": [n ...n

樣式處理

create-webpack-app 支持 SCSS、CSS Modules 以及 styled-components 這三種樣式定義方式。

Webpack 性能優化

公共代碼分割

create-webpack-app 使用了 CommonsChunkPlugin 進行代碼分割,默認在 dev-config/webpack/plugins.js 文件中定義了對於 node_modules 中依賴文件的自動抽取:

new webpack.optimize.CommonsChunkPlugin({n name: "vendor",n filename: "vendor.bundle.js",n minChunks: ({ resource }) =>n resource &&n resource.indexOf("node_modules") >= 0 &&n resource.match(/.(js|less|scss)$/)n })n

該插件會自動生成 vendor.bundle.js 文件,我們需要在應用入口文件之前引用它;開發者也可以自定義 CommonsChunkPlugin 插件以自定義需要提取的公共代碼。

構建性能優化

隨著項目複雜度與體量的增加,我們發現初始化編譯與增量編譯的速度都有所下降,為了提升構建性能首先我們要做的就是保持 Webpack 版本的更新速度;此外,create-webpack-app 還默認啟動了 DllPlugin 在開發狀態下將所有的依賴提取為 dll 文件以提高增量編譯的速度。因為考慮到靈活性,即隨時有可能增減依賴的情況,create-webpack-app 目前設置的是每次使用 npm start 的時候都會重新生成 dll 文件;如果是已經穩定的項目可以考慮僅生成一次依賴。

const path = require("path");nconst pkg = require("../package.json");nconst webpack = require("webpack");nnlet dllConfig = {n name: "vendor",n entry: Object.keys(pkg.dependencies),n output: {n path: path.resolve(__dirname, "../public/dll"),n filename: "vendor.bundle.js",n library: "vendor_[hash]"n },n plugins: [n new webpack.DllPlugin({n name: "vendor_[hash]",n path: path.resolve(__dirname, "../public/dll/manifest.json")n })n ]n};nnmodule.exports = dllConfig;nn// 在 public/index.html 文件中需要引入該依賴n// index.htmln<script src="dll/vendor.bundle.js"></script>n

代碼編譯優化

create-webpack-app 中也內置了其他的編譯之後的代碼性能優化插件,首先是利用 Webpack 3 的 Scope Hositing 特性來優化生成的模塊;這一點需要使用 ModuleConcatenationPlugin 插件。此外,還使用了 PrepackWebpackPlugin 對於打包生成的文件進行過濾與重構;不過需要注意的是 PrepackWebpackPlugin 會較大地降低編譯速度,因此也是可以根據實際的項目情況選用。

// 使用 Scope Hositing 特性n new webpack.optimize.ModuleConcatenationPlugin(),nn // 使用 Prepack 優化包體大小n // 暫時存在 Bug,等待修復n // 使用前 21 - 425n // 使用後 21 - 433n new PrepackWebpackPlugin({n mathRandomSeed: "0"n }),n

PWA

create-webpack-app 中只是簡單地使用了 Offline Plugin,其配置如下:

// webpack.config.js examplennvar OfflinePlugin = require(offline-plugin);nnmodule.exports = {n // ...nn plugins: [n // ... other pluginsn // its always better if OfflinePlugin is the last plugin addedn new OfflinePlugin()n ]n // ...n}nn// render.jsnrequire(offline-plugin/runtime).install();n

觀察網路面板中的資源請求情況,我們可以看到腳本等已經被緩存在了本地:

設計模式

JavaScript 優先

本小節僅代表本人個人意見,請多多指點

官方文檔中介紹的單文件組件(Single File Component)包含了 template、script、style 這三個部分,筆者感覺算是典型的前端項目三要素。不過筆者習慣的開發模式是以 JavaScript 為中心,即 JavaScript 文件單獨可測試,而不是和樣式以及標籤混合在一起(JSX 本質上也是 JavaScript)。因此在該模板中,筆者是將標籤、樣式與腳本分到了三個文件中:

// App.jsn// @flownnexport default {n name: "app",n data() {n return {n msg: "Vue.js & Webpack App Boilerplate by 王下邀月熊"n };n }n};nn// App.scssn#app {n font-family: Avenir, Helvetica, Arial, sans-serif;n -webkit-font-smoothing: antialiased;n -moz-osx-font-smoothing: grayscale;n text-align: center;n color: #2c3e50;n margin-top: 60px;n}nnh1, h2 {n font-weight: normal;n}nnul {n list-style-type: none;n padding: 0;n}nnli {n display: inline-block;n margin: 0 10px;n}nna {n color: #42b983;n}nn// App.vuen<template>n <div id="app">n <img src="../../public/assets/logo.png">n <h1>{{ msg }}</h1>n <h2>Essential Links</h2>n <ul>n <li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>n <li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>n <li><a href="https://gitter.im/vuejs/vue" target="_blank">Gitter Chat</a></li>n <li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>n </ul>n <h2>Ecosystem</h2>n <ul>n <li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>n <li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>n <li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>n <li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>n </ul>n </div>n</template>nn<script>n import App from ./App.js;n export default App;n</script>nn<style lang="scss">n @import "App";n</style>n

筆者個人感覺這種模式更符合單一職責原則,對於複雜的組件能夠提高代碼可讀性;同時將 JavaScript 代碼獨立出來也能更加方便地進行單元測試與類型檢測等操作。

代碼風格

詳細的 JavaScript 編程樣式指南已經遷移到了 Web 項目開發風格指南與 JavaScript 編程樣式指南,涵蓋了基本原則闡述、代碼風格、代碼格式化與語法檢測、項目架構等幾個部分。不過本部分建議是類似於 Create React APP 配置提交時自動進行格式化,首先需要安裝如下依賴:

npm install --save husky lint-staged prettiern// ornyarn add husky lint-staged prettiern

然後在 package.json 中添加 Hook:

"scripts": {n "precommit": "lint-staged",n ...n

同時添加 lint-staged 配置:

"dependencies": {n // ...n },n+ "lint-staged": {n+ "{src,stories}/**/*.{js,jsx,json,css}": [n+ "prettier --single-quote --write",n+ "git add"n+ ]n+ },n "scripts": {n

這樣當我們提交代碼時就會自動使用 Prettier 優化代碼,不過需要注意的是這種配置僅作用於根目錄下;如果某個倉庫中包含了多個應用配置,那麼我們還需要在根目錄下單獨配置腳本。我們也可以使用 ./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx}" 來手動進行項目文件的格式化。另外因為模板項目時放置在了子文件下,如果使用者希望使用該特性需要手動地在 package.json 中添加 "precommit": "lint-staged" 這個配置。


推薦閱讀:

阿里雲前端工程化方案dawn
webpack真的適合SPA么?
webpack如何全局引入jquery和插件?
徹底解決Webpack打包性能問題
webpack技術講解及入門

TAG:Web开发 | webpack | Vuejs |